From 7f2598d0d68a85056ae02c8e554201d33acad84d Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 8 Jul 2026 08:50:47 +0200 Subject: [PATCH 01/89] POC --- CMakeLists.txt | 2 +- .../shaders/110/texture_displacement_bump.fs | 81 + .../shaders/110/texture_displacement_bump.vs | 30 + .../shaders/140/texture_displacement_bump.fs | 92 + .../shaders/140/texture_displacement_bump.vs | 32 + src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Model.cpp | 5 + src/libslic3r/Model.hpp | 100 +- src/libslic3r/TextureDisplacement.cpp | 226 + src/libslic3r/TextureDisplacement.hpp | 120 + src/slic3r/CMakeLists.txt | 4 + src/slic3r/GUI/GLShadersManager.cpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 3 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 362 ++ .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 70 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 12 + src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 88 + .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 51 + .../GUI/generated/Invalidation_generated.cpp | 255 + .../GUI/generated/OptionKeys_generated.cpp | 61 + .../generated/Preset_options_generated.cpp | 726 +++ .../generated/PrintConfigDef_generated.cpp | 5716 +++++++++++++++++ .../GUI/generated/TabLayout_generated.cpp | 973 +++ tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_texture_displacement.cpp | 136 + 26 files changed, 9144 insertions(+), 7 deletions(-) create mode 100644 resources/shaders/110/texture_displacement_bump.fs create mode 100644 resources/shaders/110/texture_displacement_bump.vs create mode 100644 resources/shaders/140/texture_displacement_bump.fs create mode 100644 resources/shaders/140/texture_displacement_bump.vs create mode 100644 src/libslic3r/TextureDisplacement.cpp create mode 100644 src/libslic3r/TextureDisplacement.hpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp create mode 100644 src/slic3r/GUI/generated/Invalidation_generated.cpp create mode 100644 src/slic3r/GUI/generated/OptionKeys_generated.cpp create mode 100644 src/slic3r/GUI/generated/Preset_options_generated.cpp create mode 100644 src/slic3r/GUI/generated/PrintConfigDef_generated.cpp create mode 100644 src/slic3r/GUI/generated/TabLayout_generated.cpp create mode 100644 tests/libslic3r/test_texture_displacement.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bd97d61cb..a5fbc8d4c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -945,7 +945,7 @@ if (WIN32) endif () endif () set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Orca Slicer is an open source slicer for FDM printers") -set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer/OrcaSlicer") +set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer_3/OrcaSlicer") set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) set (CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/images\\\\OrcaSlicer.ico") set (CPACK_NSIS_MUI_ICON "${CPACK_PACKAGE_ICON}") diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs new file mode 100644 index 0000000000..e39a484e3a --- /dev/null +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -0,0 +1,81 @@ +#version 110 + +// See resources/shaders/140/texture_displacement_bump.fs for full documentation; this is the +// GLSL 1.10 compatibility variant (same logic, older syntax). + +#define INTENSITY_CORRECTION 0.6 + +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform vec4 uniform_color; +uniform bool volume_mirrored; + +uniform mat4 view_model_matrix; +uniform mat3 view_normal_matrix; + +uniform sampler2D height_tex; +uniform vec2 height_tex_texel; +uniform float depth_mm; +uniform float tiling_scale; +uniform float rotation_rad; +uniform vec2 uv_offset; +uniform bool invert; + +varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying vec4 world_pos; +varying float weight; + +vec2 project_uv(vec3 p, vec3 n) +{ + vec3 an = abs(n); + vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; + float cs = cos(rotation_rad); + float sn = sin(rotation_rad); + return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; +} + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + if (volume_mirrored) + triangle_normal = -triangle_normal; + + if (weight > 0.0) { + vec2 uv = project_uv(model_pos.xyz, triangle_normal); + float hL = texture2D(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; + float hR = texture2D(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; + float hD = texture2D(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; + float hU = texture2D(height_tex, uv + vec2(0.0, height_tex_texel.y)).r; + float sign_mul = invert ? -1.0 : 1.0; + vec3 bumped_normal = normalize(triangle_normal + sign_mul * depth_mm * vec3(hL - hR, hD - hU, 0.0)); + triangle_normal = normalize(mix(triangle_normal, bumped_normal, clamp(weight, 0.0, 1.0))); + } + + vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + vec2 intensity = vec2(0.0); + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (view_model_matrix * model_pos).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/110/texture_displacement_bump.vs b/resources/shaders/110/texture_displacement_bump.vs new file mode 100644 index 0000000000..14a42ac0b1 --- /dev/null +++ b/resources/shaders/110/texture_displacement_bump.vs @@ -0,0 +1,30 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +uniform mat4 volume_world_matrix; +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; +// Per-vertex paint weight for the currently active texture-displacement layer, 0..1. +attribute float v_weight; + +varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying vec4 world_pos; +varying float weight; + +void main() +{ + model_pos = vec4(v_position, 1.0); + world_pos = volume_world_matrix * model_pos; + + gl_Position = projection_matrix * view_model_matrix * model_pos; + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + weight = v_weight; +} diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs new file mode 100644 index 0000000000..e023e43d74 --- /dev/null +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -0,0 +1,92 @@ +#version 140 + +// Fast, geometry-free preview of texture displacement: perturbs the *shading* normal from the +// height texture's local gradient (a bump map), faded out by the per-vertex paint weight. Used +// while the brush is actively dragging (see project plan, "Preview shaders" section); the true, +// exact result is what "Bake" produces via libslic3r/TextureDisplacement.cpp on the CPU. +// +// NOTE: the gradient-to-normal conversion below assumes the two planar-projection axes chosen by +// project_uv() are reasonably aligned with the surface here; it is a cheap approximation (no +// tangent/bitangent basis is reconstructed on the GPU), acceptable for a live preview but not +// intended to be pixel-exact versus the CPU-side bake. + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform vec4 uniform_color; +uniform bool volume_mirrored; + +uniform mat4 view_model_matrix; +uniform mat3 view_normal_matrix; + +uniform sampler2D height_tex; +uniform vec2 height_tex_texel; // (1/width, 1/height) of height_tex +uniform float depth_mm; +uniform float tiling_scale; +uniform float rotation_rad; +uniform vec2 uv_offset; +uniform bool invert; + +in vec3 clipping_planes_dots; +in vec4 model_pos; +in vec4 world_pos; +in float weight; + +out vec4 out_color; + +vec2 project_uv(vec3 p, vec3 n) +{ + vec3 an = abs(n); + vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; + float cs = cos(rotation_rad); + float sn = sin(rotation_rad); + return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; +} + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + if (volume_mirrored) + triangle_normal = -triangle_normal; + + if (weight > 0.0) { + vec2 uv = project_uv(model_pos.xyz, triangle_normal); + float hL = texture(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; + float hR = texture(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; + float hD = texture(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; + float hU = texture(height_tex, uv + vec2(0.0, height_tex_texel.y)).r; + float sign_mul = invert ? -1.0 : 1.0; + vec3 bumped_normal = normalize(triangle_normal + sign_mul * depth_mm * vec3(hL - hR, hD - hU, 0.0)); + triangle_normal = normalize(mix(triangle_normal, bumped_normal, clamp(weight, 0.0, 1.0))); + } + + vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + vec2 intensity = vec2(0.0); + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (view_model_matrix * model_pos).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + out_color = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/140/texture_displacement_bump.vs b/resources/shaders/140/texture_displacement_bump.vs new file mode 100644 index 0000000000..636cdf525c --- /dev/null +++ b/resources/shaders/140/texture_displacement_bump.vs @@ -0,0 +1,32 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +uniform mat4 volume_world_matrix; +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +in vec3 v_position; +// Per-vertex paint weight for the currently active texture-displacement layer, 0..1 (see +// TriangleSelectorWeighted... note: Phase 1 stores this as a plain enforced/not-enforced mask, +// so this is 0.0 or 1.0 today; a continuous value is a natural later-phase extension). +in float v_weight; + +out vec3 clipping_planes_dots; +out vec4 model_pos; +out vec4 world_pos; +out float weight; + +void main() +{ + model_pos = vec4(v_position, 1.0); + world_pos = volume_world_matrix * model_pos; + + gl_Position = projection_matrix * view_model_matrix * model_pos; + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + weight = v_weight; +} diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 93946ca477..fb4b251c99 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -442,6 +442,8 @@ set(lisbslic3r_sources Tesselate.cpp Tesselate.hpp TextConfiguration.hpp + TextureDisplacement.cpp + TextureDisplacement.hpp Thread.cpp Thread.hpp Time.cpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 1177a5227d..b6bb2762a2 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1977,6 +1977,11 @@ void ModelVolume::reset_extra_facets() this->seam_facets.reset(); this->mmu_segmentation_facets.reset(); this->fuzzy_skin_facets.reset(); + // Texture-displacement paint data has no remap-across-topology-change support yet (see + // build_texture_displacement()'s documented limitation), so it must be dropped here rather + // than left referring to a mesh that no longer matches it. + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + this->texture_displacement_facet(i).reset(); } std::optional ModelVolume::save_painting() const diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b15124d9ed..61c7f3f2f1 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -19,6 +19,7 @@ #include "TextConfiguration.hpp" #include "EmbossShape.hpp" #include "TriangleSelector.hpp" +#include "TextureDisplacement.hpp" //BBS: add bbs 3mf #include "Format/bbs_3mf.hpp" @@ -28,6 +29,7 @@ #include "Format/STL.hpp" #include "Format/OBJ.hpp" +#include #include #include #include @@ -878,6 +880,64 @@ public: // List of mesh facets painted for fuzzy skin. FacetsAnnotation fuzzy_skin_facets; + // One independent paint mask per texture-displacement layer slot (see texture_displacement_layers + // below). Unlike the other facets fields above, a triangle may be painted (ENFORCER) in more + // than one of these simultaneously -- that overlap is what makes the layers "blend". + // + // These are 8 plain named fields rather than a std::array: FacetsAnnotation's + // default/copy constructors are private and friended only to ModelVolume, but std::array's own + // implicitly-defined default/copy constructors are generated with std::array's access rights, + // not ModelVolume's -- so an array of FacetsAnnotation ends up with its default/copy + // constructors implicitly deleted regardless of the friend declaration. Use + // texture_displacement_facet(slot) below for array-like indexed access. + FacetsAnnotation texture_displacement_facets_0; + FacetsAnnotation texture_displacement_facets_1; + FacetsAnnotation texture_displacement_facets_2; + FacetsAnnotation texture_displacement_facets_3; + FacetsAnnotation texture_displacement_facets_4; + FacetsAnnotation texture_displacement_facets_5; + FacetsAnnotation texture_displacement_facets_6; + FacetsAnnotation texture_displacement_facets_7; + + FacetsAnnotation& texture_displacement_facet(int slot) { + switch (slot) { + case 0: return texture_displacement_facets_0; + case 1: return texture_displacement_facets_1; + case 2: return texture_displacement_facets_2; + case 3: return texture_displacement_facets_3; + case 4: return texture_displacement_facets_4; + case 5: return texture_displacement_facets_5; + case 6: return texture_displacement_facets_6; + default: assert(slot == 7); return texture_displacement_facets_7; + } + } + const FacetsAnnotation& texture_displacement_facet(int slot) const { return const_cast(this)->texture_displacement_facet(slot); } + + // Small helpers for the constructor asserts below (kept out of line-noise at each call site). + bool texture_displacement_facets_ids_valid() const { + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + if (!texture_displacement_facet(i).id().valid() || texture_displacement_facet(i).id() == this->id()) + return false; + return true; + } + bool texture_displacement_facets_ids_invalid() const { + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + if (texture_displacement_facet(i).id().valid()) + return false; + return true; + } + bool texture_displacement_facets_all_empty() const { + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + if (!texture_displacement_facet(i).empty()) + return false; + return true; + } + + // Texture assets (height maps) and their projection/displacement parameters. Element order + // is not meaningful for baking (layers are applied in TextureDisplacementLayer::slot order, + // see build_texture_displacement()); it only reflects UI insertion order. + std::vector texture_displacement_layers; + // Save painting data before reset_extra_facets() discards it. // Used for replacing mesh without losing painting data. // Only for model parts (not modifiers/connectors). @@ -1007,13 +1067,18 @@ public: this->seam_facets.set_new_unique_id(); this->mmu_segmentation_facets.set_new_unique_id(); this->fuzzy_skin_facets.set_new_unique_id(); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + this->texture_displacement_facet(i).set_new_unique_id(); } bool is_fdm_support_painted() const { return !this->supported_facets.empty(); } bool is_seam_painted() const { return !this->seam_facets.empty(); } bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); } bool is_fuzzy_skin_painted() const { return !this->fuzzy_skin_facets.empty(); } - bool is_any_painted() const { return is_fdm_support_painted() || is_seam_painted() || is_mm_painted() || is_fuzzy_skin_painted(); } + bool is_texture_displacement_painted() const { return !this->texture_displacement_facets_all_empty(); } + bool is_any_painted() const { + return is_fdm_support_painted() || is_seam_painted() || is_mm_painted() || is_fuzzy_skin_painted() || is_texture_displacement_painted(); + } // Orca: Implement prusa's filament shrink compensation approach // Returns 0-based indices of extruders painted by multi-material painting gizmo. @@ -1066,6 +1131,7 @@ private: assert(this->seam_facets.id().valid()); assert(this->mmu_segmentation_facets.id().valid()); assert(this->fuzzy_skin_facets.id().valid()); + assert(this->texture_displacement_facets_ids_valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); @@ -1082,6 +1148,7 @@ private: assert(this->seam_facets.id().valid()); assert(this->mmu_segmentation_facets.id().valid()); assert(this->fuzzy_skin_facets.id().valid()); + assert(this->texture_displacement_facets_ids_valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); @@ -1096,6 +1163,7 @@ private: assert(this->seam_facets.id().valid()); assert(this->mmu_segmentation_facets.id().valid()); assert(this->fuzzy_skin_facets.id().valid()); + assert(this->texture_displacement_facets_ids_valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); @@ -1109,10 +1177,16 @@ private: name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets), - fuzzy_skin_facets(other.fuzzy_skin_facets), cut_info(other.cut_info), text_configuration(other.text_configuration), emboss_shape(other.emboss_shape) + fuzzy_skin_facets(other.fuzzy_skin_facets), + texture_displacement_facets_0(other.texture_displacement_facets_0), texture_displacement_facets_1(other.texture_displacement_facets_1), + texture_displacement_facets_2(other.texture_displacement_facets_2), texture_displacement_facets_3(other.texture_displacement_facets_3), + texture_displacement_facets_4(other.texture_displacement_facets_4), texture_displacement_facets_5(other.texture_displacement_facets_5), + texture_displacement_facets_6(other.texture_displacement_facets_6), texture_displacement_facets_7(other.texture_displacement_facets_7), + texture_displacement_layers(other.texture_displacement_layers), + cut_info(other.cut_info), text_configuration(other.text_configuration), emboss_shape(other.emboss_shape) { - assert(this->id().valid()); - assert(this->config.id().valid()); + assert(this->id().valid()); + assert(this->config.id().valid()); assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); assert(this->mmu_segmentation_facets.id().valid()); @@ -1162,6 +1236,8 @@ private: assert(this->seam_facets.empty()); assert(this->mmu_segmentation_facets.empty()); assert(this->fuzzy_skin_facets.empty()); + assert(this->texture_displacement_facets_all_empty()); + assert(this->texture_displacement_layers.empty()); } ModelVolume& operator=(ModelVolume &rhs) = delete; @@ -1169,13 +1245,17 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; // Used for deserialization, therefore no IDs are allocated. - ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), mmu_segmentation_facets(-1), fuzzy_skin_facets(-1), object(nullptr) { + ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), mmu_segmentation_facets(-1), fuzzy_skin_facets(-1), + texture_displacement_facets_0(-1), texture_displacement_facets_1(-1), texture_displacement_facets_2(-1), texture_displacement_facets_3(-1), + texture_displacement_facets_4(-1), texture_displacement_facets_5(-1), texture_displacement_facets_6(-1), texture_displacement_facets_7(-1), + object(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); assert(this->supported_facets.id().invalid()); assert(this->seam_facets.id().invalid()); assert(this->mmu_segmentation_facets.id().invalid()); assert(this->fuzzy_skin_facets.id().invalid()); + assert(this->texture_displacement_facets_ids_invalid()); } template void load(Archive &ar) { bool has_convex_hull; @@ -1195,6 +1275,13 @@ private: mesh_changed |= t != mmu_segmentation_facets.timestamp(); cereal::load_by_value(ar, fuzzy_skin_facets); mesh_changed |= t != fuzzy_skin_facets.timestamp(); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) { + FacetsAnnotation &f = texture_displacement_facet(i); + Timestamp tf = f.timestamp(); + cereal::load_by_value(ar, f); + mesh_changed |= tf != f.timestamp(); + } + ar(texture_displacement_layers); cereal::load_by_value(ar, config); cereal::load(ar, text_configuration); cereal::load(ar, emboss_shape); @@ -1216,6 +1303,9 @@ private: cereal::save_by_value(ar, seam_facets); cereal::save_by_value(ar, mmu_segmentation_facets); cereal::save_by_value(ar, fuzzy_skin_facets); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + cereal::save_by_value(ar, texture_displacement_facet(i)); + ar(texture_displacement_layers); cereal::save_by_value(ar, config); cereal::save(ar, text_configuration); cereal::save(ar, emboss_shape); diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp new file mode 100644 index 0000000000..54948316ae --- /dev/null +++ b/src/libslic3r/TextureDisplacement.cpp @@ -0,0 +1,226 @@ +#include "TextureDisplacement.hpp" + +#include +#include +#include +#include + +#include "Model.hpp" +#include "PNGReadWrite.hpp" +#include "TriangleSelector.hpp" + +namespace Slic3r { + +float DecodedHeightTexture::sample(const Vec2f &uv) const +{ + if (empty()) + return 0.f; + + auto wrap01 = [](float x) { + x = std::fmod(x, 1.f); + return x < 0.f ? x + 1.f : x; + }; + + const float fx = wrap01(uv.x()) * float(width); + const float fy = wrap01(uv.y()) * float(height); + int x0 = int(std::floor(fx)) % width; + int y0 = int(std::floor(fy)) % height; + if (x0 < 0) x0 += width; + if (y0 < 0) y0 += height; + const int x1 = (x0 + 1) % width; + const int y1 = (y0 + 1) % height; + const float tx = fx - std::floor(fx); + const float ty = fy - std::floor(fy); + + auto at = [this](int x, int y) { return float(pixels[size_t(y) * size_t(width) + size_t(x)]) / 255.f; }; + const float top = at(x0, y0) * (1.f - tx) + at(x1, y0) * tx; + const float bottom = at(x0, y1) * (1.f - tx) + at(x1, y1) * tx; + return top * (1.f - ty) + bottom * ty; +} + +DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer) +{ + DecodedHeightTexture result; + if (layer.empty()) + return result; + + const png::ReadBuf rbuf{ layer.image_data->data(), layer.image_data->size() }; + if (!png::is_png(rbuf)) + // Only 8-bit grayscale PNG height maps are supported. The GUI is responsible for + // converting any imported image (jpg, color png, ...) to that format on import, so this + // code never needs a dependency on wxWidgets/libjpeg to decode arbitrary user images. + return result; + + png::ImageGreyscale img; + if (!png::decode_png(rbuf, img) || img.cols == 0 || img.rows == 0) + return result; + + result.width = int(img.cols); + result.height = int(img.rows); + result.pixels = std::move(img.buf); + return result; +} + +Vec2f project_texture_displacement_uv(const Vec3f &position, const Vec3f &normal, const TextureDisplacementLayer &layer) +{ + // Planar-project onto the two axes orthogonal to the dominant component of `normal`. Using a + // single projection axis for an entire patch (rather than per-vertex normals) avoids visible + // seams where the local normal direction changes. Proper seam-aware UV parametrization is a + // later-phase improvement. + const Vec3f n = normal.cwiseAbs(); + Vec2f planar; + if (n.x() >= n.y() && n.x() >= n.z()) + planar = Vec2f(position.y(), position.z()); + else if (n.y() >= n.x() && n.y() >= n.z()) + planar = Vec2f(position.x(), position.z()); + else + planar = Vec2f(position.x(), position.y()); + + const float scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f; + planar *= scale; + + const float rad = layer.rotation_deg * float(M_PI) / 180.f; + const float cs = std::cos(rad); + const float sn = std::sin(rad); + const Vec2f rotated(planar.x() * cs - planar.y() * sn, planar.x() * sn + planar.y() * cs); + + return rotated + layer.offset; +} + +// Area-weighted vertex normals computed from the patch's own (pre-displacement) connectivity. +// Since the patch's vertices have not moved yet at the point this is called, these are identical +// to the surface normals of the mesh the patch was extracted from. +static std::vector texture_displacement_vertex_normals(const indexed_triangle_set &its) +{ + std::vector normals(its.vertices.size(), Vec3f::Zero()); + for (const stl_triangle_vertex_indices &tri : its.indices) { + const Vec3f &a = its.vertices[tri[0]]; + const Vec3f &b = its.vertices[tri[1]]; + const Vec3f &c = its.vertices[tri[2]]; + // Cross product magnitude is twice the face area, so this naturally area-weights the + // contribution of each incident face to its vertices. + const Vec3f area_weighted_normal = (b - a).cross(c - a); + normals[tri[0]] += area_weighted_normal; + normals[tri[1]] += area_weighted_normal; + normals[tri[2]] += area_weighted_normal; + } + for (Vec3f &n : normals) { + const float len = n.norm(); + n = (len > 1e-8f) ? Vec3f(n / len) : Vec3f::UnitZ(); + } + return normals; +} + +indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data) +{ + const indexed_triangle_set original_mesh = base_mesh; + indexed_triangle_set working_mesh = original_mesh; + bool mesh_modified = false; + + // Layers behave like stacked image-editor layers: applied in slot order, each one sculpting + // the surface left by the layers before it. This is what makes overlapping layers "blend". + std::vector ordered_layers; + for (const TextureDisplacementLayer &layer : layers) + if (!layer.empty() && layer.slot >= 0 && size_t(layer.slot) < TEXTURE_DISPLACEMENT_MAX_LAYERS) + ordered_layers.push_back(&layer); + std::sort(ordered_layers.begin(), ordered_layers.end(), + [](const TextureDisplacementLayer *a, const TextureDisplacementLayer *b) { return a->slot < b->slot; }); + + for (const TextureDisplacementLayer *layer : ordered_layers) { + const TriangleSelector::TriangleSplittingData &stored_data = facets_data[size_t(layer->slot)]; + if (stored_data.triangles_to_split.empty()) + continue; + + const DecodedHeightTexture height = decode_height_texture(*layer); + if (height.empty()) + continue; + + TriangleSelector::TriangleSplittingData data = stored_data; + if (mesh_modified) { + // The stored paint mask is relative to the volume's original (unbaked) mesh; remap it + // onto the working mesh, which earlier layers may have already displaced/subdivided. + data = TriangleSelector::remap_painting(original_mesh, data, working_mesh, Transform3d::Identity(), + std::optional>{}); + if (data.bitstream.empty()) + continue; + } + + const TriangleMesh selector_mesh(working_mesh); + TriangleSelector selector(selector_mesh); + selector.deserialize(data, false); + + const indexed_triangle_set patch = selector.get_facets_strict(EnforcerBlockerType::ENFORCER); + if (patch.indices.empty()) + continue; + // get_facets_strict() returns the *entire* mesh's vertex array regardless of which state + // was asked for (only the returned triangle indices are filtered by state) -- so `patch` + // and `rest` share the exact same vertex indexing. That is what lets the weld below be a + // plain index check instead of a position-based lookup. + indexed_triangle_set rest = selector.get_facets_strict(EnforcerBlockerType::NONE); + assert(rest.vertices.size() == patch.vertices.size()); + + // A vertex that is also used by at least one *unpainted* triangle is a boundary vertex: + // its final position is ambiguous (it belongs to both the painted and untouched surface), + // so it must never be displaced -- otherwise the baked patch would tear away from the + // rest of the mesh. Only vertices used exclusively by painted triangles ("interior" to the + // patch) are eligible for displacement. This is what keeps the bake seamless without any + // remeshing/hole-filling at the boundary. + std::vector is_boundary_vertex(rest.vertices.size(), false); + for (const stl_triangle_vertex_indices &tri : rest.indices) + for (int i = 0; i < 3; ++i) + is_boundary_vertex[tri[i]] = true; + + const std::vector vertex_normals = texture_displacement_vertex_normals(patch); + + Vec3f average_normal = Vec3f::Zero(); + for (const stl_triangle_vertex_indices &tri : patch.indices) + for (int i = 0; i < 3; ++i) + average_normal += vertex_normals[tri[i]]; + average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); + + const float sign = layer->invert ? -1.f : 1.f; + std::vector interior_vertex_remap(rest.vertices.size(), -1); + indexed_triangle_set working_mesh_next = std::move(rest); + for (const stl_triangle_vertex_indices &tri : patch.indices) { + stl_triangle_vertex_indices final_tri; + for (int i = 0; i < 3; ++i) { + const int vi = tri[i]; + if (is_boundary_vertex[vi]) { + // Shared with the untouched surface: reuse the existing, undisplaced vertex. + final_tri[i] = vi; + continue; + } + if (interior_vertex_remap[vi] < 0) { + const Vec2f uv = project_texture_displacement_uv(patch.vertices[vi], average_normal, *layer); + const float h = height.sample(uv); + const Vec3f displaced = patch.vertices[vi] + vertex_normals[vi] * (h * layer->depth_mm * sign); + interior_vertex_remap[vi] = int(working_mesh_next.vertices.size()); + working_mesh_next.vertices.push_back(displaced); + } + final_tri[i] = interior_vertex_remap[vi]; + } + working_mesh_next.indices.push_back(final_tri); + } + + working_mesh = std::move(working_mesh_next); + mesh_modified = true; + } + + if (mesh_modified) + its_compactify_vertices(working_mesh); + + return working_mesh; +} + +indexed_triangle_set build_texture_displacement(const ModelVolume &volume) +{ + TextureDisplacementFacetsData facets_data; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); + + return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data); +} + +} // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp new file mode 100644 index 0000000000..4097bcd946 --- /dev/null +++ b/src/libslic3r/TextureDisplacement.hpp @@ -0,0 +1,120 @@ +#ifndef slic3r_TextureDisplacement_hpp_ +#define slic3r_TextureDisplacement_hpp_ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "Point.hpp" +#include "TriangleMesh.hpp" +#include "TriangleSelector.hpp" + +namespace Slic3r { + +class ModelVolume; + +// Maximum number of simultaneous texture-displacement layers a single ModelVolume can hold. +// Each layer owns its own paint mask (ModelVolume::texture_displacement_facet(slot)), so this +// is also the number of independent EnforcerBlockerType selectors kept per volume. +static constexpr size_t TEXTURE_DISPLACEMENT_MAX_LAYERS = 8; + +// One texture asset plus its projection/displacement parameters. Several layers may be painted +// onto overlapping areas of the same volume: they are applied in slot order (like layers in an +// image editor), each one displacing the surface that resulted from the layers before it. This +// is what "layered/blended" texture displacement means in this feature. +struct TextureDisplacementLayer +{ + // Index into ModelVolume::texture_displacement_facets, assigned once when the layer is + // created. Not reused for the lifetime of the ModelVolume, so a deleted layer's slot simply + // becomes unused rather than being handed to a different layer. + int slot = -1; + + std::string name; + // Path on the local filesystem the image was loaded from (informational; may be stale or + // empty, e.g. after loading a .3mf on a different machine). + std::string path; + // Path inside the .3mf archive once saved (empty until the project is saved once). + std::string path_in_3mf; + // Raw encoded image bytes. Only 8-bit grayscale PNG is understood by decode_height_texture() + // (libslic3r has no GUI image toolkit available); the GUI converts any imported image to that + // format before storing it here, so the baking code never needs to depend on wxWidgets. + std::shared_ptr> image_data; + + float depth_mm = 0.4f; // maximum displacement along the surface normal, in mm + float tiling_scale = 10.f; // size of one texture tile, in mm + float rotation_deg = 0.f; + Vec2f offset = Vec2f::Zero(); + bool invert = false; + + bool empty() const { return !image_data || image_data->empty(); } + + template void save(Archive &ar) const + { + std::string blob = image_data ? std::string(image_data->begin(), image_data->end()) : std::string(); + ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert); + } + template void load(Archive &ar) + { + std::string blob; + ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert); + image_data = blob.empty() ? nullptr : std::make_shared>(blob.begin(), blob.end()); + } +}; + +// Decoded 8-bit grayscale height sample, independent of any GUI/OpenGL texture object so it can +// be evaluated from a background bake Job as well as from GUI-side preview code. +struct DecodedHeightTexture +{ + std::vector pixels; // row-major, top-to-bottom, one byte per pixel + int width = 0; + int height = 0; + + bool empty() const { return width <= 0 || height <= 0 || pixels.empty(); } + // Bilinearly sampled height in [0, 1] at a tiling (wrapping) normalized uv coordinate. + float sample(const Vec2f &uv) const; +}; + +// Decode a layer's raw image bytes into sampleable grayscale height data. Returns an empty +// DecodedHeightTexture if image_data is empty or is not an 8-bit grayscale PNG. +DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer); + +// Project a mesh-local position onto a layer's height texture, honouring its tiling scale, +// rotation and offset. Uses a simple dominant-axis planar projection of the given normal: proper +// seam-aware UV parametrization is a later-phase improvement (see project plan). +Vec2f project_texture_displacement_uv(const Vec3f &position, const Vec3f &normal, const TextureDisplacementLayer &layer); + +// One paint mask (as stored by ModelVolume::texture_displacement_facets) per possible layer slot. +using TextureDisplacementFacetsData = std::array; + +// Bake all painted texture-displacement layers into `base_mesh`'s geometry, restricted to the +// painted area(s) only (the rest of the mesh is left untouched). Layers are applied in slot +// order, each measuring its displacement from the surface left by the previous one. Returns the +// mesh unchanged if nothing is painted or no layer has a usable texture. +// +// Takes plain copied data rather than a ModelVolume reference so it is safe to call from a +// background thread (e.g. a bake Job's process() method) on a snapshot captured on the main +// thread, without touching the live Model concurrently with the UI. +// +// Known Phase 1-3 limitation: this does not attempt to remap texture-displacement paint data +// across topology-changing operations performed outside this gizmo (e.g. ModelObject::split(), +// mesh-boolean ops) the way TriangleSelector::remap_painting() does for the other paint channels. +// Such operations will silently drop any unbaked texture-displacement paint on the affected +// volume. This is an explicit extension point for a later phase, not an oversight. +indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data); + +// Convenience overload for main-thread callers: extracts the mesh/layers/paint data from `volume` +// and forwards to the overload above. +indexed_triangle_set build_texture_displacement(const ModelVolume &volume); + +} // namespace Slic3r + +#endif // slic3r_TextureDisplacement_hpp_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 969dcb693a..4b498a3185 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -172,6 +172,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmosManager.hpp GUI/Gizmos/GLGizmoSVG.cpp GUI/Gizmos/GLGizmoSVG.hpp + GUI/Gizmos/GLGizmoTextureDisplacement.cpp + GUI/Gizmos/GLGizmoTextureDisplacement.hpp GUI/Gizmos/GLGizmoUtils.cpp GUI/Gizmos/GLGizmoUtils.hpp #GUI/Gizmos/GLGizmoText.cpp @@ -297,6 +299,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/SLAImportDialog.hpp GUI/Jobs/SLAImportJob.cpp GUI/Jobs/SLAImportJob.hpp + GUI/Jobs/TextureDisplacementBakeJob.cpp + GUI/Jobs/TextureDisplacementBakeJob.hpp GUI/Jobs/ThreadSafeQueue.hpp GUI/Jobs/UpgradeNetworkJob.cpp GUI/Jobs/UpgradeNetworkJob.hpp diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index d368aaecd2..d602ca1e06 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -100,6 +100,8 @@ std::pair GLShadersManager::init() valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }, { "FLIP_TRIANGLE_NORMALS"sv }); else valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }); + // Fast bump-map preview for the texture displacement gizmo (see libslic3r/TextureDisplacement.hpp). + valid &= append_shader("texture_displacement_bump", { prefix + "texture_displacement_bump.vs", prefix + "texture_displacement_bump.fs" }); return { valid, error }; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 8ec1cb1fb0..ed1d232a43 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -27,7 +27,8 @@ enum class PainterGizmoType { FDM_SUPPORTS, SEAM, MM_SEGMENTATION, - FUZZY_SKIN + FUZZY_SKIN, + TEXTURE_DISPLACEMENT }; class TriangleSelectorGUI : public TriangleSelector { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp new file mode 100644 index 0000000000..021d6c31a0 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -0,0 +1,362 @@ +#include "GLGizmoTextureDisplacement.hpp" + +#include + +#include +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/PNGReadWrite.hpp" + +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp" +#include "slic3r/Utils/UndoRedo.hpp" +#include "GLGizmoUtils.hpp" + +#include +#include + +namespace Slic3r::GUI { + +GLGizmoTextureDisplacement::GLGizmoTextureDisplacement(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoPainterBase(parent, icon_filename, sprite_id) +{ +} + +bool GLGizmoTextureDisplacement::on_init() +{ + m_desc["cursor_size"] = _L("Brush size"); + m_desc["circle"] = _L("Circle"); + m_desc["sphere"] = _L("Sphere"); + m_desc["add_texture"] = _L("Add texture..."); + m_desc["remove_layer"] = _L("Remove"); + m_desc["bake"] = _L("Bake"); + m_desc["remove_all"] = _L("Erase all"); + return true; +} + +std::string GLGizmoTextureDisplacement::on_get_name() const +{ + return _u8L("Texture displacement"); +} + +void GLGizmoTextureDisplacement::on_shutdown() +{ + m_parent.toggle_model_objects_visibility(true); +} + +PainterGizmoType GLGizmoTextureDisplacement::get_painter_type() const +{ + return PainterGizmoType::TEXTURE_DISPLACEMENT; +} + +wxString GLGizmoTextureDisplacement::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const +{ + return shift_down ? _L("Erase texture displacement paint") : _L("Paint texture displacement"); +} + +void GLGizmoTextureDisplacement::render_painter_gizmo() +{ + const Selection &selection = m_parent.get_selection(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + // Phase 1 reuses the standard paint-mask overlay (as every other paint gizmo does) to show + // which area of the active layer is painted. A dedicated bump-map preview shader + // (resources/shaders/*/texture_displacement_bump.*) is registered and ready to use, but + // wiring it into this render path is deferred to a later phase -- see the project plan. + // "Bake" already shows the true, real-geometry result, which is the most important preview. + render_triangles(selection); + m_c->object_clipper()->render_cut(); + m_c->instances_hider()->render_cut(); + render_cursor(); + + glsafe(::glDisable(GL_BLEND)); +} + +ModelVolume* GLGizmoTextureDisplacement::texture_volume() +{ + ModelObject *mo = m_c->selection_info()->model_object(); + if (!mo) + return nullptr; + for (ModelVolume *mv : mo->volumes) + if (mv->is_model_part()) + return mv; + return nullptr; +} + +const ModelVolume* GLGizmoTextureDisplacement::texture_volume() const +{ + return const_cast(this)->texture_volume(); +} + +void GLGizmoTextureDisplacement::update_model_object() +{ + bool updated = false; + ModelObject *mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume *mv : mo->volumes) { + if (!mv->is_model_part()) + continue; + ++idx; + updated |= mv->texture_displacement_facet(m_active_layer_slot).set(*m_triangle_selectors[idx]); + } + + if (updated) { + const ModelObjectPtrs &mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } +} + +void GLGizmoTextureDisplacement::update_from_model_object(bool first_update) +{ + wxBusyCursor wait; + + const ModelObject *mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + + std::vector ebt_colors; + ebt_colors.push_back(GLVolume::NEUTRAL_COLOR); + ebt_colors.push_back(TriangleSelectorGUI::enforcers_color); + ebt_colors.push_back(TriangleSelectorGUI::blockers_color); + for (const ModelVolume *mv : mo->volumes) { + if (!mv->is_model_part()) + continue; + + const TriangleMesh *mesh = &mv->mesh(); + m_triangle_selectors.emplace_back(std::make_unique(*mesh, ebt_colors)); + m_triangle_selectors.back()->deserialize(mv->texture_displacement_facet(m_active_layer_slot).get_data(), false); + m_triangle_selectors.back()->request_update_render_data(); + } +} + +void GLGizmoTextureDisplacement::set_active_layer(int slot) +{ + if (slot == m_active_layer_slot) + return; + // Flush edits made while the previous layer was active before switching what the selectors + // reflect -- otherwise they would be silently lost. + update_model_object(); + m_active_layer_slot = slot; + update_from_model_object(false); +} + +void GLGizmoTextureDisplacement::add_texture_layer() +{ + ModelVolume *mv = texture_volume(); + if (!mv) + return; + + std::array used{}; + for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) + if (l.slot >= 0 && size_t(l.slot) < used.size()) + used[size_t(l.slot)] = true; + int free_slot = -1; + for (size_t i = 0; i < used.size(); ++i) + if (!used[i]) { free_slot = int(i); break; } + if (free_slot < 0) { + show_error(nullptr, _u8L("Maximum number of texture displacement layers reached.")); + return; + } + + const wxString wildcard = "Images (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp"; + wxFileDialog dialog(nullptr, _L("Choose a texture image (height map)"), wxEmptyString, wxEmptyString, wildcard, + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() != wxID_OK) + return; + + const std::string path = into_u8(dialog.GetPath()); + + wxImage image; + if (!image.LoadFile(dialog.GetPath()) || !image.IsOk()) { + show_error(nullptr, _u8L("Could not load the selected image.")); + return; + } + + // libslic3r's PNG decoder (used by the baking code, which has no wxWidgets dependency) only + // understands true 8-bit grayscale PNG. Convert and re-encode through Slic3r's own PNG writer + // here rather than relying on wxImage's PNG encoder to pick a compatible color type. + const wxImage gray = image.ConvertToGreyscale(); + const int w = gray.GetWidth(); + const int h = gray.GetHeight(); + if (w <= 0 || h <= 0) { + show_error(nullptr, _u8L("The selected image is empty.")); + return; + } + std::vector gray_pixels(size_t(w) * size_t(h)); + const unsigned char *rgb = gray.GetData(); + for (size_t i = 0; i < gray_pixels.size(); ++i) + gray_pixels[i] = rgb[i * 3]; + + const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("orca_texdisp_%%%%%%%%.png"); + if (!Slic3r::png::write_gray_to_file(tmp_path.string(), size_t(w), size_t(h), gray_pixels)) { + show_error(nullptr, _u8L("Failed to prepare the texture for use.")); + return; + } + std::vector bytes; + { + std::ifstream ifs(tmp_path.string(), std::ios::binary); + bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp_path, ec); + + if (bytes.empty()) { + show_error(nullptr, _u8L("Failed to prepare the texture for use.")); + return; + } + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Add texture displacement layer"), UndoRedo::SnapshotType::GizmoAction); + + TextureDisplacementLayer layer; + layer.slot = free_slot; + layer.name = boost::filesystem::path(path).filename().string(); + layer.path = path; + layer.image_data = std::make_shared>(std::move(bytes)); + mv->texture_displacement_layers.push_back(std::move(layer)); + + set_active_layer(free_slot); + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::remove_texture_layer(int slot) +{ + ModelVolume *mv = texture_volume(); + if (!mv) + return; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Remove texture displacement layer"), UndoRedo::SnapshotType::GizmoAction); + + auto &layers = mv->texture_displacement_layers; + layers.erase(std::remove_if(layers.begin(), layers.end(), + [slot](const TextureDisplacementLayer &l) { return l.slot == slot; }), + layers.end()); + if (slot >= 0 && slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) + mv->texture_displacement_facet(slot).reset(); + + if (m_active_layer_slot == slot) + update_from_model_object(false); + + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::bake() +{ + ModelVolume *mv = texture_volume(); + if (!mv || m_bake_in_progress) + return; + + // Make sure the currently active layer's in-progress edits are flushed into the model before + // baking, otherwise the most recent, not-yet-committed strokes would be silently skipped. + update_model_object(); + + if (!mv->is_texture_displacement_painted()) { + show_error(nullptr, _u8L("Nothing is painted, there is nothing to bake.")); + return; + } + + m_bake_in_progress = true; + queue_texture_displacement_bake(*mv, [this]() { m_bake_in_progress = false; }); +} + +void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float bottom_limit) +{ + ModelObject *mo = m_c->selection_info()->model_object(); + if (!mo) + return; + ModelVolume *mv = texture_volume(); + + const float approx_height = m_imgui->scaled(24.f); + y = std::min(y, bottom_limit - approx_height); + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 1.0f, 0.0f); + + ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(); + ImGui::PushItemWidth(m_imgui->scaled(7.f)); + m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + + bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE; + if (ImGui::RadioButton(m_desc.at("circle").ToUTF8().data(), is_circle)) + m_cursor_type = TriangleSelector::CursorType::CIRCLE; + ImGui::SameLine(); + if (ImGui::RadioButton(m_desc.at("sphere").ToUTF8().data(), !is_circle)) + m_cursor_type = TriangleSelector::CursorType::SPHERE; + + ImGui::Separator(); + m_imgui->text(_L("Texture layers")); + + if (mv != nullptr) { + std::vector ordered; + for (TextureDisplacementLayer &l : mv->texture_displacement_layers) + ordered.push_back(&l); + std::sort(ordered.begin(), ordered.end(), [](const auto *a, const auto *b) { return a->slot < b->slot; }); + + for (TextureDisplacementLayer *layer : ordered) { + ImGui::PushID(layer->slot); + const bool is_active = layer->slot == m_active_layer_slot; + if (ImGui::RadioButton("##active", is_active)) + set_active_layer(layer->slot); + ImGui::SameLine(); + ImGui::Text("%s", layer->name.empty() ? "texture" : layer->name.c_str()); + ImGui::SameLine(); + if (m_imgui->button(m_desc.at("remove_layer"))) + remove_texture_layer(layer->slot); + + ImGui::PushItemWidth(m_imgui->scaled(7.f)); + m_imgui->slider_float(_u8L("Depth (mm)"), &layer->depth_mm, 0.02f, 5.f, "%.2f"); + m_imgui->slider_float(_u8L("Tile size (mm)"), &layer->tiling_scale, 0.5f, 100.f, "%.1f"); + m_imgui->slider_float(_u8L("Rotation"), &layer->rotation_deg, 0.f, 360.f, "%.0f"); + ImGui::PopItemWidth(); + ImGui::Checkbox(_u8L("Invert").c_str(), &layer->invert); + ImGui::PopID(); + ImGui::Separator(); + } + } + + if (m_imgui->button(m_desc.at("add_texture"))) + add_texture_layer(); + + ImGui::Separator(); + + m_imgui->disabled_begin(mv == nullptr || !mv->is_texture_displacement_painted()); + if (m_imgui->button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset texture displacement selection"), UndoRedo::SnapshotType::GizmoAction); + int idx = -1; + for (ModelVolume *v : mo->volumes) + if (v->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + m_triangle_selectors[idx]->request_update_render_data(); + } + update_model_object(); + m_parent.set_as_dirty(); + } + m_imgui->disabled_end(); + + ImGui::SameLine(); + m_imgui->disabled_begin(m_bake_in_progress || mv == nullptr || !mv->is_texture_displacement_painted()); + if (m_imgui->button(m_bake_in_progress ? _L("Baking...") : m_desc.at("bake"))) + bake(); + m_imgui->disabled_end(); + + ImGui::Separator(); + if (m_imgui->button(_L("Done"))) + m_parent.reset_all_gizmos(); + + GizmoImguiEnd(); + ImGuiWrapper::pop_toolbar_style(); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp new file mode 100644 index 0000000000..ef9c3f25b2 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -0,0 +1,70 @@ +#ifndef slic3r_GLGizmoTextureDisplacement_hpp_ +#define slic3r_GLGizmoTextureDisplacement_hpp_ + +#include "GLGizmoPainterBase.hpp" +#include "libslic3r/TextureDisplacement.hpp" +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r::GUI { + +// Paint-style gizmo that assigns one or more texture-displacement "layers" (see +// libslic3r/TextureDisplacement.hpp) to painted areas of a model, and can bake the result into +// real mesh geometry. See the project plan for the overall architecture; in short: +// - each layer owns its own independent paint mask (ModelVolume::texture_displacement_facets), +// reusing the same TriangleSelector/FacetsAnnotation machinery as every other paint gizmo -- +// only one layer is "active" (paintable) at a time, selected in the panel below; +// - "Bake" runs build_texture_displacement() in a background job and commits the result exactly +// like the Emboss/SVG "project on surface" gizmo does. +class GLGizmoTextureDisplacement : public GLGizmoPainterBase +{ +public: + GLGizmoTextureDisplacement(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + void render_painter_gizmo() override; + +protected: + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + + wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override; + + std::string get_gizmo_entering_text() const override { return _u8L("Entering Texture displacement painting"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Texture displacement painting"); } + std::string get_action_snapshot_name() const override { return _u8L("Texture displacement editing"); } + + EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType::ENFORCER; } + EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType::NONE; } + +private: + bool on_init() override; + void update_model_object() override; + void update_from_model_object(bool first_update) override; + void on_opening() override {} + void on_shutdown() override; + PainterGizmoType get_painter_type() const override; + + // Phase 1 restricts the texture layer list to the first model-part volume of the current + // object (the common single-volume case); multi-part objects only get texture layers on + // their first part until a later phase. Returns nullptr if there is no model part. + ModelVolume* texture_volume(); + const ModelVolume* texture_volume() const; + + void add_texture_layer(); + void remove_texture_layer(int slot); + void set_active_layer(int slot); // flushes the previous layer's edits, then reloads selectors + void bake(); + + // Which of the up to TEXTURE_DISPLACEMENT_MAX_LAYERS paint masks the brush currently writes + // into. Always a valid slot index (0 by default) so the base class's per-volume selector + // machinery always has something to work with, even before any texture has been added -- + // painting into a slot with no texture assigned is harmless, it just has no visible/bake + // effect until a texture is added to that slot. + int m_active_layer_slot = 0; + bool m_bake_in_progress = false; + + std::map m_desc; +}; + +} // namespace Slic3r::GUI + +#endif // slic3r_GLGizmoTextureDisplacement_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 94c76d896b..9803fad2cb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -22,6 +22,7 @@ //#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" #include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp" @@ -164,6 +165,9 @@ void GLGizmosManager::switch_gizmos_icon_filename() case(EType::FuzzySkin): gizmo->set_icon_filename(m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg"); break; + case(EType::TextureDisplacement): + gizmo->set_icon_filename(m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg"); + break; case(EType::MeshBoolean): gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"); break; @@ -213,6 +217,9 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); m_gizmos.emplace_back(new GLGizmoFuzzySkin(m_parent, m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg", EType::FuzzySkin)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmSegmentation)); + // TODO: placeholder icon reused from the fuzzy-skin gizmo; swap in a dedicated + // toolbar_texture_displacement(_dark).svg once one exists. + m_gizmos.emplace_back(new GLGizmoTextureDisplacement(m_parent, m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg", EType::TextureDisplacement)); m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss)); m_gizmos.emplace_back(new GLGizmoSVG(m_parent)); m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure)); @@ -524,6 +531,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == FuzzySkin) return dynamic_cast(m_gizmos[FuzzySkin].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == TextureDisplacement) + return dynamic_cast(m_gizmos[TextureDisplacement].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MeshBoolean) return dynamic_cast(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == BrimEars) @@ -537,6 +546,7 @@ bool GLGizmosManager::is_paint_gizmo() return m_current == EType::FdmSupports || m_current == EType::MmSegmentation || m_current == EType::FuzzySkin || + m_current == EType::TextureDisplacement || m_current == EType::Seam; } @@ -1486,6 +1496,8 @@ std::string get_name_from_gizmo_etype(GLGizmosManager::EType type) return "Color Painting"; case GLGizmosManager::EType::FuzzySkin: return "Fuzzy Skin Painting"; + case GLGizmosManager::EType::TextureDisplacement: + return "Texture Displacement"; default: return ""; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 01814521aa..5055660918 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -84,6 +84,7 @@ public: Seam, FuzzySkin, MmSegmentation, + TextureDisplacement, Emboss, Svg, Measure, diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp new file mode 100644 index 0000000000..9e76c55666 --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -0,0 +1,88 @@ +#include "TextureDisplacementBakeJob.hpp" + +#include + +#include "libslic3r/Model.hpp" + +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/Utils/UndoRedo.hpp" + +namespace Slic3r::GUI { + +TextureDisplacementBakeJob::TextureDisplacementBakeJob(TextureDisplacementBakeInput &&input, std::function on_finished) + : m_input(std::move(input)), m_on_finished(std::move(on_finished)) +{ +} + +void TextureDisplacementBakeJob::process(Ctl &ctl) +{ + ctl.update_status(0, _u8L("Baking texture displacement")); + + // Only ever touches m_input (captured by value before this job was queued) and local state -- + // never the live Model -- so this is safe to run concurrently with the UI thread. + m_result = TriangleMesh(build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data)); +} + +void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &eptr) +{ + struct OnExit + { + std::function fn; + ~OnExit() { if (fn) fn(); } + } on_exit{m_on_finished}; + + if (canceled || eptr || m_result.empty()) + return; + + Plater *plater = wxGetApp().plater(); + + Plater::TakeSnapshot snapshot(plater, _u8L("Bake texture displacement"), UndoRedo::SnapshotType::GizmoAction); + + ModelVolume *volume = get_model_volume(m_input.volume_id, plater->model().objects); + if (volume == nullptr) + return; + + volume->set_mesh(std::move(m_result)); + volume->set_new_unique_id(); + volume->calculate_convex_hull(); + + // Clear the paint mask of every layer that was actually baked so a repeat bake (or the paint + // overlay) doesn't act on triangles that no longer represent the same unbaked surface. The + // texture layer definitions themselves (and paint outside the baked area, if any) are left + // untouched so the user can keep sculpting with the same textures. + for (const TextureDisplacementLayer &layer : m_input.layers) + if (!layer.empty() && layer.slot >= 0 && layer.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) + volume->texture_displacement_facet(layer.slot).reset(); + + ModelObject *object = volume->get_object(); + if (object == nullptr) + return; + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), object); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + + plater->changed_object(*object); +} + +void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished) +{ + TextureDisplacementBakeInput input; + input.volume_id = volume.id(); + input.base_mesh = volume.mesh().its; + input.layers = volume.texture_displacement_layers; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + input.facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); + + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::make_unique(std::move(input), std::move(on_finished))); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp new file mode 100644 index 0000000000..2d8a22c759 --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -0,0 +1,51 @@ +#ifndef slic3r_TextureDisplacementBakeJob_hpp_ +#define slic3r_TextureDisplacementBakeJob_hpp_ + +#include +#include + +#include "libslic3r/ObjectID.hpp" +#include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TriangleMesh.hpp" + +#include "Job.hpp" + +namespace Slic3r::GUI { + +// Everything process() needs, captured by value on the main thread when the job is queued so the +// worker thread never touches the live Model concurrently with the UI (mirrors how EmbossJob's +// DataBase is captured before process() runs). +struct TextureDisplacementBakeInput +{ + ObjectID volume_id; + indexed_triangle_set base_mesh; + std::vector layers; + TextureDisplacementFacetsData facets_data; +}; + +// Bakes a volume's painted texture-displacement layers into real mesh geometry in the background, +// then commits the result on the main thread -- mirrors EmbossJob's UpdateJob/update_volume() +// bake-and-commit pattern (see EmbossJob.cpp). +class TextureDisplacementBakeJob : public Job +{ +public: + TextureDisplacementBakeJob(TextureDisplacementBakeInput &&input, std::function on_finished); + + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; + +private: + TextureDisplacementBakeInput m_input; + TriangleMesh m_result; + std::function m_on_finished; +}; + +// Captures `volume`'s current mesh/layers/paint data and queues a TextureDisplacementBakeJob on +// the app's UI job worker. `on_finished` is always called once the job settles (success, failure, +// or cancellation), so the caller can clear its own "bake in progress" UI state. Must be called +// from the main thread. +void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished); + +} // namespace Slic3r::GUI + +#endif // slic3r_TextureDisplacementBakeJob_hpp_ diff --git a/src/slic3r/GUI/generated/Invalidation_generated.cpp b/src/slic3r/GUI/generated/Invalidation_generated.cpp new file mode 100644 index 0000000000..7f65e44a78 --- /dev/null +++ b/src/slic3r/GUI/generated/Invalidation_generated.cpp @@ -0,0 +1,255 @@ +// ===== AUTO-GENERATED by tools/config_codegen.py ===== + +static const std::unordered_map> s_print_steps_map = { + {"accel_to_decel_enable", {psGCodeExport}}, + {"accel_to_decel_factor", {psGCodeExport}}, + {"activate_air_filtration", {psGCodeExport}}, + {"activate_air_filtration_during_print", {psGCodeExport}}, + {"activate_air_filtration_on_completion", {psGCodeExport}}, + {"activate_chamber_temp_control", {psGCodeExport}}, + {"additional_cooling_fan_speed", {psGCodeExport}}, + {"bed_temperature_formula", {psGCodeExport}}, + {"before_layer_change_gcode", {psGCodeExport}}, + {"bridge_acceleration", {psGCodeExport}}, + {"chamber_temperature", {psSkirtBrim, psWipeTower, psGCodeExport}}, + {"change_filament_gcode", {psGCodeExport}}, + {"close_fan_the_first_x_layers", {psGCodeExport}}, + {"complete_print_exhaust_fan_speed", {psGCodeExport}}, + {"cool_plate_temp", {psSkirtBrim, psWipeTower}}, + {"cool_plate_temp_initial_layer", {psGCodeExport}}, + {"curr_bed_type", {psGCodeExport}}, + {"default_acceleration", {psGCodeExport}}, + {"default_filament_colour", {psGCodeExport}}, + {"deretraction_speed", {psGCodeExport}}, + {"disable_m73", {psGCodeExport}}, + {"dont_slow_down_outer_wall", {psGCodeExport}}, + {"draft_shield", {psSkirtBrim}}, + {"during_print_exhaust_fan_speed", {psGCodeExport}}, + {"enable_arc_fitting", {psSkirtBrim}}, + {"enable_filament_ramming", {psSkirtBrim, psWipeTower}}, + {"enable_long_retraction_when_cut", {psGCodeExport}}, + {"enable_overhang_bridge_fan", {psGCodeExport}}, + {"enable_pressure_advance", {psGCodeExport}}, + {"enable_prime_tower", {psSkirtBrim, psWipeTower}}, + {"enable_tower_interface_features", {psSkirtBrim, psWipeTower}}, + {"enable_wrapping_detection", {psSkirtBrim, psWipeTower}}, + {"eng_plate_temp", {psSkirtBrim, psWipeTower}}, + {"eng_plate_temp_initial_layer", {psGCodeExport}}, + {"exclude_object", {psGCodeExport}}, + {"extruder_ams_count", {psSkirtBrim, psWipeTower}}, + {"extruder_clearance_height_to_lid", {psGCodeExport}}, + {"extruder_clearance_height_to_rod", {psGCodeExport}}, + {"extruder_clearance_radius", {psGCodeExport}}, + {"extruder_colour", {psGCodeExport}}, + {"extruder_offset", {psGCodeExport}}, + {"extrusion_rate_smoothing_external_perimeter_only", {psGCodeExport}}, + {"fan_cooling_layer_time", {psGCodeExport}}, + {"fan_kickstart", {psGCodeExport}}, + {"fan_max_speed", {psGCodeExport}}, + {"fan_min_speed", {psGCodeExport}}, + {"fan_speedup_overhangs", {psGCodeExport}}, + {"fan_speedup_time", {psGCodeExport}}, + {"filament_adaptive_volumetric_speed", {psSkirtBrim, psWipeTower}}, + {"filament_adhesiveness_category", {psSkirtBrim, psWipeTower}}, + {"filament_change_length", {psWipeTower}}, + {"filament_colour", {psGCodeExport}}, + {"filament_cooling_final_speed", {psSkirtBrim, psWipeTower}}, + {"filament_cooling_initial_speed", {psSkirtBrim, psWipeTower}}, + {"filament_cooling_moves", {psSkirtBrim, psWipeTower}}, + {"filament_cost", {psGCodeExport}}, + {"filament_density", {psGCodeExport}}, + {"filament_diameter", {psGCodeExport}}, + {"filament_end_gcode", {psGCodeExport}}, + {"filament_flow_ratio", {psGCodeExport}}, + {"filament_is_support", {psWipeTower}}, + {"filament_loading_speed", {psSkirtBrim, psWipeTower}}, + {"filament_loading_speed_start", {psSkirtBrim, psWipeTower}}, + {"filament_map", {psSkirtBrim, psWipeTower}}, + {"filament_map_mode", {psSkirtBrim, psWipeTower}}, + {"filament_max_volumetric_speed", {psSkirtBrim, psWipeTower}}, + {"filament_minimal_purge_on_wipe_tower", {psSkirtBrim, psWipeTower}}, + {"filament_multitool_ramming", {psSkirtBrim, psWipeTower}}, + {"filament_multitool_ramming_flow", {psSkirtBrim, psWipeTower}}, + {"filament_multitool_ramming_volume", {psSkirtBrim, psWipeTower}}, + {"filament_notes", {psGCodeExport}}, + {"filament_printable", {psWipeTower}}, + {"filament_ramming_parameters", {psSkirtBrim, psWipeTower}}, + {"filament_soluble", {psWipeTower}}, + {"filament_stamping_distance", {psSkirtBrim, psWipeTower}}, + {"filament_stamping_loading_speed", {psSkirtBrim, psWipeTower}}, + {"filament_start_gcode", {psGCodeExport}}, + {"filament_toolchange_delay", {psSkirtBrim, psWipeTower}}, + {"filament_tower_interface_pre_extrusion_dist", {psSkirtBrim, psWipeTower}}, + {"filament_tower_interface_pre_extrusion_length", {psSkirtBrim, psWipeTower}}, + {"filament_tower_interface_print_temp", {psSkirtBrim, psWipeTower}}, + {"filament_tower_interface_purge_volume", {psSkirtBrim, psWipeTower}}, + {"filament_tower_ironing_area", {psSkirtBrim, psWipeTower}}, + {"filament_type", {psSkirtBrim, psWipeTower}}, + {"filament_unloading_speed", {psSkirtBrim, psWipeTower}}, + {"filament_unloading_speed_start", {psSkirtBrim, psWipeTower}}, + {"filename_format", {psGCodeExport}}, + {"first_layer_print_sequence", {psSkirtBrim, psWipeTower}}, + {"flush_into_infill", {psSkirtBrim, psWipeTower}}, + {"flush_into_support", {psSkirtBrim, psWipeTower}}, + {"flush_volumes_matrix", {psSkirtBrim, psWipeTower}}, + {"full_fan_speed_layer", {psGCodeExport}}, + {"gcode_add_line_number", {psGCodeExport}}, + {"gcode_comments", {psGCodeExport}}, + {"gcode_flavor", {psSkirtBrim, psWipeTower}}, + {"gcode_label_objects", {psGCodeExport}}, + {"grab_length", {psGCodeExport}}, + {"hot_plate_temp", {psSkirtBrim, psWipeTower}}, + {"hot_plate_temp_initial_layer", {psGCodeExport}}, + {"idle_temperature", {psSkirtBrim, psWipeTower}}, + {"independent_support_layer_height", {psWipeTower}}, + {"initial_layer_acceleration", {psGCodeExport}}, + {"initial_layer_infill_speed", {psSkirtBrim, psWipeTower}}, + {"initial_layer_line_width", {psSkirtBrim}}, + {"initial_layer_speed", {psSkirtBrim, psWipeTower}}, + {"initial_layer_travel_acceleration", {psSkirtBrim, psWipeTower}}, + {"initial_layer_travel_jerk", {psSkirtBrim, psWipeTower}}, + {"initial_layer_travel_speed", {psSkirtBrim, psWipeTower}}, + {"inner_wall_acceleration", {psGCodeExport}}, + {"internal_bridge_fan_speed", {psGCodeExport}}, + {"internal_solid_infill_acceleration", {psGCodeExport}}, + {"ironing_fan_speed", {psGCodeExport}}, + {"is_infill_first", {psGCodeExport}}, + {"layer_change_gcode", {psGCodeExport}}, + {"long_retractions_when_cut", {psGCodeExport}}, + {"machine_end_gcode", {psGCodeExport}}, + {"machine_start_gcode", {psGCodeExport}}, + {"manual_filament_change", {psGCodeExport}}, + {"max_layer_height", {psSkirtBrim}}, + {"max_travel_detour_distance", {psGCodeExport}}, + {"max_volumetric_extrusion_rate_slope", {psGCodeExport}}, + {"max_volumetric_extrusion_rate_slope_segment_length", {psGCodeExport}}, + {"min_layer_height", {psSkirtBrim}}, + {"min_skirt_length", {psSkirtBrim}}, + {"nozzle_height", {psGCodeExport}}, + {"nozzle_hrc", {psGCodeExport}}, + {"nozzle_temperature", {psSkirtBrim, psWipeTower}}, + {"nozzle_temperature_initial_layer", {psSkirtBrim, psWipeTower}}, + {"nozzle_volume", {psGCodeExport}}, + {"ooze_prevention", {psSkirtBrim}}, + {"other_layers_print_sequence", {psSkirtBrim, psWipeTower}}, + {"other_layers_print_sequence_nums", {psSkirtBrim, psWipeTower}}, + {"outer_wall_acceleration", {psGCodeExport}}, + {"overhang_fan_speed", {psGCodeExport}}, + {"overhang_fan_threshold", {psGCodeExport}}, + {"part_cooling_fan_min_pwm", {psGCodeExport}}, + {"post_process", {psGCodeExport}}, + {"preheat_steps", {psGCodeExport}}, + {"preheat_time", {psGCodeExport}}, + {"pressure_advance", {psGCodeExport}}, + {"prime_tower_brim_width", {psSkirtBrim, psWipeTower}}, + {"prime_tower_enable_framework", {psSkirtBrim, psWipeTower}}, + {"prime_tower_flat_ironing", {psSkirtBrim, psWipeTower}}, + {"prime_tower_skip_points", {psSkirtBrim, psWipeTower}}, + {"prime_tower_width", {psSkirtBrim, psWipeTower}}, + {"prime_volume", {psSkirtBrim, psWipeTower}}, + {"print_order", {psSkirtBrim}}, + {"print_sequence", {psSkirtBrim, psWipeTower}}, + {"printer_notes", {psGCodeExport}}, + {"printing_by_object_gcode", {psGCodeExport}}, + {"purge_in_prime_tower", {psSkirtBrim, psWipeTower}}, + {"reduce_crossing_wall", {psGCodeExport}}, + {"reduce_fan_stop_start_freq", {psGCodeExport}}, + {"reduce_infill_retraction", {psGCodeExport}}, + {"required_nozzle_HRC", {psGCodeExport}}, + {"resolution", {psSkirtBrim}}, + {"retract_before_wipe", {psGCodeExport}}, + {"retract_length_toolchange", {psGCodeExport}}, + {"retract_lift_above", {psGCodeExport}}, + {"retract_lift_below", {psGCodeExport}}, + {"retract_lift_enforce", {psGCodeExport}}, + {"retract_restart_extra", {psGCodeExport}}, + {"retract_restart_extra_toolchange", {psGCodeExport}}, + {"retract_when_changing_layer", {psGCodeExport}}, + {"retraction_distances_when_cut", {psGCodeExport}}, + {"retraction_length", {psGCodeExport}}, + {"retraction_minimum_travel", {psGCodeExport}}, + {"retraction_speed", {psGCodeExport}}, + {"role_based_wipe_speed", {psGCodeExport}}, + {"seam_gap", {psGCodeExport}}, + {"single_extruder_multi_material", {psSkirtBrim, psWipeTower}}, + {"single_extruder_multi_material_priming", {psGCodeExport}}, + {"single_loop_draft_shield", {psSkirtBrim}}, + {"skirt_distance", {psSkirtBrim}}, + {"skirt_height", {psSkirtBrim}}, + {"skirt_loops", {psSkirtBrim}}, + {"skirt_speed", {psSkirtBrim}}, + {"skirt_start_angle", {psSkirtBrim}}, + {"skirt_type", {psSkirtBrim}}, + {"slow_down_for_layer_cooling", {psGCodeExport}}, + {"slow_down_layer_time", {psGCodeExport}}, + {"slow_down_layers", {psSkirtBrim, psWipeTower}}, + {"slow_down_min_speed", {psGCodeExport}}, + {"sparse_infill_acceleration", {psGCodeExport}}, + {"standby_temperature_delta", {psGCodeExport}}, + {"supertack_plate_temp", {psSkirtBrim, psWipeTower}}, + {"supertack_plate_temp_initial_layer", {psGCodeExport}}, + {"support_material_interface_fan_speed", {psGCodeExport}}, + {"support_multi_bed_types", {psSkirtBrim, psWipeTower}}, + {"textured_cool_plate_temp", {psSkirtBrim, psWipeTower}}, + {"textured_cool_plate_temp_initial_layer", {psGCodeExport}}, + {"textured_plate_temp", {psSkirtBrim, psWipeTower}}, + {"textured_plate_temp_initial_layer", {psGCodeExport}}, + {"thumbnails", {psGCodeExport}}, + {"thumbnails_format", {psGCodeExport}}, + {"time_lapse_gcode", {psGCodeExport}}, + {"tool_change_on_wipe_tower", {psSkirtBrim, psWipeTower}}, + {"top_surface_acceleration", {psGCodeExport}}, + {"travel_acceleration", {psGCodeExport}}, + {"travel_slope", {psGCodeExport}}, + {"travel_speed", {psSkirtBrim, psWipeTower}}, + {"travel_speed_z", {psSkirtBrim, psWipeTower}}, + {"upward_compatible_machine", {psGCodeExport}}, + {"use_firmware_retraction", {psGCodeExport}}, + {"use_relative_e_distances", {psGCodeExport}}, + {"volumetric_speed_coefficients", {psGCodeExport}}, + {"wall_sequence", {psSkirtBrim}}, + {"wipe", {psGCodeExport}}, + {"wipe_distance", {psGCodeExport}}, + {"wipe_on_loops", {psGCodeExport}}, + {"wipe_speed", {psGCodeExport}}, + {"wipe_tower_bridging", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_cone_angle", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_extra_flow", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_extra_rib_length", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_extra_spacing", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_filament", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_fillet_wall", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_max_purge_speed", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_no_sparse_layers", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_rib_width", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_rotation_angle", {psSkirtBrim}}, + {"wipe_tower_type", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_wall_type", {psSkirtBrim, psWipeTower}}, + {"wipe_tower_x", {psSkirtBrim}}, + {"wipe_tower_y", {psSkirtBrim}}, + {"wiping_volumes_extruders", {psSkirtBrim, psWipeTower}}, + {"wrapping_detection_gcode", {psGCodeExport}}, + {"z_hop", {psGCodeExport}}, + {"z_offset", {psSkirtBrim, psWipeTower}}, +}; + +static const std::unordered_map> s_object_steps_map = { + {"enable_arc_fitting", {posSupportMaterial, posInfill, posPerimeters}}, + {"filament_change_length", {posSupportMaterial}}, + {"filament_is_support", {posSupportMaterial}}, + {"filament_printable", {posSupportMaterial}}, + {"filament_shrink", {posSlice}}, + {"filament_shrinkage_compensation_z", {posSlice}}, + {"filament_soluble", {posSupportMaterial}}, + {"independent_support_layer_height", {posSupportMaterial}}, + {"initial_layer_line_width", {posSupportMaterial, posInfill, posPerimeters}}, + {"initial_layer_print_height", {posSlice}}, + {"max_layer_height", {posSupportMaterial, posInfill, posPerimeters}}, + {"min_layer_height", {posSupportMaterial, posInfill, posPerimeters}}, + {"nozzle_diameter", {posSlice}}, + {"precise_z_height", {posSlice}}, + {"print_order", {posSupportMaterial, posInfill, posPerimeters}}, + {"resolution", {posSupportMaterial, posInfill, posPerimeters, posSlice}}, + {"spiral_mode", {posSlice}}, + {"wall_sequence", {posSupportMaterial, posInfill, posPerimeters}}, +}; \ No newline at end of file diff --git a/src/slic3r/GUI/generated/OptionKeys_generated.cpp b/src/slic3r/GUI/generated/OptionKeys_generated.cpp new file mode 100644 index 0000000000..644503ea50 --- /dev/null +++ b/src/slic3r/GUI/generated/OptionKeys_generated.cpp @@ -0,0 +1,61 @@ +// ===== AUTO-GENERATED by tools/config_codegen.py ===== + +static const std::vector s_extruder_option_keys = { + "default_filament_profile", + "default_nozzle_volume_type", + "deretraction_speed", + "extruder_colour", + "extruder_offset", + "extruder_type", + "long_retractions_when_cut", + "max_layer_height", + "min_layer_height", + "nozzle_diameter", + "nozzle_flush_dataset", + "nozzle_type", + "nozzle_volume", + "retract_before_wipe", + "retract_length_toolchange", + "retract_lift_above", + "retract_lift_below", + "retract_lift_enforce", + "retract_restart_extra", + "retract_restart_extra_toolchange", + "retract_when_changing_layer", + "retraction_distances_when_cut", + "retraction_length", + "retraction_minimum_travel", + "retraction_speed", + "travel_slope", + "wipe", + "wipe_distance", + "z_hop", + "z_hop_types", +}; + +static const std::vector s_filament_option_keys = { + "default_filament_profile", + "deretraction_speed", + "filament_colour", + "filament_diameter", + "long_retractions_when_cut", + "max_layer_height", + "min_layer_height", + "retract_before_wipe", + "retract_length_toolchange", + "retract_lift_above", + "retract_lift_below", + "retract_lift_enforce", + "retract_restart_extra", + "retract_restart_extra_toolchange", + "retract_when_changing_layer", + "retraction_distances_when_cut", + "retraction_length", + "retraction_minimum_travel", + "retraction_speed", + "volumetric_speed_coefficients", + "wipe", + "wipe_distance", + "z_hop", + "z_hop_types", +}; diff --git a/src/slic3r/GUI/generated/Preset_options_generated.cpp b/src/slic3r/GUI/generated/Preset_options_generated.cpp new file mode 100644 index 0000000000..dc82d4f593 --- /dev/null +++ b/src/slic3r/GUI/generated/Preset_options_generated.cpp @@ -0,0 +1,726 @@ +// ===== AUTO-GENERATED by tools/config_codegen.py ===== + +static const std::vector s_Preset_print_options = { + "accel_to_decel_enable", + "accel_to_decel_factor", + "adaptive_layer_height", + "align_infill_direction_to_model", + "alternate_extra_wall", + "bbl_calib_mark_logo", + "bed_temperature_formula", + "bottom_shell_layers", + "bottom_shell_thickness", + "bottom_solid_infill_flow_ratio", + "bottom_surface_density", + "bottom_surface_pattern", + "bridge_acceleration", + "bridge_angle", + "bridge_density", + "bridge_flow", + "bridge_no_support", + "bridge_speed", + "brim_ears", + "brim_ears_detection_length", + "brim_ears_max_angle", + "brim_flow_ratio", + "brim_object_gap", + "brim_type", + "brim_use_efc_outline", + "brim_width", + "calib_flowrate_topinfill_special_order", + "combine_brims", + "compatible_machine_expression_group", + "compatible_printers", + "compatible_printers_condition", + "compatible_prints", + "compatible_process_expression_group", + "cooling_tube_length", + "cooling_tube_retraction", + "counterbore_hole_bridging", + "curr_bed_type", + "default_acceleration", + "default_bed_type", + "default_filament_profile", + "default_jerk", + "default_junction_deviation", + "default_nozzle_volume_type", + "default_print_profile", + "deretraction_speed", + "detect_narrow_internal_solid_infill", + "detect_overhang_wall", + "detect_thin_wall", + "different_settings_to_system", + "dont_filter_internal_bridges", + "draft_shield", + "elefant_foot_compensation", + "elefant_foot_compensation_layers", + "elefant_foot_layers_density", + "emit_machine_limits_to_gcode", + "enable_arc_fitting", + "enable_extra_bridge_layer", + "enable_filament_dynamic_map", + "enable_filament_ramming", + "enable_long_retraction_when_cut", + "enable_overhang_speed", + "enable_prime_tower", + "enable_support", + "enable_tower_interface_cooldown_during_tower", + "enable_tower_interface_features", + "enable_wrapping_detection", + "enforce_support_layers", + "ensure_vertical_shell_thickness", + "exclude_object", + "extra_loading_move", + "extra_perimeters_on_overhangs", + "extra_solid_infills", + "extruder", + "extruder_ams_count", + "extruder_colour", + "extruder_offset", + "extruder_type", + "extruder_variant_list", + "extrusion_rate_smoothing_external_perimeter_only", + "filament_colour", + "filament_colour_type", + "filament_extruder_id", + "filament_flush_temp", + "filament_ids", + "filament_map", + "filament_map_mode", + "filament_multi_colour", + "filament_ramming_parameters", + "filament_self_index", + "filament_settings_id", + "filename_format", + "fill_multiline", + "filter_out_gap_fill", + "first_layer_flow_ratio", + "first_layer_print_sequence", + "first_layer_sequence_choice", + "flush_into_infill", + "flush_into_objects", + "flush_into_support", + "flush_multiplier", + "flush_volumes_matrix", + "flush_volumes_vector", + "fuzzy_skin", + "fuzzy_skin_first_layer", + "fuzzy_skin_layers_between_ripple_offset", + "fuzzy_skin_mode", + "fuzzy_skin_noise_type", + "fuzzy_skin_octaves", + "fuzzy_skin_persistence", + "fuzzy_skin_point_distance", + "fuzzy_skin_ripple_offset", + "fuzzy_skin_ripples_per_layer", + "fuzzy_skin_scale", + "fuzzy_skin_thickness", + "gap_fill_flow_ratio", + "gap_fill_target", + "gap_infill_speed", + "gcode_add_line_number", + "gcode_comments", + "gcode_label_objects", + "grab_length", + "gyroid_optimized", + "has_filament_switcher", + "has_scarf_joint_seam", + "head_wrap_detect_zone", + "high_current_on_filament_swap", + "hole_to_polyhole", + "hole_to_polyhole_threshold", + "hole_to_polyhole_twisted", + "host_type", + "independent_support_layer_height", + "infill_anchor", + "infill_anchor_max", + "infill_combination", + "infill_combination_max_layer_height", + "infill_direction", + "infill_jerk", + "infill_lock_depth", + "infill_overhang_angle", + "infill_shift_step", + "infill_wall_overlap", + "inherits", + "inherits_group", + "initial_layer_acceleration", + "initial_layer_infill_speed", + "initial_layer_jerk", + "initial_layer_line_width", + "initial_layer_min_bead_width", + "initial_layer_print_height", + "initial_layer_speed", + "initial_layer_travel_acceleration", + "initial_layer_travel_jerk", + "initial_layer_travel_speed", + "inner_wall_acceleration", + "inner_wall_flow_ratio", + "inner_wall_jerk", + "inner_wall_line_width", + "inner_wall_speed", + "input_shaping_damp_x", + "input_shaping_damp_y", + "input_shaping_emit", + "input_shaping_freq_x", + "input_shaping_freq_y", + "input_shaping_type", + "interface_shells", + "interlocking_beam", + "interlocking_beam_layer_count", + "interlocking_beam_width", + "interlocking_boundary_avoidance", + "interlocking_depth", + "interlocking_orientation", + "internal_bridge_angle", + "internal_bridge_density", + "internal_bridge_flow", + "internal_bridge_speed", + "internal_solid_infill_acceleration", + "internal_solid_infill_flow_ratio", + "internal_solid_infill_line_width", + "internal_solid_infill_pattern", + "internal_solid_infill_speed", + "ironing_angle", + "ironing_angle_fixed", + "ironing_expansion", + "ironing_flow", + "ironing_inset", + "ironing_pattern", + "ironing_spacing", + "ironing_speed", + "ironing_type", + "is_infill_first", + "lateral_lattice_angle_1", + "lateral_lattice_angle_2", + "layer_height", + "line_width", + "long_retractions_when_cut", + "machine_load_filament_time", + "machine_max_acceleration_e", + "machine_max_acceleration_extruding", + "machine_max_acceleration_retracting", + "machine_max_acceleration_travel", + "machine_max_acceleration_x", + "machine_max_acceleration_y", + "machine_max_acceleration_z", + "machine_max_jerk_e", + "machine_max_jerk_x", + "machine_max_jerk_y", + "machine_max_jerk_z", + "machine_max_junction_deviation", + "machine_max_speed_e", + "machine_max_speed_x", + "machine_max_speed_y", + "machine_max_speed_z", + "machine_min_extruding_rate", + "machine_min_travel_rate", + "machine_tool_change_time", + "machine_unload_filament_time", + "make_overhang_printable", + "make_overhang_printable_angle", + "make_overhang_printable_hole_size", + "manual_filament_change", + "master_extruder_id", + "max_bridge_length", + "max_layer_height", + "max_resonance_avoidance_speed", + "max_travel_detour_distance", + "max_volumetric_extrusion_rate_slope", + "max_volumetric_extrusion_rate_slope_segment_length", + "min_bead_width", + "min_feature_size", + "min_layer_height", + "min_length_factor", + "min_resonance_avoidance_speed", + "min_skirt_length", + "min_width_top_surface", + "minimum_sparse_infill_area", + "mmu_segmented_region_interlocking_depth", + "mmu_segmented_region_max_width", + "notes", + "nozzle_diameter", + "nozzle_flush_dataset", + "nozzle_height", + "nozzle_volume", + "nozzle_volume_type", + "only_one_wall_first_layer", + "only_one_wall_top", + "ooze_prevention", + "other_layers_print_sequence", + "other_layers_print_sequence_nums", + "other_layers_sequence_choice", + "outer_wall_acceleration", + "outer_wall_flow_ratio", + "outer_wall_jerk", + "outer_wall_line_width", + "outer_wall_speed", + "overhang_1_4_speed", + "overhang_2_4_speed", + "overhang_3_4_speed", + "overhang_4_4_speed", + "overhang_flow_ratio", + "overhang_reverse", + "overhang_reverse_internal_only", + "overhang_reverse_threshold", + "parking_pos_retraction", + "physical_extruder_map", + "post_process", + "precise_outer_wall", + "precise_z_height", + "preheat_steps", + "preheat_time", + "prime_tower_brim_width", + "prime_tower_enable_framework", + "prime_tower_flat_ironing", + "prime_tower_infill_gap", + "prime_tower_skip_points", + "prime_tower_width", + "prime_volume", + "print_compatible_printers", + "print_extruder_id", + "print_extruder_variant", + "print_flow_ratio", + "print_order", + "print_sequence", + "print_settings_id", + "printer_extruder_id", + "printer_extruder_variant", + "printer_model", + "printer_settings_id", + "printer_variant", + "process_change_extrusion_role_gcode", + "purge_in_prime_tower", + "raft_contact_distance", + "raft_expansion", + "raft_first_layer_density", + "raft_first_layer_expansion", + "raft_layers", + "reduce_crossing_wall", + "reduce_infill_retraction", + "resolution", + "resonance_avoidance", + "retract_before_wipe", + "retract_length_toolchange", + "retract_lift_above", + "retract_lift_below", + "retract_lift_enforce", + "retract_restart_extra", + "retract_restart_extra_toolchange", + "retract_when_changing_layer", + "retraction_distances_when_cut", + "retraction_length", + "retraction_minimum_travel", + "retraction_speed", + "role_based_wipe_speed", + "scarf_angle_threshold", + "scarf_joint_flow_ratio", + "scarf_joint_speed", + "scarf_overhang_threshold", + "seam_gap", + "seam_position", + "seam_slope_conditional", + "seam_slope_entire_loop", + "seam_slope_inner_walls", + "seam_slope_min_length", + "seam_slope_start_height", + "seam_slope_steps", + "seam_slope_type", + "set_other_flow_ratios", + "silent_mode", + "single_extruder_multi_material", + "single_extruder_multi_material_priming", + "single_loop_draft_shield", + "skeleton_infill_density", + "skeleton_infill_line_width", + "skin_infill_density", + "skin_infill_depth", + "skin_infill_line_width", + "skirt_distance", + "skirt_height", + "skirt_loops", + "skirt_speed", + "skirt_start_angle", + "skirt_type", + "slice_closing_radius", + "slicing_mode", + "slow_down_layers", + "slowdown_for_curled_perimeters", + "small_area_infill_flow_compensation", + "small_area_infill_flow_compensation_model", + "small_perimeter_speed", + "small_perimeter_threshold", + "solid_infill_direction", + "solid_infill_filament", + "solid_infill_rotate_template", + "spaghetti_detector", + "sparse_infill_acceleration", + "sparse_infill_density", + "sparse_infill_filament", + "sparse_infill_flow_ratio", + "sparse_infill_line_width", + "sparse_infill_pattern", + "sparse_infill_rotate_template", + "sparse_infill_speed", + "spiral_finishing_flow_ratio", + "spiral_mode", + "spiral_mode_max_xy_smoothing", + "spiral_mode_smooth", + "spiral_starting_flow_ratio", + "staggered_inner_seams", + "standby_temperature_delta", + "start_end_points", + "support_angle", + "support_base_pattern", + "support_base_pattern_spacing", + "support_bottom_interface_spacing", + "support_bottom_z_distance", + "support_critical_regions_only", + "support_expansion", + "support_filament", + "support_flow_ratio", + "support_interface_bottom_layers", + "support_interface_filament", + "support_interface_flow_ratio", + "support_interface_loop_pattern", + "support_interface_not_for_body", + "support_interface_pattern", + "support_interface_spacing", + "support_interface_speed", + "support_interface_top_layers", + "support_ironing", + "support_ironing_flow", + "support_ironing_pattern", + "support_ironing_spacing", + "support_line_width", + "support_object_first_layer_gap", + "support_object_skip_flush", + "support_object_xy_distance", + "support_on_build_plate_only", + "support_remove_small_overhang", + "support_speed", + "support_style", + "support_threshold_angle", + "support_threshold_overlap", + "support_top_z_distance", + "support_type", + "symmetric_infill_y_axis", + "thick_bridges", + "thick_internal_bridges", + "thumbnails_format", + "timelapse_type", + "tool_change_on_wipe_tower", + "top_bottom_infill_wall_overlap", + "top_shell_layers", + "top_shell_thickness", + "top_solid_infill_flow_ratio", + "top_surface_acceleration", + "top_surface_density", + "top_surface_jerk", + "top_surface_line_width", + "top_surface_pattern", + "top_surface_speed", + "travel_acceleration", + "travel_jerk", + "travel_slope", + "travel_speed", + "travel_speed_z", + "tree_support_angle_slow", + "tree_support_auto_brim", + "tree_support_branch_angle", + "tree_support_branch_angle_organic", + "tree_support_branch_diameter", + "tree_support_branch_diameter_angle", + "tree_support_branch_diameter_organic", + "tree_support_branch_distance", + "tree_support_branch_distance_organic", + "tree_support_brim_width", + "tree_support_tip_diameter", + "tree_support_top_rate", + "tree_support_wall_count", + "tree_support_with_infill", + "upward_compatible_machine", + "volumetric_speed_coefficients", + "wall_direction", + "wall_distribution_count", + "wall_filament", + "wall_generator", + "wall_loops", + "wall_maximum_deviation", + "wall_maximum_resolution", + "wall_sequence", + "wall_transition_angle", + "wall_transition_filter_deviation", + "wall_transition_length", + "wipe", + "wipe_before_external_loop", + "wipe_distance", + "wipe_on_loops", + "wipe_speed", + "wipe_tower_bridging", + "wipe_tower_cone_angle", + "wipe_tower_extra_flow", + "wipe_tower_extra_rib_length", + "wipe_tower_extra_spacing", + "wipe_tower_filament", + "wipe_tower_fillet_wall", + "wipe_tower_max_purge_speed", + "wipe_tower_no_sparse_layers", + "wipe_tower_rib_width", + "wipe_tower_rotation_angle", + "wipe_tower_type", + "wipe_tower_wall_type", + "wipe_tower_x", + "wipe_tower_y", + "wiping_volumes_extruders", + "wrapping_detection_layers", + "wrapping_exclude_area", + "xy_contour_compensation", + "xy_hole_compensation", + "z_hop", + "z_hop_types", + "zaa_dont_alternate_fill_direction", + "zaa_enabled", + "zaa_min_z", + "zaa_minimize_perimeter_height", +}; + +static const std::vector s_Preset_filament_options = { + "activate_air_filtration", + "activate_air_filtration_during_print", + "activate_air_filtration_on_completion", + "activate_chamber_temp_control", + "adaptive_pressure_advance", + "adaptive_pressure_advance_bridges", + "adaptive_pressure_advance_model", + "adaptive_pressure_advance_overhangs", + "additional_cooling_fan_speed", + "additional_fan_full_speed_layer", + "chamber_temperature", + "close_additional_fan_first_x_layers", + "close_fan_the_first_x_layers", + "compatible_printers", + "compatible_printers_condition", + "compatible_prints", + "compatible_prints_condition", + "complete_print_exhaust_fan_speed", + "cool_plate_temp", + "cool_plate_temp_initial_layer", + "default_filament_colour", + "dont_slow_down_outer_wall", + "during_print_exhaust_fan_speed", + "enable_overhang_bridge_fan", + "enable_pressure_advance", + "eng_plate_temp", + "eng_plate_temp_initial_layer", + "fan_cooling_layer_time", + "fan_max_speed", + "fan_min_speed", + "filament_adaptive_volumetric_speed", + "filament_adhesiveness_category", + "filament_change_extrusion_role_gcode", + "filament_change_length", + "filament_colour", + "filament_cooling_before_tower", + "filament_cooling_final_speed", + "filament_cooling_initial_speed", + "filament_cooling_moves", + "filament_cost", + "filament_density", + "filament_deretraction_speed", + "filament_diameter", + "filament_end_gcode", + "filament_extruder_variant", + "filament_flow_ratio", + "filament_flush_temp", + "filament_flush_volumetric_speed", + "filament_ironing_flow", + "filament_ironing_inset", + "filament_ironing_spacing", + "filament_ironing_speed", + "filament_is_support", + "filament_loading_speed", + "filament_loading_speed_start", + "filament_long_retractions_when_cut", + "filament_max_volumetric_speed", + "filament_minimal_purge_on_wipe_tower", + "filament_multitool_ramming", + "filament_multitool_ramming_flow", + "filament_multitool_ramming_volume", + "filament_notes", + "filament_printable", + "filament_ramming_parameters", + "filament_retract_before_wipe", + "filament_retract_lift_above", + "filament_retract_lift_below", + "filament_retract_lift_enforce", + "filament_retract_restart_extra", + "filament_retract_when_changing_layer", + "filament_retraction_distances_when_cut", + "filament_retraction_length", + "filament_retraction_minimum_travel", + "filament_retraction_speed", + "filament_shrink", + "filament_shrinkage_compensation_z", + "filament_soluble", + "filament_stamping_distance", + "filament_stamping_loading_speed", + "filament_start_gcode", + "filament_toolchange_delay", + "filament_tower_interface_pre_extrusion_dist", + "filament_tower_interface_pre_extrusion_length", + "filament_tower_interface_print_temp", + "filament_tower_interface_purge_volume", + "filament_tower_ironing_area", + "filament_type", + "filament_unloading_speed", + "filament_unloading_speed_start", + "filament_vendor", + "filament_wipe", + "filament_wipe_distance", + "filament_z_hop", + "filament_z_hop_types", + "first_x_layer_fan_speed", + "full_fan_speed_layer", + "hot_plate_temp", + "hot_plate_temp_initial_layer", + "idle_temperature", + "inherits", + "internal_bridge_fan_speed", + "ironing_fan_speed", + "long_retractions_when_ec", + "nozzle_temperature", + "nozzle_temperature_initial_layer", + "nozzle_temperature_range_high", + "nozzle_temperature_range_low", + "overhang_fan_speed", + "overhang_fan_threshold", + "pellet_flow_coefficient", + "pressure_advance", + "reduce_fan_stop_start_freq", + "required_nozzle_HRC", + "retraction_distances_when_ec", + "slow_down_for_layer_cooling", + "slow_down_layer_time", + "slow_down_min_speed", + "supertack_plate_temp", + "supertack_plate_temp_initial_layer", + "support_material_interface_fan_speed", + "temperature_vitrification", + "textured_cool_plate_temp", + "textured_cool_plate_temp_initial_layer", + "textured_plate_temp", + "textured_plate_temp_initial_layer", + "volumetric_speed_coefficients", +}; + +static const std::vector s_Preset_printer_options = { + "adaptive_bed_mesh_margin", + "auxiliary_fan", + "bbl_use_printhost", + "bed_custom_model", + "bed_custom_texture", + "bed_exclude_area", + "bed_mesh_max", + "bed_mesh_min", + "bed_mesh_probe_distance", + "bed_temperature_formula", + "before_layer_change_gcode", + "best_object_pos", + "change_extrusion_role_gcode", + "change_filament_gcode", + "cooling_tube_length", + "cooling_tube_retraction", + "default_bed_type", + "default_nozzle_volume_type", + "default_print_profile", + "disable_m73", + "emit_machine_limits_to_gcode", + "enable_filament_ramming", + "enable_long_retraction_when_cut", + "enable_power_loss_recovery", + "extra_loading_move", + "extruder_clearance_height_to_lid", + "extruder_clearance_height_to_rod", + "extruder_clearance_radius", + "extruder_printable_area", + "extruder_printable_height", + "extruder_type", + "extruder_variant_list", + "fan_kickstart", + "fan_speedup_overhangs", + "fan_speedup_time", + "file_start_gcode", + "gcode_flavor", + "grab_length", + "head_wrap_detect_zone", + "high_current_on_filament_swap", + "host_type", + "inherits", + "layer_change_gcode", + "long_retractions_when_cut", + "machine_end_gcode", + "machine_load_filament_time", + "machine_pause_gcode", + "machine_start_gcode", + "machine_tool_change_time", + "machine_unload_filament_time", + "manual_filament_change", + "master_extruder_id", + "nozzle_flush_dataset", + "nozzle_height", + "nozzle_hrc", + "nozzle_type", + "nozzle_volume", + "parking_pos_retraction", + "part_cooling_fan_min_pwm", + "pellet_modded_printer", + "physical_extruder_map", + "preferred_orientation", + "print_host", + "print_host_webui", + "printable_area", + "printable_height", + "printer_agent", + "printer_extruder_id", + "printer_extruder_variant", + "printer_model", + "printer_notes", + "printer_structure", + "printer_technology", + "printer_variant", + "printhost_apikey", + "printhost_authorization_type", + "printhost_cafile", + "printhost_password", + "printhost_port", + "printhost_ssl_ignore_revoke", + "printhost_user", + "printing_by_object_gcode", + "purge_in_prime_tower", + "retract_lift_enforce", + "retraction_distances_when_cut", + "scan_first_layer", + "silent_mode", + "single_extruder_multi_material", + "support_air_filtration", + "support_chamber_temp_control", + "support_multi_bed_types", + "support_object_skip_flush", + "template_custom_gcode", + "thumbnails", + "thumbnails_format", + "time_cost", + "time_lapse_gcode", + "tool_change_on_wipe_tower", + "travel_slope", + "upward_compatible_machine", + "use_firmware_retraction", + "use_relative_e_distances", + "wipe_tower_type", + "wrapping_detection_gcode", + "wrapping_detection_layers", + "wrapping_exclude_area", + "z_hop_types", + "z_offset", +}; diff --git a/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp b/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp new file mode 100644 index 0000000000..8d2b375c6c --- /dev/null +++ b/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp @@ -0,0 +1,5716 @@ +// ===== AUTO-GENERATED by tools/config_codegen.py ===== +// DO NOT EDIT MANUALLY. Edit .proto files and re-run codegen. + + def = this->add("filament_end_gcode", coStrings); + def->label = L("End G-code"); + def->category = L("Filament/Advanced"); + def->tooltip = L("End G-code when finishing the printing of this filament."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 120; + def->set_default_value(new ConfigOptionStrings{" "}); + + def = this->add("filament_start_gcode", coStrings); + def->label = L("Start G-code"); + def->category = L("Filament/Advanced"); + def->tooltip = L("Start G-code when starting the printing of this filament."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 12; + def->set_default_value(new ConfigOptionStrings{" "}); + + def = this->add("filament_change_extrusion_role_gcode", coStrings); + def->label = L("Change extrusion role G-code (filament)"); + def->category = L("Filament/Advanced"); + def->tooltip = L("This G-code is inserted when the extrusion role is changed for the active filament."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("enable_overhang_bridge_fan", coBools); + def->label = L("Force cooling for overhangs and bridges"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Enable this option to allow adjustment of the part cooling fan speed for specifically for overhangs, internal and external bridges. Setting the fan speed specifically for these features can improve overall print quality and reduce warping."); + def->set_default_value(new ConfigOptionBools{true}); + + def = this->add("overhang_fan_speed", coInts); + def->label = L("Overhangs and external bridges fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Use this part cooling fan speed when printing bridges or overhang walls with an overhang threshold that exceeds the value set in the 'Overhangs cooling threshold' parameter above. Increasing the cooling specifically for overhangs and bridges can improve the overall print quality of these features.\n\nPlease note, this fan speed is clamped on the lower end by the minimum fan speed threshold set above. It is also adjusted upwards up to the maximum fan speed threshold when the minimum layer time threshold is not met."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{100}); + + def = this->add("overhang_fan_threshold", coEnums); + def->label = L("Overhang cooling activation threshold"); + def->category = L("Filament/Cooling"); + def->tooltip = L("When the overhang exceeds this specified threshold, force the cooling fan to run at the 'Overhang Fan Speed' set below. This threshold is expressed as a percentage, indicating the portion of each line's width that is unsupported by the layer beneath it. Setting this value to 0% forces the cooling fan to run for all outer walls, regardless of the overhang degree."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("0%"); + def->enum_values.push_back("10%"); + def->enum_values.push_back("25%"); + def->enum_values.push_back("50%"); + def->enum_values.push_back("75%"); + def->enum_values.push_back("95%"); + def->enum_labels.push_back(L("0%")); + def->enum_labels.push_back(L("10%")); + def->enum_labels.push_back(L("25%")); + def->enum_labels.push_back(L("50%")); + def->enum_labels.push_back(L("75%")); + def->enum_labels.push_back(L("95%")); + def->set_default_value(new ConfigOptionEnumsGeneric{ (int)Overhang_threshold_bridge }); + + def = this->add("slow_down_for_layer_cooling", coBools); + def->label = L("Slow printing down for better layer cooling"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Enable this option to slow printing speed down to make the final layer time not shorter than the layer time threshold in \"Max fan speed threshold\", so that layer can be cooled for longer time. This can improve the cooling quality for needle and small details."); + def->set_default_value(new ConfigOptionBools{true}); + + def = this->add("activate_air_filtration", coBools); + def->label = L("Activate air filtration"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Activate for better air filtration. G-code command: M106 P3 S(0-255)"); + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("activate_air_filtration_during_print", coBools); + def->full_label = L("During print"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Enable this to override the fan speed set in custom G-code during print."); + def->set_default_value(new ConfigOptionBools{true}); + + def = this->add("activate_air_filtration_on_completion", coBools); + def->full_label = L("On completion"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Enable this to override the fan speed set in custom G-code after print completion."); + def->set_default_value(new ConfigOptionBools{true}); + + def = this->add("during_print_exhaust_fan_speed", coInts); + def->label = L("Fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Speed of exhaust fan during printing. This speed will override the speed in filament custom G-code."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionInts{60}); + + def = this->add("complete_print_exhaust_fan_speed", coInts); + def->label = L("Fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Speed of exhaust fan after printing completes."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionInts{80}); + + def = this->add("close_fan_the_first_x_layers", coInts); + def->label = L("No cooling for the first"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Turn off all cooling fans for the first few layers. This can be used to improve build plate adhesion."); + def->sidetext = L("layers"); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("reduce_fan_stop_start_freq", coBools); + def->label = L("Keep fan always on"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Enabling this setting means that the part cooling fan will never stop completely and will run at least at minimum speed to reduce the frequency of starting and stopping."); + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("dont_slow_down_outer_wall", coBools); + def->label = L("Don't slow down outer walls"); + def->category = L("Filament/Cooling"); + def->tooltip = L("If enabled, this setting will ensure external perimeters are not slowed down to meet the minimum layer time. This is particularly helpful in the below scenarios:\n1. To avoid changes in shine when printing glossy filaments\n2. To avoid changes in external wall speed which may create slight wall artifacts that appear like Z banding\n3. To avoid printing at speeds which cause VFAs (fine artifacts) on the external walls"); + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("fan_cooling_layer_time", coFloats); + def->label = L("Layer time"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Part cooling fan will be enabled for layers of which estimated time is shorter than this value. Fan speed is interpolated between the minimum and maximum fan speeds according to layer printing time."); + def->sidetext = L("s"); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionFloats{60.0f}); + + def = this->add("full_fan_speed_layer", coInts); + def->label = L("Full fan speed at layer"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Fan speed will be ramped up linearly from zero at layer \"close_fan_the_first_x_layers\" to maximum at layer \"full_fan_speed_layer\". \"full_fan_speed_layer\" will be ignored if lower than \"close_fan_the_first_x_layers\", in which case the fan will be running at maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1."); + def->sidetext = L("layer"); + def->min = 0; + def->max = 1000; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("support_material_interface_fan_speed", coInts); + def->label = L("Support interface fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("This part cooling fan speed is applied when printing support interfaces. Setting this parameter to a higher than regular speed reduces the layer binding strength between supports and the supported part, making them easier to separate.\nSet to -1 to disable it.\nThis setting is overridden by disable_fan_first_layers."); + def->sidetext = L("%"); + def->min = -1; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{-1}); + + def = this->add("internal_bridge_fan_speed", coInts); + def->label = L("Internal bridges fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("The part cooling fan speed used for all internal bridges. Set to -1 to use the overhang fan speed settings instead.\n\nReducing the internal bridges fan speed, compared to your regular fan speed, can help reduce part warping due to excessive cooling applied over a large surface for a prolonged period of time."); + def->sidetext = L("%"); + def->min = -1; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{-1}); + + def = this->add("ironing_fan_speed", coInts); + def->label = L("Ironing fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("This part cooling fan speed is applied when ironing. Setting this parameter to a lower than regular speed reduces possible nozzle clogging due to the low volumetric flow rate, making the interface smoother.\nSet to -1 to disable it."); + def->sidetext = L("%"); + def->min = -1; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{-1}); + + def = this->add("fan_max_speed", coFloats); + def->label = L("Fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Part cooling fan speed may be increased when auto cooling is enabled. This is the maximum speed for the part cooling fan."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionFloats{100}); + + def = this->add("fan_min_speed", coFloats); + def->label = L("Fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Minimum speed for part cooling fan."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionFloats{20}); + + def = this->add("additional_cooling_fan_speed", coInts); + def->label = L("Fan speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed during printing except the first several layers which is defined by no cooling layers.\nPlease enable auxiliary_fan in printer settings to use this feature. G-code command: M106 P2 S(0-255)"); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("slow_down_min_speed", coFloats); + def->label = L("Min print speed"); + def->category = L("Filament/Cooling"); + def->tooltip = L("The minimum print speed to which the printer slows down to maintain the minimum layer time defined above when the slowdown for better layer cooling is enabled."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{10.}); + + def = this->add("slow_down_layer_time", coFloats); + def->label = L("Layer time"); + def->category = L("Filament/Cooling"); + def->tooltip = L("The printing speed in exported G-code will be slowed down when the estimated layer time is shorter than this value in order to get better cooling for these layers."); + def->sidetext = L("s"); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionFloats{5.0f}); + + def = this->add("compatible_printers_condition", coString); + def->label = L("Condition"); + def->category = L("Filament/Dependencies"); + def->tooltip = L("A boolean expression using the configuration values of an active printer profile. If this expression evaluates to true, this profile is considered compatible with the active printer profile."); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("compatible_prints_condition", coString); + def->label = L("Condition"); + def->category = L("Filament/Dependencies"); + def->tooltip = L("A boolean expression using the configuration values of an active print profile. If this expression evaluates to true, this profile is considered compatible with the active print profile."); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("supertack_plate_temp", coInts); + def->label = L("Other layers"); + def->full_label = L("Bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Cool Plate SuperTack."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 120; + def->set_default_value(new ConfigOptionInts{35}); + + def = this->add("cool_plate_temp", coInts); + def->label = L("Other layers"); + def->full_label = L("Bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Cool Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{35}); + + def = this->add("textured_cool_plate_temp", coInts); + def->label = L("Other layers"); + def->full_label = L("Bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Textured Cool Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{40}); + + def = this->add("eng_plate_temp", coInts); + def->label = L("Other layers"); + def->full_label = L("Bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Engineering Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{45}); + + def = this->add("hot_plate_temp", coInts); + def->label = L("Other layers"); + def->full_label = L("Bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the High Temp Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{45}); + + def = this->add("textured_plate_temp", coInts); + def->label = L("Other layers"); + def->full_label = L("Bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Textured PEI Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{45}); + + def = this->add("supertack_plate_temp_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Cool Plate SuperTack."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 120; + def->set_default_value(new ConfigOptionInts{35}); + + def = this->add("cool_plate_temp_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Cool Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 120; + def->set_default_value(new ConfigOptionInts{35}); + + def = this->add("textured_cool_plate_temp_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Textured Cool Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 120; + def->set_default_value(new ConfigOptionInts{40}); + + def = this->add("eng_plate_temp_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Engineering Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{45}); + + def = this->add("hot_plate_temp_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the High Temp Plate."); + def->sidetext = L("\u2103"); + def->max = 300; + def->set_default_value(new ConfigOptionInts{45}); + + def = this->add("textured_plate_temp_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer bed temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Textured PEI Plate."); + def->sidetext = L("\u2103"); + def->min = 0; + def->max = 300; + def->set_default_value(new ConfigOptionInts{45}); + + def = this->add("filament_flow_ratio", coFloats); + def->label = L("Flow ratio"); + def->category = L("Filament/Filament"); + def->tooltip = L("The material may have volumetric change after switching between molten and crystalline states. This setting changes all extrusion flow of this filament in G-code proportionally. The recommended value range is between 0.95 and 1.05. You may be able to tune this value to get a nice flat surface if there is slight overflow or underflow."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{1.}); + + def = this->add("enable_pressure_advance", coBools); + def->label = L("Enable pressure advance"); + def->category = L("Filament/Filament"); + def->tooltip = L("Enable pressure advance, auto calibration result will be overwritten once enabled."); + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("pressure_advance", coFloats); + def->label = L("Pressure advance"); + def->category = L("Filament/Filament"); + def->tooltip = L("Pressure advance (Klipper) AKA Linear advance factor (Marlin)."); + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.02}); + + def = this->add("adaptive_pressure_advance", coBools); + def->label = L("Enable adaptive pressure advance (beta)"); + def->category = L("Filament/Filament"); + def->tooltip = L("With increasing print speeds (and hence increasing volumetric flow through the nozzle) and increasing accelerations, it has been observed that the effective PA value typically decreases. This means that a single PA value is not always 100% optimal for all features and a compromise value is usually used that does not cause too much bulging on features with lower flow speed and accelerations while also not causing gaps on faster features.\n\nThis feature aims to address this limitation by modeling the response of your printer's extrusion system depending on the volumetric flow speed and acceleration it is printing at. Internally, it generates a fitted model that can extrapolate the needed pressure advance for any given volumetric flow speed and acceleration, which is then emitted to the printer depending on the current print conditions.\n\nWhen enabled, the pressure advance value above is overridden. However, a reasonable default value above is strongly recommended to act as a fallback and for when tool changing.\n\n"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("adaptive_pressure_advance_model", coStrings); + def->label = L("Adaptive pressure advance measurements (beta)"); + def->category = L("Filament/Filament"); + def->tooltip = L("Add sets of pressure advance (PA) values, the volumetric flow speeds and accelerations they were measured at, separated by a comma. One set of values per line. For example\n0.04,3.96,3000\n0.033,3.96,10000\n0.029,7.91,3000\n0.026,7.91,10000\n\nHow to calibrate:\n1. Run the pressure advance test for at least 3 speeds per acceleration value. It is recommended that the test is run for at least the speed of the external perimeters, the speed of the internal perimeters and the fastest feature print speed in your profile (usually its the sparse or solid infill). Then run them for the same speeds for the slowest and fastest print accelerations, and no faster than the recommended maximum acceleration as given by the Klipper input shaper\n2. Take note of the optimal PA value for each volumetric flow speed and acceleration. You can find the flow number by selecting flow from the color scheme drop down and move the horizontal slider over the PA pattern lines. The number should be visible at the bottom of the page. The ideal PA value should be decreasing the higher the volumetric flow is. If it is not, confirm that your extruder is functioning correctly. The slower and with less acceleration you print, the larger the range of acceptable PA values. If no difference is visible, use the PA value from the faster test\n3. Enter the triplets of PA values, Flow and Accelerations in the text box here and save your filament profile."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 15; + def->set_default_value(new ConfigOptionStrings{"0,0,0\n0,0,0"}); + + def = this->add("adaptive_pressure_advance_overhangs", coBools); + def->label = L("Enable adaptive pressure advance for overhangs (beta)"); + def->category = L("Filament/Filament"); + def->tooltip = L("Enable adaptive PA for overhangs as well as when flow changes within the same feature. This is an experimental option, as if the PA profile is not set accurately, it will cause uniformity issues on the external surfaces before and after overhangs.\n"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("adaptive_pressure_advance_bridges", coFloats); + def->label = L("Pressure advance for bridges"); + def->category = L("Filament/Filament"); + def->tooltip = L("Pressure advance value for bridges. Set to 0 to disable.\n\nA lower PA value when printing bridges helps reduce the appearance of slight under extrusion immediately after bridges. This is caused by the pressure drop in the nozzle when printing in the air and a lower PA helps counteract this."); + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.0}); + + def = this->add("default_filament_colour", coStrings); + def->label = L("Default color"); + def->category = L("Filament/Filament"); + def->tooltip = L("Default filament color.\nRight click to reset value to system default."); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::color; + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("required_nozzle_HRC", coInts); + def->label = L("Required nozzle HRC"); + def->category = L("Filament/Filament"); + def->tooltip = L("Minimum HRC of nozzle required to print the filament. Zero means no checking of nozzle's HRC."); + def->min = 0; + def->max = 500; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("filament_max_volumetric_speed", coFloats); + def->label = L("Max volumetric speed"); + def->category = L("Filament/Filament"); + def->tooltip = L("This setting stands for how much volume of filament can be melted and extruded per second. Printing speed is limited by max volumetric speed, in case of too high and unreasonable speed setting. Can't be zero."); + def->sidetext = L("mm³/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{2.}); + + def = this->add("filament_diameter", coFloats); + def->label = L("Diameter"); + def->category = L("Filament/Filament"); + def->tooltip = L("Filament diameter is used to calculate extrusion in G-code, so it is important and should be accurate."); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloats{1.75}); + + def = this->add("pellet_flow_coefficient", coFloats); + def->label = L("Pellet flow coefficient"); + def->category = L("Filament/Filament"); + def->tooltip = L("Pellet flow coefficient is empirically derived and allows for volume calculation for pellet printers.\n\nInternally it is converted to filament_diameter. All other volume calculations remain the same.\n\nfilament_diameter = sqrt( (4 * pellet_flow_coefficient) / PI )"); + def->min = 0; + def->set_default_value(new ConfigOptionFloats{0.4157}); + + def = this->add("filament_adaptive_volumetric_speed", coBools); + def->label = L("Adaptive volumetric speed"); + def->category = L("Filament/Filament"); + def->tooltip = L("When enabled, the extrusion flow is limited by the smaller of the fitted value (calculated from line width and layer height) and the user-defined maximum flow. When disabled, only the user-defined maximum flow is applied.\n\nNote: Experimental and incomplete feature imported from BBS. Functional for some profiles that already have the variable saved."); + def->mode = comDevelop; + def->nullable = true; + def->set_default_value(new ConfigOptionBoolsNullable{false}); + + def = this->add("filament_shrink", coPercents); + def->label = L("Shrinkage (XY)"); + def->category = L("Filament/Filament"); + def->tooltip = L("Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm). The part will be scaled in XY to compensate. For multi-material prints, ensure filament shrinkage matches across all used filaments\nBe sure to allow enough space between objects, as this compensation is done after the checks."); + def->sidetext = L("%"); + def->min = 50; + def->max = 150; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercents{100}); + + def = this->add("filament_shrinkage_compensation_z", coPercents); + def->label = L("Shrinkage (Z)"); + def->category = L("Filament/Filament"); + def->tooltip = L("Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm). The part will be scaled in Z to compensate."); + def->sidetext = L("%"); + def->min = 50; + def->max = 150; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercents{100}); + + def = this->add("filament_adhesiveness_category", coInts); + def->label = L("Adhesiveness Category"); + def->category = L("Filament/Filament"); + def->tooltip = L("Filament category."); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("filament_density", coFloats); + def->label = L("Density"); + def->category = L("Filament/Filament"); + def->tooltip = L("Filament density. For statistics only."); + def->sidetext = L("g/cm³"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("filament_type", coStrings); + def->label = L("Type"); + def->category = L("Filament/Filament"); + def->tooltip = L("The material type of filament."); + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; + def->gui_flags = "show_value"; + def->set_default_value(new ConfigOptionStrings{"PLA"}); + + def = this->add("filament_soluble", coBools); + def->label = L("Soluble material"); + def->category = L("Filament/Filament"); + def->tooltip = L("Soluble material is commonly used to print supports and support interfaces."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("filament_change_length", coFloats); + def->label = L("Filament ramming length"); + def->category = L("Filament/Filament"); + def->tooltip = L("When changing the extruder, it is recommended to extrude a certain length of filament from the original extruder. This helps minimize nozzle oozing."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{10}); + + def = this->add("filament_is_support", coBools); + def->label = L("Support material"); + def->category = L("Filament/Filament"); + def->tooltip = L("Support material is commonly used to print supports and support interfaces."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("temperature_vitrification", coInts); + def->label = L("Softening temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("The material softens at this temperature, so when the bed temperature is equal to or greater than this, it's highly recommended to open the front door and/or remove the upper glass to avoid clogging."); + def->sidetext = L("\u2103"); + def->set_default_value(new ConfigOptionInts{100}); + + def = this->add("filament_cost", coFloats); + def->label = L("Price"); + def->category = L("Filament/Filament"); + def->tooltip = L("Filament price. For statistics only."); + def->sidetext = L("money/kg"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("filament_vendor", coStrings); + def->label = L("Vendor"); + def->category = L("Filament/Filament"); + def->tooltip = L("Vendor of filament. For show only."); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings{L("(Undefined)")}); + + def = this->add("nozzle_temperature_initial_layer", coInts); + def->label = L("First layer"); + def->full_label = L("First layer nozzle temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Nozzle temperature for printing the first layer when using this filament."); + def->sidetext = L("\u2103"); + def->min = 0; + def->set_default_value(new ConfigOptionInts{200}); + + def = this->add("activate_chamber_temp_control", coBools); + def->label = L("Activate temperature control"); + def->category = L("Filament/Filament"); + def->tooltip = L("Enable this option for automated chamber temperature control. This option activates the emitting of an M191 command before the \"machine_start_gcode\"\n which sets the chamber temperature and waits until it is reached. In addition, it emits an M141 command at the end of the print to turn off the chamber heater, if present.\n\nThis option relies on the firmware supporting the M191 and M141 commands either via macros or natively and is usually used when an active chamber heater is installed."); + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("chamber_temperature", coInts); + def->label = L("Chamber temperature"); + def->full_label = L("Chamber temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("For high-temperature materials like ABS, ASA, PC, and PA, a higher chamber temperature can help suppress or reduce warping and potentially lead to higher interlayer bonding strength. However, at the same time, a higher chamber temperature will reduce the efficiency of air filtration for ABS and ASA.\n\nFor PLA, PETG, TPU, PVA, and other low-temperature materials, this option should be disabled (set to 0) as the chamber temperature should be low to avoid extruder clogging caused by material softening at the heat break.\n\nIf enabled, this parameter also sets a G-code variable named chamber_temperature, which can be used to pass the desired chamber temperature to your print start macro, or a heat soak macro like this: PRINT_START (other variables) CHAMBER_TEMP=[chamber_temperature]. This may be useful if your printer does not support M141/M191 commands, or if you desire to handle heat soaking in the print start macro if no active chamber heater is installed."); + def->sidetext = L("\u2103"); + def->min = 0; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("nozzle_temperature", coInts); + def->label = L("Other layers"); + def->full_label = L("Nozzle temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Nozzle temperature for layers after the initial one."); + def->sidetext = L("\u2103"); + def->min = 0; + def->set_default_value(new ConfigOptionInts{200}); + + def = this->add("nozzle_temperature_range_low", coInts); + def->label = L("Min"); + def->category = L("Filament/Filament"); + def->sidetext = L("\u2103"); + def->min = 0; + def->set_default_value(new ConfigOptionInts{190}); + + def = this->add("nozzle_temperature_range_high", coInts); + def->label = L("Max"); + def->category = L("Filament/Filament"); + def->sidetext = L("\u2103"); + def->min = 0; + def->set_default_value(new ConfigOptionInts{240}); + + def = this->add("idle_temperature", coInts); + def->label = L("Idle temperature"); + def->category = L("Filament/Filament"); + def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups. This is only used when 'Ooze prevention' is active in Print Settings. Set to 0 to disable."); + def->sidetext = L("\u2103"); + def->min = 0; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("filament_loading_speed", coFloats); + def->label = L("Loading speed"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Speed used for loading the filament on the wipe tower."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{28.}); + + def = this->add("filament_loading_speed_start", coFloats); + def->label = L("Loading speed at the start"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Speed used at the very beginning of loading phase."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{3.}); + + def = this->add("filament_unloading_speed", coFloats); + def->label = L("Unloading speed"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect initial part of unloading just after ramming)."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{90.}); + + def = this->add("filament_unloading_speed_start", coFloats); + def->label = L("Unloading speed at the start"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{100.}); + + def = this->add("filament_toolchange_delay", coFloats); + def->label = L("Delay after unloading"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Time to wait after the filament is unloaded. May help to get reliable tool changes with flexible materials that may need more time to shrink to original dimensions."); + def->sidetext = L("s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("filament_cooling_moves", coInts); + def->label = L("Number of cooling moves"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Filament is cooled by being moved back and forth in the cooling tubes. Specify desired number of these moves."); + def->min = 0; + def->max = 20; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{4}); + + def = this->add("filament_stamping_loading_speed", coFloats); + def->label = L("Stamping loading speed"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Speed used for stamping."); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("filament_stamping_distance", coFloats); + def->label = L("Stamping distance measured from the center of the cooling tube"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("If set to non-zero value, filament is moved toward the nozzle between the individual cooling moves (\"stamping\"). This option configures how long this movement should be before the filament is retracted again."); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("filament_cooling_initial_speed", coFloats); + def->label = L("Speed of the first cooling move"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{2.2}); + + def = this->add("filament_minimal_purge_on_wipe_tower", coFloats); + def->label = L("Minimal purge on wipe tower"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside the nozzle may not be known, and the filament pressure is likely not yet stable. Before purging the print head into an infill or a sacrificial object, Orca Slicer will always prime this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably."); + def->sidetext = L("mm³"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{15.}); + + def = this->add("filament_tower_interface_pre_extrusion_dist", coFloats); + def->label = L("Interface layer pre-extrusion distance"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Pre-extrusion distance for prime tower interface layer (where different materials meet)."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{10.}); + + def = this->add("filament_tower_interface_pre_extrusion_length", coFloats); + def->label = L("Interface layer pre-extrusion length"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Pre-extrusion length for prime tower interface layer (where different materials meet)."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("filament_tower_ironing_area", coFloats); + def->label = L("Tower ironing area"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Ironing area for prime tower interface layer (where different materials meet)."); + def->sidetext = L("mm²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{4.}); + + def = this->add("filament_tower_interface_purge_volume", coFloats); + def->label = L("Interface layer purge length"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Purge length for prime tower interface layer (where different materials meet)."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{20.}); + + def = this->add("filament_tower_interface_print_temp", coInts); + def->label = L("Interface layer print temperature"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Print temperature for prime tower interface layer (where different materials meet). If set to -1, use max recommended nozzle temperature."); + def->sidetext = L("\u2103"); + def->min = -1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInts{-1}); + + def = this->add("filament_cooling_final_speed", coFloats); + def->label = L("Speed of the last cooling move"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Cooling moves are gradually accelerating towards this speed."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{3.4}); + + def = this->add("filament_multitool_ramming", coBools); + def->label = L("Enable ramming for multi-tool setups"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Perform ramming when using multi-tool printer (i.e. when the 'Single Extruder Multimaterial' in Printer Settings is unchecked). When checked, a small amount of filament is rapidly extruded on the wipe tower just before the tool change. This option is only used when the wipe tower is enabled."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("filament_multitool_ramming_volume", coFloats); + def->label = L("Multi-tool ramming volume"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("The volume to be rammed before the tool change."); + def->sidetext = L("mm³"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{10.}); + + def = this->add("filament_multitool_ramming_flow", coFloats); + def->label = L("Multi-tool ramming flow"); + def->category = L("Filament/Multimaterial"); + def->tooltip = L("Flow used for ramming the filament before the tool change."); + def->sidetext = L("mm³/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{10.}); + + def = this->add("long_retractions_when_ec", coBools); + def->label = L("Long retraction when extruder change"); + def->category = L("Filament/Multimaterial"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionBoolsNullable{false}); + + def = this->add("retraction_distances_when_ec", coFloats); + def->label = L("Retraction distance when extruder change"); + def->category = L("Filament/Multimaterial"); + def->sidetext = L("mm"); + def->min = 0; + def->max = 10; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{10}); + + def = this->add("filament_notes", coStrings); + def->label = L("Filament notes"); + def->category = L("Filament/Notes"); + def->tooltip = L("You can put your notes regarding the filament here."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 13; + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("filament_ironing_flow", coPercents); + def->label = L("Ironing flow"); + def->category = L("Quality"); + def->tooltip = L("Filament-specific override for ironing flow. This allows you to customize the ironing flow for each filament type. Too high value results in overextrusion on the surface."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionPercentsNullable{ConfigOptionPercentsNullable::nil_value()}); + + def = this->add("filament_ironing_spacing", coFloats); + def->label = L("Ironing line spacing"); + def->category = L("Quality"); + def->tooltip = L("Filament-specific override for ironing line spacing. This allows you to customize the spacing between ironing lines for each filament type."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 1; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{ConfigOptionFloatsNullable::nil_value()}); + + def = this->add("filament_ironing_inset", coFloats); + def->label = L("Ironing inset"); + def->category = L("Quality"); + def->tooltip = L("Filament-specific override for ironing inset. This allows you to customize the distance to keep from the edges when ironing for each filament type."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{ConfigOptionFloatsNullable::nil_value()}); + + def = this->add("filament_ironing_speed", coFloats); + def->label = L("Ironing speed"); + def->category = L("Speed"); + def->tooltip = L("Filament-specific override for ironing speed. This allows you to customize the print speed of ironing lines for each filament type."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{ConfigOptionFloatsNullable::nil_value()}); + + def = this->add("filament_flush_volumetric_speed", coFloats); + def->label = L("Flush volumetric speed"); + def->tooltip = L("Volumetric speed when flushing filament. 0 indicates the max volumetric speed."); + def->sidetext = L("mm³/s"); + def->min = 0; + def->max = 200; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0}); + + def = this->add("filament_cooling_before_tower", coFloats); + def->label = L("Wipe tower cooling"); + def->tooltip = L("Temperature drop before entering filament tower"); + def->sidetext = L("\u2103"); + def->mode = comDevelop; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{10.}); + + def = this->add("filament_printable", coInts); + def->label = L("Filament printable"); + def->tooltip = L("The filament is printable in extruder."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInts{3}); + + def = this->add("close_additional_fan_first_x_layers", coInts); + def->label = L("For the first"); + def->tooltip = L("Set special auxiliary cooling fan for the first certain layers."); + def->sidetext = L("layers"); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("additional_fan_full_speed_layer", coInts); + def->label = L("Full fan speed at layer"); + def->tooltip = L("Auxiliary fan speed will be ramped up linearly from layer \"For the first\" to maximum at layer \"Full fan speed at layer\".\n\"Full fan speed at layer\" will be ignored if lower than \"For the first\", in which case the fan will run at maximum allowed speed at layer \"For the first\" + 1."); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("first_x_layer_fan_speed", coFloats); + def->label = L("Fan speed"); + def->tooltip = L("Special auxiliary cooling fan speed, effective only for the first x layers."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionFloats{0}); + + def = this->add("filament_extruder_variant", coStrings); + def->label = L("Filament's extruder variant"); + def->tooltip = L("Filament's extruder variant."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); + + def = this->add("filament_retraction_length", coFloats); + def->label = L("Length"); + def->full_label = L("Retraction Length"); + def->tooltip = L("Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction."); + def->sidetext = L("mm"); + def->mode = comSimple; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0.8}); + + def = this->add("filament_z_hop", coFloats); + def->label = L("Z-hop height"); + def->tooltip = L("Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. It prevents nozzle from hitting the print when travel move. Using spiral lines to lift Z can prevent stringing."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 5; + def->mode = comSimple; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0.4}); + + def = this->add("filament_z_hop_types", coEnums); + def->label = L("Z-hop type"); + def->tooltip = L("Type of Z-hop."); + def->mode = comAdvanced; + def->nullable = true; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Auto Lift"); + def->enum_values.push_back("Normal Lift"); + def->enum_values.push_back("Slope Lift"); + def->enum_values.push_back("Spiral Lift"); + def->enum_labels.push_back(L("Auto")); + def->enum_labels.push_back(L("Normal")); + def->enum_labels.push_back(L("Slope")); + def->enum_labels.push_back(L("Spiral")); + def->set_default_value(new ConfigOptionEnumsGenericNullable{ ZHopType::zhtSlope }); + + def = this->add("filament_retract_lift_above", coFloats); + def->label = L("Only lift Z above"); + def->tooltip = L("If you set this to a positive value, Z lift will only take place above the specified absolute Z."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0.}); + + def = this->add("filament_retract_lift_below", coFloats); + def->label = L("Only lift Z below"); + def->tooltip = L("If you set this to a positive value, Z lift will only take place below the specified absolute Z."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0.}); + + def = this->add("filament_retract_lift_enforce", coEnums); + def->label = L("On surfaces"); + def->tooltip = L("Enforce Z-Hop behavior. This setting is impacted by the above settings (Only lift Z above/below)."); + def->mode = comAdvanced; + def->nullable = true; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("All Surfaces"); + def->enum_values.push_back("Top Only"); + def->enum_values.push_back("Bottom Only"); + def->enum_values.push_back("Top and Bottom"); + def->enum_labels.push_back(L("All Surfaces")); + def->enum_labels.push_back(L("Top Only")); + def->enum_labels.push_back(L("Bottom Only")); + def->enum_labels.push_back(L("Top and Bottom")); + def->set_default_value(new ConfigOptionEnumsGenericNullable{ RetractLiftEnforceType ::rletAllSurfaces }); + + def = this->add("filament_retraction_speed", coFloats); + def->label = L("Retraction Speed"); + def->full_label = L("Retraction Speed"); + def->tooltip = L("Speed for retracting filament from the nozzle."); + def->sidetext = L("mm/s"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{30.}); + + def = this->add("filament_deretraction_speed", coFloats); + def->label = L("De-retraction Speed"); + def->full_label = L("De-retraction Speed"); + def->tooltip = L("Speed for reloading filament into the nozzle. Zero means same speed of retraction."); + def->sidetext = L("mm/s"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0.}); + + def = this->add("filament_retract_restart_extra", coFloats); + def->label = L("Extra length on restart"); + def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{0.}); + + def = this->add("filament_retraction_minimum_travel", coFloats); + def->label = L("Travel distance threshold"); + def->tooltip = L("Only trigger retraction when the travel distance is longer than this threshold."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{2.}); + + def = this->add("filament_wipe_distance", coFloats); + def->label = L("Wipe Distance"); + def->tooltip = L("Describe how long the nozzle will move along the last path when retracting."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{1.}); + + def = this->add("filament_retract_when_changing_layer", coBools); + def->label = L("Retract when change layer"); + def->tooltip = L("Force a retraction when changes layer."); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionBoolsNullable{false}); + + def = this->add("filament_wipe", coBools); + def->label = L("Wipe while retracting"); + def->tooltip = L("Move nozzle along the last extrusion path when retracting to clean any leaked material on the nozzle. This can minimize blobs when printing a new part after traveling."); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionBoolsNullable{false}); + + def = this->add("filament_retract_before_wipe", coPercents); + def->label = L("Retract amount before wipe"); + def->tooltip = L("The length of fast retraction before wipe, relative to retraction length."); + def->sidetext = L("%"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionPercentsNullable{100}); + + def = this->add("filament_long_retractions_when_cut", coBools); + def->label = L("Long retraction when cut (beta)"); + def->tooltip = L("Experimental feature: Retracting and cutting off the filament at a longer distance during changes to minimize purge. While this reduces flush significantly, it may also raise the risk of nozzle clogs or other printing problems."); + def->mode = comSimple; + def->nullable = true; + def->set_default_value(new ConfigOptionBoolsNullable{false}); + + def = this->add("filament_retraction_distances_when_cut", coFloats); + def->label = L("Retraction distance when cut"); + def->tooltip = L("Experimental feature: Retraction length before cutting off during filament change."); + def->min = 10; + def->max = 18; + def->mode = comSimple; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{18}); + + def = this->add("extruder", coInt); + def->label = L("Extruder"); + def->category = L("Extruders"); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->enum_labels.push_back(L("default")); + def->enum_labels.push_back(L("1")); + def->enum_labels.push_back(L("2")); + def->enum_labels.push_back(L("3")); + def->enum_labels.push_back(L("4")); + def->enum_labels.push_back(L("5")); + + def = this->add("has_scarf_joint_seam", coBool); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("machine_max_junction_deviation", coFloats); + def->full_label = L("Maximum Junction Deviation"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum junction deviation (M205 J, only apply if JD > 0 for Marlin Firmware\nIf your Marlin 2 printer uses Classic Jerk set this value to 0.)"); + def->sidetext = L("mm"); + def->min = 0; + def->max = 0.3; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.01f}); + + def = this->add("machine_min_extruding_rate", coFloats); + def->full_label = L("Minimum speed for extruding"); + def->category = L("Machine limits"); + def->tooltip = L("Minimum speed for extruding (M205 S)"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloats{0., 0.}); + + def = this->add("machine_min_travel_rate", coFloats); + def->full_label = L("Minimum travel speed"); + def->category = L("Machine limits"); + def->tooltip = L("Minimum travel speed (M205 T)"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloats{0., 0.}); + + def = this->add("machine_max_acceleration_extruding", coFloats); + def->full_label = L("Maximum acceleration for extruding"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration for extruding (M204 P)"); + def->sidetext = L("mm/s²"); + def->min = 0; + def->set_default_value(new ConfigOptionFloats{1500., 1250.}); + + def = this->add("machine_max_acceleration_retracting", coFloats); + def->full_label = L("Maximum acceleration for retracting"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration for retracting (M204 R)"); + def->sidetext = L("mm/s²"); + def->min = 0; + def->set_default_value(new ConfigOptionFloats{1500., 1250.}); + + def = this->add("machine_max_acceleration_travel", coFloats); + def->full_label = L("Maximum acceleration for travel"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration for travel (M204 T), it only applies to Marlin 2."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0., 0.}); + + def = this->add("machine_max_speed_x", coFloats); + def->full_label = L("Maximum speed X"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum speed of X axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{500., 200.}); + + def = this->add("machine_max_acceleration_x", coFloats); + def->full_label = L("Maximum acceleration X"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration of the X axis"); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{1000., 1000.}); + + def = this->add("machine_max_jerk_x", coFloats); + def->full_label = L("Maximum jerk X"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum jerk of the X axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{10., 10.}); + + def = this->add("machine_max_speed_y", coFloats); + def->full_label = L("Maximum speed Y"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum speed of Y axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{500., 200.}); + + def = this->add("machine_max_acceleration_y", coFloats); + def->full_label = L("Maximum acceleration Y"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration of the Y axis"); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{1000., 1000.}); + + def = this->add("machine_max_jerk_y", coFloats); + def->full_label = L("Maximum jerk Y"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum jerk of the Y axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{10., 10.}); + + def = this->add("machine_max_speed_z", coFloats); + def->full_label = L("Maximum speed Z"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum speed of Z axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{12., 12.}); + + def = this->add("machine_max_acceleration_z", coFloats); + def->full_label = L("Maximum acceleration Z"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration of the Z axis"); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{500., 200.}); + + def = this->add("machine_max_jerk_z", coFloats); + def->full_label = L("Maximum jerk Z"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum jerk of the Z axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{0.2, 0.4}); + + def = this->add("machine_max_speed_e", coFloats); + def->full_label = L("Maximum speed E"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum speed of E axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{120., 120.}); + + def = this->add("machine_max_acceleration_e", coFloats); + def->full_label = L("Maximum acceleration E"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum acceleration of the E axis"); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{5000., 5000.}); + + def = this->add("machine_max_jerk_e", coFloats); + def->full_label = L("Maximum jerk E"); + def->category = L("Machine limits"); + def->tooltip = L("Maximum jerk of the E axis"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloats{2.5, 2.5}); + + def = this->add("emit_machine_limits_to_gcode", coBool); + def->label = L("Emit limits to G-code"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("If enabled, the machine limits will be emitted to G-code file.\nThis option will be ignored if the G-code flavor is set to Klipper."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("resonance_avoidance", coBool); + def->label = L("Resonance avoidance"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("By reducing the speed of the outer wall to avoid the resonance zone of the printer, ringing on the surface of the model are avoided.\nPlease turn this option off when testing ringing."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("min_resonance_avoidance_speed", coFloat); + def->label = L("Min"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Minimum speed of resonance avoidance."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(70)); + + def = this->add("max_resonance_avoidance_speed", coFloat); + def->label = L("Max"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Maximum speed of resonance avoidance."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(120)); + + def = this->add("input_shaping_emit", coBool); + def->label = L("Emit input shaping"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Override firmware input shaping settings.\nIf disabled, firmware settings are used."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("input_shaping_type", coEnum); + def->label = L("Input shaper type"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Choose the input shaper algorithm.\nDefault uses the firmware default settings.\nDisable turns off input shaping in the firmware."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->set_default_value(new ConfigOptionEnum(InputShaperType::Default)); + + def = this->add("input_shaping_freq_x", coFloat); + def->label = L("X"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Resonant frequency for the X axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal."); + def->sidetext = L("Hz"); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("input_shaping_freq_y", coFloat); + def->label = L("Y"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Resonant frequency for the Y axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type."); + def->sidetext = L("Hz"); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("input_shaping_damp_x", coFloat); + def->label = L("X"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Damping ratio for the X axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal."); + def->min = 0; + def->max = 1; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("input_shaping_damp_y", coFloat); + def->label = L("Y"); + def->category = L("PrinterKinematics/Motion ability"); + def->tooltip = L("Damping ratio for the Y axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type."); + def->min = 0; + def->max = 1; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("brim_width", coFloat); + def->label = L("Brim width"); + def->category = L("Print/Others"); + def->tooltip = L("Distance from model to the outermost brim line."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("brim_type", coEnum); + def->label = L("Brim type"); + def->category = L("Print/Others"); + def->tooltip = L("This controls the generation of the brim at outer and/or inner side of models. Auto means the brim width is analyzed and calculated automatically."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("auto_brim"); + def->enum_values.push_back("brim_ears"); + def->enum_values.push_back("painted"); + def->enum_values.push_back("outer_only"); + def->enum_values.push_back("inner_only"); + def->enum_values.push_back("outer_and_inner"); + def->enum_values.push_back("no_brim"); + def->enum_labels.push_back(L("Auto")); + def->enum_labels.push_back(L("Mouse ear")); + def->enum_labels.push_back(L("Painted")); + def->enum_labels.push_back(L("Outer brim only")); + def->enum_labels.push_back(L("Inner brim only")); + def->enum_labels.push_back(L("Outer and inner brim")); + def->enum_labels.push_back(L("No-brim")); + def->set_default_value(new ConfigOptionEnum(btAutoBrim)); + + def = this->add("brim_object_gap", coFloat); + def->label = L("Brim-object gap"); + def->category = L("Print/Others"); + def->tooltip = L("A gap between innermost brim line and object can make brim be removed more easily."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("brim_flow_ratio", coFloat); + def->label = L("Brim flow ratio"); + def->category = L("Print/Others"); + def->tooltip = L("This factor affects the amount of material for brims.\n\nThe actual brim flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.\n\nNote: The resulting value will not be affected by the first-layer flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("brim_use_efc_outline", coBool); + def->label = L("Brim follows compensated outline"); + def->category = L("Print/Others"); + def->tooltip = L("When enabled, the brim is aligned with the first-layer perimeter geometry after Elephant Foot Compensation is applied.\nThis option is intended for cases where Elephant Foot Compensation significantly alters the first-layer footprint.\n\nIf your current setup already works well, enabling it may be unnecessary and can cause the brim to fuse with upper layers."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("combine_brims", coBool); + def->label = L("Combine brims"); + def->category = L("Print/Others"); + def->tooltip = L("Combine multiple brims into one when they are close to each other. This can improve brim adhesion."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("brim_ears_max_angle", coFloat); + def->label = L("Brim ear max angle"); + def->category = L("Print/Others"); + def->tooltip = L("Maximum angle to let a brim ear appear.\nIf set to 0, no brim will be created.\nIf set to ~180, brim will be created on everything but straight sections."); + def->sidetext = L("°"); + def->min = 0; + def->max = 180; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(125)); + + def = this->add("brim_ears_detection_length", coFloat); + def->label = L("Brim ear detection radius"); + def->category = L("Print/Others"); + def->tooltip = L("The geometry will be decimated before detecting sharp angles. This parameter indicates the minimum length of the deviation for the decimation.\n0 to deactivate."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("print_sequence", coEnum); + def->label = L("Print sequence"); + def->category = L("Print/Others"); + def->tooltip = L("Print sequence, layer by layer or object by object."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("by layer"); + def->enum_values.push_back("by object"); + def->enum_labels.push_back(L("By layer")); + def->enum_labels.push_back(L("By object")); + def->set_default_value(new ConfigOptionEnum(PrintSequence::ByLayer)); + + def = this->add("print_order", coEnum); + def->label = L("Intra-layer order"); + def->category = L("Print/Others"); + def->tooltip = L("Print order within a single layer."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("default"); + def->enum_values.push_back("as_obj_list"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("As object list")); + def->set_default_value(new ConfigOptionEnum(PrintOrder::Default)); + + def = this->add("fuzzy_skin", coEnum); + def->label = L("Fuzzy Skin"); + def->category = L("Print/Others"); + def->tooltip = L("Randomly jitter while printing the wall, so that the surface has a rough look. This setting controls the fuzzy position."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("none"); + def->enum_values.push_back("external"); + def->enum_values.push_back("hole"); + def->enum_values.push_back("all"); + def->enum_values.push_back("allwalls"); + def->enum_values.push_back("disabled_fuzzy"); + def->enum_labels.push_back(L("Painted only")); + def->enum_labels.push_back(L("Contour")); + def->enum_labels.push_back(L("Hole")); + def->enum_labels.push_back(L("Contour and hole")); + def->enum_labels.push_back(L("All walls")); + def->enum_labels.push_back(L("Disabled")); + def->set_default_value(new ConfigOptionEnum(FuzzySkinType::Disabled_fuzzy)); + + def = this->add("fuzzy_skin_thickness", coFloat); + def->label = L("Fuzzy skin thickness"); + def->category = L("Print/Others"); + def->tooltip = L("The width within which to jitter. It's advised to be below outer wall line width."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 2; + def->set_default_value(new ConfigOptionFloat(0.2)); + + def = this->add("fuzzy_skin_point_distance", coFloat); + def->label = L("Fuzzy skin point distance"); + def->category = L("Print/Others"); + def->tooltip = L("The average distance between the random points introduced on each line segment."); + def->sidetext = L("mm"); + def->max = 5; + def->set_default_value(new ConfigOptionFloat(0.3f)); + + def = this->add("fuzzy_skin_first_layer", coBool); + def->label = L("Apply fuzzy skin to first layer"); + def->category = L("Print/Others"); + def->tooltip = L("Whether to apply fuzzy skin on the first layer."); + def->set_default_value(new ConfigOptionBool(0)); + + def = this->add("fuzzy_skin_mode", coEnum); + def->label = L("Fuzzy skin generator mode"); + def->category = L("Print/Others"); + def->tooltip = L("Fuzzy skin generation mode. Works only with Arachne!\nDisplacement: Сlassic mode when the pattern is formed by shifting the nozzle sideways from the original path.\nExtrusion: The mode when the pattern formed by the amount of extruded plastic. This is the fast and straight algorithm without unnecessary nozzle shake that gives a smooth pattern. But it is more useful for forming loose walls in the entire they array.\nCombined: Joint mode [Displacement] + [Extrusion]. The appearance of the walls is similar to [Displacement] Mode, but it leaves no pores between the perimeters.\n\nAttention! The [Extrusion] and [Combined] modes works only the fuzzy_skin_thickness parameter not more than the thickness of printed loop. At the same time, the width of the extrusion for a particular layer should also not be below a certain level. It is usually equal 15-25%% of a layer height. Therefore, the maximum fuzzy skin thickness with a perimeter width of 0.4 mm and a layer height of 0.2 mm will be 0.4-(0.2*0.25)=±0.35mm! If you enter a higher parameter than this, the error Flow::spacing() will displayed, and the model will not be sliced. You can choose this number until this error is repeated."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("displacement"); + def->enum_values.push_back("extrusion"); + def->enum_values.push_back("combined"); + def->enum_labels.push_back(L("Displacement")); + def->enum_labels.push_back(L("Extrusion")); + def->enum_labels.push_back(L("Combined")); + def->set_default_value(new ConfigOptionEnum(FuzzySkinMode::Displacement)); + + def = this->add("fuzzy_skin_noise_type", coEnum); + def->label = L("Fuzzy skin noise type"); + def->category = L("Print/Others"); + def->tooltip = L("Noise type to use for fuzzy skin generation:\nClassic: Classic uniform random noise.\nPerlin: Perlin noise, which gives a more consistent texture.\nBillow: Similar to perlin noise, but clumpier.\nRidged Multifractal: Ridged noise with sharp, jagged features. Creates marble-like textures.\nVoronoi: Divides the surface into voronoi cells, and displaces each one by a random amount. Creates a patchwork texture.\nRipple: Uniform ripple pattern that ripples left and right of the original path. Repeating pattern, woven appearance."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("classic"); + def->enum_values.push_back("perlin"); + def->enum_values.push_back("billow"); + def->enum_values.push_back("ridgedmulti"); + def->enum_values.push_back("voronoi"); + def->enum_values.push_back("ripple"); + def->enum_labels.push_back(L("Classic")); + def->enum_labels.push_back(L("Perlin")); + def->enum_labels.push_back(L("Billow")); + def->enum_labels.push_back(L("Ridged Multifractal")); + def->enum_labels.push_back(L("Voronoi")); + def->enum_labels.push_back(L("Ripple")); + def->set_default_value(new ConfigOptionEnum(NoiseType::Classic)); + + def = this->add("fuzzy_skin_scale", coFloat); + def->label = L("Fuzzy skin feature size"); + def->category = L("Print/Others"); + def->tooltip = L("The base size of the coherent noise features, in mm. Higher values will result in larger features."); + def->sidetext = L("mm"); + def->min = 0.1; + def->max = 500; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1.0)); + + def = this->add("fuzzy_skin_octaves", coInt); + def->label = L("Fuzzy Skin Noise Octaves"); + def->category = L("Print/Others"); + def->tooltip = L("The number of octaves of coherent noise to use. Higher values increase the detail of the noise, but also increase computation time."); + def->min = 1; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(4)); + + def = this->add("fuzzy_skin_persistence", coFloat); + def->label = L("Fuzzy skin noise persistence"); + def->category = L("Print/Others"); + def->tooltip = L("The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise."); + def->min = 0.01; + def->max = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.5)); + + def = this->add("fuzzy_skin_ripples_per_layer", coInt); + def->label = L("Number of ripples per layer"); + def->category = L("Print/Others"); + def->tooltip = L("Controls how many full cycles of ripples will be added per layer."); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(15)); + + def = this->add("fuzzy_skin_ripple_offset", coPercent); + def->label = L("Ripple offset"); + def->category = L("Print/Others"); + def->tooltip = L("Shifts the ripple phase forward along the print path by the specified percentage of a wavelength each layer period.\n- 0% keeps every layer identical.\n- 50% shifts the pattern by half a wavelength, effectively inverting the phase.\n- 100% shifts the pattern by a full wavelength, returning to the original phase.\n\nThe shift is applied once every number of layers set by Layers between ripple offset, so layers within the same group are printed identically."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(50)); + + def = this->add("fuzzy_skin_layers_between_ripple_offset", coInt); + def->label = L("Layers between ripple offset"); + def->category = L("Print/Others"); + def->tooltip = L("Specifies how many consecutive layers share the same ripple phase before the offset is applied.\nFor example:\n- 1 = Layer 1 is printed with the base ripple pattern, then layer 2 is shifted by the configured offset, then layer 3 returns to the base pattern, and so on.\n- 3 = Layers 1 to 3 are printed with the base ripple pattern, then layers 4 to 6 are shifted by the configured offset, then layers 7 to 9 return to the base pattern, etc."); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("gcode_add_line_number", coBool); + def->label = L("Add line number"); + def->category = L("Print/Others"); + def->tooltip = L("Enable this to add line number(Nx) at the beginning of each G-code line."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(0)); + + def = this->add("gcode_label_objects", coBool); + def->label = L("Label objects"); + def->category = L("Print/Others"); + def->tooltip = L("Enable this to add comments into the G-code labeling print moves with what object they belong to, which is useful for the Octoprint CancelObject plug-in. This setting is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(1)); + + def = this->add("exclude_object", coBool); + def->label = L("Exclude objects"); + def->category = L("Print/Others"); + def->tooltip = L("Enable this option to add EXCLUDE OBJECT command in G-code."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("gcode_comments", coBool); + def->label = L("Verbose G-code"); + def->category = L("Print/Others"); + def->tooltip = L("Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(0)); + + def = this->add("enable_wrapping_detection", coBool); + def->label = L("Enable clumping detection"); + def->category = L("Print/Others"); + def->tooltip = L("Enable clumping detection"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("notes", coString); + def->label = L("Configuration notes"); + def->category = L("Print/Others"); + def->tooltip = L("You can put here your personal notes. This text will be added to the G-code header comments."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 13; + def->set_default_value(new ConfigOptionString()); + + def = this->add("reduce_infill_retraction", coBool); + def->label = L("Reduce infill retraction"); + def->category = L("Print/Others"); + def->tooltip = L("Don't retract when the travel is entirely within an infill area. That means the oozing can't been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that z-hop is also not performed in areas where retraction is skipped."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("filename_format", coString); + def->label = L("Filename format"); + def->category = L("Print/Others"); + def->tooltip = L("Users can define the project file name when exporting."); + def->mode = comAdvanced; + def->full_width = true; + def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode")); + + def = this->add("post_process", coStrings); + def->label = L("Post-processing Scripts"); + def->category = L("Print/Others"); + def->tooltip = L("If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Orca Slicer config settings by reading environment variables."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 6; + def->gui_flags = "serialized"; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("process_change_extrusion_role_gcode", coString); + def->label = L("Change extrusion role G-code (process)"); + def->category = L("Print/Others"); + def->tooltip = L("This G-code is inserted when the extrusion role is changed. It runs after the machine and filament extrusion role G-code."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString()); + + def = this->add("skirt_distance", coFloat); + def->label = L("Skirt distance"); + def->category = L("Print/Others"); + def->tooltip = L("The distance from the skirt to the brim or the object."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 60; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(2)); + + def = this->add("skirt_start_angle", coFloat); + def->label = L("Skirt start point"); + def->category = L("Print/Others"); + def->tooltip = L("Angle from the object center to skirt start point. Zero is the most right position, counter clockwise is positive angle."); + def->sidetext = L("°"); + def->min = -180; + def->max = 180; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(-135)); + + def = this->add("skirt_height", coInt); + def->label = L("Skirt height"); + def->category = L("Print/Others"); + def->tooltip = L("How many layers of skirt. Usually only one layer."); + def->sidetext = L("layers"); + def->max = 10000; + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("single_loop_draft_shield", coBool); + def->label = L("Single loop after first layer"); + def->category = L("Print/Others"); + def->tooltip = L("Limits the skirt/draft shield loops to one wall after the first layer. This is useful, on occasion, to conserve filament but may cause the draft shield/skirt to warp / crack."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("draft_shield", coEnum); + def->label = L("Draft shield"); + def->category = L("Print/Others"); + def->tooltip = L("A draft shield is useful to protect an ABS or ASA print from warping and detaching from print bed due to wind draft. It is usually needed only with open frame printers, i.e. without an enclosure.\n\nEnabled = skirt is as tall as the highest printed object. Otherwise 'Skirt height' is used.\nNote: With the draft shield active, the skirt will be printed at skirt distance from the object. Therefore, if brims are active it may intersect with them. To avoid this, increase the skirt distance value.\n"); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("disabled"); + def->enum_values.push_back("enabled"); + def->enum_labels.push_back(L("Disabled")); + def->enum_labels.push_back(L("Enabled")); + def->set_default_value(new ConfigOptionEnum(dsDisabled)); + + def = this->add("skirt_type", coEnum); + def->label = L("Skirt type"); + def->full_label = L("Skirt type"); + def->category = L("Print/Others"); + def->tooltip = L("Combined - single skirt for all objects, Per object - individual object skirt."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("combined"); + def->enum_values.push_back("perobject"); + def->enum_labels.push_back(L("Combined")); + def->enum_labels.push_back(L("Per object")); + def->set_default_value(new ConfigOptionEnum(stCombined)); + + def = this->add("skirt_loops", coInt); + def->label = L("Skirt loops"); + def->full_label = L("Skirt loops"); + def->category = L("Print/Others"); + def->tooltip = L("Number of loops for the skirt. Zero means disabling skirt."); + def->min = 0; + def->max = 10; + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("skirt_speed", coFloat); + def->label = L("Skirt speed"); + def->full_label = L("Skirt speed"); + def->category = L("Print/Others"); + def->tooltip = L("Speed of skirt, in mm/s. Zero means use default layer extrusion speed."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(50.0)); + + def = this->add("min_skirt_length", coFloat); + def->label = L("Skirt minimum extrusion length"); + def->full_label = L("Skirt minimum extrusion length"); + def->category = L("Print/Others"); + def->tooltip = L("Minimum filament extrusion length in mm when printing the skirt. Zero means this feature is disabled.\n\nUsing a non-zero value is useful if the printer is set up to print without a prime line.\nFinal number of loops is not taking into account while arranging or validating objects distance. Increase loop number in such case."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.0)); + + def = this->add("spiral_mode", coBool); + def->label = L("Spiral vase"); + def->category = L("Print/Others"); + def->tooltip = L("Spiralize smooths out the Z moves of the outer contour. And turns a solid model into a single walled print with solid bottom layers. The final generated model has no seam."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("spiral_mode_smooth", coBool); + def->label = L("Smooth Spiral"); + def->category = L("Print/Others"); + def->tooltip = L("Smooth Spiral smooths out X and Y moves as well, resulting in no visible seam at all, even in the XY directions on walls that are not vertical."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("spiral_mode_max_xy_smoothing", coFloatOrPercent); + def->label = L("Max XY Smoothing"); + def->category = L("Print/Others"); + def->tooltip = L("Maximum distance to move points in XY to try to achieve a smooth spiral. If expressed as a %, it will be computed over nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(200, true)); + + def = this->add("spiral_starting_flow_ratio", coFloat); + def->label = L("Spiral starting flow ratio"); + def->category = L("Print/Others"); + def->tooltip = L("Sets the starting flow ratio while transitioning from the last bottom layer to the spiral. Normally the spiral transition scales the flow ratio from 0% to 100% during the first loop which can in some cases lead to under extrusion at the start of the spiral."); + def->min = 0; + def->max = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("spiral_finishing_flow_ratio", coFloat); + def->label = L("Spiral finishing flow ratio"); + def->category = L("Print/Others"); + def->tooltip = L("Sets the finishing flow ratio while ending the spiral. Normally the spiral transition scales the flow ratio from 100% to 0% during the last loop which can in some cases lead to under extrusion at the end of the spiral."); + def->min = 0; + def->max = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("timelapse_type", coEnum); + def->label = L("Timelapse"); + def->category = L("Print/Others"); + def->tooltip = L("If smooth or traditional mode is selected, a timelapse video will be generated for each print. After each layer is printed, a snapshot is taken with the chamber camera. All of these snapshots are composed into a timelapse video when printing completes. If smooth mode is selected, the toolhead will move to the excess chute after each layer is printed and then take a snapshot. Since the melt filament may leak from the nozzle during the process of taking a snapshot, a prime tower is required for smooth mode to wipe nozzle."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("0"); + def->enum_values.push_back("1"); + def->enum_labels.push_back(L("Traditional")); + def->enum_labels.push_back(L("Smooth")); + def->set_default_value(new ConfigOptionEnum(tlTraditional)); + + def = this->add("slicing_mode", coEnum); + def->label = L("Slicing Mode"); + def->category = L("Print/Others"); + def->tooltip = L("Use \"Even-odd\" for 3DLabPrint airplane models. Use \"Close holes\" to close all holes in the model."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("regular"); + def->enum_values.push_back("even_odd"); + def->enum_values.push_back("close_holes"); + def->enum_labels.push_back(L("Regular")); + def->enum_labels.push_back(L("Even-odd")); + def->enum_labels.push_back(L("Close holes")); + def->set_default_value(new ConfigOptionEnum(SlicingMode::Regular)); + + def = this->add("sparse_infill_filament", coInt); + def->label = L("Infill"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Filament to print internal sparse infill.\n\"Default\" uses the active object/part filament."); + def->min = 0; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("interface_shells", coBool); + def->label = L("Interface shells"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("mmu_segmented_region_max_width", coFloat); + def->label = L("Maximum width of a segmented region"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Maximum width of a segmented region. Zero disables this feature."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("mmu_segmented_region_interlocking_depth", coFloat); + def->label = L("Interlocking depth of a segmented region"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Interlocking depth of a segmented region. It will be ignored if \"mmu_segmented_region_max_width\" is zero or if \"mmu_segmented_region_interlocking_depth\" is bigger than \"mmu_segmented_region_max_width\". Zero disables this feature."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("interlocking_beam", coBool); + def->label = L("Use beam interlocking"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Generate interlocking beam structure at the locations where different filaments touch. This improves the adhesion between filaments, especially models printed in different materials."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("interlocking_beam_width", coFloat); + def->label = L("Interlocking beam width"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The width of the interlocking structure beams."); + def->sidetext = L("mm"); + def->min = 0.01; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.8)); + + def = this->add("interlocking_orientation", coFloat); + def->label = L("Interlocking direction"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Orientation of interlock beams."); + def->sidetext = L("°"); + def->min = 0; + def->max = 360; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(22.5)); + + def = this->add("interlocking_beam_layer_count", coInt); + def->label = L("Interlocking beam layers"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The height of the beams of the interlocking structure, measured in number of layers. Less layers is stronger, but more prone to defects."); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(2)); + + def = this->add("interlocking_depth", coInt); + def->label = L("Interlocking depth"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The distance from the boundary between filaments to generate interlocking structure, measured in cells. Too few cells will result in poor adhesion."); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(2)); + + def = this->add("interlocking_boundary_avoidance", coInt); + def->label = L("Interlocking boundary avoidance"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The distance from the outside of a model where interlocking structures will not be generated, measured in cells."); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(2)); + + def = this->add("ooze_prevention", coBool); + def->label = L("Enable"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wall_filament", coInt); + def->label = L("Walls"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Filament to print walls.\n\"Default\" uses the active object/part filament."); + def->min = 0; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("solid_infill_filament", coInt); + def->label = L("Solid infill"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Filament to print solid infill.\n\"Default\" uses the active object/part filament."); + def->min = 0; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("standby_temperature_delta", coInt); + def->label = L("Temperature variation"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Temperature difference to be applied when an extruder is not active. The value is not used when 'idle_temperature' in filament settings is set to non-zero value."); + def->sidetext = L("\u2206\u2103"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(-5)); + + def = this->add("preheat_time", coFloat); + def->label = L("Preheat time"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("To reduce the waiting time after tool change, Orca can preheat the next tool while the current tool is still in use. This setting specifies the time in seconds to preheat the next tool. Orca will insert a M104 command to preheat the tool in advance."); + def->sidetext = L("s"); + def->min = 0; + def->max = 120; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(30.0)); + + def = this->add("preheat_steps", coInt); + def->label = L("Preheat steps"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Insert multiple preheat commands (e.g. M104.1). Only useful for Prusa XL. For other printers, please set it to 1."); + def->min = 1; + def->max = 10; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("wipe_tower_no_sparse_layers", coBool); + def->label = L("No sparse layers (beta)"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no tool changes. On layers with a tool change, extruder will travel downward to print the wipe tower. User is responsible for ensuring there is no collision with the print."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("single_extruder_multi_material_priming", coBool); + def->label = L("Prime all printing extruders"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("enable_prime_tower", coBool); + def->label = L("Enable"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The wiping tower can be used to clean up the residue on the nozzle and stabilize the chamber pressure inside the nozzle, in order to avoid appearance defects when printing objects."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("prime_tower_enable_framework", coBool); + def->label = L("Internal ribs"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Enable internal ribs to increase the stability of the prime tower."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("prime_volume", coFloat); + def->label = L("Prime volume"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The volume of material to prime extruder on tower."); + def->sidetext = L("mm³"); + def->min = 1; + def->set_default_value(new ConfigOptionFloat(45.)); + + def = this->add("prime_tower_width", coFloat); + def->label = L("Width"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Width of the prime tower."); + def->sidetext = L("mm"); + def->min = 2; + def->set_default_value(new ConfigOptionFloat(60.)); + + def = this->add("wipe_tower_rotation_angle", coFloat); + def->label = L("Wipe tower rotation angle"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Wipe tower rotation angle with respect to X axis."); + def->sidetext = L("°"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("prime_tower_brim_width", coFloat); + def->label = L("Brim width"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Brim width of prime tower, negative number means auto calculated width based on the height of prime tower."); + def->sidetext = L("mm"); + def->min = -1; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; + def->enum_values.push_back("-1"); + def->enum_labels.push_back(L("Auto")); + def->set_default_value(new ConfigOptionFloat(3.)); + + def = this->add("wipe_tower_cone_angle", coFloat); + def->label = L("Stabilization cone apex angle"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Angle at the apex of the cone that is used to stabilize the wipe tower. Larger angle means wider base."); + def->sidetext = L("°"); + def->min = 0; + def->max = 90; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(30.0)); + + def = this->add("wipe_tower_max_purge_speed", coFloat); + def->label = L("Maximum wipe tower print speed"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The maximum print speed when purging in the wipe tower and printing the wipe tower sparse layers. When purging, if the sparse infill speed or calculated speed from the filament max volumetric speed is lower, the lowest will be used instead.\n\nWhen printing the sparse layers, if the internal perimeter speed or calculated speed from the filament max volumetric speed is lower, the lowest will be used instead.\n\nIncreasing this speed may affect the tower's stability as well as increase the force with which the nozzle collides with any blobs that may have formed on the wipe tower.\n\nBefore increasing this parameter beyond the default of 90 mm/s, make sure your printer can reliably bridge at the increased speeds and that ooze when tool changing is well controlled.\n\nFor the wipe tower external perimeters the internal perimeter speed is used regardless of this setting."); + def->sidetext = L("mm/s"); + def->min = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(90.)); + + def = this->add("wipe_tower_wall_type", coEnum); + def->label = L("Wall type"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Wipe tower outer wall type.\n1. Rectangle: The default wall type, a rectangle with fixed width and height.\n2. Cone: A cone with a fillet at the bottom to help stabilize the wipe tower.\n3. Rib: Adds four ribs to the tower wall for enhanced stability."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("rectangle"); + def->enum_values.push_back("cone"); + def->enum_values.push_back("rib"); + def->enum_labels.push_back(L("Rectangle")); + def->enum_labels.push_back(L("Cone")); + def->enum_labels.push_back(L("Rib")); + def->set_default_value(new ConfigOptionEnum(wtwRib)); + + def = this->add("wipe_tower_extra_rib_length", coFloat); + def->label = L("Extra rib length"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Positive values can increase the size of the rib wall, while negative values can reduce the size. However, the size of the rib wall can not be smaller than that determined by the cleaning volume."); + def->sidetext = L("mm"); + def->max = 300; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("wipe_tower_rib_width", coFloat); + def->label = L("Rib width"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Rib width is always less than half the prime tower side length."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 300; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(8)); + + def = this->add("wipe_tower_fillet_wall", coBool); + def->label = L("Fillet wall"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The wall of prime tower will fillet."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("wipe_tower_filament", coInt); + def->label = L("Wipe tower"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The extruder to use when printing perimeter of the wipe tower. Set to 0 to use the one that is available (non-soluble would be preferred)."); + def->min = 0; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("prime_tower_skip_points", coBool); + def->label = L("Skip points"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("The wall of prime tower will skip the start points of wipe path."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("enable_tower_interface_features", coBool); + def->label = L("Enable tower interface features"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Enable optimized prime tower interface behavior when different materials meet."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("enable_tower_interface_cooldown_during_tower", coBool); + def->label = L("Cool down from interface boost during prime tower"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("When interface-layer temperature boost is active, set the nozzle back to print temperature at the start of the prime tower so it cools down during the tower."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("prime_tower_infill_gap", coPercent); + def->label = L("Infill gap"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Infill gap."); + def->sidetext = L("%"); + def->min = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(150)); + + def = this->add("flush_into_infill", coBool); + def->label = L("Flush into objects' infill"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Purging after filament change will be done inside objects' infills. This may lower the amount of waste and decrease the print time. If the walls are printed with transparent filament, the mixed color infill will be seen outside. It will not take effect, unless the prime tower is enabled."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("flush_into_support", coBool); + def->label = L("Flush into objects' support"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Purging after filament change will be done inside objects' support. This may lower the amount of waste and decrease the print time. It will not take effect, unless the prime tower is enabled."); + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("flush_into_objects", coBool); + def->label = L("Flush into this object"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("This object will be used to purge the nozzle after a filament change to save filament and decrease the print time. Colors of the objects will be mixed as a result. It will not take effect unless the prime tower is enabled."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wipe_tower_bridging", coFloat); + def->label = L("Maximal bridging distance"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Maximal distance between supports on sparse infill sections."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(10.)); + + def = this->add("wipe_tower_extra_spacing", coPercent); + def->label = L("Wipe tower purge lines spacing"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Spacing of purge lines on the wipe tower."); + def->sidetext = L("%"); + def->min = 100; + def->max = 300; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(100.)); + + def = this->add("wipe_tower_extra_flow", coPercent); + def->label = L("Extra flow for purging"); + def->category = L("Print/Multimaterial"); + def->tooltip = L("Extra flow used for the purging lines on the wipe tower. This makes the purging lines thicker or narrower than they normally would be. The spacing is adjusted automatically."); + def->sidetext = L("%"); + def->min = 100; + def->max = 300; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(100.)); + + def = this->add("reduce_crossing_wall", coBool); + def->label = L("Avoid crossing walls"); + def->category = L("Print/Quality"); + def->tooltip = L("Detour to avoid traveling across walls, which may cause blobs on the surface."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(INITIAL_REDUCE_CROSSING_WALL)); + + def = this->add("max_travel_detour_distance", coFloatOrPercent); + def->label = L("Avoid crossing walls - Max detour length"); + def->category = L("Print/Quality"); + def->tooltip = L("Maximum detour distance for avoiding crossing wall. Don't detour if the detour distance is larger than this value. Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. Zero to disable."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max_literal = 1000; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("bridge_density", coPercent); + def->label = L("External bridge density"); + def->category = L("Print/Quality"); + def->tooltip = L("Controls the density (spacing) of external bridge lines. Default is 100%.\n\nLower density external bridges can help improve reliability as there is more space for air to circulate around the extruded bridge, improving its cooling speed. Minimum is 10%.\n\nHigher densities can produce smoother bridge surfaces, as overlapping lines provide additional support during printing. Maximum is 120%.\nNote: Bridge density that is too high can cause warping or overextrusion."); + def->sidetext = L("%"); + def->min = 10; + def->max = 120; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(100)); + + def = this->add("internal_bridge_density", coPercent); + def->label = L("Internal bridge density"); + def->category = L("Print/Quality"); + def->tooltip = L("Controls the density (spacing) of internal bridge lines. 100% means solid bridge. Default is 100%.\n\nLower density internal bridges can help reduce top surface pillowing and improve internal bridge reliability as there is more space for air to circulate around the extruded bridge, improving its cooling speed.\n\nThis option works particularly well when combined with the second internal bridge over infill option, further improving internal bridging structure before solid infill is extruded."); + def->sidetext = L("%"); + def->min = 10; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(100)); + + def = this->add("bridge_flow", coFloat); + def->label = L("Bridge flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("Decrease this value slightly (for example 0.9) to reduce the amount of material for bridge, to improve sag.\n\nThe actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("internal_bridge_flow", coFloat); + def->label = L("Internal bridge flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This value governs the thickness of the internal bridge layer. This is the first layer over sparse infill. Decrease this value slightly (for example 0.9) to improve surface quality over sparse infill.\n\nThe actual internal bridge flow used is calculated by multiplying this value with the bridge flow ratio, the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("top_solid_infill_flow_ratio", coFloat); + def->label = L("Top surface flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for top solid infill. You can decrease it slightly to have smooth surface finish.\n\nThe actual top surface flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("bottom_solid_infill_flow_ratio", coFloat); + def->label = L("Bottom surface flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for bottom solid infill.\n\nThe actual bottom solid infill flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("set_other_flow_ratios", coBool); + def->label = L("Set other flow ratios"); + def->category = L("Print/Quality"); + def->tooltip = L("Change flow ratios for other extrusion path types."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("first_layer_flow_ratio", coFloat); + def->label = L("First layer flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material on the first layer for the extrusion path roles listed in this section.\n\nFor the first layer, the actual flow ratio for each path role (does not affect brims and skirts) will be multiplied by this value."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("outer_wall_flow_ratio", coFloat); + def->label = L("Outer wall flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for outer walls.\n\nThe actual outer wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("inner_wall_flow_ratio", coFloat); + def->label = L("Inner wall flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for inner walls.\n\nThe actual inner wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("overhang_flow_ratio", coFloat); + def->label = L("Overhang flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for overhangs.\n\nThe actual overhang flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("sparse_infill_flow_ratio", coFloat); + def->label = L("Sparse infill flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for sparse infill.\n\nThe actual sparse infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("internal_solid_infill_flow_ratio", coFloat); + def->label = L("Internal solid infill flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for internal solid infill.\n\nThe actual internal solid infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("gap_fill_flow_ratio", coFloat); + def->label = L("Gap fill flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for filling the gaps.\n\nThe actual gap filling flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("support_flow_ratio", coFloat); + def->label = L("Support flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for support.\n\nThe actual support flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("support_interface_flow_ratio", coFloat); + def->label = L("Support interface flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for the support interface.\n\nThe actual support interface flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("precise_outer_wall", coBool); + def->label = L("Precise wall"); + def->category = L("Print/Quality"); + def->tooltip = L("Improve shell precision by adjusting outer wall spacing. This also improves layer consistency. NOTE: This option will be ignored for outer-inner or inner-outer-inner wall sequences."); + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("only_one_wall_top", coBool); + def->label = L("Only one wall on top surfaces"); + def->category = L("Print/Quality"); + def->tooltip = L("Use only one wall on flat top surfaces, to give more space to the top infill pattern."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("min_width_top_surface", coFloatOrPercent); + def->label = L("One wall threshold"); + def->category = L("Print/Quality"); + def->tooltip = L("If a top surface has to be printed and it's partially covered by another layer, it won't be considered at a top layer where its width is below this value. This can be useful to not let the 'one perimeter on top' trigger on surface that should be covered only by perimeters. This value can be a mm or a % of the perimeter extrusion width.\nWarning: If enabled, artifacts can be created if you have some thin features on the next layer, like letters. Set this setting to 0 to remove these artifacts."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max_literal = 15; + def->ratio_over = "inner_wall_line_width"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(300, true)); + + def = this->add("only_one_wall_first_layer", coBool); + def->label = L("Only one wall on first layer"); + def->category = L("Print/Quality"); + def->tooltip = L("Use only one wall on first layer, to give more space to the bottom infill pattern."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("extra_perimeters_on_overhangs", coBool); + def->label = L("Extra perimeters on overhangs"); + def->category = L("Print/Quality"); + def->tooltip = L("Create additional perimeter paths over steep overhangs and areas where bridges cannot be anchored."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("overhang_reverse", coBool); + def->label = L("Reverse on even"); + def->full_label = L("Overhang reversal"); + def->category = L("Print/Quality"); + def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on even layers. This alternating pattern can drastically improve steep overhangs.\n\nThis setting can also help reduce part warping due to the reduction of stresses in the part walls."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("overhang_reverse_internal_only", coBool); + def->label = L("Reverse only internal perimeters"); + def->full_label = L("Reverse only internal perimeters"); + def->category = L("Print/Quality"); + def->tooltip = L("Apply the reverse perimeters logic only on internal perimeters.\n\nThis setting greatly reduces part stresses as they are now distributed in alternating directions. This should reduce part warping while also maintaining external wall quality. This feature can be very useful for warp prone material, like ABS/ASA, and also for elastic filaments, like TPU and Silk PLA. It can also help reduce warping on floating regions over supports.\n\nFor this setting to be the most effective, it is recommended to set the Reverse Threshold to 0 so that all internal walls print in alternating directions on even layers irrespective of their overhang degree."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("counterbore_hole_bridging", coEnum); + def->label = L("Bridge counterbore holes"); + def->category = L("Print/Quality"); + def->tooltip = L("This option creates bridges for counterbore holes, allowing them to be printed without support. Available modes include:\n1. None: No bridge is created\n2. Partially Bridged: Only a part of the unsupported area will be bridged\n3. Sacrificial Layer: A full sacrificial bridge layer is created"); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("none"); + def->enum_values.push_back("partiallybridge"); + def->enum_values.push_back("sacrificiallayer"); + def->enum_labels.push_back(L("None")); + def->enum_labels.push_back(L("Partially bridged")); + def->enum_labels.push_back(L("Sacrificial layer")); + def->set_default_value(new ConfigOptionEnum(chbNone)); + + def = this->add("overhang_reverse_threshold", coFloatOrPercent); + def->label = L("Reverse threshold"); + def->full_label = L("Overhang reversal threshold"); + def->category = L("Print/Quality"); + def->tooltip = L("Number of mm the overhang need to be for the reversal to be considered useful. Can be a % of the perimeter width.\nValue 0 enables reversal on every even layers regardless.\nWhen Detect overhang wall is not enabled, this option is ignored and reversal happens on every even layers regardless."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max_literal = 20; + def->ratio_over = "line_width"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(50, true)); + + def = this->add("thick_bridges", coBool); + def->label = L("Thick external bridges"); + def->category = L("Print/Quality"); + def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. If disabled, bridges look better but are reliable just for shorter bridged distances."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("thick_internal_bridges", coBool); + def->label = L("Thick internal bridges"); + def->category = L("Print/Quality"); + def->tooltip = L("If enabled, thick internal bridges will be used. It's usually recommended to have this feature turned on. However, consider turning it off if you are using large nozzles."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("enable_extra_bridge_layer", coEnum); + def->label = L("Extra bridge layers (beta)"); + def->category = L("Print/Quality"); + def->tooltip = L("This option enables the generation of an extra bridge layer over internal and/or external bridges.\n\nExtra bridge layers help improve bridge appearance and reliability, as the solid infill is better supported. This is especially useful in fast printers, where the bridge and solid infill speeds vary greatly. The extra bridge layer results in reduced pillowing on top surfaces, as well as reduced separation of the external bridge layer from its surrounding perimeters.\n\nIt is generally recommended to set this to at least 'External bridge only', unless specific issues with the sliced model are found.\n\nOptions:\n1. Disabled - does not generate second bridge layers. This is the default and is set for compatibility purposes\n2. External bridge only - generates second bridge layers for external-facing bridges only. Please note that small bridges that are shorter or narrower than the set number of perimeters will be skipped as they would not benefit from a second bridge layer. If generated, the second bridge layer will be extruded parallel to the first bridge layer to reinforce the bridge strength\n3. Internal bridge only - generates second bridge layers for internal bridges over sparse infill only. Please note that the internal bridges count towards the top shell layer count of your model. The second internal bridge layer will be extruded as close to perpendicular to the first as possible. If multiple regions in the same island, with varying bridge angles are present, the last region of that island will be selected as the angle reference\n4. Apply to all - generates second bridge layers for both internal and external-facing bridges\n"); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("disabled"); + def->enum_values.push_back("external_bridge_only"); + def->enum_values.push_back("internal_bridge_only"); + def->enum_values.push_back("apply_to_all"); + def->enum_labels.push_back(L("Disabled")); + def->enum_labels.push_back(L("External bridge only")); + def->enum_labels.push_back(L("Internal bridge only")); + def->enum_labels.push_back(L("Apply to all")); + def->set_default_value(new ConfigOptionEnum(eblDisabled)); + + def = this->add("dont_filter_internal_bridges", coEnum); + def->label = L("Filter out small internal bridges"); + def->category = L("Print/Quality"); + def->tooltip = L("This option can help reduce pillowing on top surfaces in heavily slanted or curved models.\nBy default, small internal bridges are filtered out and the internal solid infill is printed directly over the sparse infill. This works well in most cases, speeding up printing without too much compromise on top surface quality.\nHowever, in heavily slanted or curved models, especially where too low a sparse infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\nEnabling limited filtering or no filtering will print internal bridge layer over slightly unsupported internal solid infill. The options below control the sensitivity of the filtering, i.e. they control where internal bridges are created:\n1. Filter - enables this option. This is the default behavior and works well in most cases\n2. Limited filtering - creates internal bridges on heavily slanted surfaces while avoiding unnecessary bridges. This works well for most difficult models\n3. No filtering - creates internal bridges on every potential internal overhang. This option is useful for heavily slanted top surface models; however, in most cases, it creates too many "); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("disabled"); + def->enum_values.push_back("limited"); + def->enum_values.push_back("nofilter"); + def->enum_labels.push_back(L("Filter")); + def->enum_labels.push_back(L("Limited filtering")); + def->enum_labels.push_back(L("No filtering")); + def->set_default_value(new ConfigOptionEnum(ibfDisabled)); + + def = this->add("outer_wall_line_width", coFloatOrPercent); + def->label = L("Outer wall"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width of outer wall. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("wall_sequence", coEnum); + def->label = L("Walls printing order"); + def->category = L("Print/Quality"); + def->tooltip = L("Print sequence of the internal (inner) and external (outer) walls.\n\nUse Inner/Outer for best overhangs. This is because the overhanging walls can adhere to a neighbouring perimeter while printing. However, this option results in slightly reduced surface quality as the external perimeter is deformed by being squashed to the internal perimeter.\n\nUse Inner/Outer/Inner for the best external surface finish and dimensional accuracy as the external wall is printed undisturbed from an internal perimeter. However, overhang performance will reduce as there is no internal perimeter to print the external wall against. This option requires a minimum of 3 walls to be effective as it prints the internal walls from the 3rd perimeter onwards first, then the external perimeter and, finally, the first internal perimeter. This option is recommended against the Outer/Inner option in most cases.\n\nUse Outer/Inner for the same external wall quality and dimensional accuracy benefits of Inner/Outer/Inner option. However, the Z seams will appear less consistent as the first extrusion of a new layer starts on a visible surface."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("inner wall/outer wall"); + def->enum_values.push_back("outer wall/inner wall"); + def->enum_values.push_back("inner-outer-inner wall"); + def->enum_labels.push_back(L("Inner/Outer")); + def->enum_labels.push_back(L("Outer/Inner")); + def->enum_labels.push_back(L("Inner/Outer/Inner")); + def->set_default_value(new ConfigOptionEnum(WallSequence::InnerOuter)); + + def = this->add("is_infill_first", coBool); + def->label = L("Print infill first"); + def->category = L("Print/Quality"); + def->tooltip = L("Order of wall/infill. When the tickbox is unchecked the walls are printed first, which works best in most cases.\n\nPrinting infill first may help with extreme overhangs as the walls have the neighbouring infill to adhere to. However, the infill will slightly push out the printed walls where it is attached to them, resulting in a worse external surface finish. It can also cause the infill to shine through the external surfaces of the part."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wall_direction", coEnum); + def->label = L("Wall loop direction"); + def->category = L("Print/Quality"); + def->tooltip = L("The direction which the contour wall loops are extruded when looking down from the top.\nHoles are printed in the opposite direction to the contour to maintain alignment with layers whose contour polygons are incomplete and change direction, also partially forming the contour of a hole.\n\nThis option will be disabled if spiral vase mode is enabled."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("ccw"); + def->enum_values.push_back("cw"); + def->enum_labels.push_back(L("Counter clockwise")); + def->enum_labels.push_back(L("Clockwise")); + def->set_default_value(new ConfigOptionEnum(WallDirection::CounterClockwise)); + + def = this->add("print_flow_ratio", coFloat); + def->label = L("Flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("The material may have volumetric change after switching between molten and crystalline states. This setting changes all extrusion flow of this filament in G-code proportionally. The recommended value range is between 0.95 and 1.05. You may be able to tune this value to get a nice flat surface if there is slight overflow or underflow.\n\nThe final object flow ratio is this value multiplied by the filament flow ratio."); + def->min = 0.01; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1.f)); + + def = this->add("line_width", coFloatOrPercent); + def->label = L("Default"); + def->category = L("Print/Quality"); + def->tooltip = L("Default line width if other line widths are set to 0. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + + def = this->add("initial_layer_line_width", coFloatOrPercent); + def->label = L("First layer"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width of the first layer. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("initial_layer_print_height", coFloat); + def->label = L("First layer height"); + def->category = L("Print/Quality"); + def->tooltip = L("Height of the first layer. Making the first layer height thicker can improve build plate adhesion."); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(0.2)); + + def = this->add("precise_z_height", coBool); + def->label = L("Precise Z height"); + def->category = L("Print/Quality"); + def->tooltip = L("Enable this to get precise Z height of object after slicing. It will get the precise object height by fine-tuning the layer heights of the last few layers. Note that this is an experimental parameter."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(0)); + + def = this->add("enable_arc_fitting", coBool); + def->label = L("Arc fitting"); + def->category = L("Print/Quality"); + def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. The fitting tolerance is same as the resolution.\n\nNote: For Klipper machines, this option is recommended to be disabled. Klipper does not benefit from arc commands as these are split again into line segments by the firmware. This results in a reduction in surface quality as line segments are converted to arcs by the slicer and then back to line segments by the firmware."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(0)); + + def = this->add("sparse_infill_line_width", coFloatOrPercent); + def->label = L("Sparse infill"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width of internal sparse infill. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("ironing_type", coEnum); + def->label = L("Ironing Type"); + def->category = L("Print/Quality"); + def->tooltip = L("Ironing is using small flow to print on same height of surface again to make flat surface more smooth. This setting controls which layer being ironed."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("no ironing"); + def->enum_values.push_back("top"); + def->enum_values.push_back("topmost"); + def->enum_values.push_back("solid"); + def->enum_labels.push_back(L("No ironing")); + def->enum_labels.push_back(L("Top surfaces")); + def->enum_labels.push_back(L("Topmost surface")); + def->enum_labels.push_back(L("All solid layers")); + def->set_default_value(new ConfigOptionEnum(IroningType::NoIroning)); + + def = this->add("ironing_pattern", coEnum); + def->label = L("Ironing Pattern"); + def->category = L("Print/Quality"); + def->tooltip = L("The pattern that will be used when ironing."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("concentric"); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->set_default_value(new ConfigOptionEnum(ipRectilinear)); + + def = this->add("ironing_flow", coPercent); + def->label = L("Ironing flow"); + def->category = L("Print/Quality"); + def->tooltip = L("The amount of material to extrude during ironing. Relative to flow of normal layer height. Too high value results in overextrusion on the surface."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->ratio_over = "layer_height"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(10)); + + def = this->add("ironing_spacing", coFloat); + def->label = L("Ironing line spacing"); + def->category = L("Print/Quality"); + def->tooltip = L("The distance between the lines of ironing."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("ironing_inset", coFloat); + def->label = L("Ironing inset"); + def->category = L("Print/Quality"); + def->tooltip = L("The distance to keep from the edges. A value of 0 sets this to half of the nozzle diameter."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("ironing_angle", coFloat); + def->label = L("Ironing angle offset"); + def->category = L("Print/Quality"); + def->tooltip = L("The angle of ironing lines offset from the top surface."); + def->sidetext = L("°"); + def->min = 0; + def->max = 359; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("ironing_angle_fixed", coBool); + def->label = L("Fixed ironing angle"); + def->category = L("Print/Quality"); + def->tooltip = L("Use a fixed absolute angle for ironing."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("zaa_enabled", coBool); + def->label = L("Z contouring enabled"); + def->category = L("Print/Quality"); + def->tooltip = L("Enable Z-layer contouring (aka Z-layer anti-aliasing)."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("zaa_minimize_perimeter_height", coFloat); + def->label = L("Minimize wall height angle"); + def->category = L("Print/Quality"); + def->tooltip = L("Reduce the height of top-surface perimeters to match the model edge height.\nAffects perimeters with a slope less than this angle (degrees).\nA reasonable value is 35. Set to 0 to disable."); + def->sidetext = L("°"); + def->min = 0; + def->max = 90; + def->set_default_value(new ConfigOptionFloat(35)); + + def = this->add("zaa_dont_alternate_fill_direction", coBool); + def->label = L("Don't alternate fill direction"); + def->category = L("Print/Quality"); + def->tooltip = L("Disable alternating fill direction when using Z contouring."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("zaa_min_z", coFloat); + def->label = L("Minimum z height"); + def->category = L("Print/Quality"); + def->tooltip = L("Minimum Z-layer height.\nAlso controls the slicing plane."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionFloat(0.05)); + + def = this->add("small_area_infill_flow_compensation", coBool); + def->label = L("Small area flow compensation (beta)"); + def->category = L("Print/Quality"); + def->tooltip = L("Enable flow compensation for small infill areas."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("small_area_infill_flow_compensation_model", coStrings); + def->label = L("Flow Compensation Model"); + def->category = L("Print/Quality"); + def->tooltip = L("Flow Compensation Model, used to adjust the flow for small infill areas. The model is expressed as a comma separated pair of values for extrusion length and flow correction factor. Each pair is on a separate line, followed by a semicolon, in the following format: \"1.234, 5.678;\""); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 15; + def->gui_flags = "serialized"; + def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"}); + + def = this->add("make_overhang_printable", coBool); + def->label = L("Make overhangs printable"); + def->category = L("Print/Quality"); + def->tooltip = L("Modify the geometry to print overhangs without support material."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("make_overhang_printable_angle", coFloat); + def->label = L("Make overhangs printable - Maximum angle"); + def->category = L("Print/Quality"); + def->tooltip = L("Maximum angle of overhangs to allow after making more steep overhangs printable.90° will not change the model at all and allow any overhang, while 0 will replace all overhangs with conical material."); + def->sidetext = L("°"); + def->min = 0; + def->max = 90; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(55.)); + + def = this->add("make_overhang_printable_hole_size", coFloat); + def->label = L("Make overhangs printable - Hole area"); + def->category = L("Print/Quality"); + def->tooltip = L("Maximum area of a hole in the base of the model before it's filled by conical material. A value of 0 will fill all the holes in the model base."); + def->sidetext = L("mm²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("detect_overhang_wall", coBool); + def->label = L("Detect overhang walls"); + def->category = L("Print/Quality"); + def->tooltip = L("Detect the overhang percentage relative to line width and use different speed to print. For 100%% overhang, bridge speed is used."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("inner_wall_line_width", coFloatOrPercent); + def->label = L("Inner wall"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width of inner wall. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("resolution", coFloat); + def->label = L("Resolution"); + def->category = L("Print/Quality"); + def->tooltip = L("The G-code path is generated after simplifying the contour of models to avoid too many points and G-code lines. Smaller value means higher resolution and more time to slice."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.01)); + + def = this->add("seam_position", coEnum); + def->label = L("Seam position"); + def->category = L("Print/Quality"); + def->tooltip = L("The start position to print each part of outer wall."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("nearest"); + def->enum_values.push_back("aligned"); + def->enum_values.push_back("aligned_back"); + def->enum_values.push_back("back"); + def->enum_values.push_back("random"); + def->enum_labels.push_back(L("Nearest")); + def->enum_labels.push_back(L("Aligned")); + def->enum_labels.push_back(L("Aligned back")); + def->enum_labels.push_back(L("Back")); + def->enum_labels.push_back(L("Random")); + def->set_default_value(new ConfigOptionEnum(spAligned)); + + def = this->add("staggered_inner_seams", coBool); + def->label = L("Staggered inner seams"); + def->category = L("Print/Quality"); + def->tooltip = L("This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("seam_gap", coFloatOrPercent); + def->label = L("Seam gap"); + def->category = L("Print/Quality"); + def->tooltip = L("In order to reduce the visibility of the seam in a closed loop extrusion, the loop is interrupted and shortened by a specified amount.\nThis amount can be specified in millimeters or as a percentage of the current extruder diameter. The default value for this parameter is 10%."); + def->sidetext = L("mm or %"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(10,true)); + + def = this->add("seam_slope_type", coEnum); + def->label = L("Scarf joint seam (beta)"); + def->category = L("Print/Quality"); + def->tooltip = L("Use scarf joint to minimize seam visibility and increase seam strength."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("none"); + def->enum_values.push_back("external"); + def->enum_values.push_back("all"); + def->enum_labels.push_back(L("None")); + def->enum_labels.push_back(L("Contour")); + def->enum_labels.push_back(L("Contour and hole")); + def->set_default_value(new ConfigOptionEnum(SeamScarfType::None)); + + def = this->add("seam_slope_conditional", coBool); + def->label = L("Conditional scarf joint"); + def->category = L("Print/Quality"); + def->tooltip = L("Apply scarf joints only to smooth perimeters where traditional seams do not conceal the seams at sharp corners effectively."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("scarf_angle_threshold", coInt); + def->label = L("Conditional angle threshold"); + def->category = L("Print/Quality"); + def->tooltip = L("This option sets the threshold angle for applying a conditional scarf joint seam.\nIf the maximum angle within the perimeter loop exceeds this value (indicating the absence of sharp corners), a scarf joint seam will be used. The default value is 155°."); + def->sidetext = L("°"); + def->min = 0; + def->max = 180; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(155)); + + def = this->add("scarf_overhang_threshold", coPercent); + def->label = L("Conditional overhang threshold"); + def->category = L("Print/Quality"); + def->tooltip = L("This option determines the overhang threshold for the application of scarf joint seams. If the unsupported portion of the perimeter is less than this threshold, scarf joint seams will be applied. The default threshold is set at 40% of the external wall's width. Due to performance considerations, the degree of overhang is estimated."); + def->sidetext = L("%"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(40)); + + def = this->add("scarf_joint_speed", coFloatOrPercent); + def->label = L("Scarf joint speed"); + def->category = L("Print/Quality"); + def->tooltip = L("This option sets the printing speed for scarf joints. It is recommended to print scarf joints at a slow speed (less than 100 mm/s). It's also advisable to enable 'Extrusion rate smoothing' if the set speed varies significantly from the speed of the outer or inner walls. If the speed specified here is higher than the speed of the outer or inner walls, the printer will default to the slower of the two speeds. When specified as a percentage (e.g., 80%), the speed is calculated based on the respective outer or inner wall speed. The default value is set to 100%."); + def->sidetext = L("mm/s or %"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("scarf_joint_flow_ratio", coFloat); + def->label = L("Scarf joint flow ratio"); + def->category = L("Print/Quality"); + def->tooltip = L("This factor affects the amount of material for scarf joints."); + def->min = 0; + def->max = 2; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("seam_slope_start_height", coFloatOrPercent); + def->label = L("Scarf start height"); + def->category = L("Print/Quality"); + def->tooltip = L("Start height of the scarf.\nThis amount can be specified in millimeters or as a percentage of the current layer height. The default value for this parameter is 0."); + def->sidetext = L("mm or %"); + def->min = 0; + def->ratio_over = "layer_height"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + + def = this->add("seam_slope_entire_loop", coBool); + def->label = L("Scarf around entire wall"); + def->category = L("Print/Quality"); + def->tooltip = L("The scarf extends to the entire length of the wall."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("seam_slope_min_length", coFloat); + def->label = L("Scarf length"); + def->category = L("Print/Quality"); + def->tooltip = L("Length of the scarf. Setting this parameter to zero effectively disables the scarf."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(20)); + + def = this->add("seam_slope_steps", coInt); + def->label = L("Scarf steps"); + def->category = L("Print/Quality"); + def->tooltip = L("Minimum number of segments of each scarf."); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(10)); + + def = this->add("seam_slope_inner_walls", coBool); + def->label = L("Scarf joint for inner walls"); + def->category = L("Print/Quality"); + def->tooltip = L("Use scarf joint for inner walls as well."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("role_based_wipe_speed", coBool); + def->label = L("Role base wipe speed"); + def->category = L("Print/Quality"); + def->tooltip = L("The wipe speed is determined by the speed of the current extrusion role. e.g. if a wipe action is executed immediately following an outer wall extrusion, the speed of the outer wall extrusion will be utilized for the wipe action."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("wipe_on_loops", coBool); + def->label = L("Wipe on loops"); + def->category = L("Print/Quality"); + def->tooltip = L("To minimize the visibility of the seam in a closed loop extrusion, a small inward movement is executed before the extruder leaves the loop."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wipe_before_external_loop", coBool); + def->label = L("Wipe before external loop"); + def->category = L("Print/Quality"); + def->tooltip = L("To minimize visibility of potential overextrusion at the start of an external perimeter when printing with Outer/Inner or Inner/Outer/Inner wall print order, the de-retraction is performed slightly on the inside from the start of the external perimeter. That way any potential over extrusion is hidden from the outside surface.\n\nThis is useful when printing with Outer/Inner or Inner/Outer/Inner wall print order as in these modes it is more likely an external perimeter is printed immediately after a de-retraction move."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wipe_speed", coFloatOrPercent); + def->label = L("Wipe speed"); + def->category = L("Print/Quality"); + def->tooltip = L("The wipe speed is determined by the speed setting specified in this configuration. If the value is expressed as a percentage (e.g. 80%), it will be calculated based on the travel speed setting above. The default value for this parameter is 80%."); + def->sidetext = L("mm/s or %"); + def->min = 0; + def->ratio_over = "travel_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(80,true)); + + def = this->add("internal_solid_infill_line_width", coFloatOrPercent); + def->label = L("Internal solid infill"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width of internal solid infill. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("slice_closing_radius", coFloat); + def->label = L("Slice gap closing radius"); + def->category = L("Print/Quality"); + def->tooltip = L("Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.049)); + + def = this->add("support_line_width", coFloatOrPercent); + def->label = L("Support"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width of support. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("top_surface_line_width", coFloatOrPercent); + def->label = L("Top surface"); + def->category = L("Print/Quality"); + def->tooltip = L("Line width for top surfaces. If expressed as a %, it will be computed over the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 1000; + def->max_literal = 10; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); + + def = this->add("xy_hole_compensation", coFloat); + def->label = L("X-Y hole compensation"); + def->category = L("Print/Quality"); + def->tooltip = L("Holes in objects will expand or contract in the XY plane by the configured value. Positive values make holes bigger, negative values make holes smaller. This function is used to adjust sizes slightly when the objects have assembling issues."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("xy_contour_compensation", coFloat); + def->label = L("X-Y contour compensation"); + def->category = L("Print/Quality"); + def->tooltip = L("Contours of objects will expand or contract in the XY plane by the configured value. Positive values make contours bigger, negative values make contours smaller. This function is used to adjust sizes slightly when the objects have assembling issues."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("hole_to_polyhole", coBool); + def->label = L("Convert holes to polyholes"); + def->category = L("Print/Quality"); + def->tooltip = L("Search for almost-circular holes that span more than one layer and convert the geometry to polyholes. Use the nozzle size and the (biggest) diameter to compute the polyhole.\nSee http://hydraraptor.blogspot.com/2011/02/polyholes.html"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("hole_to_polyhole_threshold", coFloatOrPercent); + def->label = L("Polyhole detection margin"); + def->category = L("Print/Quality"); + def->tooltip = L("Maximum defection of a point to the estimated radius of the circle.\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference. This setting allows you some leeway to broaden the detection.\nIn mm or in % of the radius."); + def->sidetext = L("mm or %"); + def->max_literal = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0.01, false)); + + def = this->add("hole_to_polyhole_twisted", coBool); + def->label = L("Polyhole twist"); + def->category = L("Print/Quality"); + def->tooltip = L("Rotate the polyhole every layer."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("wall_generator", coEnum); + def->label = L("Wall generator"); + def->category = L("Print/Quality"); + def->tooltip = L("Classic wall generator produces walls with constant extrusion width and for very thin areas is used gap-fill. Arachne engine produces walls with variable extrusion width."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("classic"); + def->enum_values.push_back("arachne"); + def->enum_labels.push_back(L("Classic")); + def->enum_labels.push_back(L("Arachne")); + def->set_default_value(new ConfigOptionEnum(PerimeterGeneratorType::Arachne)); + + def = this->add("wall_transition_length", coPercent); + def->label = L("Wall transition length"); + def->category = L("Print/Quality"); + def->tooltip = L("When transitioning between different numbers of walls as the part becomes thinner, a certain amount of space is allotted to split or join the wall segments. It's expressed as a percentage over nozzle diameter."); + def->sidetext = L("%"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(100)); + + def = this->add("wall_transition_filter_deviation", coPercent); + def->label = L("Wall transitioning filter margin"); + def->category = L("Print/Quality"); + def->tooltip = L("Prevent transitioning back and forth between one extra wall and one less. This margin extends the range of extrusion widths which follow to [Minimum wall width - margin, 2 * Minimum wall width + margin]. Increasing this margin reduces the number of transitions, which reduces the number of extrusion starts/stops and travel time. However, large extrusion width variation can lead to under- or overextrusion problems. It's expressed as a percentage over nozzle diameter."); + def->sidetext = L("%"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(25)); + + def = this->add("wall_transition_angle", coFloat); + def->label = L("Wall transitioning threshold angle"); + def->category = L("Print/Quality"); + def->tooltip = L("When to create transitions between even and odd numbers of walls. A wedge shape with an angle greater than this setting will not have transitions and no walls will be printed in the center to fill the remaining space. Reducing this setting reduces the number and length of these center walls, but may leave gaps or overextrude."); + def->sidetext = L("°"); + def->min = 1; + def->max = 59; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(10.)); + + def = this->add("wall_distribution_count", coInt); + def->label = L("Wall distribution count"); + def->category = L("Print/Quality"); + def->tooltip = L("The number of walls, counted from the center, over which the variation needs to be spread. Lower values mean that the outer walls don't change in width."); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("min_feature_size", coPercent); + def->label = L("Minimum feature size"); + def->category = L("Print/Quality"); + def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker than than this value will be widened to the minimum wall width. It's expressed as a percentage over nozzle diameter."); + def->sidetext = L("%"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(25)); + + def = this->add("min_length_factor", coFloat); + def->label = L("Minimum wall length"); + def->category = L("Print/Quality"); + def->tooltip = L("Adjust this value to prevent short, unclosed walls from being printed, which could increase print time. Higher values remove more and longer walls.\n\nNOTE: Bottom and top surfaces will not be affected by this value to prevent visual gaps on the outside of the model. Adjust 'One wall threshold' in the Advanced settings below to adjust the sensitivity of what is considered a top-surface. 'One wall threshold' is only visible if this setting is set above the default value of 0.5, or if single-wall top surfaces is enabled."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 25; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.5)); + + def = this->add("wall_maximum_resolution", coFloat); + def->label = L("Maximum wall resolution"); + def->category = L("Print/Quality"); + def->tooltip = L("This value determines the smallest wall line segment length in mm. The smaller you set this value, the more accurate and precise the walls will be."); + def->sidetext = L("mm"); + def->min = 0.005; + def->max = 0.5; + def->set_default_value(new ConfigOptionFloat(0.5f)); + + def = this->add("wall_maximum_deviation", coFloat); + def->label = L("Maximum wall deviation"); + def->category = L("Print/Quality"); + def->tooltip = L("The maximum deviation allowed when reducing the resolution for the 'Maximum wall resolution' setting. If you increase this, the print will be less accurate, but the G-Code will be smaller. 'Maximum wall deviation' limits 'Maximum wall resolution', so if the two conflict, 'Maximum wall deviation' takes precedence."); + def->sidetext = L("mm"); + def->min = 0.005; + def->max = 0.05; + def->set_default_value(new ConfigOptionFloat(0.025f)); + + def = this->add("initial_layer_min_bead_width", coPercent); + def->label = L("First layer minimum wall width"); + def->category = L("Print/Quality"); + def->tooltip = L("The minimum wall width that should be used for the first layer is recommended to be set to the same size as the nozzle. This adjustment is expected to enhance adhesion."); + def->sidetext = L("%"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(85)); + + def = this->add("min_bead_width", coPercent); + def->label = L("Minimum wall width"); + def->category = L("Print/Quality"); + def->tooltip = L("Width of the wall that will replace thin features (according to the Minimum feature size) of the model. If the Minimum wall width is thinner than the thickness of the feature, the wall will become as thick as the feature itself. It's expressed as a percentage over nozzle diameter."); + def->sidetext = L("%"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(85)); + + def = this->add("enable_overhang_speed", coBool); + def->label = L("Slow down for overhang"); + def->category = L("Print/Speed"); + def->tooltip = L("Enable this option to slow printing down for different overhang degree."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("slowdown_for_curled_perimeters", coBool); + def->label = L("Slow down for curled perimeters"); + def->category = L("Print/Speed"); + def->tooltip = L("Enable this option to slow down printing in areas where perimeters may have curled upwards. For example, additional slowdown will be applied when printing overhangs on sharp corners like the front of the Benchy hull, reducing curling which compounds over multiple layers.\n\nIt is generally recommended to have this option switched on unless your printer cooling is powerful enough or the print speed slow enough that perimeter curling does not happen. If printing with a high external perimeter speed, this parameter may introduce slight artifacts when slowing down due to the large variance in print speeds. If you notice artifacts, ensure your pressure advance is tuned correctly.\n\nNote: When this option is enabled, overhang perimeters are treated like overhangs, meaning the overhang speed is applied even if the overhanging perimeter is part of a bridge. For example, when the perimeters are 100% overhanging, with no wall supporting them from underneath, the 100% overhang speed will be applied."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("overhang_1_4_speed", coFloatOrPercent); + def->label = L("10%"); + def->full_label = L("10%"); + def->category = L("Print/Speed"); + def->sidetext = L("mm/s or %"); + def->min = 0; + def->ratio_over = "outer_wall_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + + def = this->add("overhang_2_4_speed", coFloatOrPercent); + def->label = L("25%"); + def->full_label = L("25%"); + def->category = L("Print/Speed"); + def->sidetext = L("mm/s or %"); + def->min = 0; + def->ratio_over = "outer_wall_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + + def = this->add("overhang_3_4_speed", coFloatOrPercent); + def->label = L("50%"); + def->full_label = L("50%"); + def->category = L("Print/Speed"); + def->sidetext = L("mm/s or %"); + def->min = 0; + def->ratio_over = "outer_wall_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + + def = this->add("overhang_4_4_speed", coFloatOrPercent); + def->label = L("75%"); + def->full_label = L("75%"); + def->category = L("Print/Speed"); + def->sidetext = L("mm/s or %"); + def->min = 0; + def->ratio_over = "outer_wall_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + + def = this->add("bridge_speed", coFloat); + def->label = L("External"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of the externally visible bridge extrusions.\n\nIn addition, if Slow down for curled perimeters is disabled or Classic overhang mode is enabled, it will be the print speed of overhang walls that are supported by less than 13%, whether they are part of a bridge or an overhang."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(25)); + + def = this->add("internal_bridge_speed", coFloatOrPercent); + def->label = L("Internal"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of internal bridges. If the value is expressed as a percentage, it will be calculated based on the bridge_speed. Default value is 150%."); + def->sidetext = L("mm/s or %"); + def->min = 1; + def->ratio_over = "bridge_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(150, true)); + + def = this->add("default_acceleration", coFloat); + def->label = L("Normal printing"); + def->category = L("Print/Speed"); + def->tooltip = L("The default acceleration of both normal printing and travel except initial layer."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(500.0)); + + def = this->add("outer_wall_speed", coFloat); + def->label = L("Outer wall"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of outer wall which is outermost and visible. It's used to be slower than inner wall speed to get better quality."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(60)); + + def = this->add("small_perimeter_speed", coFloatOrPercent); + def->label = L("Small perimeters"); + def->category = L("Print/Speed"); + def->tooltip = L("This separate setting will affect the speed of perimeters having radius <= small_perimeter_threshold (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the outer wall speed setting above. Set to zero for auto."); + def->sidetext = L("mm/s or %"); + def->min = 1; + def->ratio_over = "outer_wall_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(50, true)); + + def = this->add("small_perimeter_threshold", coFloat); + def->label = L("Small perimeters threshold"); + def->category = L("Print/Speed"); + def->tooltip = L("This sets the threshold for small perimeter length. Default threshold is 0mm."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("inner_wall_acceleration", coFloat); + def->label = L("Inner wall"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of inner walls."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(10000)); + + def = this->add("travel_acceleration", coFloat); + def->label = L("Travel"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of travel moves."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(10000)); + + def = this->add("top_surface_acceleration", coFloat); + def->label = L("Top surface"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of top surface infill. Using a lower value may improve top surface quality."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(500)); + + def = this->add("outer_wall_acceleration", coFloat); + def->label = L("Outer wall"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of outer wall. Using a lower value can improve quality."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(500)); + + def = this->add("bridge_acceleration", coFloatOrPercent); + def->label = L("Bridge"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of bridges. If the value is expressed as a percentage (e.g. 50%), it will be calculated based on the outer wall acceleration."); + def->sidetext = L("mm/s² or %"); + def->min = 0; + def->ratio_over = "outer_wall_acceleration"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(50,true)); + + def = this->add("sparse_infill_acceleration", coFloatOrPercent); + def->label = L("Sparse infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of sparse infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration."); + def->sidetext = L("mm/s² or %"); + def->min = 0; + def->ratio_over = "default_acceleration"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("internal_solid_infill_acceleration", coFloatOrPercent); + def->label = L("Internal solid infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of internal solid infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration."); + def->sidetext = L("mm/s² or %"); + def->min = 0; + def->ratio_over = "default_acceleration"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("initial_layer_acceleration", coFloat); + def->label = L("First layer"); + def->category = L("Print/Speed"); + def->tooltip = L("Acceleration of the first layer. Using a lower value can improve build plate adhesion."); + def->sidetext = L("mm/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(300)); + + def = this->add("initial_layer_travel_acceleration", coFloatOrPercent); + def->label = L("First layer travel"); + def->category = L("Print/Speed"); + def->tooltip = L("Travel acceleration of first layer.\nThe percentage value is relative to Travel Acceleration."); + def->sidetext = L("mm/s² or %"); + def->min = 0; + def->ratio_over = "travel_acceleration"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("accel_to_decel_enable", coBool); + def->label = L("Enable accel_to_decel"); + def->category = L("Print/Speed"); + def->tooltip = L("Klipper's max_accel_to_decel will be adjusted automatically."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("accel_to_decel_factor", coPercent); + def->label = L("accel_to_decel"); + def->category = L("Print/Speed"); + def->tooltip = L("Klipper's max_accel_to_decel will be adjusted to this %% of acceleration."); + def->sidetext = L("%"); + def->min = 1; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(50)); + + def = this->add("default_jerk", coFloat); + def->label = L("Default"); + def->category = L("Print/Speed"); + def->tooltip = L("Default jerk."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("default_junction_deviation", coFloat); + def->label = L("Junction Deviation"); + def->category = L("Print/Speed"); + def->tooltip = L("Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting)."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 0.3; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.f)); + + def = this->add("outer_wall_jerk", coFloat); + def->label = L("Outer wall"); + def->category = L("Print/Speed"); + def->tooltip = L("Jerk of outer walls."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(9)); + + def = this->add("inner_wall_jerk", coFloat); + def->label = L("Inner wall"); + def->category = L("Print/Speed"); + def->tooltip = L("Jerk of inner walls."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(9)); + + def = this->add("top_surface_jerk", coFloat); + def->label = L("Top surface"); + def->category = L("Print/Speed"); + def->tooltip = L("Jerk for top surface."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(9)); + + def = this->add("infill_jerk", coFloat); + def->label = L("Infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Jerk for infill."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(9)); + + def = this->add("initial_layer_jerk", coFloat); + def->label = L("First layer"); + def->category = L("Print/Speed"); + def->tooltip = L("Jerk for the first layer."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(9)); + + def = this->add("travel_jerk", coFloat); + def->label = L("Travel"); + def->category = L("Print/Speed"); + def->tooltip = L("Jerk for travel."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(12)); + + def = this->add("initial_layer_travel_jerk", coFloatOrPercent); + def->label = L("First layer travel"); + def->category = L("Print/Speed"); + def->tooltip = L("Travel jerk of first layer.\nThe percentage value is relative to Travel Jerk."); + def->sidetext = L("mm/s or %"); + def->min = 0; + def->ratio_over = "travel_jerk"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("initial_layer_speed", coFloat); + def->label = L("First layer"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of the first layer except the solid infill part."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(30)); + + def = this->add("initial_layer_infill_speed", coFloat); + def->label = L("First layer infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of solid infill part of the first layer."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(60.0)); + + def = this->add("initial_layer_travel_speed", coFloatOrPercent); + def->label = L("First layer travel speed"); + def->category = L("Print/Speed"); + def->tooltip = L("Travel speed of the first layer."); + def->sidetext = L("mm/s or %"); + def->min = 1; + def->ratio_over = "travel_speed"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("slow_down_layers", coInt); + def->label = L("Number of slow layers"); + def->category = L("Print/Speed"); + def->tooltip = L("The first few layers are printed slower than normal. The speed is gradually increased in a linear fashion over the specified number of layers."); + def->sidetext = L("layers"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("gap_infill_speed", coFloat); + def->label = L("Gap infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of gap infill. Gap usually has irregular line width and should be printed more slowly."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(30)); + + def = this->add("sparse_infill_speed", coFloat); + def->label = L("Sparse infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of internal sparse infill."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(100)); + + def = this->add("ironing_speed", coFloat); + def->label = L("Ironing speed"); + def->category = L("Print/Speed"); + def->tooltip = L("Print speed of ironing lines."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(20)); + + def = this->add("max_volumetric_extrusion_rate_slope", coFloat); + def->label = L("Extrusion rate smoothing"); + def->category = L("Print/Speed"); + def->tooltip = L("This parameter smooths out sudden extrusion rate changes that happen when the printer transitions from printing a high flow (high speed/larger width) extrusion to a lower flow (lower speed/smaller width) extrusion and vice versa.\n\nIt defines the maximum rate by which the extruded volumetric flow in mm³/s can change over time. Higher values mean higher extrusion rate changes are allowed, resulting in faster speed transitions.\n\nA value of 0 disables the feature.\n\nFor a high speed, high flow direct drive printer (like the Bambu lab or Voron) this value is usually not needed. However it can provide some marginal benefit in certain cases where feature speeds vary greatly. For example, when there are aggressive slowdowns due to overhangs. In these cases a high value of around 300-350 mm³/s² is recommended as this allows for just enough smoothing to assist pressure advance achieve a smoother flow transition.\n\nFor slower printers without pressure advance, the value should be set much lower. A value of 10-15 mm³/s² is a good starting point for direct drive extruders and 5-10 mm³/s² for Bowden style.\n\nThis feature is known as Pressure Equalizer in Prusa slicer.\n\nNote: this parameter disables arc fitting."); + def->sidetext = L("mm³/s²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("max_volumetric_extrusion_rate_slope_segment_length", coFloat); + def->label = L("Smoothing segment length"); + def->category = L("Print/Speed"); + def->tooltip = L("A lower value results in smoother extrusion rate transitions. However, this results in a significantly larger G-code file and more instructions for the printer to process.\n\nDefault value of 3 works well for most cases. If your printer is stuttering, increase this value to reduce the number of adjustments made.\n\nAllowed values: 0.5-5"); + def->sidetext = L("mm"); + def->min = 0.5; + def->max = 5; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(3.0)); + + def = this->add("extrusion_rate_smoothing_external_perimeter_only", coBool); + def->label = L("Apply only on external features"); + def->category = L("Print/Speed"); + def->tooltip = L("Applies extrusion rate smoothing only on external perimeters and overhangs. This can help reduce artefacts due to sharp speed transitions on externally visible overhangs without impacting the print speed of features that will not be visible to the user."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("inner_wall_speed", coFloat); + def->label = L("Inner wall"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of inner wall."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(60)); + + def = this->add("internal_solid_infill_speed", coFloat); + def->label = L("Internal solid infill"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of internal solid infill, not the top and bottom surface."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(100)); + + def = this->add("support_interface_speed", coFloat); + def->label = L("Support interface"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of support interface."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(80)); + + def = this->add("support_speed", coFloat); + def->label = L("Support"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of support."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(80)); + + def = this->add("top_surface_speed", coFloat); + def->label = L("Top surface"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of top surface infill which is solid."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(100)); + + def = this->add("travel_speed", coFloat); + def->label = L("Travel"); + def->category = L("Print/Speed"); + def->tooltip = L("Speed of travel which is faster and without extrusion."); + def->sidetext = L("mm/s"); + def->min = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(120)); + + def = this->add("bridge_no_support", coBool); + def->label = L("Don't support bridges"); + def->category = L("Print/Support"); + def->tooltip = L("Don't support the whole bridge area which make support very large. Bridges can usually be printed directly without support if not very long."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("max_bridge_length", coFloat); + def->label = L("Max bridge length"); + def->category = L("Print/Support"); + def->tooltip = L("Max length of bridges that don't need support. Set it to 0 if you want all bridges to be supported, and set it to a very large value if you don't want any bridges to be supported."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(10)); + + def = this->add("raft_contact_distance", coFloat); + def->label = L("Raft contact Z distance"); + def->category = L("Print/Support"); + def->tooltip = L("Z gap between raft and object. If Support Top Z Distance is 0, this value is ignored and the object is printed in direct contact with the raft (no gap)."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("raft_first_layer_density", coPercent); + def->label = L("First layer density"); + def->category = L("Print/Support"); + def->tooltip = L("Density of the first raft or support layer."); + def->sidetext = L("%"); + def->min = 10; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(90)); + + def = this->add("raft_first_layer_expansion", coFloat); + def->label = L("First layer expansion"); + def->category = L("Print/Support"); + def->tooltip = L("Expand the first raft or support layer to improve bed plate adhesion."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(2.0)); + + def = this->add("raft_layers", coInt); + def->label = L("Raft layers"); + def->category = L("Print/Support"); + def->tooltip = L("Object will be raised by this number of support layers. Use this function to avoid warping when printing ABS."); + def->sidetext = L("layers"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(INITIAL_RAFT_LAYERS)); + + def = this->add("enable_support", coBool); + def->label = L("Enable support"); + def->category = L("Print/Support"); + def->tooltip = L("Enable support generation."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("support_type", coEnum); + def->label = L("Type"); + def->category = L("Print/Support"); + def->tooltip = L("Normal (auto) and Tree (auto) are used to generate support automatically. If Normal (manual) or Tree (manual) is selected, only support enforcers are generated."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("normal(auto)"); + def->enum_values.push_back("tree(auto)"); + def->enum_values.push_back("normal(manual)"); + def->enum_values.push_back("tree(manual)"); + def->enum_labels.push_back(L("Normal (auto)")); + def->enum_labels.push_back(L("Tree (auto)")); + def->enum_labels.push_back(L("Normal (manual)")); + def->enum_labels.push_back(L("Tree (manual)")); + def->set_default_value(new ConfigOptionEnum(stNormalAuto)); + + def = this->add("support_object_xy_distance", coFloat); + def->label = L("Support/object XY distance"); + def->category = L("Print/Support"); + def->tooltip = L("XY separation between an object and its support."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.35)); + + def = this->add("support_object_first_layer_gap", coFloat); + def->label = L("Support/object first layer gap"); + def->category = L("Print/Support"); + def->tooltip = L("XY separation between an object and its support at the first layer."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.2)); + + def = this->add("support_angle", coFloat); + def->label = L("Pattern angle"); + def->category = L("Print/Support"); + def->tooltip = L("Use this setting to rotate the support pattern on the horizontal plane."); + def->sidetext = L("°"); + def->min = 0; + def->max = 359; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("support_on_build_plate_only", coBool); + def->label = L("On build plate only"); + def->category = L("Print/Support"); + def->tooltip = L("Don't create support on model surface, only on build plate."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("support_critical_regions_only", coBool); + def->label = L("Support critical regions only"); + def->category = L("Print/Support"); + def->tooltip = L("Only create support for critical regions including sharp tail, cantilever, etc."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("support_remove_small_overhang", coBool); + def->label = L("Ignore small overhangs"); + def->category = L("Print/Support"); + def->tooltip = L("Ignore small overhangs that possibly don't require support."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("support_top_z_distance", coFloat); + def->label = L("Top Z distance"); + def->category = L("Print/Support"); + def->tooltip = L("Z gap between the support's top and object."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->enum_values.push_back("0"); + def->enum_values.push_back("0.1"); + def->enum_values.push_back("0.2"); + def->enum_labels.push_back(L("0 (soluble)")); + def->enum_labels.push_back(L("0.1 (semi-detachable)")); + def->enum_labels.push_back(L("0.2 (detachable)")); + def->set_default_value(new ConfigOptionFloat(0.2)); + + def = this->add("support_bottom_z_distance", coFloat); + def->label = L("Bottom Z distance"); + def->category = L("Print/Support"); + def->tooltip = L("Z gap between the object and the support bottom. If Support Top Z Distance is 0 and the bottom has interface layers, this value is ignored and the support is printed in direct contact with the object (no gap)."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.2)); + + def = this->add("support_filament", coInt); + def->label = L("Support/raft base"); + def->category = L("Print/Support"); + def->tooltip = L("Filament to print support base and raft. \"Default\" means no specific filament for support and current filament is used."); + def->min = 0; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("support_interface_not_for_body", coBool); + def->label = L("Avoid interface filament for base"); + def->category = L("Print/Support"); + def->tooltip = L("Avoid using support interface filament to print support base if possible."); + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("support_interface_filament", coInt); + def->label = L("Support/raft interface"); + def->category = L("Print/Support"); + def->tooltip = L("Filament to print support interface. \"Default\" means no specific filament for support interface and current filament is used."); + def->min = 0; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("support_interface_top_layers", coInt); + def->label = L("Top interface layers"); + def->category = L("Print/Support"); + def->tooltip = L("Number of top interface layers."); + def->sidetext = L("layers"); + def->min = 0; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->enum_values.push_back("0"); + def->enum_values.push_back("1"); + def->enum_values.push_back("2"); + def->enum_values.push_back("3"); + def->enum_labels.push_back(L("0")); + def->enum_labels.push_back(L("1")); + def->enum_labels.push_back(L("2")); + def->enum_labels.push_back(L("3")); + def->set_default_value(new ConfigOptionInt(3)); + + def = this->add("support_interface_bottom_layers", coInt); + def->label = L("Bottom interface layers"); + def->category = L("Print/Support"); + def->tooltip = L("Number of bottom interface layers."); + def->sidetext = L("layers"); + def->min = -1; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; + def->enum_values.push_back("-1"); + def->enum_labels.push_back(L("Same as top")); + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("support_interface_spacing", coFloat); + def->label = L("Top interface spacing"); + def->category = L("Print/Support"); + def->tooltip = L("Spacing of interface lines. Zero means solid interface.\nForce using solid interface when support ironing is enabled."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.5)); + + def = this->add("support_bottom_interface_spacing", coFloat); + def->label = L("Bottom interface spacing"); + def->category = L("Print/Support"); + def->tooltip = L("Spacing of bottom interface lines. Zero means solid interface."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.5)); + + def = this->add("support_base_pattern", coEnum); + def->label = L("Base pattern"); + def->category = L("Print/Support"); + def->tooltip = L("Line pattern of support.\n\nThe Default option for Tree supports is Hollow, which means no base pattern. For other support types, the Default option is the Rectilinear pattern.\n\nNOTE: For Organic supports, the two walls are supported only with the Hollow/Default base pattern. The Lightning base pattern is supported only by Tree Slim/Strong/Hybrid supports. For the other support types, the Rectilinear will be used instead of Lightning."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("default"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("rectilinear-grid"); + def->enum_values.push_back("honeycomb"); + def->enum_values.push_back("lightning"); + def->enum_values.push_back("hollow"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Rectilinear grid")); + def->enum_labels.push_back(L("Honeycomb")); + def->enum_labels.push_back(L("Lightning")); + def->enum_labels.push_back(L("Hollow")); + def->set_default_value(new ConfigOptionEnum(smpDefault)); + + def = this->add("support_interface_pattern", coEnum); + def->label = L("Interface pattern"); + def->category = L("Print/Support"); + def->tooltip = L("Line pattern of support interface. Default pattern for non-soluble support interface is Rectilinear, while default pattern for soluble support interface is Concentric."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("auto"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("rectilinear_interlaced"); + def->enum_values.push_back("grid"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Rectilinear Interlaced")); + def->enum_labels.push_back(L("Grid")); + def->set_default_value(new ConfigOptionEnum(smipAuto)); + + def = this->add("support_base_pattern_spacing", coFloat); + def->label = L("Base pattern spacing"); + def->category = L("Print/Support"); + def->tooltip = L("Spacing between support lines."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(2.5)); + + def = this->add("support_expansion", coFloat); + def->label = L("Normal Support expansion"); + def->category = L("Print/Support"); + def->tooltip = L("Expand (+) or shrink (-) the horizontal span of normal support."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("support_style", coEnum); + def->label = L("Style"); + def->category = L("Print/Support"); + def->tooltip = L("Style and shape of the support. For normal support, projecting the supports into a regular grid will create more stable supports (default), while snug support towers will save material and reduce object scarring.\nFor tree support, slim and organic style will merge branches more aggressively and save a lot of material (default organic), while hybrid style will create similar structure to normal support under large flat overhangs."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("default"); + def->enum_values.push_back("grid"); + def->enum_values.push_back("snug"); + def->enum_values.push_back("organic"); + def->enum_values.push_back("tree_slim"); + def->enum_values.push_back("tree_strong"); + def->enum_values.push_back("tree_hybrid"); + def->enum_labels.push_back(L("Default (Grid/Organic)")); + def->enum_labels.push_back(L("Grid")); + def->enum_labels.push_back(L("Snug")); + def->enum_labels.push_back(L("Organic")); + def->enum_labels.push_back(L("Tree Slim")); + def->enum_labels.push_back(L("Tree Strong")); + def->enum_labels.push_back(L("Tree Hybrid")); + def->set_default_value(new ConfigOptionEnum(smsDefault)); + + def = this->add("independent_support_layer_height", coBool); + def->label = L("Independent support layer height"); + def->category = L("Print/Support"); + def->tooltip = L("Support layer uses layer height independent with object layer. This is to support customizing Z-gap and save print time. This option will be invalid when the prime tower is enabled."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("support_threshold_angle", coInt); + def->label = L("Threshold angle"); + def->category = L("Print/Support"); + def->tooltip = L("Support will be generated for overhangs whose slope angle is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support.\nNote: If set to 0, normal supports use the Threshold overlap instead, while tree supports fall back to a default value of 30."); + def->sidetext = L("°"); + def->min = 0; + def->max = 90; + def->set_default_value(new ConfigOptionInt(30)); + + def = this->add("support_threshold_overlap", coFloatOrPercent); + def->label = L("Threshold overlap"); + def->category = L("Print/Support"); + def->tooltip = L("If threshold angle is zero, support will be generated for overhangs whose overlap is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max = 100; + def->max_literal = 0.5; + def->set_default_value(new ConfigOptionFloatOrPercent(50., true)); + + def = this->add("tree_support_branch_angle", coFloat); + def->label = L("Tree support branch angle"); + def->category = L("Print/Support"); + def->tooltip = L("This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther."); + def->sidetext = L("°"); + def->min = 0; + def->max = 60; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(40.)); + + def = this->add("tree_support_branch_angle_organic", coFloat); + def->label = L("Tree support branch angle"); + def->category = L("Print/Support"); + def->tooltip = L("This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther."); + def->sidetext = L("°"); + def->min = 0; + def->max = 60; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(40.)); + + def = this->add("tree_support_angle_slow", coFloat); + def->label = L("Preferred Branch Angle"); + def->category = L("Print/Support"); + def->tooltip = L("The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster."); + def->sidetext = L("°"); + def->min = 10; + def->max = 85; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(25)); + + def = this->add("tree_support_branch_distance", coFloat); + def->label = L("Tree support branch distance"); + def->category = L("Print/Support"); + def->tooltip = L("This setting determines the distance between neighboring tree support nodes."); + def->sidetext = L("mm"); + def->min = 1; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(5.)); + + def = this->add("tree_support_branch_distance_organic", coFloat); + def->label = L("Tree support branch distance"); + def->category = L("Print/Support"); + def->tooltip = L("This setting determines the distance between neighboring tree support nodes."); + def->sidetext = L("mm"); + def->min = 1; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1.)); + + def = this->add("tree_support_top_rate", coPercent); + def->label = L("Branch Density"); + def->category = L("Print/Support"); + def->tooltip = L("Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces instead of a high branch density value if dense interfaces are needed."); + def->sidetext = L("%"); + def->min = 5; + def->max_literal = 35; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(30)); + + def = this->add("tree_support_auto_brim", coBool); + def->label = L("Auto brim width"); + def->category = L("Print/Support"); + def->tooltip = L("Enabling this option means the width of the brim for tree support will be automatically calculated."); + def->set_default_value(new ConfigOptionBool(1)); + + def = this->add("tree_support_brim_width", coFloat); + def->label = L("Tree support brim width"); + def->category = L("Print/Support"); + def->tooltip = L("Distance from tree branch to the outermost brim line."); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(3)); + + def = this->add("tree_support_tip_diameter", coFloat); + def->label = L("Tip Diameter"); + def->category = L("Print/Support"); + def->tooltip = L("Branch tip diameter for organic supports."); + def->sidetext = L("mm"); + def->min = 0.1; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.8)); + + def = this->add("tree_support_branch_diameter", coFloat); + def->label = L("Tree support branch diameter"); + def->category = L("Print/Support"); + def->tooltip = L("This setting determines the initial diameter of support nodes."); + def->sidetext = L("mm"); + def->min = 1; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(5.)); + + def = this->add("tree_support_branch_diameter_angle", coFloat); + def->label = L("Branch Diameter Angle"); + def->category = L("Print/Support"); + def->tooltip = L("The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the organic support."); + def->sidetext = L("°"); + def->min = 0; + def->max = 15; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(5)); + + def = this->add("tree_support_branch_diameter_organic", coFloat); + def->label = L("Tree support branch diameter"); + def->category = L("Print/Support"); + def->tooltip = L("This setting determines the initial diameter of support nodes."); + def->sidetext = L("mm"); + def->min = 1; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(2.)); + + def = this->add("tree_support_wall_count", coInt); + def->label = L("Support wall loops"); + def->category = L("Print/Support"); + def->tooltip = L("This setting specifies the count of support walls in the range of [0,2]. 0 means auto."); + def->min = 0; + def->max = 2; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("support_ironing", coBool); + def->label = L("Ironing Support Interface"); + def->category = L("Print/Support"); + def->tooltip = L("Ironing is using small flow to print on same height of support interface again to make it more smooth. This setting controls whether support interface being ironed. When enabled, support interface will be extruded as solid too."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("support_ironing_pattern", coEnum); + def->label = L("Support Ironing Pattern"); + def->category = L("Print/Support"); + def->tooltip = L("The pattern that will be used when ironing."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("concentric"); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->set_default_value(new ConfigOptionEnum(ipRectilinear)); + + def = this->add("support_ironing_flow", coPercent); + def->label = L("Support Ironing flow"); + def->category = L("Print/Support"); + def->tooltip = L("The amount of material to extrude during ironing. Relative to flow of normal support interface layer height. Too high value results in overextrusion on the surface."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->ratio_over = "layer_height"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(10)); + + def = this->add("support_ironing_spacing", coFloat); + def->label = L("Support Ironing line spacing"); + def->category = L("Print/Support"); + def->tooltip = L("The distance between the lines of ironing."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("first_layer_sequence_choice", coEnum); + def->label = L("First layer filament sequence"); + def->category = L("Quality"); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Auto"); + def->enum_values.push_back("Customize"); + def->enum_labels.push_back(L("Auto")); + def->enum_labels.push_back(L("Customize")); + def->set_default_value(new ConfigOptionEnum(flsAuto)); + + def = this->add("other_layers_sequence_choice", coEnum); + def->label = L("Other layers filament sequence"); + def->category = L("Quality"); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Auto"); + def->enum_values.push_back("Customize"); + def->enum_labels.push_back(L("Auto")); + def->enum_labels.push_back(L("Customize")); + def->set_default_value(new ConfigOptionEnum(flsAuto)); + + def = this->add("ironing_expansion", coFloat); + def->label = L("Ironing expansion"); + def->category = L("Quality"); + def->tooltip = L("Expand or contract the ironing area."); + def->sidetext = L("mm"); + def->min = -100; + def->max = 100; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("bottom_shell_layers", coInt); + def->label = L("Bottom shell layers"); + def->full_label = L("Bottom shell layers"); + def->category = L("Print/Strength"); + def->tooltip = L("This is the number of solid layers of bottom shell, including the bottom surface layer. When the thickness calculated by this value is thinner than bottom shell thickness, the bottom shell layers will be increased."); + def->sidetext = L("layers"); + def->min = 0; + def->set_default_value(new ConfigOptionInt(3)); + + def = this->add("bottom_shell_thickness", coFloat); + def->label = L("Bottom shell thickness"); + def->full_label = L("Bottom shell thickness"); + def->category = L("Print/Strength"); + def->tooltip = L("The number of bottom solid layers is increased when slicing if the thickness calculated by bottom shell layers is thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that this setting is disabled and thickness of bottom shell is absolutely determined by bottom shell layers."); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("gap_fill_target", coEnum); + def->label = L("Apply gap fill"); + def->category = L("Print/Strength"); + def->tooltip = L("Enables gap fill for the selected solid surfaces. The minimum gap length that will be filled can be controlled from the filter out tiny gaps option below.\n\nOptions:\n1. Everywhere: Applies gap fill to top, bottom and internal solid surfaces for maximum strength\n2. Top and Bottom surfaces: Applies gap fill to top and bottom surfaces only, balancing print speed, reducing potential over extrusion in the solid infill and making sure the top and bottom surfaces have no pinhole gaps\n3. Nowhere: Disables gap fill for all solid infill areas\n\nNote that if using the classic perimeter generator, gap fill may also be generated between perimeters, if a full width line cannot fit between them. That perimeter gap fill is not controlled by this setting.\n\nIf you would like all gap fill, including the classic perimeter generated one, removed, set the filter out tiny gaps value to a large number, like 999999.\n\nHowever this is not advised, as gap fill between perimeters is contributing to the model's strength. For models where excessive gap fill is generated between perimeters, a better option would be to switch to the arachne wall generator and use this option to control whether the cosmetic top and bottom surface gap fill is generated."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("everywhere"); + def->enum_values.push_back("topbottom"); + def->enum_values.push_back("nowhere"); + def->enum_labels.push_back(L("Everywhere")); + def->enum_labels.push_back(L("Top and bottom surfaces")); + def->enum_labels.push_back(L("Nowhere")); + def->set_default_value(new ConfigOptionEnum(gftNowhere)); + + def = this->add("bridge_angle", coFloat); + def->label = L("External bridge infill direction"); + def->category = L("Print/Strength"); + def->tooltip = L("Bridging angle override. If left to zero, the bridging angle will be calculated automatically. Otherwise the provided angle will be used for external bridges. Use 180° for zero angle."); + def->sidetext = L("°"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("internal_bridge_angle", coFloat); + def->label = L("Internal bridge infill direction"); + def->category = L("Print/Strength"); + def->tooltip = L("Internal bridging angle override. If left to zero, the bridging angle will be calculated automatically. Otherwise the provided angle will be used for internal bridges. Use 180° for zero angle.\n\nIt is recommended to leave it at 0 unless there is a specific model need not to."); + def->sidetext = L("°"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("ensure_vertical_shell_thickness", coEnum); + def->label = L("Ensure vertical shell thickness"); + def->category = L("Print/Strength"); + def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top+bottom solid layers)\nNone: No solid infill will be added anywhere. Caution: Use this option carefully if your model has sloped surfaces\nCritical Only: Avoid adding solid infill for walls\nModerate: Add solid infill for heavily sloping surfaces only\nAll: Add solid infill for all suitable sloping surfaces\nDefault value is All."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("none"); + def->enum_values.push_back("ensure_critical_only"); + def->enum_values.push_back("ensure_moderate"); + def->enum_values.push_back("ensure_all"); + def->enum_labels.push_back(L("None")); + def->enum_labels.push_back(L("Critical Only")); + def->enum_labels.push_back(L("Moderate")); + def->enum_labels.push_back(L("All")); + def->set_default_value(new ConfigOptionEnum(EnsureVerticalShellThickness::evstAll)); + + def = this->add("top_surface_pattern", coEnum); + def->label = L("Top surface pattern"); + def->category = L("Print/Strength"); + def->tooltip = L("Line pattern of top surface infill."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("monotonic"); + def->enum_values.push_back("monotonicline"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("alignedrectilinear"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("hilbertcurve"); + def->enum_values.push_back("archimedeanchords"); + def->enum_values.push_back("octagramspiral"); + def->enum_labels.push_back(L("Monotonic")); + def->enum_labels.push_back(L("Monotonic line")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Aligned Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Archimedean Chords")); + def->enum_labels.push_back(L("Octagram Spiral")); + def->set_default_value(new ConfigOptionEnum(ipMonotonicLine)); + + def = this->add("bottom_surface_pattern", coEnum); + def->label = L("Bottom surface pattern"); + def->category = L("Print/Strength"); + def->tooltip = L("Line pattern of bottom surface infill, not bridge infill."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("monotonic"); + def->enum_values.push_back("monotonicline"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("alignedrectilinear"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("hilbertcurve"); + def->enum_values.push_back("archimedeanchords"); + def->enum_values.push_back("octagramspiral"); + def->enum_labels.push_back(L("Monotonic")); + def->enum_labels.push_back(L("Monotonic line")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Aligned Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Archimedean Chords")); + def->enum_labels.push_back(L("Octagram Spiral")); + def->set_default_value(new ConfigOptionEnum(ipMonotonic)); + + def = this->add("internal_solid_infill_pattern", coEnum); + def->label = L("Internal solid infill pattern"); + def->category = L("Print/Strength"); + def->tooltip = L("Line pattern of internal solid infill. if the detect narrow internal solid infill be enabled, the concentric pattern will be used for the small area."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("monotonic"); + def->enum_values.push_back("monotonicline"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("alignedrectilinear"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("hilbertcurve"); + def->enum_values.push_back("archimedeanchords"); + def->enum_values.push_back("octagramspiral"); + def->enum_labels.push_back(L("Monotonic")); + def->enum_labels.push_back(L("Monotonic line")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Aligned Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Archimedean Chords")); + def->enum_labels.push_back(L("Octagram Spiral")); + def->set_default_value(new ConfigOptionEnum(ipMonotonic)); + + def = this->add("infill_direction", coFloat); + def->label = L("Sparse infill direction"); + def->category = L("Print/Strength"); + def->tooltip = L("Angle for sparse infill pattern, which controls the start or main direction of line."); + def->sidetext = L("°"); + def->min = 0; + def->max = 360; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(45)); + + def = this->add("solid_infill_direction", coFloat); + def->label = L("Solid infill direction"); + def->category = L("Print/Strength"); + def->tooltip = L("Angle for solid infill pattern, which controls the start or main direction of line."); + def->sidetext = L("°"); + def->min = 0; + def->max = 360; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(45)); + + def = this->add("sparse_infill_density", coPercent); + def->label = L("Sparse infill density"); + def->category = L("Print/Strength"); + def->tooltip = L("Density of internal sparse infill, 100% turns all sparse infill into solid infill and internal solid infill pattern will be used."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionPercent(20)); + + def = this->add("align_infill_direction_to_model", coBool); + def->label = L("Align infill direction to model"); + def->category = L("Print/Strength"); + def->tooltip = L("Aligns infill and surface fill directions to follow the model's orientation on the build plate. When enabled, fill directions rotate with the model to maintain optimal strength characteristics."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("extra_solid_infills", coString); + def->label = L("Insert solid layers"); + def->category = L("Print/Strength"); + def->tooltip = L("Insert solid infill at specific layers. Use N to insert every Nth layer, N#K to insert K consecutive solid layers every N layers (K is optional, e.g. '5#' equals '5#1'), or a comma-separated list (e.g. 1,7,9) to insert at explicit layers. Layers are 1-based."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString()); + + def = this->add("fill_multiline", coInt); + def->label = L("Fill Multiline"); + def->category = L("Print/Strength"); + def->tooltip = L("Using multiple lines for the infill pattern, if supported by infill pattern."); + def->min = 1; + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("gyroid_optimized", coBool); + def->label = L("Z-buckling bias optimization (experimental)"); + def->category = L("Print/Strength"); + def->tooltip = L("Tightens the gyroid wave along the Z (vertical) axis at low infill density to shorten the effective vertical column length and improve Z-axis compression buckling resistance. Filament use is preserved. No effect at ~30% sparse infill density and above. Only applies when Sparse infill pattern is set to Gyroid."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("sparse_infill_pattern", coEnum); + def->label = L("Sparse infill pattern"); + def->category = L("Print/Strength"); + def->tooltip = L("Line pattern for internal sparse infill."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("alignedrectilinear"); + def->enum_values.push_back("zigzag"); + def->enum_values.push_back("crosszag"); + def->enum_values.push_back("lockedzag"); + def->enum_values.push_back("line"); + def->enum_values.push_back("grid"); + def->enum_values.push_back("triangles"); + def->enum_values.push_back("tri-hexagon"); + def->enum_values.push_back("cubic"); + def->enum_values.push_back("adaptivecubic"); + def->enum_values.push_back("quartercubic"); + def->enum_values.push_back("supportcubic"); + def->enum_values.push_back("lightning"); + def->enum_values.push_back("honeycomb"); + def->enum_values.push_back("3dhoneycomb"); + def->enum_values.push_back("lateral-honeycomb"); + def->enum_values.push_back("lateral-lattice"); + def->enum_values.push_back("crosshatch"); + def->enum_values.push_back("tpmsd"); + def->enum_values.push_back("tpmsfk"); + def->enum_values.push_back("gyroid"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("hilbertcurve"); + def->enum_values.push_back("archimedeanchords"); + def->enum_values.push_back("octagramspiral"); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Aligned Rectilinear")); + def->enum_labels.push_back(L("Zig Zag")); + def->enum_labels.push_back(L("Cross Zag")); + def->enum_labels.push_back(L("Locked Zag")); + def->enum_labels.push_back(L("Line")); + def->enum_labels.push_back(L("Grid")); + def->enum_labels.push_back(L("Triangles")); + def->enum_labels.push_back(L("Tri-hexagon")); + def->enum_labels.push_back(L("Cubic")); + def->enum_labels.push_back(L("Adaptive Cubic")); + def->enum_labels.push_back(L("Quarter Cubic")); + def->enum_labels.push_back(L("Support Cubic")); + def->enum_labels.push_back(L("Lightning")); + def->enum_labels.push_back(L("Honeycomb")); + def->enum_labels.push_back(L("3D Honeycomb")); + def->enum_labels.push_back(L("Lateral Honeycomb")); + def->enum_labels.push_back(L("Lateral Lattice")); + def->enum_labels.push_back(L("Cross Hatch")); + def->enum_labels.push_back(L("TPMS-D")); + def->enum_labels.push_back(L("TPMS-FK")); + def->enum_labels.push_back(L("Gyroid")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Archimedean Chords")); + def->enum_labels.push_back(L("Octagram Spiral")); + def->set_default_value(new ConfigOptionEnum(ipCrossHatch)); + + def = this->add("lateral_lattice_angle_1", coFloat); + def->label = L("Lateral lattice angle 1"); + def->category = L("Print/Strength"); + def->tooltip = L("The angle of the first set of Lateral lattice elements in the Z direction. Zero is vertical."); + def->sidetext = L("°"); + def->min = -75; + def->max = 75; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(-45)); + + def = this->add("lateral_lattice_angle_2", coFloat); + def->label = L("Lateral lattice angle 2"); + def->category = L("Print/Strength"); + def->tooltip = L("The angle of the second set of Lateral lattice elements in the Z direction. Zero is vertical."); + def->sidetext = L("°"); + def->min = -75; + def->max = 75; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(45)); + + def = this->add("infill_overhang_angle", coFloat); + def->label = L("Infill overhang angle"); + def->category = L("Print/Strength"); + def->tooltip = L("The angle of the infill angled lines. 60° will result in a pure honeycomb."); + def->sidetext = L("°"); + def->min = 15; + def->max = 75; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(60)); + + def = this->add("infill_anchor", coFloatOrPercent); + def->label = L("Sparse infill anchor length"); + def->category = L("Print/Strength"); + def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Orca Slicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one side and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max.\nSet this parameter to zero to disable anchoring perimeters connected to a single infill line."); + def->sidetext = L("mm or %"); + def->max_literal = 1000; + def->ratio_over = "sparse_infill_line_width"; + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; + def->enum_values.push_back("0"); + def->enum_values.push_back("1"); + def->enum_values.push_back("2"); + def->enum_values.push_back("5"); + def->enum_values.push_back("10"); + def->enum_values.push_back("1000"); + def->enum_labels.push_back(L("0 (no open anchors)")); + def->enum_labels.push_back(L("1 mm")); + def->enum_labels.push_back(L("2 mm")); + def->enum_labels.push_back(L("5 mm")); + def->enum_labels.push_back(L("10 mm")); + def->enum_labels.push_back(L("1000 (unlimited)")); + def->set_default_value(new ConfigOptionFloatOrPercent(400, true)); + + def = this->add("infill_anchor_max", coFloatOrPercent); + def->label = L("Maximum length of the infill anchor"); + def->category = L("Print/Strength"); + def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Orca Slicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter.\nIf set to 0, the old algorithm for infill connection will be used, it should create the same result as with 1000 & 0."); + def->mode = comAdvanced; + def->enum_labels.push_back(L("0 (Simple connect)")); + def->enum_labels.push_back(L("1 mm")); + def->enum_labels.push_back(L("2 mm")); + def->enum_labels.push_back(L("5 mm")); + def->enum_labels.push_back(L("10 mm")); + def->enum_labels.push_back(L("1000 (unlimited)")); + def->set_default_value(new ConfigOptionFloatOrPercent(20, false)); + + def = this->add("filter_out_gap_fill", coFloat); + def->label = L("Filter out tiny gaps"); + def->category = L("Print/Strength"); + def->tooltip = L("Don't print gap fill with a length is smaller than the threshold specified (in mm). This setting applies to top, bottom and solid infill and, if using the classic perimeter generator, to wall gap fill."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("infill_combination", coBool); + def->label = L("Infill combination"); + def->category = L("Print/Strength"); + def->tooltip = L("Automatically Combine sparse infill of several layers to print together to reduce time. Wall is still printed with original layer height."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("infill_shift_step", coFloat); + def->label = L("Infill shift step"); + def->category = L("Print/Strength"); + def->tooltip = L("This parameter adds a slight displacement to each layer of infill to create a cross texture."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.4)); + + def = this->add("sparse_infill_rotate_template", coString); + def->label = L("Sparse infill rotation template"); + def->category = L("Print/Strength"); + def->tooltip = L("Rotate the sparse infill direction per layer using a template of angles. Enter comma-separated degrees (e.g., '0,30,60,90'). Angles are applied in order by layer and repeat when the list ends. Advanced syntax is supported: '+5' rotates +5° every layer; '+5#5' rotates +5° every 5 layers. See the Wiki for details. "); + def->sidetext = L("°"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("solid_infill_rotate_template", coString); + def->label = L("Solid infill rotation template"); + def->category = L("Print/Strength"); + def->tooltip = L("This parameter adds a rotation of solid infill direction to each layer according to the specified template. The template is a comma-separated list of angles in degrees, e.g. '0,90'. The first angle is applied to the first layer, the second angle to the second layer, and so on. If there are more layers than angles, the angles will be repeated. Note that not all solid infill patterns support rotation."); + def->sidetext = L("°"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("skeleton_infill_density", coPercent); + def->label = L("Skeleton infill density"); + def->category = L("Print/Strength"); + def->tooltip = L("The remaining part of the model contour after removing a certain depth from the surface is called the skeleton. This parameter is used to adjust the density of this section. When two regions have the same sparse infill settings but different skeleton densities, their skeleton areas will develop overlapping sections. Default is as same as infill density."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(25)); + + def = this->add("skin_infill_density", coPercent); + def->label = L("Skin infill density"); + def->category = L("Print/Strength"); + def->tooltip = L("The portion of the model's outer surface within a certain depth range is called the skin. This parameter is used to adjust the density of this section. When two regions have the same sparse infill settings but different skin densities, this area will not be split into two separate regions. Default is as same as infill density."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(25)); + + def = this->add("skin_infill_depth", coFloat); + def->label = L("Skin infill depth"); + def->category = L("Print/Strength"); + def->tooltip = L("The parameter sets the depth of skin."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(2.0)); + + def = this->add("infill_lock_depth", coFloat); + def->label = L("Infill lock depth"); + def->category = L("Print/Strength"); + def->tooltip = L("The parameter sets the overlapping depth between the interior and skin."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1.0)); + + def = this->add("skin_infill_line_width", coFloatOrPercent); + def->label = L("Skin line width"); + def->category = L("Print/Strength"); + def->tooltip = L("Adjust the line width of the selected skin paths."); + def->sidetext = L("mm"); + def->min = 0; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("skeleton_infill_line_width", coFloatOrPercent); + def->label = L("Skeleton line width"); + def->category = L("Print/Strength"); + def->tooltip = L("Adjust the line width of the selected skeleton paths."); + def->sidetext = L("mm"); + def->min = 0; + def->ratio_over = "nozzle_diameter"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + + def = this->add("symmetric_infill_y_axis", coBool); + def->label = L("Symmetric infill Y axis"); + def->category = L("Print/Strength"); + def->tooltip = L("If the model has two parts that are symmetric about the Y axis, and you want these parts to have symmetric textures, please click this option on one of the parts."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("infill_combination_max_layer_height", coFloatOrPercent); + def->label = L("Infill combination - Max layer height"); + def->category = L("Print/Strength"); + def->tooltip = L("Maximum layer height for the combined sparse infill.\n\nSet it to 0 or 100% to use the nozzle diameter (for maximum reduction in print time) or a value of ~80% to maximize sparse infill strength.\n\nThe number of layers over which infill is combined is derived by dividing this value with the layer height and rounded down to the nearest decimal.\n\nUse either absolute mm values (eg. 0.32mm for a 0.4mm nozzle) or % values (eg 80%). This value must not be larger than the nozzle diameter."); + def->sidetext = L("mm or %"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100., true)); + + def = this->add("infill_wall_overlap", coPercent); + def->label = L("Infill/Wall overlap"); + def->category = L("Print/Strength"); + def->tooltip = L("Infill area is enlarged slightly to overlap with wall for better bonding. The percentage value is relative to line width of sparse infill. Set this value to ~10-15% to minimize potential over extrusion and accumulation of material resulting in rough top surfaces."); + def->sidetext = L("%"); + def->ratio_over = "inner_wall_line_width"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(15)); + + def = this->add("top_bottom_infill_wall_overlap", coPercent); + def->label = L("Top/Bottom solid infill/wall overlap"); + def->category = L("Print/Strength"); + def->tooltip = L("Top solid infill area is enlarged slightly to overlap with wall for better bonding and to minimize the appearance of pinholes where the top infill meets the walls. A value of 25-30% is a good starting point, minimizing the appearance of pinholes. The percentage value is relative to line width of sparse infill."); + def->sidetext = L("%"); + def->ratio_over = "inner_wall_line_width"; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercent(25)); + + def = this->add("wall_loops", coInt); + def->label = L("Wall loops"); + def->category = L("Print/Strength"); + def->tooltip = L("Number of walls of every layer."); + def->min = 0; + def->max = 1000; + def->set_default_value(new ConfigOptionInt(2)); + + def = this->add("alternate_extra_wall", coBool); + def->label = L("Alternate extra wall"); + def->category = L("Print/Strength"); + def->tooltip = L("This setting adds an extra wall to every other layer. This way the infill gets wedged vertically between the walls, resulting in stronger prints.\n\nWhen this option is enabled, the ensure vertical shell thickness option needs to be disabled.\n\nUsing lightning infill together with this option is not recommended as there is limited infill to anchor the extra perimeters to."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("minimum_sparse_infill_area", coFloat); + def->label = L("Minimum sparse infill threshold"); + def->category = L("Print/Strength"); + def->tooltip = L("Sparse infill areas smaller than this threshold value are replaced by internal solid infill."); + def->sidetext = L("mm²"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(15)); + + def = this->add("detect_thin_wall", coBool); + def->label = L("Detect thin walls"); + def->category = L("Print/Strength"); + def->tooltip = L("Detect thin walls which can't contain two line widths, and use single line to print. Maybe not printed very well, because it's not a closed loop."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("top_shell_layers", coInt); + def->label = L("Top shell layers"); + def->full_label = L("Top solid layers"); + def->category = L("Print/Strength"); + def->tooltip = L("This is the number of solid layers of top shell, including the top surface layer. When the thickness calculated by this value is thinner than top shell thickness, the top shell layers will be increased."); + def->sidetext = L("layers"); + def->min = 0; + def->set_default_value(new ConfigOptionInt(4)); + + def = this->add("top_shell_thickness", coFloat); + def->label = L("Top shell thickness"); + def->full_label = L("Top shell thickness"); + def->category = L("Print/Strength"); + def->tooltip = L("The number of top solid layers is increased when slicing if the thickness calculated by top shell layers is thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that this setting is disabled and thickness of top shell is absolutely determined by top shell layers."); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(0.6)); + + def = this->add("top_surface_density", coPercent); + def->label = L("Top surface density"); + def->category = L("Print/Strength"); + def->tooltip = L("Density of top surface layer. A value of 100% creates a fully solid, smooth top layer. Reducing this value results in a textured top surface, according to the chosen top surface pattern. A value of 0% will result in only the walls on the top layer being created. Intended for aesthetic or functional purposes, not to fix issues such as over-extrusion."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->set_default_value(new ConfigOptionPercent(100)); + + def = this->add("bottom_surface_density", coPercent); + def->label = L("Bottom surface density"); + def->category = L("Print/Strength"); + def->tooltip = L("Density of the bottom surface layer. Intended for aesthetic or functional purposes, not to fix issues such as over-extrusion.\nWARNING: Lowering this value may negatively affect bed adhesion."); + def->sidetext = L("%"); + def->min = 10; + def->max = 100; + def->set_default_value(new ConfigOptionPercent(100)); + + def = this->add("detect_narrow_internal_solid_infill", coBool); + def->label = L("Detect narrow internal solid infills"); + def->category = L("Print/Strength"); + def->tooltip = L("This option will auto-detect narrow internal solid infill areas. If enabled, the concentric pattern will be used for the area to speed up printing. Otherwise, the rectilinear pattern will be used by default."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("brim_ears", coBool); + def->label = L("Brim ears"); + def->category = L("Support"); + def->tooltip = L("Only draw brim over the sharp edges of the model."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("raft_expansion", coFloat); + def->label = L("Raft expansion"); + def->category = L("Support"); + def->tooltip = L("Expand all raft layers in XY plane."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1.5)); + + def = this->add("enforce_support_layers", coInt); + def->category = L("Support"); + def->sidetext = L("layers"); + def->min = 0; + def->max = 5000; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("support_interface_loop_pattern", coBool); + def->label = L("Interface use loop pattern"); + def->category = L("Support"); + def->tooltip = L("Cover the top contact layer of the supports with loops. Disabled by default."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("tree_support_with_infill", coBool); + def->label = L("Tree support with infill"); + def->category = L("Support"); + def->tooltip = L("This setting specifies whether to add infill inside large hollows of tree support."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("curr_bed_type", coEnum); + def->label = L("Bed type"); + def->tooltip = L("Bed types supported by the printer."); + def->enum_keys_map = &s_keys_map_BedType; + def->enum_values.push_back("Cool Plate"); + def->enum_values.push_back("Engineering Plate"); + def->enum_values.push_back("High Temp Plate"); + def->enum_values.push_back("Textured PEI Plate"); + def->enum_values.push_back("Textured Cool Plate"); + def->enum_values.push_back("Supertack Plate"); + def->enum_labels.push_back(L("Smooth Cool Plate")); + def->enum_labels.push_back(L("Engineering Plate")); + def->enum_labels.push_back(L("Smooth High Temp Plate")); + def->enum_labels.push_back(L("Textured PEI Plate")); + def->enum_labels.push_back(L("Textured Cool Plate")); + def->enum_labels.push_back(L("Cool Plate (SuperTack)")); + def->set_default_value(new ConfigOptionEnum(btPC)); + + def = this->add("default_bed_type", coString); + def->label = L("Default bed type"); + def->tooltip = L("Default bed type for the printer (supports both numeric and string format)."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString()); + + def = this->add("first_layer_print_sequence", coInts); + def->label = L("First layer print sequence"); + def->min = 0; + def->max = 16; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("other_layers_print_sequence", coInts); + def->label = L("Other layers print sequence"); + def->min = 0; + def->max = 16; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("other_layers_print_sequence_nums", coInt); + def->label = L("The number of other layers print sequence"); + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("compatible_printers", coStrings); + def->label = L("Select printers"); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("upward_compatible_machine", coStrings); + def->label = L("upward compatible machine"); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("compatible_prints", coStrings); + def->label = L("Select profiles"); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("compatible_machine_expression_group", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("compatible_process_expression_group", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("different_settings_to_system", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("print_compatible_printers", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("default_filament_profile", coStrings); + def->label = L("Default filament profile"); + def->tooltip = L("Default filament profile when switching to this machine profile."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("default_print_profile", coString); + def->label = L("Default process profile"); + def->tooltip = L("Default process profile when switching to this machine profile."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("nozzle_height", coFloat); + def->label = L("Nozzle height"); + def->tooltip = L("The height of nozzle tip."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloat(2.5)); + + def = this->add("grab_length", coFloats); + def->label = L("Grab length"); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloats{0}); + + def = this->add("extruder_colour", coStrings); + def->label = L("Extruder Color"); + def->tooltip = L("Only used as a visual help on UI."); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::color; + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("extruder_offset", coPoints); + def->label = L("Extruder offset"); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoints{Vec2d(0,0)}); + + def = this->add("filament_colour", coStrings); + def->label = L("Color"); + def->tooltip = L("Only used as a visual help on UI."); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::color; + def->set_default_value(new ConfigOptionStrings{"#F2754E"}); + + def = this->add("filament_multi_colour", coStrings); + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("filament_colour_type", coStrings); + def->set_default_value(new ConfigOptionStrings{"1"}); + + def = this->add("filament_map", coInts); + def->label = L("Filament map to extruder"); + def->tooltip = L("Filament map to extruder."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("physical_extruder_map", coInts); + def->label = L("Map the logical extruder to physical extruder"); + def->tooltip = L("Map the logical extruder to physical extruder."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInts{0}); + + def = this->add("filament_map_mode", coEnum); + def->label = L("filament mapping mode"); + def->tooltip = L("Filament mapping mode used as plate param."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Auto For Flush"); + def->enum_values.push_back("Auto For Match"); + def->enum_values.push_back("Manual"); + def->enum_values.push_back("Default"); + def->enum_labels.push_back(L("Auto For Flush")); + def->enum_labels.push_back(L("Auto For Match")); + def->enum_labels.push_back(L("Manual")); + def->enum_labels.push_back(L("Default")); + def->set_default_value(new ConfigOptionEnum(fmmAutoForFlush)); + + def = this->add("enable_filament_dynamic_map", coBool); + def->label = L("Enable filament dynamic map"); + def->tooltip = L("Enable dynamic filament mapping during print."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("has_filament_switcher", coBool); + def->label = L("Has filament switcher"); + def->tooltip = L("Printer has a filament switcher hardware (e.g., AMS)."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("filament_flush_temp", coInts); + def->label = L("Flush temperature"); + def->tooltip = L("Temperature when flushing filament. 0 indicates the upper bound of the recommended nozzle temperature range."); + def->sidetext = L("\u2103"); + def->min = 0; + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionIntsNullable{0}); + + def = this->add("machine_load_filament_time", coFloat); + def->label = L("Filament load time"); + def->tooltip = L("Time to load new filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only."); + def->sidetext = L("s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.0)); + + def = this->add("machine_unload_filament_time", coFloat); + def->label = L("Filament unload time"); + def->tooltip = L("Time to unload old filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only."); + def->sidetext = L("s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.0)); + + def = this->add("machine_tool_change_time", coFloat); + def->label = L("Tool change time"); + def->tooltip = L("Time taken to switch tools. It's usually applicable for tool changers or multi-tool machines. For single-extruder multi-material machines, it's typically 0. For statistics only."); + def->sidetext = L("s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("support_object_skip_flush", coBool); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("bed_temperature_formula", coEnum); + def->label = L("Bed temperature type"); + def->tooltip = L("This option determines how the bed temperature is set during slicing: based on the temperature of the first filament or the highest temperature of the printed filaments."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("by_first_filament"); + def->enum_values.push_back("by_highest_temp"); + def->enum_labels.push_back(L("By First filament")); + def->enum_labels.push_back(L("By Highest Temp")); + def->set_default_value(new ConfigOptionEnum(BedTempFormula::btfHighestTemp)); + + def = this->add("nozzle_flush_dataset", coInts); + def->nullable = true; + def->set_default_value(new ConfigOptionIntsNullable{0}); + + def = this->add("volumetric_speed_coefficients", coStrings); + def->label = L("Max volumetric speed multinomial coefficients"); + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("filament_ramming_parameters", coStrings); + def->label = L("Ramming parameters"); + def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionStrings{"120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|" " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"}); + + def = this->add("filament_settings_id", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings{""}); + + def = this->add("filament_ids", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("adaptive_layer_height", coBool); + + def = this->add("spaghetti_detector", coBool); + + def = this->add("wrapping_detection_layers", coInt); + def->label = L("Clumping detection layers"); + def->tooltip = L("Clumping detection layers."); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(20)); + + def = this->add("wrapping_exclude_area", coPoints); + def->label = L("Probing exclude area of clumping"); + def->tooltip = L("Probing exclude area of clumping."); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::one_string; + def->set_default_value(new ConfigOptionPoints()); + + def = this->add("inherits", coString); + def->label = L("Inherits profile"); + def->tooltip = L("Name of parent profile."); + def->full_width = true; + def->height = 5; + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("inherits_group", coStrings); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("calib_flowrate_topinfill_special_order", coBool); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("silent_mode", coBool); + def->label = L("Supports silent mode"); + def->tooltip = L("Whether the machine supports silent mode in which machine use lower acceleration to print."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("max_layer_height", coFloats); + def->label = L("Max"); + def->tooltip = L("The highest printable layer height for the extruder. Used to limit the maximum layer height when enable adaptive layer height."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("min_layer_height", coFloats); + def->label = L("Min"); + def->tooltip = L("The lowest printable layer height for the extruder. Used to limit the minimum layer height when enable adaptive layer height."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.07}); + + def = this->add("nozzle_diameter", coFloats); + def->label = L("Nozzle diameter"); + def->tooltip = L("The diameter of nozzle."); + def->sidetext = L("mm"); + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.4}); + + def = this->add("host_type", coEnum); + def->label = L("Host Type"); + def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field must contain the kind of the host."); + def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("prusalink"); + def->enum_values.push_back("prusaconnect"); + def->enum_values.push_back("octoprint"); + def->enum_values.push_back("duet"); + def->enum_values.push_back("flashair"); + def->enum_values.push_back("astrobox"); + def->enum_values.push_back("repetier"); + def->enum_values.push_back("mks"); + def->enum_values.push_back("esp3d"); + def->enum_values.push_back("crealityprint"); + def->enum_values.push_back("obico"); + def->enum_values.push_back("flashforge"); + def->enum_values.push_back("simplyprint"); + def->enum_values.push_back("elegoolink"); + def->enum_labels.push_back(L("PrusaLink")); + def->enum_labels.push_back(L("PrusaConnect")); + def->enum_labels.push_back(L("Octo/Klipper")); + def->enum_labels.push_back(L("Duet")); + def->enum_labels.push_back(L("FlashAir")); + def->enum_labels.push_back(L("AstroBox")); + def->enum_labels.push_back(L("Repetier")); + def->enum_labels.push_back(L("MKS")); + def->enum_labels.push_back(L("ESP3D")); + def->enum_labels.push_back(L("CrealityPrint")); + def->enum_labels.push_back(L("Obico")); + def->enum_labels.push_back(L("Flashforge")); + def->enum_labels.push_back(L("SimplyPrint")); + def->enum_labels.push_back(L("Elegoo Link")); + def->set_default_value(new ConfigOptionEnum(htOctoPrint)); + + def = this->add("nozzle_volume", coFloats); + def->label = L("Nozzle volume"); + def->tooltip = L("Volume of nozzle between the cutter and the end of nozzle."); + def->sidetext = L("mm³"); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{{0.0}}); + + def = this->add("cooling_tube_retraction", coFloat); + def->label = L("Cooling tube position"); + def->tooltip = L("Distance of the center-point of the cooling tube from the extruder tip."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(91.5)); + + def = this->add("cooling_tube_length", coFloat); + def->label = L("Cooling tube length"); + def->tooltip = L("Length of the cooling tube to limit space for cooling moves inside it."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(5.)); + + def = this->add("high_current_on_filament_swap", coBool); + def->label = L("High extruder current on filament swap"); + def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange sequence to allow for rapid ramming feed rates and to overcome resistance when loading a filament with an ugly shaped tip."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(0)); + + def = this->add("parking_pos_retraction", coFloat); + def->label = L("Filament parking position"); + def->tooltip = L("Distance of the extruder tip from the position where the filament is parked when unloaded. This should match the value in printer firmware."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(92.)); + + def = this->add("extra_loading_move", coFloat); + def->label = L("Extra loading distance"); + def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load is exactly the same as it was moved back during unload. When positive, it is loaded further, if negative, the loading move is shorter than unloading."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(-2.)); + + def = this->add("start_end_points", coPoints); + def->label = L("Start end points"); + def->tooltip = L("The start and end points which is from cutter area to garbage can."); + def->mode = comDevelop; + def->readonly = true; + def->set_default_value(new ConfigOptionPoints{Vec2d(30, -3), Vec2d(54, 245)}); + + def = this->add("printer_model", coString); + def->label = L("Printer type"); + def->tooltip = L("Type of the printer."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("printer_variant", coString); + def->label = L("Printer variant"); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("print_settings_id", coString); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("printer_settings_id", coString); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionString()); + + def = this->add("retraction_minimum_travel", coFloats); + def->label = L("Travel distance threshold"); + def->tooltip = L("Only trigger retraction when the travel distance is longer than this threshold."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{2.}); + + def = this->add("retract_before_wipe", coPercents); + def->label = L("Retract amount before wipe"); + def->tooltip = L("The length of fast retraction before wipe, relative to retraction length."); + def->sidetext = L("%"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPercents{100}); + + def = this->add("retract_when_changing_layer", coBools); + def->label = L("Retract when change layer"); + def->tooltip = L("Force a retraction when changes layer."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("retraction_length", coFloats); + def->label = L("Length"); + def->full_label = L("Retraction Length"); + def->tooltip = L("Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction."); + def->sidetext = L("mm"); + def->set_default_value(new ConfigOptionFloats{0.8}); + + def = this->add("enable_long_retraction_when_cut", coInt); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("long_retractions_when_cut", coBools); + def->label = L("Long retraction when cut (beta)"); + def->tooltip = L("Experimental feature: Retracting and cutting off the filament at a longer distance during changes to minimize purge. While this reduces flush significantly, it may also raise the risk of nozzle clogs or other printing problems."); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("retraction_distances_when_cut", coFloats); + def->label = L("Retraction distance when cut"); + def->tooltip = L("Experimental feature: Retraction length before cutting off during filament change."); + def->min = 10; + def->max = 18; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloats{18}); + + def = this->add("retract_length_toolchange", coFloats); + def->label = L("Length"); + def->full_label = L("Retraction Length (Toolchange)"); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{10.}); + + def = this->add("z_hop", coFloats); + def->label = L("Z-hop height"); + def->tooltip = L("Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. It prevents nozzle from hitting the print when travel move. Using spiral lines to lift Z can prevent stringing."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 5; + def->set_default_value(new ConfigOptionFloats{0.4}); + + def = this->add("retract_lift_above", coFloats); + def->label = L("Only lift Z above"); + def->tooltip = L("If you set this to a positive value, Z lift will only take place above the specified absolute Z."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("retract_lift_below", coFloats); + def->label = L("Only lift Z below"); + def->tooltip = L("If you set this to a positive value, Z lift will only take place below the specified absolute Z."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("z_hop_types", coEnums); + def->label = L("Z-hop type"); + def->tooltip = L("Type of Z-hop."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Auto Lift"); + def->enum_values.push_back("Normal Lift"); + def->enum_values.push_back("Slope Lift"); + def->enum_values.push_back("Spiral Lift"); + def->enum_labels.push_back(L("Auto")); + def->enum_labels.push_back(L("Normal")); + def->enum_labels.push_back(L("Slope")); + def->enum_labels.push_back(L("Spiral")); + def->set_default_value(new ConfigOptionEnumsGeneric{ ZHopType::zhtSlope }); + + def = this->add("travel_slope", coFloats); + def->label = L("Traveling angle"); + def->tooltip = L("Traveling angle for Slope and Spiral Z-hop type. Setting it to 90° results in Normal Lift."); + def->sidetext = L("°"); + def->min = 1; + def->max = 90; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{3}); + + def = this->add("retract_lift_enforce", coEnums); + def->label = L("On surfaces"); + def->tooltip = L("Enforce Z-Hop behavior. This setting is impacted by the above settings (Only lift Z above/below)."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("All Surfaces"); + def->enum_values.push_back("Top Only"); + def->enum_values.push_back("Bottom Only"); + def->enum_values.push_back("Top and Bottom"); + def->enum_labels.push_back(L("All Surfaces")); + def->enum_labels.push_back(L("Top Only")); + def->enum_labels.push_back(L("Bottom Only")); + def->enum_labels.push_back(L("Top and Bottom")); + def->set_default_value(new ConfigOptionEnumsGeneric{ RetractLiftEnforceType ::rletAllSurfaces }); + + def = this->add("extruder_type", coEnums); + def->label = L("Type"); + def->tooltip = L("This setting is only used for initial value of manual calibration of pressure advance. Bowden extruder usually has larger PA value. This setting doesn't influence normal slicing."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Direct Drive"); + def->enum_values.push_back("Bowden"); + def->enum_labels.push_back(L("Direct Drive")); + def->enum_labels.push_back(L("Bowden")); + def->set_default_value(new ConfigOptionEnumsGeneric{ ExtruderType::etDirectDrive }); + + def = this->add("nozzle_volume_type", coEnums); + def->label = L("Nozzle Volume Type"); + def->tooltip = L("Nozzle volume type for extruders."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Standard"); + def->enum_values.push_back("High Flow"); + def->enum_labels.push_back(L("Standard")); + def->enum_labels.push_back(L("High Flow")); + def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard }); + + def = this->add("default_nozzle_volume_type", coEnums); + def->label = L("Default Nozzle Volume Type."); + def->tooltip = L("Default Nozzle volume type for extruders in this printer."); + def->mode = comDevelop; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("Standard"); + def->enum_values.push_back("High Flow"); + def->enum_labels.push_back(L("Standard")); + def->enum_labels.push_back(L("High Flow")); + def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard }); + + def = this->add("extruder_variant_list", coStrings); + def->label = L("Extruder variant list"); + def->tooltip = L("Extruder variant list."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); + + def = this->add("extruder_ams_count", coStrings); + def->label = L("Extruder AMS count"); + def->tooltip = L("AMS counts per extruder."); + def->set_default_value(new ConfigOptionStrings()); + + def = this->add("printer_extruder_id", coInts); + def->label = L("Printer extruder id"); + def->tooltip = L("Printer extruder id."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("printer_extruder_variant", coStrings); + def->label = L("Printer's extruder variant"); + def->tooltip = L("Printer's extruder variant."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); + + def = this->add("master_extruder_id", coInt); + def->label = L("Master extruder id"); + def->tooltip = L("Default extruder id to place filament."); + def->set_default_value(new ConfigOptionInt(1)); + + def = this->add("print_extruder_id", coInts); + def->label = L("Print extruder id"); + def->tooltip = L("Print extruder id."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("print_extruder_variant", coStrings); + def->label = L("Print's extruder variant"); + def->tooltip = L("Print's extruder variant."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); + + def = this->add("filament_extruder_id", coInts); + def->label = L("Filament extruder id"); + def->tooltip = L("Filament extruder id."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("filament_self_index", coInts); + def->label = L("Filament self index"); + def->tooltip = L("Filament self index."); + def->cli = ConfigOptionDef::nocli; + def->set_default_value(new ConfigOptionInts{1}); + + def = this->add("retract_restart_extra", coFloats); + def->label = L("Extra length on restart"); + def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("retract_restart_extra_toolchange", coFloats); + def->label = L("Extra length on restart"); + def->tooltip = L("When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("retraction_speed", coFloats); + def->label = L("Retraction Speed"); + def->full_label = L("Retraction Speed"); + def->tooltip = L("Speed for retracting filament from the nozzle."); + def->sidetext = L("mm/s"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{30.}); + + def = this->add("deretraction_speed", coFloats); + def->label = L("De-retraction Speed"); + def->full_label = L("De-retraction Speed"); + def->tooltip = L("Speed for reloading filament into the nozzle. Zero means same speed of retraction."); + def->sidetext = L("mm/s"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{0.}); + + def = this->add("bbl_calib_mark_logo", coBool); + def->label = L("Show auto-calibration marks"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("single_extruder_multi_material", coBool); + def->label = L("Single Extruder Multi Material"); + def->tooltip = L("Use single nozzle to print multi filament."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("manual_filament_change", coBool); + def->label = L("Manual Filament Change"); + def->tooltip = L("Enable this option to omit the custom Change filament G-code only at the beginning of the print. The tool change command (e.g., T0) will be skipped throughout the entire print. This is useful for manual multi-material printing, where we use M600/PAUSE to trigger the manual filament change action."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wipe_tower_type", coEnum); + def->label = L("Wipe tower type"); + def->tooltip = L("Choose the wipe tower implementation for multi-material prints. Type 1 is recommended for Bambu and Qidi printers with a filament cutter. Type 2 offers better compatibility with multi-tool and MMU printers and provide overall better compatibility."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("type1"); + def->enum_values.push_back("type2"); + def->enum_labels.push_back(L("Type 1")); + def->enum_labels.push_back(L("Type 2")); + def->set_default_value(new ConfigOptionEnum(WipeTowerType::Type2)); + + def = this->add("purge_in_prime_tower", coBool); + def->label = L("Purge in prime tower"); + def->tooltip = L("Purge remaining filament into prime tower."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("enable_filament_ramming", coBool); + def->label = L("Enable filament ramming"); + def->tooltip = L("Enable filament ramming"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("tool_change_on_wipe_tower", coBool); + def->label = L("Tool change on wipe tower"); + def->tooltip = L("Force the toolhead to travel to the wipe tower before issuing the tool change command (Tx). Only relevant for multi-extruder (multi-toolhead) printers using a Type 2 wipe tower. By default Orca skips the travel on multi-toolhead machines because the firmware handles the head swap, which can result in the Tx command being issued above the printed part. Enable this option if you want the tool change to always be issued above the wipe tower instead."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("head_wrap_detect_zone", coPoints); + def->label = L("Head wrap detect zone"); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionPoints()); + + def = this->add("travel_speed_z", coFloat); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloat(0.)); + + def = this->add("wipe", coBools); + def->label = L("Wipe while retracting"); + def->tooltip = L("Move nozzle along the last extrusion path when retracting to clean any leaked material on the nozzle. This can minimize blobs when printing a new part after traveling."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBools{false}); + + def = this->add("wipe_distance", coFloats); + def->label = L("Wipe Distance"); + def->tooltip = L("Describe how long the nozzle will move along the last path when retracting.\n\nDepending on how long the wipe operation lasts, how fast and long the extruder/filament retraction settings are, a retraction move may be needed to retract the remaining filament.\n\nSetting a value in the retract amount before wipe setting below will perform any excess retraction before the wipe, else it will be performed after."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloats{1.}); + + def = this->add("flush_volumes_vector", coFloats); + def->label = L("Purging volumes - load/unload volumes"); + def->set_default_value(new ConfigOptionFloats{140.f, 140.f, 140.f, 140.f, 140.f, 140.f, 140.f, 140.f}); + + def = this->add("flush_volumes_matrix", coFloats); + def->label = L("Purging volumes"); + def->set_default_value(new ConfigOptionFloats{0.f, 280.f, 280.f, 280.f, 280.f, 0.f, 280.f, 280.f, 280.f, 280.f, 0.f, 280.f, 280.f, 280.f, 280.f, 0.f}); + + def = this->add("flush_multiplier", coFloats); + def->label = L("Flush multiplier"); + def->tooltip = L("The actual flushing volumes is equal to the flush multiplier multiplied by the flushing volumes in the table."); + def->set_default_value(new ConfigOptionFloats{0.3}); + + def = this->add("wipe_tower_x", coFloats); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloats{15.}); + + def = this->add("wipe_tower_y", coFloats); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionFloats{220.}); + + def = this->add("wiping_volumes_extruders", coFloats); + def->label = L("Purging volumes - load/unload volumes"); + def->tooltip = L("This vector saves required volumes to change from/to each tool used on the wipe tower. These values are used to simplify creation of the full purging volumes below."); + def->set_default_value(new ConfigOptionFloats{70., 70., 70., 70., 70., 70., 70., 70., 70., 70.}); + + def = this->add("prime_tower_flat_ironing", coBool); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("thumbnails_format", coEnum); + def->label = L("Format of G-code thumbnails"); + def->tooltip = L("Format of G-code thumbnails: PNG for best quality, JPG for smallest size, QOI for low memory firmware."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("PNG"); + def->enum_values.push_back("JPG"); + def->enum_values.push_back("QOI"); + def->enum_values.push_back("BTT_TFT"); + def->enum_values.push_back("COLPIC"); + def->enum_labels.push_back(L("PNG")); + def->enum_labels.push_back(L("JPG")); + def->enum_labels.push_back(L("QOI")); + def->enum_labels.push_back(L("BTT TT")); + def->enum_labels.push_back(L("ColPic")); + def->set_default_value(new ConfigOptionEnum(GCodeThumbnailsFormat::PNG)); + + def = this->add("extruder_clearance_height_to_rod", coFloat); + def->label = L("Height to rod"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Distance of the nozzle tip to the lower rod. Used for collision avoidance in by-object printing."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(40)); + + def = this->add("extruder_clearance_height_to_lid", coFloat); + def->label = L("Height to lid"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Distance of the nozzle tip to the lid. Used for collision avoidance in by-object printing."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(120)); + + def = this->add("extruder_clearance_radius", coFloat); + def->label = L("Radius"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Clearance radius around extruder. Used for collision avoidance in by-object printing."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(40)); + + def = this->add("bed_mesh_min", coPoint); + def->label = L("Bed mesh min"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This option sets the min point for the allowed bed mesh area. Due to the probe's XY offset, most printers are unable to probe the entire bed. To ensure the probe point does not go outside the bed area, the minimum and maximum points of the bed mesh should be set appropriately. OrcaSlicer ensures that adaptive_bed_mesh_min/adaptive_bed_mesh_max values do not exceed these min/max points. This information can usually be obtained from your printer manufacturer. The default setting is (-99999, -99999), which means there are no limits, thus allowing probing across the entire bed."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(-99999, -99999))); + + def = this->add("bed_mesh_max", coPoint); + def->label = L("Bed mesh max"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This option sets the max point for the allowed bed mesh area. Due to the probe's XY offset, most printers are unable to probe the entire bed. To ensure the probe point does not go outside the bed area, the minimum and maximum points of the bed mesh should be set appropriately. OrcaSlicer ensures that adaptive_bed_mesh_min/adaptive_bed_mesh_max values do not exceed these min/max points. This information can usually be obtained from your printer manufacturer. The default setting is (99999, 99999), which means there are no limits, thus allowing probing across the entire bed."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(99999, 99999))); + + def = this->add("bed_mesh_probe_distance", coPoint); + def->label = L("Probe point distance"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This option sets the preferred distance between probe points (grid size) for the X and Y directions, with the default being 50mm for both X and Y."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(50, 50))); + + def = this->add("adaptive_bed_mesh_margin", coFloat); + def->label = L("Mesh margin"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This option determines the additional distance by which the adaptive bed mesh area should be expanded in the XY directions."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("scan_first_layer", coBool); + def->label = L("Scan first layer"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Enable this to enable the camera on printer to check the quality of first layer."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("enable_power_loss_recovery", coEnum); + def->label = L("Power Loss Recovery"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Choose how to control power loss recovery. When set to Printer configuration, the slicer will not emit power loss recovery G-code and will leave the printer's configuration unchanged. Applicable to Bambu Lab or Marlin 2 firmware based printers."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("printer_configuration"); + def->enum_values.push_back("enable"); + def->enum_values.push_back("disable"); + def->enum_labels.push_back(L("Printer configuration")); + def->enum_labels.push_back(L("Enable")); + def->enum_labels.push_back(L("Disable")); + def->set_default_value(new ConfigOptionEnum(PowerLossRecoveryMode::PrinterConfiguration)); + + def = this->add("nozzle_type", coEnums); + def->label = L("Nozzle type"); + def->category = L("Printer/Basic information"); + def->tooltip = L("The metallic material of nozzle. This determines the abrasive resistance of nozzle, and what kind of filament can be printed."); + def->mode = comAdvanced; + def->nullable = true; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("undefine"); + def->enum_values.push_back("hardened_steel"); + def->enum_values.push_back("stainless_steel"); + def->enum_values.push_back("tungsten_carbide"); + def->enum_values.push_back("brass"); + def->enum_labels.push_back(L("Undefine")); + def->enum_labels.push_back(L("Hardened steel")); + def->enum_labels.push_back(L("Stainless steel")); + def->enum_labels.push_back(L("Tungsten carbide")); + def->enum_labels.push_back(L("Brass")); + def->set_default_value(new ConfigOptionEnumsGenericNullable{ ntUndefine }); + + def = this->add("nozzle_hrc", coInt); + def->label = L("Nozzle HRC"); + def->category = L("Printer/Basic information"); + def->tooltip = L("The nozzle's hardness. Zero means no checking for nozzle's hardness during slicing."); + def->sidetext = L("HRC"); + def->min = 0; + def->max = 500; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("printer_structure", coEnum); + def->label = L("Printer structure"); + def->category = L("Printer/Basic information"); + def->tooltip = L("The physical arrangement and components of a printing device."); + def->mode = comDevelop; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("undefine"); + def->enum_values.push_back("corexy"); + def->enum_values.push_back("i3"); + def->enum_values.push_back("hbot"); + def->enum_values.push_back("delta"); + def->enum_labels.push_back(L("Undefine")); + def->enum_labels.push_back(L("CoreXY")); + def->enum_labels.push_back(L("I3")); + def->enum_labels.push_back(L("Hbot")); + def->enum_labels.push_back(L("Delta")); + def->set_default_value(new ConfigOptionEnum(psUndefine)); + + def = this->add("best_object_pos", coPoint); + def->label = L("Best object position"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Best auto arranging position in range [0,1] w.r.t. bed shape."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(0.5, 0.5))); + + def = this->add("auxiliary_fan", coBool); + def->label = L("Auxiliary part cooling fan"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Enable this option if machine has auxiliary part cooling fan. G-code command: M106 P2 S(0-255)."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("fan_speedup_time", coFloat); + def->category = L("Printer/Basic information"); + def->tooltip = L("Start the fan this number of seconds earlier than its target start time (you can use fractional seconds). It assumes infinite acceleration for this time estimation, and will only take into account G1 and G0 moves (arc fitting is unsupported).\nIt won't move fan commands from custom G-code (they act as a sort of 'barrier').\nIt won't move fan commands into the start G-code if the 'only custom start G-code' is activated.\nUse 0 to deactivate."); + def->sidetext = L("s"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("fan_speedup_overhangs", coBool); + def->label = L("Only overhangs"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Will only take into account the delay for the cooling of overhangs."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("fan_kickstart", coFloat); + def->label = L("Fan kick-start time"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Emit a max fan speed command for this amount of seconds before reducing to target speed to kick-start the cooling fan.\nThis is useful for fans where a low PWM/power may be insufficient to get the fan started spinning from a stop, or to get the fan up to speed faster.\nSet to 0 to deactivate."); + def->sidetext = L("s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("part_cooling_fan_min_pwm", coInt); + def->label = L("Minimum non-zero part cooling fan speed"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Some part-cooling fans cannot start spinning when commanded below a certain PWM duty cycle. When set above 0, any non-zero part-cooling fan command will be raised to at least this percentage so the fan reliably starts. A fan command of 0 (fan off) is always honoured exactly. This clamp is applied after every other fan calculation (first-layer ramp, layer-time interpolation, overhang/bridge/support-interface/ironing overrides), so scaling still operates within the range [this value, 100%].\nIf your firmware already disables the fan below a threshold (for example Klipper's [fan] off_below: 0.10 shuts the fan off whenever the commanded duty cycle is below 10%), this option and the firmware threshold should ideally be set to the same value. Matching them (e.g. off_below: 0.10 in Klipper and 10% here) guarantees the slicer never emits a non-zero value that the firmware would silently drop, and the fan never receives a value below the one you know it can actually spool at.\nSet to 0 to deactivate."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(0)); + + def = this->add("time_cost", coFloat); + def->label = L("Time cost"); + def->category = L("Printer/Basic information"); + def->tooltip = L("The printer cost per hour."); + def->sidetext = L("money/h"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("support_chamber_temp_control", coBool); + def->label = L("Support control chamber temperature"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This option is enabled if machine support controlling chamber temperature\nG-code command: M141 S(0-255)"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("support_air_filtration", coBool); + def->label = L("Support air filtration"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Enable this if printer support air filtration\nG-code command: M106 P3 S(0-255)"); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("gcode_flavor", coEnum); + def->label = L("G-code flavor"); + def->category = L("Printer/Basic information"); + def->tooltip = L("What kind of G-code the printer is compatible with."); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("marlin"); + def->enum_values.push_back("klipper"); + def->enum_values.push_back("reprapfirmware"); + def->enum_values.push_back("repetier"); + def->enum_values.push_back("marlin2"); + def->enum_values.push_back("reprap"); + def->enum_values.push_back("teacup"); + def->enum_values.push_back("makerware"); + def->enum_values.push_back("sailfish"); + def->enum_values.push_back("mach3"); + def->enum_values.push_back("machinekit"); + def->enum_values.push_back("smoothie"); + def->enum_values.push_back("no-extrusion"); + def->enum_labels.push_back(L("Marlin(legacy)")); + def->enum_labels.push_back(L("Klipper")); + def->enum_labels.push_back(L("RepRapFirmware")); + def->enum_labels.push_back(L("Repetier")); + def->enum_labels.push_back(L("Marlin 2")); + def->enum_labels.push_back(L("RepRap/Sprinter")); + def->enum_labels.push_back(L("Teacup")); + def->enum_labels.push_back(L("MakerWare (MakerBot)")); + def->enum_labels.push_back(L("Sailfish (MakerBot)")); + def->enum_labels.push_back(L("Mach3/LinuxCNC")); + def->enum_labels.push_back(L("Machinekit")); + def->enum_labels.push_back(L("Smoothie")); + def->enum_labels.push_back(L("No extrusion")); + def->set_default_value(new ConfigOptionEnum(gcfMarlinLegacy)); + + def = this->add("pellet_modded_printer", coBool); + def->label = L("Pellet Modded Printer"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Enable this option if your printer uses pellets instead of filaments."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("support_multi_bed_types", coBool); + def->label = L("Support multi bed types"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Enable this option if you want to use multiple bed types."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("use_firmware_retraction", coBool); + def->label = L("Use firmware retraction"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("disable_m73", coBool); + def->label = L("Disable set remaining print time"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Disable generating of the M73: Set remaining print time in the final G-code."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("z_offset", coFloat); + def->label = L("Z offset"); + def->category = L("Printer/Basic information"); + def->tooltip = L("This value will be added (or subtracted) from all the Z coordinates in the output G-code. It is used to compensate for bad Z endstop position: for example, if your endstop zero actually leaves the nozzle 0.3mm far from the print bed, set this to -0.3 (or fix your endstop)."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("thumbnails", coString); + def->label = L("G-code thumbnails"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\""); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::one_string; + def->set_default_value(new ConfigOptionString("48x48/PNG,300x300/PNG")); + + def = this->add("use_relative_e_distances", coBool); + def->label = L("Use relative E distances"); + def->category = L("Printer/Basic information"); + def->tooltip = L("Relative extrusion is recommended when using \"label_objects\" option. Some extruders work better with this option unchecked (absolute extrusion mode). Wipe tower is only compatible with relative mode. It is recommended on most printers. Default is checked."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("before_layer_change_gcode", coString); + def->label = L("Before layer change G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("This G-code is inserted at every layer change before the Z lift."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString()); + + def = this->add("machine_end_gcode", coString); + def->label = L("End G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("End G-code when finishing the entire print."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 12; + def->set_default_value(new ConfigOptionString("M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n")); + + def = this->add("printing_by_object_gcode", coString); + def->label = L("Between Object G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("Insert G-code between objects. This parameter will only come into effect when you print your models object by object."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 12; + def->set_default_value(new ConfigOptionString()); + + def = this->add("layer_change_gcode", coString); + def->label = L("Layer change G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("This G-code is inserted at every layer change after the Z lift."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString()); + + def = this->add("time_lapse_gcode", coString); + def->label = L("Timelapse G-code"); + def->category = L("Printer/Machine G-code"); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString()); + + def = this->add("wrapping_detection_gcode", coString); + def->label = L("Clumping detection G-code"); + def->category = L("Printer/Machine G-code"); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("machine_pause_gcode", coString); + def->label = L("Pause G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("This G-code will be used as a code for the pause print. Users can insert pause G-code in the G-code viewer."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 12; + def->set_default_value(new ConfigOptionString()); + + def = this->add("template_custom_gcode", coString); + def->label = L("Custom G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("This G-code will be used as a custom code."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 12; + def->set_default_value(new ConfigOptionString()); + + def = this->add("file_start_gcode", coString); + def->label = L("File header G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("G-code written at the very top of the output file, before any other content. Useful for adding metadata that printer firmware reads from the first lines of the file (e.g. estimated print time, filament usage). Supports placeholders like {print_time_sec} and {used_filament_length}."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 8; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("machine_start_gcode", coString); + def->label = L("Start G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("Start G-code when starting the entire print."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 12; + def->set_default_value(new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n")); + + def = this->add("change_filament_gcode", coString); + def->label = L("Change filament G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("This G-code is inserted when filament is changed, including T commands to trigger tool change."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString()); + + def = this->add("change_extrusion_role_gcode", coString); + def->label = L("Change extrusion role G-code"); + def->category = L("Printer/Machine G-code"); + def->tooltip = L("This G-code is inserted when the extrusion role is changed."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 5; + def->set_default_value(new ConfigOptionString()); + + def = this->add("printer_notes", coString); + def->label = L("Printer notes"); + def->category = L("Printer/Notes"); + def->tooltip = L("You can put your notes regarding the printer here."); + def->mode = comAdvanced; + def->multiline = true; + def->full_width = true; + def->height = 13; + def->set_default_value(new ConfigOptionString()); diff --git a/src/slic3r/GUI/generated/TabLayout_generated.cpp b/src/slic3r/GUI/generated/TabLayout_generated.cpp new file mode 100644 index 0000000000..3fe01759c2 --- /dev/null +++ b/src/slic3r/GUI/generated/TabLayout_generated.cpp @@ -0,0 +1,973 @@ +// ===== AUTO-GENERATED by tools/config_codegen.py from layout.yaml ===== +// DO NOT EDIT MANUALLY. Edit layout.yaml and re-run: python tools/run_codegen.py +// +// Included inside namespace Slic3r::GUI in Tab.cpp after validate_custom_gcode_cb +// forward declaration. No namespace wrapper needed here. + +namespace { constexpr int gcode_field_height = 15; } +namespace { constexpr int notes_field_height = 25; } + +inline void TabPrint_build_quality_layout(TabPrint& tab) +{ + PageShp page = tab.add_options_page(L("Quality"), "custom-gcode_quality"); + { + auto optgroup = page->new_optgroup(L("Layer height")); + optgroup->append_single_option_line("layer_height"); + optgroup->append_single_option_line("initial_layer_print_height"); + } + { + auto optgroup = page->new_optgroup(L("Line width")); + optgroup->append_single_option_line("line_width"); + optgroup->append_single_option_line("initial_layer_line_width"); + optgroup->append_single_option_line("outer_wall_line_width"); + optgroup->append_single_option_line("inner_wall_line_width"); + optgroup->append_single_option_line("top_surface_line_width"); + optgroup->append_single_option_line("sparse_infill_line_width"); + optgroup->append_single_option_line("internal_solid_infill_line_width"); + optgroup->append_single_option_line("support_line_width"); + } + { + auto optgroup = page->new_optgroup(L("Seam")); + optgroup->append_single_option_line("seam_position"); + optgroup->append_single_option_line("staggered_inner_seams"); + optgroup->append_single_option_line("seam_gap"); + optgroup->append_single_option_line("seam_slope_type"); + optgroup->append_single_option_line("seam_slope_conditional"); + optgroup->append_single_option_line("scarf_angle_threshold"); + optgroup->append_single_option_line("scarf_overhang_threshold"); + optgroup->append_single_option_line("scarf_joint_speed"); + optgroup->append_single_option_line("seam_slope_start_height"); + optgroup->append_single_option_line("seam_slope_entire_loop"); + optgroup->append_single_option_line("seam_slope_min_length"); + optgroup->append_single_option_line("seam_slope_steps"); + optgroup->append_single_option_line("scarf_joint_flow_ratio"); + optgroup->append_single_option_line("seam_slope_inner_walls"); + optgroup->append_single_option_line("role_based_wipe_speed"); + optgroup->append_single_option_line("wipe_speed"); + optgroup->append_single_option_line("wipe_on_loops"); + optgroup->append_single_option_line("wipe_before_external_loop"); + } + { + auto optgroup = page->new_optgroup(L("Precision")); + optgroup->append_single_option_line("slice_closing_radius"); + optgroup->append_single_option_line("resolution"); + optgroup->append_single_option_line("enable_arc_fitting"); + optgroup->append_single_option_line("xy_hole_compensation"); + optgroup->append_single_option_line("xy_contour_compensation"); + optgroup->append_single_option_line("elefant_foot_compensation"); + optgroup->append_single_option_line("elefant_foot_layers_density"); + optgroup->append_single_option_line("elefant_foot_compensation_layers"); + optgroup->append_single_option_line("precise_outer_wall"); + optgroup->append_single_option_line("precise_z_height"); + optgroup->append_single_option_line("hole_to_polyhole"); + optgroup->append_single_option_line("hole_to_polyhole_threshold"); + optgroup->append_single_option_line("hole_to_polyhole_twisted"); + } + { + auto optgroup = page->new_optgroup(L("Ironing")); + optgroup->append_single_option_line("ironing_type"); + optgroup->append_single_option_line("ironing_pattern"); + optgroup->append_single_option_line("ironing_flow"); + optgroup->append_single_option_line("ironing_spacing"); + optgroup->append_single_option_line("ironing_inset"); + optgroup->append_single_option_line("ironing_angle"); + optgroup->append_single_option_line("ironing_angle_fixed"); + } + { + auto optgroup = page->new_optgroup(L("Wall generator")); + optgroup->append_single_option_line("wall_generator"); + optgroup->append_single_option_line("wall_transition_angle"); + optgroup->append_single_option_line("wall_transition_filter_deviation"); + optgroup->append_single_option_line("wall_transition_length"); + optgroup->append_single_option_line("wall_distribution_count"); + optgroup->append_single_option_line("initial_layer_min_bead_width"); + optgroup->append_single_option_line("min_bead_width"); + optgroup->append_single_option_line("min_feature_size"); + optgroup->append_single_option_line("min_length_factor"); + optgroup->append_single_option_line("wall_maximum_resolution"); + optgroup->append_single_option_line("wall_maximum_deviation"); + } + { + auto optgroup = page->new_optgroup(L("Walls and surfaces")); + optgroup->append_single_option_line("wall_sequence"); + optgroup->append_single_option_line("is_infill_first"); + optgroup->append_single_option_line("wall_direction"); + optgroup->append_single_option_line("print_flow_ratio"); + optgroup->append_single_option_line("top_solid_infill_flow_ratio"); + optgroup->append_single_option_line("bottom_solid_infill_flow_ratio"); + optgroup->append_single_option_line("set_other_flow_ratios"); + optgroup->append_single_option_line("first_layer_flow_ratio"); + optgroup->append_single_option_line("outer_wall_flow_ratio"); + optgroup->append_single_option_line("inner_wall_flow_ratio"); + optgroup->append_single_option_line("overhang_flow_ratio"); + optgroup->append_single_option_line("sparse_infill_flow_ratio"); + optgroup->append_single_option_line("internal_solid_infill_flow_ratio"); + optgroup->append_single_option_line("gap_fill_flow_ratio"); + optgroup->append_single_option_line("support_flow_ratio"); + optgroup->append_single_option_line("support_interface_flow_ratio"); + optgroup->append_single_option_line("only_one_wall_first_layer"); + optgroup->append_single_option_line("only_one_wall_top"); + optgroup->append_single_option_line("min_width_top_surface"); + optgroup->append_single_option_line("reduce_crossing_wall"); + optgroup->append_single_option_line("max_travel_detour_distance"); + optgroup->append_single_option_line("small_area_infill_flow_compensation"); + optgroup->append_single_option_line("small_area_infill_flow_compensation_model"); + } + { + auto optgroup = page->new_optgroup(L("Bridging")); + optgroup->append_single_option_line("bridge_flow"); + optgroup->append_single_option_line("internal_bridge_flow"); + optgroup->append_single_option_line("bridge_density"); + optgroup->append_single_option_line("internal_bridge_density"); + optgroup->append_single_option_line("thick_bridges"); + optgroup->append_single_option_line("thick_internal_bridges"); + optgroup->append_single_option_line("enable_extra_bridge_layer"); + optgroup->append_single_option_line("dont_filter_internal_bridges"); + optgroup->append_single_option_line("counterbore_hole_bridging"); + } + { + auto optgroup = page->new_optgroup(L("Overhangs")); + optgroup->append_single_option_line("detect_overhang_wall"); + optgroup->append_single_option_line("make_overhang_printable"); + optgroup->append_single_option_line("make_overhang_printable_angle"); + optgroup->append_single_option_line("make_overhang_printable_hole_size"); + optgroup->append_single_option_line("extra_perimeters_on_overhangs"); + optgroup->append_single_option_line("overhang_reverse"); + optgroup->append_single_option_line("overhang_reverse_internal_only"); + optgroup->append_single_option_line("overhang_reverse_threshold"); + } +} + +inline void TabPrint_build_strength_layout(TabPrint& tab) +{ + PageShp page = tab.add_options_page(L("Strength"), "custom-gcode_strength"); + { + auto optgroup = page->new_optgroup(L("Walls")); + optgroup->append_single_option_line("wall_loops"); + optgroup->append_single_option_line("alternate_extra_wall"); + optgroup->append_single_option_line("detect_thin_wall"); + } + { + auto optgroup = page->new_optgroup(L("Top/bottom shells")); + optgroup->append_single_option_line("top_shell_layers"); + optgroup->append_single_option_line("top_shell_thickness"); + optgroup->append_single_option_line("top_surface_density"); + optgroup->append_single_option_line("top_surface_pattern"); + optgroup->append_single_option_line("bottom_shell_layers"); + optgroup->append_single_option_line("bottom_shell_thickness"); + optgroup->append_single_option_line("bottom_surface_density"); + optgroup->append_single_option_line("bottom_surface_pattern"); + optgroup->append_single_option_line("top_bottom_infill_wall_overlap"); + } + { + auto optgroup = page->new_optgroup(L("Infill")); + optgroup->append_single_option_line("sparse_infill_density"); + optgroup->append_single_option_line("fill_multiline"); + optgroup->append_single_option_line("sparse_infill_pattern"); + optgroup->append_single_option_line("infill_direction"); + optgroup->append_single_option_line("sparse_infill_rotate_template"); + optgroup->append_single_option_line("skin_infill_density"); + optgroup->append_single_option_line("skeleton_infill_density"); + optgroup->append_single_option_line("infill_lock_depth"); + optgroup->append_single_option_line("skin_infill_depth"); + optgroup->append_single_option_line("skin_infill_line_width"); + optgroup->append_single_option_line("skeleton_infill_line_width"); + optgroup->append_single_option_line("symmetric_infill_y_axis"); + optgroup->append_single_option_line("infill_shift_step"); + optgroup->append_single_option_line("lateral_lattice_angle_1"); + optgroup->append_single_option_line("lateral_lattice_angle_2"); + optgroup->append_single_option_line("infill_overhang_angle"); + optgroup->append_single_option_line("infill_anchor_max"); + optgroup->append_single_option_line("infill_anchor"); + optgroup->append_single_option_line("internal_solid_infill_pattern"); + optgroup->append_single_option_line("solid_infill_direction"); + optgroup->append_single_option_line("solid_infill_rotate_template"); + optgroup->append_single_option_line("gap_fill_target"); + optgroup->append_single_option_line("filter_out_gap_fill"); + optgroup->append_single_option_line("infill_wall_overlap"); + } + { + auto optgroup = page->new_optgroup(L("Advanced")); + optgroup->append_single_option_line("align_infill_direction_to_model"); + optgroup->append_single_option_line("extra_solid_infills"); + optgroup->append_single_option_line("bridge_angle"); + optgroup->append_single_option_line("internal_bridge_angle"); + optgroup->append_single_option_line("minimum_sparse_infill_area"); + optgroup->append_single_option_line("infill_combination"); + optgroup->append_single_option_line("infill_combination_max_layer_height"); + optgroup->append_single_option_line("detect_narrow_internal_solid_infill"); + optgroup->append_single_option_line("ensure_vertical_shell_thickness"); + } +} + +inline void TabPrint_build_speed_layout(TabPrint& tab) +{ + PageShp page = tab.add_options_page(L("Speed"), "custom-gcode_speed"); + { + auto optgroup = page->new_optgroup(L("First layer speed")); + optgroup->append_single_option_line("initial_layer_speed"); + optgroup->append_single_option_line("initial_layer_infill_speed"); + optgroup->append_single_option_line("initial_layer_travel_speed"); + optgroup->append_single_option_line("slow_down_layers"); + } + { + auto optgroup = page->new_optgroup(L("Other layers speed")); + optgroup->append_single_option_line("outer_wall_speed"); + optgroup->append_single_option_line("inner_wall_speed"); + optgroup->append_single_option_line("small_perimeter_speed"); + optgroup->append_single_option_line("small_perimeter_threshold"); + optgroup->append_single_option_line("sparse_infill_speed"); + optgroup->append_single_option_line("internal_solid_infill_speed"); + optgroup->append_single_option_line("top_surface_speed"); + optgroup->append_single_option_line("gap_infill_speed"); + optgroup->append_single_option_line("ironing_speed"); + optgroup->append_single_option_line("support_speed"); + optgroup->append_single_option_line("support_interface_speed"); + } + { + auto optgroup = page->new_optgroup(L("Overhang speed")); + optgroup->append_single_option_line("enable_overhang_speed"); + optgroup->append_single_option_line("slowdown_for_curled_perimeters"); + { + Line line_{optgroup->get_option("overhang_1_4_speed").opt.label, optgroup->get_option("overhang_1_4_speed").opt.tooltip}; + line_.append_option(optgroup->get_option("overhang_1_4_speed")); + line_.append_option(optgroup->get_option("overhang_2_4_speed")); + line_.append_option(optgroup->get_option("overhang_3_4_speed")); + line_.append_option(optgroup->get_option("overhang_4_4_speed")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("bridge_speed").opt.label, optgroup->get_option("bridge_speed").opt.tooltip}; + line_.append_option(optgroup->get_option("bridge_speed")); + line_.append_option(optgroup->get_option("internal_bridge_speed")); + optgroup->append_line(line_); + } + } + { + auto optgroup = page->new_optgroup(L("Travel speed")); + optgroup->append_single_option_line("travel_speed"); + } + { + auto optgroup = page->new_optgroup(L("Acceleration")); + optgroup->append_single_option_line("default_acceleration"); + optgroup->append_single_option_line("outer_wall_acceleration"); + optgroup->append_single_option_line("inner_wall_acceleration"); + optgroup->append_single_option_line("bridge_acceleration"); + optgroup->append_single_option_line("sparse_infill_acceleration"); + optgroup->append_single_option_line("internal_solid_infill_acceleration"); + optgroup->append_single_option_line("initial_layer_acceleration"); + optgroup->append_single_option_line("initial_layer_travel_acceleration"); + optgroup->append_single_option_line("top_surface_acceleration"); + optgroup->append_single_option_line("travel_acceleration"); + optgroup->append_single_option_line("accel_to_decel_enable"); + optgroup->append_single_option_line("accel_to_decel_factor"); + } + { + auto optgroup = page->new_optgroup(L("Jerk(XY)")); + optgroup->append_single_option_line("default_junction_deviation"); + optgroup->append_single_option_line("default_jerk"); + optgroup->append_single_option_line("outer_wall_jerk"); + optgroup->append_single_option_line("inner_wall_jerk"); + optgroup->append_single_option_line("infill_jerk"); + optgroup->append_single_option_line("top_surface_jerk"); + optgroup->append_single_option_line("initial_layer_jerk"); + optgroup->append_single_option_line("initial_layer_travel_jerk"); + optgroup->append_single_option_line("travel_jerk"); + } + { + auto optgroup = page->new_optgroup(L("Advanced")); + optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope"); + optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_segment_length"); + optgroup->append_single_option_line("extrusion_rate_smoothing_external_perimeter_only"); + } +} + +inline void TabPrint_build_support_layout(TabPrint& tab) +{ + PageShp page = tab.add_options_page(L("Support"), "custom-gcode_support"); + { + auto optgroup = page->new_optgroup(L("Support")); + optgroup->append_single_option_line("enable_support"); + optgroup->append_single_option_line("support_type"); + optgroup->append_single_option_line("support_style"); + optgroup->append_single_option_line("support_threshold_angle"); + optgroup->append_single_option_line("support_threshold_overlap"); + optgroup->append_single_option_line("raft_first_layer_density"); + optgroup->append_single_option_line("raft_first_layer_expansion"); + optgroup->append_single_option_line("support_on_build_plate_only"); + optgroup->append_single_option_line("support_critical_regions_only"); + optgroup->append_single_option_line("support_remove_small_overhang"); + } + { + auto optgroup = page->new_optgroup(L("Raft")); + optgroup->append_single_option_line("raft_layers"); + optgroup->append_single_option_line("raft_contact_distance"); + } + { + auto optgroup = page->new_optgroup(L("Support filament")); + optgroup->append_single_option_line("support_filament"); + optgroup->append_single_option_line("support_interface_filament"); + optgroup->append_single_option_line("support_interface_not_for_body"); + } + { + auto optgroup = page->new_optgroup(L("Support ironing")); + optgroup->append_single_option_line("support_ironing"); + optgroup->append_single_option_line("support_ironing_pattern"); + optgroup->append_single_option_line("support_ironing_flow"); + optgroup->append_single_option_line("support_ironing_spacing"); + } + { + auto optgroup = page->new_optgroup(L("Advanced")); + optgroup->append_single_option_line("support_top_z_distance"); + optgroup->append_single_option_line("support_bottom_z_distance"); + optgroup->append_single_option_line("tree_support_wall_count"); + optgroup->append_single_option_line("support_base_pattern"); + optgroup->append_single_option_line("support_base_pattern_spacing"); + optgroup->append_single_option_line("support_angle"); + optgroup->append_single_option_line("support_interface_top_layers"); + optgroup->append_single_option_line("support_interface_bottom_layers"); + optgroup->append_single_option_line("support_interface_pattern"); + optgroup->append_single_option_line("support_interface_spacing"); + optgroup->append_single_option_line("support_bottom_interface_spacing"); + optgroup->append_single_option_line("support_expansion"); + optgroup->append_single_option_line("support_object_xy_distance"); + optgroup->append_single_option_line("support_object_first_layer_gap"); + optgroup->append_single_option_line("bridge_no_support"); + optgroup->append_single_option_line("max_bridge_length"); + optgroup->append_single_option_line("independent_support_layer_height"); + } + { + auto optgroup = page->new_optgroup(L("Tree supports")); + optgroup->append_single_option_line("tree_support_tip_diameter"); + optgroup->append_single_option_line("tree_support_branch_distance"); + optgroup->append_single_option_line("tree_support_branch_distance_organic"); + optgroup->append_single_option_line("tree_support_top_rate"); + optgroup->append_single_option_line("tree_support_branch_diameter"); + optgroup->append_single_option_line("tree_support_branch_diameter_organic"); + optgroup->append_single_option_line("tree_support_branch_diameter_angle"); + optgroup->append_single_option_line("tree_support_branch_angle"); + optgroup->append_single_option_line("tree_support_branch_angle_organic"); + optgroup->append_single_option_line("tree_support_angle_slow"); + optgroup->append_single_option_line("tree_support_auto_brim"); + optgroup->append_single_option_line("tree_support_brim_width"); + } +} + +inline void TabPrint_build_multimaterial_layout(TabPrint& tab) +{ + PageShp page = tab.add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); + { + auto optgroup = page->new_optgroup(L("Prime tower")); + optgroup->append_single_option_line("enable_prime_tower"); + optgroup->append_single_option_line("prime_tower_skip_points"); + optgroup->append_single_option_line("enable_tower_interface_features"); + optgroup->append_single_option_line("enable_tower_interface_cooldown_during_tower"); + optgroup->append_single_option_line("prime_tower_enable_framework"); + optgroup->append_single_option_line("prime_tower_width"); + optgroup->append_single_option_line("prime_volume"); + optgroup->append_single_option_line("prime_tower_brim_width"); + optgroup->append_single_option_line("prime_tower_infill_gap"); + optgroup->append_single_option_line("wipe_tower_rotation_angle"); + optgroup->append_single_option_line("wipe_tower_bridging"); + optgroup->append_single_option_line("wipe_tower_extra_spacing"); + optgroup->append_single_option_line("wipe_tower_extra_flow"); + optgroup->append_single_option_line("wipe_tower_max_purge_speed"); + optgroup->append_single_option_line("wipe_tower_wall_type"); + optgroup->append_single_option_line("wipe_tower_cone_angle"); + optgroup->append_single_option_line("wipe_tower_extra_rib_length"); + optgroup->append_single_option_line("wipe_tower_rib_width"); + optgroup->append_single_option_line("wipe_tower_fillet_wall"); + optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); + optgroup->append_single_option_line("single_extruder_multi_material_priming"); + } + { + auto optgroup = page->new_optgroup(L("Filament for Features")); + optgroup->append_single_option_line("wall_filament"); + optgroup->append_single_option_line("sparse_infill_filament"); + optgroup->append_single_option_line("solid_infill_filament"); + optgroup->append_single_option_line("wipe_tower_filament"); + } + { + auto optgroup = page->new_optgroup(L("Ooze prevention")); + optgroup->append_single_option_line("ooze_prevention"); + optgroup->append_single_option_line("standby_temperature_delta"); + optgroup->append_single_option_line("preheat_time"); + optgroup->append_single_option_line("preheat_steps"); + } + { + auto optgroup = page->new_optgroup(L("Flush options")); + optgroup->append_single_option_line("flush_into_infill"); + optgroup->append_single_option_line("flush_into_objects"); + optgroup->append_single_option_line("flush_into_support"); + } + { + auto optgroup = page->new_optgroup(L("Advanced")); + optgroup->append_single_option_line("interlocking_beam"); + optgroup->append_single_option_line("interface_shells"); + optgroup->append_single_option_line("mmu_segmented_region_max_width"); + optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth"); + optgroup->append_single_option_line("interlocking_beam_width"); + optgroup->append_single_option_line("interlocking_orientation"); + optgroup->append_single_option_line("interlocking_beam_layer_count"); + optgroup->append_single_option_line("interlocking_depth"); + optgroup->append_single_option_line("interlocking_boundary_avoidance"); + } +} + +inline void TabPrint_build_others_layout(TabPrint& tab) +{ + PageShp page = tab.add_options_page(L("Others"), "custom-gcode_other"); + { + auto optgroup = page->new_optgroup(L("Skirt")); + optgroup->append_single_option_line("skirt_loops"); + optgroup->append_single_option_line("skirt_type"); + optgroup->append_single_option_line("min_skirt_length"); + optgroup->append_single_option_line("skirt_distance"); + optgroup->append_single_option_line("skirt_start_angle"); + optgroup->append_single_option_line("skirt_speed"); + optgroup->append_single_option_line("skirt_height"); + optgroup->append_single_option_line("draft_shield"); + optgroup->append_single_option_line("single_loop_draft_shield"); + } + { + auto optgroup = page->new_optgroup(L("Brim")); + optgroup->append_single_option_line("brim_type"); + optgroup->append_single_option_line("brim_width"); + optgroup->append_single_option_line("brim_object_gap"); + optgroup->append_single_option_line("brim_flow_ratio"); + optgroup->append_single_option_line("brim_use_efc_outline"); + optgroup->append_single_option_line("combine_brims"); + optgroup->append_single_option_line("brim_ears_max_angle"); + optgroup->append_single_option_line("brim_ears_detection_length"); + } + { + auto optgroup = page->new_optgroup(L("Special mode")); + optgroup->append_single_option_line("slicing_mode"); + optgroup->append_single_option_line("print_sequence"); + optgroup->append_single_option_line("print_order"); + optgroup->append_single_option_line("spiral_mode"); + optgroup->append_single_option_line("spiral_mode_smooth"); + optgroup->append_single_option_line("spiral_mode_max_xy_smoothing"); + optgroup->append_single_option_line("spiral_starting_flow_ratio"); + optgroup->append_single_option_line("spiral_finishing_flow_ratio"); + optgroup->append_single_option_line("timelapse_type"); + optgroup->append_single_option_line("enable_wrapping_detection"); + } + { + auto optgroup = page->new_optgroup(L("Fuzzy Skin")); + optgroup->append_single_option_line("fuzzy_skin"); + optgroup->append_single_option_line("fuzzy_skin_mode"); + optgroup->append_single_option_line("fuzzy_skin_noise_type"); + optgroup->append_single_option_line("fuzzy_skin_point_distance"); + optgroup->append_single_option_line("fuzzy_skin_thickness"); + optgroup->append_single_option_line("fuzzy_skin_scale"); + optgroup->append_single_option_line("fuzzy_skin_octaves"); + optgroup->append_single_option_line("fuzzy_skin_persistence"); + optgroup->append_single_option_line("fuzzy_skin_first_layer"); + } + { + auto optgroup = page->new_optgroup(L("G-code output")); + optgroup->append_single_option_line("reduce_infill_retraction"); + optgroup->append_single_option_line("gcode_add_line_number"); + optgroup->append_single_option_line("gcode_comments"); + optgroup->append_single_option_line("gcode_label_objects"); + optgroup->append_single_option_line("exclude_object"); + optgroup->append_single_option_line("filename_format"); + } + { + auto optgroup = page->new_optgroup(L("Change extrusion role G-code")); + optgroup->append_single_option_line("process_change_extrusion_role_gcode"); + } + { + auto optgroup = page->new_optgroup(L("Post-processing Scripts")); + optgroup->append_single_option_line("post_process"); + } + { + auto optgroup = page->new_optgroup(L("Notes")); + optgroup->append_single_option_line("notes"); + } +} + +inline void TabFilament_build_filament_layout(TabFilament& tab) +{ + PageShp page = tab.add_options_page(L("Filament"), "custom-gcode_filament"); + { + auto optgroup = page->new_optgroup(L("Basic information")); + optgroup->append_single_option_line("filament_type"); + optgroup->append_single_option_line("filament_vendor"); + optgroup->append_single_option_line("filament_soluble"); + optgroup->append_single_option_line("filament_is_support"); + optgroup->append_single_option_line("filament_change_length"); + optgroup->append_single_option_line("required_nozzle_HRC"); + optgroup->append_single_option_line("default_filament_colour"); + optgroup->append_single_option_line("filament_diameter"); + optgroup->append_single_option_line("filament_adhesiveness_category"); + optgroup->append_single_option_line("filament_density"); + optgroup->append_single_option_line("filament_shrink"); + optgroup->append_single_option_line("filament_shrinkage_compensation_z"); + optgroup->append_single_option_line("filament_cost"); + optgroup->append_single_option_line("temperature_vitrification"); + optgroup->append_single_option_line("idle_temperature"); + { + Line line_{optgroup->get_option("nozzle_temperature_range_low").opt.label, optgroup->get_option("nozzle_temperature_range_low").opt.tooltip}; + line_.append_option(optgroup->get_option("nozzle_temperature_range_low")); + line_.append_option(optgroup->get_option("nozzle_temperature_range_high")); + optgroup->append_line(line_); + } + } + { + auto optgroup = page->new_optgroup(L("Flow ratio and Pressure Advance")); + optgroup->append_single_option_line("pellet_flow_coefficient"); + optgroup->append_single_option_line("filament_flow_ratio"); + optgroup->append_single_option_line("enable_pressure_advance"); + optgroup->append_single_option_line("pressure_advance"); + optgroup->append_single_option_line("adaptive_pressure_advance"); + optgroup->append_single_option_line("adaptive_pressure_advance_overhangs"); + optgroup->append_single_option_line("adaptive_pressure_advance_bridges"); + optgroup->append_single_option_line("adaptive_pressure_advance_model"); + } + { + auto optgroup = page->new_optgroup(L("Print chamber temperature")); + optgroup->append_single_option_line("activate_chamber_temp_control"); + optgroup->append_single_option_line("chamber_temperature"); + } + { + auto optgroup = page->new_optgroup(L("Print temperature")); + { + Line line_{optgroup->get_option("nozzle_temperature_initial_layer").opt.label, optgroup->get_option("nozzle_temperature_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("nozzle_temperature_initial_layer")); + line_.append_option(optgroup->get_option("nozzle_temperature")); + optgroup->append_line(line_); + } + } + { + auto optgroup = page->new_optgroup(L("Bed temperature")); + { + Line line_{optgroup->get_option("supertack_plate_temp_initial_layer").opt.label, optgroup->get_option("supertack_plate_temp_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("supertack_plate_temp_initial_layer")); + line_.append_option(optgroup->get_option("supertack_plate_temp")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("cool_plate_temp_initial_layer").opt.label, optgroup->get_option("cool_plate_temp_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("cool_plate_temp_initial_layer")); + line_.append_option(optgroup->get_option("cool_plate_temp")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("textured_cool_plate_temp_initial_layer").opt.label, optgroup->get_option("textured_cool_plate_temp_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("textured_cool_plate_temp_initial_layer")); + line_.append_option(optgroup->get_option("textured_cool_plate_temp")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("eng_plate_temp_initial_layer").opt.label, optgroup->get_option("eng_plate_temp_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("eng_plate_temp_initial_layer")); + line_.append_option(optgroup->get_option("eng_plate_temp")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("hot_plate_temp_initial_layer").opt.label, optgroup->get_option("hot_plate_temp_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("hot_plate_temp_initial_layer")); + line_.append_option(optgroup->get_option("hot_plate_temp")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("textured_plate_temp_initial_layer").opt.label, optgroup->get_option("textured_plate_temp_initial_layer").opt.tooltip}; + line_.append_option(optgroup->get_option("textured_plate_temp_initial_layer")); + line_.append_option(optgroup->get_option("textured_plate_temp")); + optgroup->append_line(line_); + } + } + { + auto optgroup = page->new_optgroup(L("Volumetric speed limitation")); + optgroup->append_single_option_line("filament_adaptive_volumetric_speed"); + optgroup->append_single_option_line("filament_max_volumetric_speed"); + } +} + +inline void TabFilament_build_cooling_layout(TabFilament& tab) +{ + PageShp page = tab.add_options_page(L("Cooling"), "custom-gcode_cooling_fan"); + { + auto optgroup = page->new_optgroup(L("Cooling for specific layer")); + optgroup->append_single_option_line("close_fan_the_first_x_layers"); + optgroup->append_single_option_line("full_fan_speed_layer"); + } + { + auto optgroup = page->new_optgroup(L("Part cooling fan")); + { + Line line_{optgroup->get_option("fan_min_speed").opt.label, optgroup->get_option("fan_min_speed").opt.tooltip}; + line_.append_option(optgroup->get_option("fan_min_speed")); + line_.append_option(optgroup->get_option("fan_cooling_layer_time")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("fan_max_speed").opt.label, optgroup->get_option("fan_max_speed").opt.tooltip}; + line_.append_option(optgroup->get_option("fan_max_speed")); + line_.append_option(optgroup->get_option("slow_down_layer_time")); + optgroup->append_line(line_); + } + optgroup->append_single_option_line("reduce_fan_stop_start_freq"); + optgroup->append_single_option_line("slow_down_for_layer_cooling"); + optgroup->append_single_option_line("dont_slow_down_outer_wall"); + optgroup->append_single_option_line("slow_down_min_speed"); + optgroup->append_single_option_line("enable_overhang_bridge_fan"); + optgroup->append_single_option_line("overhang_fan_threshold"); + optgroup->append_single_option_line("overhang_fan_speed"); + optgroup->append_single_option_line("internal_bridge_fan_speed"); + optgroup->append_single_option_line("support_material_interface_fan_speed"); + optgroup->append_single_option_line("ironing_fan_speed"); + } + { + auto optgroup = page->new_optgroup(L("Auxiliary part cooling fan")); + optgroup->append_single_option_line("additional_cooling_fan_speed"); + } + { + auto optgroup = page->new_optgroup(L("Exhaust fan")); + optgroup->append_single_option_line("activate_air_filtration"); + { + Line line_{optgroup->get_option("activate_air_filtration_during_print").opt.label, optgroup->get_option("activate_air_filtration_during_print").opt.tooltip}; + line_.append_option(optgroup->get_option("activate_air_filtration_during_print")); + line_.append_option(optgroup->get_option("during_print_exhaust_fan_speed")); + optgroup->append_line(line_); + } + { + Line line_{optgroup->get_option("activate_air_filtration_on_completion").opt.label, optgroup->get_option("activate_air_filtration_on_completion").opt.tooltip}; + line_.append_option(optgroup->get_option("activate_air_filtration_on_completion")); + line_.append_option(optgroup->get_option("complete_print_exhaust_fan_speed")); + optgroup->append_line(line_); + } + } +} + +inline void TabFilament_build_advanced_layout(TabFilament& tab) +{ + PageShp page = tab.add_options_page(L("Advanced"), "custom-gcode_advanced"); + { + auto optgroup = page->new_optgroup(L("Filament start G-code")); + optgroup->append_single_option_line("filament_start_gcode"); + } + { + auto optgroup = page->new_optgroup(L("Change extrusion role G-code")); + optgroup->append_single_option_line("filament_change_extrusion_role_gcode"); + } + { + auto optgroup = page->new_optgroup(L("Filament end G-code")); + optgroup->append_single_option_line("filament_end_gcode"); + } +} + +inline void TabFilament_build_multimaterial_layout(TabFilament& tab) +{ + PageShp page = tab.add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); + { + auto optgroup = page->new_optgroup(L("Wipe tower parameters")); + optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); + optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_dist"); + optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_length"); + optgroup->append_single_option_line("filament_tower_ironing_area"); + optgroup->append_single_option_line("filament_tower_interface_purge_volume"); + optgroup->append_single_option_line("filament_tower_interface_print_temp"); + } + { + auto optgroup = page->new_optgroup(L("Multi Filament")); + optgroup->append_single_option_line("long_retractions_when_ec"); + optgroup->append_single_option_line("retraction_distances_when_ec"); + } + { + auto optgroup = page->new_optgroup(L("Tool change parameters with single extruder MM printers")); + optgroup->append_single_option_line("filament_loading_speed_start"); + optgroup->append_single_option_line("filament_loading_speed"); + optgroup->append_single_option_line("filament_unloading_speed_start"); + optgroup->append_single_option_line("filament_unloading_speed"); + optgroup->append_single_option_line("filament_toolchange_delay"); + optgroup->append_single_option_line("filament_cooling_moves"); + optgroup->append_single_option_line("filament_cooling_initial_speed"); + optgroup->append_single_option_line("filament_cooling_final_speed"); + optgroup->append_single_option_line("filament_stamping_loading_speed"); + optgroup->append_single_option_line("filament_stamping_distance"); + optgroup->append_single_option_line("filament_ramming_parameters"); + } + { + auto optgroup = page->new_optgroup(L("Tool change parameters with multi extruder MM printers")); + optgroup->append_single_option_line("filament_multitool_ramming"); + optgroup->append_single_option_line("filament_multitool_ramming_volume"); + optgroup->append_single_option_line("filament_multitool_ramming_flow"); + } +} + +inline void TabFilament_build_dependencies_layout(TabFilament& tab) +{ + PageShp page = tab.add_options_page(L("Dependencies"), "advanced"); + { + auto optgroup = page->new_optgroup(L("Compatible printers")); + optgroup->append_single_option_line("compatible_printers"); + optgroup->append_single_option_line("compatible_printers_condition"); + } + { + auto optgroup = page->new_optgroup(L("Compatible process profiles")); + optgroup->append_single_option_line("compatible_prints"); + optgroup->append_single_option_line("compatible_prints_condition"); + } +} + +inline void TabFilament_build_notes_layout(TabFilament& tab) +{ + PageShp page = tab.add_options_page(L("Notes"), "custom-gcode_note"); + { + auto optgroup = page->new_optgroup(L("Notes")); + optgroup->append_single_option_line("filament_notes"); + } +} + +inline void TabPrinter_build_basic_information_layout(TabPrinter& tab) +{ + PageShp page = tab.add_options_page(L("Basic information"), "custom-gcode_object-info"); + { + auto optgroup = page->new_optgroup(L("Printable space"), L"param_printable_space"); + tab.layout_hook_printable_space(optgroup.get()); + } + { + auto optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); + tab.layout_hook_advanced(optgroup.get()); + } + { + auto optgroup = page->new_optgroup(L("Cooling Fan"), L"param_cooling_fan"); + tab.layout_hook_cooling_fan(optgroup.get()); + } + { + auto optgroup = page->new_optgroup(L("Extruder Clearance"), L"param_extruder_clearance"); + optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius"); + optgroup->append_single_option_line("extruder_clearance_height_to_rod", "printer_basic_information_extruder_clearance#height-to-rod"); + optgroup->append_single_option_line("extruder_clearance_height_to_lid", "printer_basic_information_extruder_clearance#height-to-lid"); + } + { + auto optgroup = page->new_optgroup(L("Adaptive bed mesh"), L"param_adaptive_mesh"); + optgroup->append_single_option_line("bed_mesh_min", "printer_basic_information_adaptive_bed_mesh#bed-mesh"); + optgroup->append_single_option_line("bed_mesh_max", "printer_basic_information_adaptive_bed_mesh#bed-mesh"); + optgroup->append_single_option_line("bed_mesh_probe_distance", "printer_basic_information_adaptive_bed_mesh#probe-point-distance"); + optgroup->append_single_option_line("adaptive_bed_mesh_margin", "printer_basic_information_adaptive_bed_mesh#mesh-margin"); + } + { + auto optgroup = page->new_optgroup(L("Accessory"), L"param_accessory"); + optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type"); + optgroup->append_single_option_line("nozzle_hrc", "printer_basic_information_accessory#nozzle-hrc"); + optgroup->append_single_option_line("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan"); + optgroup->append_single_option_line("support_chamber_temp_control", "printer_basic_information_accessory#support-controlling-chamber-temperature"); + optgroup->append_single_option_line("support_air_filtration", "printer_basic_information_accessory#support-air-filtration"); + } +} + +inline void TabPrinter_build_machine_g_code_layout(TabPrinter& tab) +{ + PageShp page = tab.add_options_page(L("Machine G-code"), "custom-gcode_gcode"); + { + auto optgroup = page->new_optgroup(L("File header G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("file_start_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option); + } + } + { + auto optgroup = page->new_optgroup(L("Machine start G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("machine_start_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#machine-start-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Machine end G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("machine_end_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#machine-end-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Printing by object G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("printing_by_object_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#between-object-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Before layer change G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("before_layer_change_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#before-layer-change-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Layer change G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("layer_change_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#after-layer-change-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Timelapse G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("time_lapse_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#time-lapse-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Clumping Detection G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("wrapping_detection_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#clumping-detection-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Change filament G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("change_filament_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#change-filament-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Change extrusion role G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("change_extrusion_role_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#change-extrusion-role-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Pause G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("machine_pause_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#pause-g-code"); + } + } + { + auto optgroup = page->new_optgroup(L("Template Custom G-code"), L"param_gcode", 0); + optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); + }; + optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; + { + Option option = optgroup->get_option("template_custom_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; + optgroup->append_single_option_line(option, "printer_machine_gcode#template-custom-g-code"); + } + } +} + +inline void TabPrinter_build_notes_layout(TabPrinter& tab) +{ + PageShp page = tab.add_options_page(L("Notes"), "custom-gcode_note"); + { + auto optgroup = page->new_optgroup(L("Notes"), L"note"); + optgroup->append_single_option_line("printer_notes"); + } +} + +// ── Aggregate wrappers (backward-compatible with Tab.cpp call sites) ── + +inline void TabPrint_build_layout(TabPrint& tab) +{ + TabPrint_build_quality_layout(tab); + TabPrint_build_strength_layout(tab); + TabPrint_build_speed_layout(tab); + TabPrint_build_support_layout(tab); + TabPrint_build_multimaterial_layout(tab); + TabPrint_build_others_layout(tab); +} + +inline void TabFilament_build_main_layout(TabFilament& tab) +{ + TabFilament_build_filament_layout(tab); + TabFilament_build_cooling_layout(tab); + TabFilament_build_multimaterial_layout(tab); +} + +inline void TabPrinter_build_basic_info_layout(TabPrinter& tab) +{ + TabPrinter_build_basic_information_layout(tab); +} + +inline void TabPrinter_build_gcode_layout(TabPrinter& tab) +{ + TabPrinter_build_machine_g_code_layout(tab); + TabPrinter_build_notes_layout(tab); +} + diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 072e1a16b2..b64c01aca2 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(${_TEST_NAME}_tests test_optimizers.cpp # test_png_io.cpp test_indexed_triangle_set.cpp + test_texture_displacement.cpp ../libnest2d/printer_parts.cpp ) diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp new file mode 100644 index 0000000000..a12ac9c1af --- /dev/null +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -0,0 +1,136 @@ +#define NOMINMAX +#include + +#include +#include + +#include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/TriangleSelector.hpp" +#include "libslic3r/PNGReadWrite.hpp" + +using namespace Slic3r; +using Catch::Matchers::WithinAbs; + +// Encodes a flat (uniform-value) grayscale image through Slic3r's own PNG writer/reader round +// trip, so decode_height_texture() (which only accepts true 8-bit grayscale PNG) is guaranteed a +// compatible file, exactly like the GUI's "Add texture" import path does. +static std::shared_ptr> make_flat_gray_png(uint8_t value, size_t w = 4, size_t h = 4) +{ + std::vector pixels(w * h, value); + const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("texdisp_test_%%%%%%%%.png"); + REQUIRE(Slic3r::png::write_gray_to_file(tmp_path.string(), w, h, pixels)); + + std::vector bytes; + { + std::ifstream ifs(tmp_path.string(), std::ios::binary); + bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp_path, ec); + + REQUIRE_FALSE(bytes.empty()); + return std::make_shared>(std::move(bytes)); +} + +TEST_CASE("TextureDisplacement: decode_height_texture round-trips an 8-bit grayscale PNG", "[TextureDisplacement]") +{ + TextureDisplacementLayer layer; + layer.image_data = make_flat_gray_png(128, 4, 4); + + DecodedHeightTexture tex = decode_height_texture(layer); + REQUIRE_FALSE(tex.empty()); + CHECK(tex.width == 4); + CHECK(tex.height == 4); + REQUIRE_THAT(tex.sample(Vec2f(0.5f, 0.5f)), WithinAbs(128.0 / 255.0, 1.0 / 255.0)); +} + +TEST_CASE("TextureDisplacement: an empty layer list leaves the mesh unchanged", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const std::vector layers; // none + TextureDisplacementFacetsData facets{}; // all empty + + const indexed_triangle_set result = build_texture_displacement(cube, layers, facets); + + REQUIRE(result.vertices.size() == cube.vertices.size()); + REQUIRE(result.indices.size() == cube.indices.size()); + for (size_t i = 0; i < cube.vertices.size(); ++i) + for (int c = 0; c < 3; ++c) + CHECK(result.vertices[i](c) == cube.vertices[i](c)); +} + +TEST_CASE("TextureDisplacement: fully painting a mesh displaces every vertex along its own normal", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const TriangleMesh cube_mesh(cube); + + TriangleSelector selector(cube_mesh); + for (int f = 0; f < int(cube.indices.size()); ++f) + selector.set_facet(f, EnforcerBlockerType::ENFORCER); + + TextureDisplacementFacetsData facets{}; + facets[0] = selector.serialize(); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 2.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); // sample() == 1.0 everywhere -> full depth_mm displacement + + const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets); + + REQUIRE(result.vertices.size() == cube.vertices.size()); + for (size_t i = 0; i < cube.vertices.size(); ++i) { + const float moved = (result.vertices[i] - cube.vertices[i]).norm(); + CHECK_THAT(moved, WithinAbs(layer.depth_mm, 1e-3f)); + } +} + +TEST_CASE("TextureDisplacement: boundary vertices shared with unpainted triangles are pinned", "[TextureDisplacement]") +{ + // A small triangle fan around a central vertex O, with 4 outer points A/B/C/D forming 4 + // triangles T0..T3 in the XY plane. Only T0, T1, T2 are painted, T3 is left unpainted: + // O: touches all 4 triangles (incl. unpainted T3) -> boundary, must NOT move + // A: touches T0 (painted) and T3 (unpainted) -> boundary, must NOT move + // D: touches T2 (painted) and T3 (unpainted) -> boundary, must NOT move + // B: touches only T0 and T1 (both painted) -> interior, SHOULD move + // C: touches only T1 and T2 (both painted) -> interior, SHOULD move + indexed_triangle_set fan; + fan.vertices = { {0.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {-1.f, 0.f, 0.f}, {0.f, -1.f, 0.f} }; + fan.indices = { {0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1} }; + + const TriangleMesh fan_mesh(fan); + TriangleSelector selector(fan_mesh); + selector.set_facet(0, EnforcerBlockerType::ENFORCER); + selector.set_facet(1, EnforcerBlockerType::ENFORCER); + selector.set_facet(2, EnforcerBlockerType::ENFORCER); + // facet 3 (T3) is left at its default EnforcerBlockerType::NONE. + + TextureDisplacementFacetsData facets{}; + facets[0] = selector.serialize(); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 1.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); + + const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets); + + // Find each named vertex's post-bake position by matching the original (pinned vertices keep + // their exact original position; moved ones won't match any original position anymore). + auto still_at_original_position = [&](const Vec3f &original) { + for (const Vec3f &v : result.vertices) + if ((v - original).norm() < 1e-6f) + return true; + return false; + }; + + CHECK(still_at_original_position(fan.vertices[0])); // O: boundary + CHECK(still_at_original_position(fan.vertices[1])); // A: boundary + CHECK(still_at_original_position(fan.vertices[4])); // D: boundary + CHECK_FALSE(still_at_original_position(fan.vertices[2])); // B: interior, must have moved + CHECK_FALSE(still_at_original_position(fan.vertices[3])); // C: interior, must have moved +} From 3514249197590670f206e3e1e0f358f8945b8b31 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 9 Jul 2026 08:40:37 +0200 Subject: [PATCH 02/89] Removed files that were accidently added --- .../GUI/generated/Invalidation_generated.cpp | 255 - .../GUI/generated/OptionKeys_generated.cpp | 61 - .../generated/Preset_options_generated.cpp | 726 --- .../generated/PrintConfigDef_generated.cpp | 5716 ----------------- .../GUI/generated/TabLayout_generated.cpp | 973 --- 5 files changed, 7731 deletions(-) delete mode 100644 src/slic3r/GUI/generated/Invalidation_generated.cpp delete mode 100644 src/slic3r/GUI/generated/OptionKeys_generated.cpp delete mode 100644 src/slic3r/GUI/generated/Preset_options_generated.cpp delete mode 100644 src/slic3r/GUI/generated/PrintConfigDef_generated.cpp delete mode 100644 src/slic3r/GUI/generated/TabLayout_generated.cpp diff --git a/src/slic3r/GUI/generated/Invalidation_generated.cpp b/src/slic3r/GUI/generated/Invalidation_generated.cpp deleted file mode 100644 index 7f65e44a78..0000000000 --- a/src/slic3r/GUI/generated/Invalidation_generated.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// ===== AUTO-GENERATED by tools/config_codegen.py ===== - -static const std::unordered_map> s_print_steps_map = { - {"accel_to_decel_enable", {psGCodeExport}}, - {"accel_to_decel_factor", {psGCodeExport}}, - {"activate_air_filtration", {psGCodeExport}}, - {"activate_air_filtration_during_print", {psGCodeExport}}, - {"activate_air_filtration_on_completion", {psGCodeExport}}, - {"activate_chamber_temp_control", {psGCodeExport}}, - {"additional_cooling_fan_speed", {psGCodeExport}}, - {"bed_temperature_formula", {psGCodeExport}}, - {"before_layer_change_gcode", {psGCodeExport}}, - {"bridge_acceleration", {psGCodeExport}}, - {"chamber_temperature", {psSkirtBrim, psWipeTower, psGCodeExport}}, - {"change_filament_gcode", {psGCodeExport}}, - {"close_fan_the_first_x_layers", {psGCodeExport}}, - {"complete_print_exhaust_fan_speed", {psGCodeExport}}, - {"cool_plate_temp", {psSkirtBrim, psWipeTower}}, - {"cool_plate_temp_initial_layer", {psGCodeExport}}, - {"curr_bed_type", {psGCodeExport}}, - {"default_acceleration", {psGCodeExport}}, - {"default_filament_colour", {psGCodeExport}}, - {"deretraction_speed", {psGCodeExport}}, - {"disable_m73", {psGCodeExport}}, - {"dont_slow_down_outer_wall", {psGCodeExport}}, - {"draft_shield", {psSkirtBrim}}, - {"during_print_exhaust_fan_speed", {psGCodeExport}}, - {"enable_arc_fitting", {psSkirtBrim}}, - {"enable_filament_ramming", {psSkirtBrim, psWipeTower}}, - {"enable_long_retraction_when_cut", {psGCodeExport}}, - {"enable_overhang_bridge_fan", {psGCodeExport}}, - {"enable_pressure_advance", {psGCodeExport}}, - {"enable_prime_tower", {psSkirtBrim, psWipeTower}}, - {"enable_tower_interface_features", {psSkirtBrim, psWipeTower}}, - {"enable_wrapping_detection", {psSkirtBrim, psWipeTower}}, - {"eng_plate_temp", {psSkirtBrim, psWipeTower}}, - {"eng_plate_temp_initial_layer", {psGCodeExport}}, - {"exclude_object", {psGCodeExport}}, - {"extruder_ams_count", {psSkirtBrim, psWipeTower}}, - {"extruder_clearance_height_to_lid", {psGCodeExport}}, - {"extruder_clearance_height_to_rod", {psGCodeExport}}, - {"extruder_clearance_radius", {psGCodeExport}}, - {"extruder_colour", {psGCodeExport}}, - {"extruder_offset", {psGCodeExport}}, - {"extrusion_rate_smoothing_external_perimeter_only", {psGCodeExport}}, - {"fan_cooling_layer_time", {psGCodeExport}}, - {"fan_kickstart", {psGCodeExport}}, - {"fan_max_speed", {psGCodeExport}}, - {"fan_min_speed", {psGCodeExport}}, - {"fan_speedup_overhangs", {psGCodeExport}}, - {"fan_speedup_time", {psGCodeExport}}, - {"filament_adaptive_volumetric_speed", {psSkirtBrim, psWipeTower}}, - {"filament_adhesiveness_category", {psSkirtBrim, psWipeTower}}, - {"filament_change_length", {psWipeTower}}, - {"filament_colour", {psGCodeExport}}, - {"filament_cooling_final_speed", {psSkirtBrim, psWipeTower}}, - {"filament_cooling_initial_speed", {psSkirtBrim, psWipeTower}}, - {"filament_cooling_moves", {psSkirtBrim, psWipeTower}}, - {"filament_cost", {psGCodeExport}}, - {"filament_density", {psGCodeExport}}, - {"filament_diameter", {psGCodeExport}}, - {"filament_end_gcode", {psGCodeExport}}, - {"filament_flow_ratio", {psGCodeExport}}, - {"filament_is_support", {psWipeTower}}, - {"filament_loading_speed", {psSkirtBrim, psWipeTower}}, - {"filament_loading_speed_start", {psSkirtBrim, psWipeTower}}, - {"filament_map", {psSkirtBrim, psWipeTower}}, - {"filament_map_mode", {psSkirtBrim, psWipeTower}}, - {"filament_max_volumetric_speed", {psSkirtBrim, psWipeTower}}, - {"filament_minimal_purge_on_wipe_tower", {psSkirtBrim, psWipeTower}}, - {"filament_multitool_ramming", {psSkirtBrim, psWipeTower}}, - {"filament_multitool_ramming_flow", {psSkirtBrim, psWipeTower}}, - {"filament_multitool_ramming_volume", {psSkirtBrim, psWipeTower}}, - {"filament_notes", {psGCodeExport}}, - {"filament_printable", {psWipeTower}}, - {"filament_ramming_parameters", {psSkirtBrim, psWipeTower}}, - {"filament_soluble", {psWipeTower}}, - {"filament_stamping_distance", {psSkirtBrim, psWipeTower}}, - {"filament_stamping_loading_speed", {psSkirtBrim, psWipeTower}}, - {"filament_start_gcode", {psGCodeExport}}, - {"filament_toolchange_delay", {psSkirtBrim, psWipeTower}}, - {"filament_tower_interface_pre_extrusion_dist", {psSkirtBrim, psWipeTower}}, - {"filament_tower_interface_pre_extrusion_length", {psSkirtBrim, psWipeTower}}, - {"filament_tower_interface_print_temp", {psSkirtBrim, psWipeTower}}, - {"filament_tower_interface_purge_volume", {psSkirtBrim, psWipeTower}}, - {"filament_tower_ironing_area", {psSkirtBrim, psWipeTower}}, - {"filament_type", {psSkirtBrim, psWipeTower}}, - {"filament_unloading_speed", {psSkirtBrim, psWipeTower}}, - {"filament_unloading_speed_start", {psSkirtBrim, psWipeTower}}, - {"filename_format", {psGCodeExport}}, - {"first_layer_print_sequence", {psSkirtBrim, psWipeTower}}, - {"flush_into_infill", {psSkirtBrim, psWipeTower}}, - {"flush_into_support", {psSkirtBrim, psWipeTower}}, - {"flush_volumes_matrix", {psSkirtBrim, psWipeTower}}, - {"full_fan_speed_layer", {psGCodeExport}}, - {"gcode_add_line_number", {psGCodeExport}}, - {"gcode_comments", {psGCodeExport}}, - {"gcode_flavor", {psSkirtBrim, psWipeTower}}, - {"gcode_label_objects", {psGCodeExport}}, - {"grab_length", {psGCodeExport}}, - {"hot_plate_temp", {psSkirtBrim, psWipeTower}}, - {"hot_plate_temp_initial_layer", {psGCodeExport}}, - {"idle_temperature", {psSkirtBrim, psWipeTower}}, - {"independent_support_layer_height", {psWipeTower}}, - {"initial_layer_acceleration", {psGCodeExport}}, - {"initial_layer_infill_speed", {psSkirtBrim, psWipeTower}}, - {"initial_layer_line_width", {psSkirtBrim}}, - {"initial_layer_speed", {psSkirtBrim, psWipeTower}}, - {"initial_layer_travel_acceleration", {psSkirtBrim, psWipeTower}}, - {"initial_layer_travel_jerk", {psSkirtBrim, psWipeTower}}, - {"initial_layer_travel_speed", {psSkirtBrim, psWipeTower}}, - {"inner_wall_acceleration", {psGCodeExport}}, - {"internal_bridge_fan_speed", {psGCodeExport}}, - {"internal_solid_infill_acceleration", {psGCodeExport}}, - {"ironing_fan_speed", {psGCodeExport}}, - {"is_infill_first", {psGCodeExport}}, - {"layer_change_gcode", {psGCodeExport}}, - {"long_retractions_when_cut", {psGCodeExport}}, - {"machine_end_gcode", {psGCodeExport}}, - {"machine_start_gcode", {psGCodeExport}}, - {"manual_filament_change", {psGCodeExport}}, - {"max_layer_height", {psSkirtBrim}}, - {"max_travel_detour_distance", {psGCodeExport}}, - {"max_volumetric_extrusion_rate_slope", {psGCodeExport}}, - {"max_volumetric_extrusion_rate_slope_segment_length", {psGCodeExport}}, - {"min_layer_height", {psSkirtBrim}}, - {"min_skirt_length", {psSkirtBrim}}, - {"nozzle_height", {psGCodeExport}}, - {"nozzle_hrc", {psGCodeExport}}, - {"nozzle_temperature", {psSkirtBrim, psWipeTower}}, - {"nozzle_temperature_initial_layer", {psSkirtBrim, psWipeTower}}, - {"nozzle_volume", {psGCodeExport}}, - {"ooze_prevention", {psSkirtBrim}}, - {"other_layers_print_sequence", {psSkirtBrim, psWipeTower}}, - {"other_layers_print_sequence_nums", {psSkirtBrim, psWipeTower}}, - {"outer_wall_acceleration", {psGCodeExport}}, - {"overhang_fan_speed", {psGCodeExport}}, - {"overhang_fan_threshold", {psGCodeExport}}, - {"part_cooling_fan_min_pwm", {psGCodeExport}}, - {"post_process", {psGCodeExport}}, - {"preheat_steps", {psGCodeExport}}, - {"preheat_time", {psGCodeExport}}, - {"pressure_advance", {psGCodeExport}}, - {"prime_tower_brim_width", {psSkirtBrim, psWipeTower}}, - {"prime_tower_enable_framework", {psSkirtBrim, psWipeTower}}, - {"prime_tower_flat_ironing", {psSkirtBrim, psWipeTower}}, - {"prime_tower_skip_points", {psSkirtBrim, psWipeTower}}, - {"prime_tower_width", {psSkirtBrim, psWipeTower}}, - {"prime_volume", {psSkirtBrim, psWipeTower}}, - {"print_order", {psSkirtBrim}}, - {"print_sequence", {psSkirtBrim, psWipeTower}}, - {"printer_notes", {psGCodeExport}}, - {"printing_by_object_gcode", {psGCodeExport}}, - {"purge_in_prime_tower", {psSkirtBrim, psWipeTower}}, - {"reduce_crossing_wall", {psGCodeExport}}, - {"reduce_fan_stop_start_freq", {psGCodeExport}}, - {"reduce_infill_retraction", {psGCodeExport}}, - {"required_nozzle_HRC", {psGCodeExport}}, - {"resolution", {psSkirtBrim}}, - {"retract_before_wipe", {psGCodeExport}}, - {"retract_length_toolchange", {psGCodeExport}}, - {"retract_lift_above", {psGCodeExport}}, - {"retract_lift_below", {psGCodeExport}}, - {"retract_lift_enforce", {psGCodeExport}}, - {"retract_restart_extra", {psGCodeExport}}, - {"retract_restart_extra_toolchange", {psGCodeExport}}, - {"retract_when_changing_layer", {psGCodeExport}}, - {"retraction_distances_when_cut", {psGCodeExport}}, - {"retraction_length", {psGCodeExport}}, - {"retraction_minimum_travel", {psGCodeExport}}, - {"retraction_speed", {psGCodeExport}}, - {"role_based_wipe_speed", {psGCodeExport}}, - {"seam_gap", {psGCodeExport}}, - {"single_extruder_multi_material", {psSkirtBrim, psWipeTower}}, - {"single_extruder_multi_material_priming", {psGCodeExport}}, - {"single_loop_draft_shield", {psSkirtBrim}}, - {"skirt_distance", {psSkirtBrim}}, - {"skirt_height", {psSkirtBrim}}, - {"skirt_loops", {psSkirtBrim}}, - {"skirt_speed", {psSkirtBrim}}, - {"skirt_start_angle", {psSkirtBrim}}, - {"skirt_type", {psSkirtBrim}}, - {"slow_down_for_layer_cooling", {psGCodeExport}}, - {"slow_down_layer_time", {psGCodeExport}}, - {"slow_down_layers", {psSkirtBrim, psWipeTower}}, - {"slow_down_min_speed", {psGCodeExport}}, - {"sparse_infill_acceleration", {psGCodeExport}}, - {"standby_temperature_delta", {psGCodeExport}}, - {"supertack_plate_temp", {psSkirtBrim, psWipeTower}}, - {"supertack_plate_temp_initial_layer", {psGCodeExport}}, - {"support_material_interface_fan_speed", {psGCodeExport}}, - {"support_multi_bed_types", {psSkirtBrim, psWipeTower}}, - {"textured_cool_plate_temp", {psSkirtBrim, psWipeTower}}, - {"textured_cool_plate_temp_initial_layer", {psGCodeExport}}, - {"textured_plate_temp", {psSkirtBrim, psWipeTower}}, - {"textured_plate_temp_initial_layer", {psGCodeExport}}, - {"thumbnails", {psGCodeExport}}, - {"thumbnails_format", {psGCodeExport}}, - {"time_lapse_gcode", {psGCodeExport}}, - {"tool_change_on_wipe_tower", {psSkirtBrim, psWipeTower}}, - {"top_surface_acceleration", {psGCodeExport}}, - {"travel_acceleration", {psGCodeExport}}, - {"travel_slope", {psGCodeExport}}, - {"travel_speed", {psSkirtBrim, psWipeTower}}, - {"travel_speed_z", {psSkirtBrim, psWipeTower}}, - {"upward_compatible_machine", {psGCodeExport}}, - {"use_firmware_retraction", {psGCodeExport}}, - {"use_relative_e_distances", {psGCodeExport}}, - {"volumetric_speed_coefficients", {psGCodeExport}}, - {"wall_sequence", {psSkirtBrim}}, - {"wipe", {psGCodeExport}}, - {"wipe_distance", {psGCodeExport}}, - {"wipe_on_loops", {psGCodeExport}}, - {"wipe_speed", {psGCodeExport}}, - {"wipe_tower_bridging", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_cone_angle", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_extra_flow", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_extra_rib_length", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_extra_spacing", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_filament", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_fillet_wall", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_max_purge_speed", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_no_sparse_layers", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_rib_width", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_rotation_angle", {psSkirtBrim}}, - {"wipe_tower_type", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_wall_type", {psSkirtBrim, psWipeTower}}, - {"wipe_tower_x", {psSkirtBrim}}, - {"wipe_tower_y", {psSkirtBrim}}, - {"wiping_volumes_extruders", {psSkirtBrim, psWipeTower}}, - {"wrapping_detection_gcode", {psGCodeExport}}, - {"z_hop", {psGCodeExport}}, - {"z_offset", {psSkirtBrim, psWipeTower}}, -}; - -static const std::unordered_map> s_object_steps_map = { - {"enable_arc_fitting", {posSupportMaterial, posInfill, posPerimeters}}, - {"filament_change_length", {posSupportMaterial}}, - {"filament_is_support", {posSupportMaterial}}, - {"filament_printable", {posSupportMaterial}}, - {"filament_shrink", {posSlice}}, - {"filament_shrinkage_compensation_z", {posSlice}}, - {"filament_soluble", {posSupportMaterial}}, - {"independent_support_layer_height", {posSupportMaterial}}, - {"initial_layer_line_width", {posSupportMaterial, posInfill, posPerimeters}}, - {"initial_layer_print_height", {posSlice}}, - {"max_layer_height", {posSupportMaterial, posInfill, posPerimeters}}, - {"min_layer_height", {posSupportMaterial, posInfill, posPerimeters}}, - {"nozzle_diameter", {posSlice}}, - {"precise_z_height", {posSlice}}, - {"print_order", {posSupportMaterial, posInfill, posPerimeters}}, - {"resolution", {posSupportMaterial, posInfill, posPerimeters, posSlice}}, - {"spiral_mode", {posSlice}}, - {"wall_sequence", {posSupportMaterial, posInfill, posPerimeters}}, -}; \ No newline at end of file diff --git a/src/slic3r/GUI/generated/OptionKeys_generated.cpp b/src/slic3r/GUI/generated/OptionKeys_generated.cpp deleted file mode 100644 index 644503ea50..0000000000 --- a/src/slic3r/GUI/generated/OptionKeys_generated.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// ===== AUTO-GENERATED by tools/config_codegen.py ===== - -static const std::vector s_extruder_option_keys = { - "default_filament_profile", - "default_nozzle_volume_type", - "deretraction_speed", - "extruder_colour", - "extruder_offset", - "extruder_type", - "long_retractions_when_cut", - "max_layer_height", - "min_layer_height", - "nozzle_diameter", - "nozzle_flush_dataset", - "nozzle_type", - "nozzle_volume", - "retract_before_wipe", - "retract_length_toolchange", - "retract_lift_above", - "retract_lift_below", - "retract_lift_enforce", - "retract_restart_extra", - "retract_restart_extra_toolchange", - "retract_when_changing_layer", - "retraction_distances_when_cut", - "retraction_length", - "retraction_minimum_travel", - "retraction_speed", - "travel_slope", - "wipe", - "wipe_distance", - "z_hop", - "z_hop_types", -}; - -static const std::vector s_filament_option_keys = { - "default_filament_profile", - "deretraction_speed", - "filament_colour", - "filament_diameter", - "long_retractions_when_cut", - "max_layer_height", - "min_layer_height", - "retract_before_wipe", - "retract_length_toolchange", - "retract_lift_above", - "retract_lift_below", - "retract_lift_enforce", - "retract_restart_extra", - "retract_restart_extra_toolchange", - "retract_when_changing_layer", - "retraction_distances_when_cut", - "retraction_length", - "retraction_minimum_travel", - "retraction_speed", - "volumetric_speed_coefficients", - "wipe", - "wipe_distance", - "z_hop", - "z_hop_types", -}; diff --git a/src/slic3r/GUI/generated/Preset_options_generated.cpp b/src/slic3r/GUI/generated/Preset_options_generated.cpp deleted file mode 100644 index dc82d4f593..0000000000 --- a/src/slic3r/GUI/generated/Preset_options_generated.cpp +++ /dev/null @@ -1,726 +0,0 @@ -// ===== AUTO-GENERATED by tools/config_codegen.py ===== - -static const std::vector s_Preset_print_options = { - "accel_to_decel_enable", - "accel_to_decel_factor", - "adaptive_layer_height", - "align_infill_direction_to_model", - "alternate_extra_wall", - "bbl_calib_mark_logo", - "bed_temperature_formula", - "bottom_shell_layers", - "bottom_shell_thickness", - "bottom_solid_infill_flow_ratio", - "bottom_surface_density", - "bottom_surface_pattern", - "bridge_acceleration", - "bridge_angle", - "bridge_density", - "bridge_flow", - "bridge_no_support", - "bridge_speed", - "brim_ears", - "brim_ears_detection_length", - "brim_ears_max_angle", - "brim_flow_ratio", - "brim_object_gap", - "brim_type", - "brim_use_efc_outline", - "brim_width", - "calib_flowrate_topinfill_special_order", - "combine_brims", - "compatible_machine_expression_group", - "compatible_printers", - "compatible_printers_condition", - "compatible_prints", - "compatible_process_expression_group", - "cooling_tube_length", - "cooling_tube_retraction", - "counterbore_hole_bridging", - "curr_bed_type", - "default_acceleration", - "default_bed_type", - "default_filament_profile", - "default_jerk", - "default_junction_deviation", - "default_nozzle_volume_type", - "default_print_profile", - "deretraction_speed", - "detect_narrow_internal_solid_infill", - "detect_overhang_wall", - "detect_thin_wall", - "different_settings_to_system", - "dont_filter_internal_bridges", - "draft_shield", - "elefant_foot_compensation", - "elefant_foot_compensation_layers", - "elefant_foot_layers_density", - "emit_machine_limits_to_gcode", - "enable_arc_fitting", - "enable_extra_bridge_layer", - "enable_filament_dynamic_map", - "enable_filament_ramming", - "enable_long_retraction_when_cut", - "enable_overhang_speed", - "enable_prime_tower", - "enable_support", - "enable_tower_interface_cooldown_during_tower", - "enable_tower_interface_features", - "enable_wrapping_detection", - "enforce_support_layers", - "ensure_vertical_shell_thickness", - "exclude_object", - "extra_loading_move", - "extra_perimeters_on_overhangs", - "extra_solid_infills", - "extruder", - "extruder_ams_count", - "extruder_colour", - "extruder_offset", - "extruder_type", - "extruder_variant_list", - "extrusion_rate_smoothing_external_perimeter_only", - "filament_colour", - "filament_colour_type", - "filament_extruder_id", - "filament_flush_temp", - "filament_ids", - "filament_map", - "filament_map_mode", - "filament_multi_colour", - "filament_ramming_parameters", - "filament_self_index", - "filament_settings_id", - "filename_format", - "fill_multiline", - "filter_out_gap_fill", - "first_layer_flow_ratio", - "first_layer_print_sequence", - "first_layer_sequence_choice", - "flush_into_infill", - "flush_into_objects", - "flush_into_support", - "flush_multiplier", - "flush_volumes_matrix", - "flush_volumes_vector", - "fuzzy_skin", - "fuzzy_skin_first_layer", - "fuzzy_skin_layers_between_ripple_offset", - "fuzzy_skin_mode", - "fuzzy_skin_noise_type", - "fuzzy_skin_octaves", - "fuzzy_skin_persistence", - "fuzzy_skin_point_distance", - "fuzzy_skin_ripple_offset", - "fuzzy_skin_ripples_per_layer", - "fuzzy_skin_scale", - "fuzzy_skin_thickness", - "gap_fill_flow_ratio", - "gap_fill_target", - "gap_infill_speed", - "gcode_add_line_number", - "gcode_comments", - "gcode_label_objects", - "grab_length", - "gyroid_optimized", - "has_filament_switcher", - "has_scarf_joint_seam", - "head_wrap_detect_zone", - "high_current_on_filament_swap", - "hole_to_polyhole", - "hole_to_polyhole_threshold", - "hole_to_polyhole_twisted", - "host_type", - "independent_support_layer_height", - "infill_anchor", - "infill_anchor_max", - "infill_combination", - "infill_combination_max_layer_height", - "infill_direction", - "infill_jerk", - "infill_lock_depth", - "infill_overhang_angle", - "infill_shift_step", - "infill_wall_overlap", - "inherits", - "inherits_group", - "initial_layer_acceleration", - "initial_layer_infill_speed", - "initial_layer_jerk", - "initial_layer_line_width", - "initial_layer_min_bead_width", - "initial_layer_print_height", - "initial_layer_speed", - "initial_layer_travel_acceleration", - "initial_layer_travel_jerk", - "initial_layer_travel_speed", - "inner_wall_acceleration", - "inner_wall_flow_ratio", - "inner_wall_jerk", - "inner_wall_line_width", - "inner_wall_speed", - "input_shaping_damp_x", - "input_shaping_damp_y", - "input_shaping_emit", - "input_shaping_freq_x", - "input_shaping_freq_y", - "input_shaping_type", - "interface_shells", - "interlocking_beam", - "interlocking_beam_layer_count", - "interlocking_beam_width", - "interlocking_boundary_avoidance", - "interlocking_depth", - "interlocking_orientation", - "internal_bridge_angle", - "internal_bridge_density", - "internal_bridge_flow", - "internal_bridge_speed", - "internal_solid_infill_acceleration", - "internal_solid_infill_flow_ratio", - "internal_solid_infill_line_width", - "internal_solid_infill_pattern", - "internal_solid_infill_speed", - "ironing_angle", - "ironing_angle_fixed", - "ironing_expansion", - "ironing_flow", - "ironing_inset", - "ironing_pattern", - "ironing_spacing", - "ironing_speed", - "ironing_type", - "is_infill_first", - "lateral_lattice_angle_1", - "lateral_lattice_angle_2", - "layer_height", - "line_width", - "long_retractions_when_cut", - "machine_load_filament_time", - "machine_max_acceleration_e", - "machine_max_acceleration_extruding", - "machine_max_acceleration_retracting", - "machine_max_acceleration_travel", - "machine_max_acceleration_x", - "machine_max_acceleration_y", - "machine_max_acceleration_z", - "machine_max_jerk_e", - "machine_max_jerk_x", - "machine_max_jerk_y", - "machine_max_jerk_z", - "machine_max_junction_deviation", - "machine_max_speed_e", - "machine_max_speed_x", - "machine_max_speed_y", - "machine_max_speed_z", - "machine_min_extruding_rate", - "machine_min_travel_rate", - "machine_tool_change_time", - "machine_unload_filament_time", - "make_overhang_printable", - "make_overhang_printable_angle", - "make_overhang_printable_hole_size", - "manual_filament_change", - "master_extruder_id", - "max_bridge_length", - "max_layer_height", - "max_resonance_avoidance_speed", - "max_travel_detour_distance", - "max_volumetric_extrusion_rate_slope", - "max_volumetric_extrusion_rate_slope_segment_length", - "min_bead_width", - "min_feature_size", - "min_layer_height", - "min_length_factor", - "min_resonance_avoidance_speed", - "min_skirt_length", - "min_width_top_surface", - "minimum_sparse_infill_area", - "mmu_segmented_region_interlocking_depth", - "mmu_segmented_region_max_width", - "notes", - "nozzle_diameter", - "nozzle_flush_dataset", - "nozzle_height", - "nozzle_volume", - "nozzle_volume_type", - "only_one_wall_first_layer", - "only_one_wall_top", - "ooze_prevention", - "other_layers_print_sequence", - "other_layers_print_sequence_nums", - "other_layers_sequence_choice", - "outer_wall_acceleration", - "outer_wall_flow_ratio", - "outer_wall_jerk", - "outer_wall_line_width", - "outer_wall_speed", - "overhang_1_4_speed", - "overhang_2_4_speed", - "overhang_3_4_speed", - "overhang_4_4_speed", - "overhang_flow_ratio", - "overhang_reverse", - "overhang_reverse_internal_only", - "overhang_reverse_threshold", - "parking_pos_retraction", - "physical_extruder_map", - "post_process", - "precise_outer_wall", - "precise_z_height", - "preheat_steps", - "preheat_time", - "prime_tower_brim_width", - "prime_tower_enable_framework", - "prime_tower_flat_ironing", - "prime_tower_infill_gap", - "prime_tower_skip_points", - "prime_tower_width", - "prime_volume", - "print_compatible_printers", - "print_extruder_id", - "print_extruder_variant", - "print_flow_ratio", - "print_order", - "print_sequence", - "print_settings_id", - "printer_extruder_id", - "printer_extruder_variant", - "printer_model", - "printer_settings_id", - "printer_variant", - "process_change_extrusion_role_gcode", - "purge_in_prime_tower", - "raft_contact_distance", - "raft_expansion", - "raft_first_layer_density", - "raft_first_layer_expansion", - "raft_layers", - "reduce_crossing_wall", - "reduce_infill_retraction", - "resolution", - "resonance_avoidance", - "retract_before_wipe", - "retract_length_toolchange", - "retract_lift_above", - "retract_lift_below", - "retract_lift_enforce", - "retract_restart_extra", - "retract_restart_extra_toolchange", - "retract_when_changing_layer", - "retraction_distances_when_cut", - "retraction_length", - "retraction_minimum_travel", - "retraction_speed", - "role_based_wipe_speed", - "scarf_angle_threshold", - "scarf_joint_flow_ratio", - "scarf_joint_speed", - "scarf_overhang_threshold", - "seam_gap", - "seam_position", - "seam_slope_conditional", - "seam_slope_entire_loop", - "seam_slope_inner_walls", - "seam_slope_min_length", - "seam_slope_start_height", - "seam_slope_steps", - "seam_slope_type", - "set_other_flow_ratios", - "silent_mode", - "single_extruder_multi_material", - "single_extruder_multi_material_priming", - "single_loop_draft_shield", - "skeleton_infill_density", - "skeleton_infill_line_width", - "skin_infill_density", - "skin_infill_depth", - "skin_infill_line_width", - "skirt_distance", - "skirt_height", - "skirt_loops", - "skirt_speed", - "skirt_start_angle", - "skirt_type", - "slice_closing_radius", - "slicing_mode", - "slow_down_layers", - "slowdown_for_curled_perimeters", - "small_area_infill_flow_compensation", - "small_area_infill_flow_compensation_model", - "small_perimeter_speed", - "small_perimeter_threshold", - "solid_infill_direction", - "solid_infill_filament", - "solid_infill_rotate_template", - "spaghetti_detector", - "sparse_infill_acceleration", - "sparse_infill_density", - "sparse_infill_filament", - "sparse_infill_flow_ratio", - "sparse_infill_line_width", - "sparse_infill_pattern", - "sparse_infill_rotate_template", - "sparse_infill_speed", - "spiral_finishing_flow_ratio", - "spiral_mode", - "spiral_mode_max_xy_smoothing", - "spiral_mode_smooth", - "spiral_starting_flow_ratio", - "staggered_inner_seams", - "standby_temperature_delta", - "start_end_points", - "support_angle", - "support_base_pattern", - "support_base_pattern_spacing", - "support_bottom_interface_spacing", - "support_bottom_z_distance", - "support_critical_regions_only", - "support_expansion", - "support_filament", - "support_flow_ratio", - "support_interface_bottom_layers", - "support_interface_filament", - "support_interface_flow_ratio", - "support_interface_loop_pattern", - "support_interface_not_for_body", - "support_interface_pattern", - "support_interface_spacing", - "support_interface_speed", - "support_interface_top_layers", - "support_ironing", - "support_ironing_flow", - "support_ironing_pattern", - "support_ironing_spacing", - "support_line_width", - "support_object_first_layer_gap", - "support_object_skip_flush", - "support_object_xy_distance", - "support_on_build_plate_only", - "support_remove_small_overhang", - "support_speed", - "support_style", - "support_threshold_angle", - "support_threshold_overlap", - "support_top_z_distance", - "support_type", - "symmetric_infill_y_axis", - "thick_bridges", - "thick_internal_bridges", - "thumbnails_format", - "timelapse_type", - "tool_change_on_wipe_tower", - "top_bottom_infill_wall_overlap", - "top_shell_layers", - "top_shell_thickness", - "top_solid_infill_flow_ratio", - "top_surface_acceleration", - "top_surface_density", - "top_surface_jerk", - "top_surface_line_width", - "top_surface_pattern", - "top_surface_speed", - "travel_acceleration", - "travel_jerk", - "travel_slope", - "travel_speed", - "travel_speed_z", - "tree_support_angle_slow", - "tree_support_auto_brim", - "tree_support_branch_angle", - "tree_support_branch_angle_organic", - "tree_support_branch_diameter", - "tree_support_branch_diameter_angle", - "tree_support_branch_diameter_organic", - "tree_support_branch_distance", - "tree_support_branch_distance_organic", - "tree_support_brim_width", - "tree_support_tip_diameter", - "tree_support_top_rate", - "tree_support_wall_count", - "tree_support_with_infill", - "upward_compatible_machine", - "volumetric_speed_coefficients", - "wall_direction", - "wall_distribution_count", - "wall_filament", - "wall_generator", - "wall_loops", - "wall_maximum_deviation", - "wall_maximum_resolution", - "wall_sequence", - "wall_transition_angle", - "wall_transition_filter_deviation", - "wall_transition_length", - "wipe", - "wipe_before_external_loop", - "wipe_distance", - "wipe_on_loops", - "wipe_speed", - "wipe_tower_bridging", - "wipe_tower_cone_angle", - "wipe_tower_extra_flow", - "wipe_tower_extra_rib_length", - "wipe_tower_extra_spacing", - "wipe_tower_filament", - "wipe_tower_fillet_wall", - "wipe_tower_max_purge_speed", - "wipe_tower_no_sparse_layers", - "wipe_tower_rib_width", - "wipe_tower_rotation_angle", - "wipe_tower_type", - "wipe_tower_wall_type", - "wipe_tower_x", - "wipe_tower_y", - "wiping_volumes_extruders", - "wrapping_detection_layers", - "wrapping_exclude_area", - "xy_contour_compensation", - "xy_hole_compensation", - "z_hop", - "z_hop_types", - "zaa_dont_alternate_fill_direction", - "zaa_enabled", - "zaa_min_z", - "zaa_minimize_perimeter_height", -}; - -static const std::vector s_Preset_filament_options = { - "activate_air_filtration", - "activate_air_filtration_during_print", - "activate_air_filtration_on_completion", - "activate_chamber_temp_control", - "adaptive_pressure_advance", - "adaptive_pressure_advance_bridges", - "adaptive_pressure_advance_model", - "adaptive_pressure_advance_overhangs", - "additional_cooling_fan_speed", - "additional_fan_full_speed_layer", - "chamber_temperature", - "close_additional_fan_first_x_layers", - "close_fan_the_first_x_layers", - "compatible_printers", - "compatible_printers_condition", - "compatible_prints", - "compatible_prints_condition", - "complete_print_exhaust_fan_speed", - "cool_plate_temp", - "cool_plate_temp_initial_layer", - "default_filament_colour", - "dont_slow_down_outer_wall", - "during_print_exhaust_fan_speed", - "enable_overhang_bridge_fan", - "enable_pressure_advance", - "eng_plate_temp", - "eng_plate_temp_initial_layer", - "fan_cooling_layer_time", - "fan_max_speed", - "fan_min_speed", - "filament_adaptive_volumetric_speed", - "filament_adhesiveness_category", - "filament_change_extrusion_role_gcode", - "filament_change_length", - "filament_colour", - "filament_cooling_before_tower", - "filament_cooling_final_speed", - "filament_cooling_initial_speed", - "filament_cooling_moves", - "filament_cost", - "filament_density", - "filament_deretraction_speed", - "filament_diameter", - "filament_end_gcode", - "filament_extruder_variant", - "filament_flow_ratio", - "filament_flush_temp", - "filament_flush_volumetric_speed", - "filament_ironing_flow", - "filament_ironing_inset", - "filament_ironing_spacing", - "filament_ironing_speed", - "filament_is_support", - "filament_loading_speed", - "filament_loading_speed_start", - "filament_long_retractions_when_cut", - "filament_max_volumetric_speed", - "filament_minimal_purge_on_wipe_tower", - "filament_multitool_ramming", - "filament_multitool_ramming_flow", - "filament_multitool_ramming_volume", - "filament_notes", - "filament_printable", - "filament_ramming_parameters", - "filament_retract_before_wipe", - "filament_retract_lift_above", - "filament_retract_lift_below", - "filament_retract_lift_enforce", - "filament_retract_restart_extra", - "filament_retract_when_changing_layer", - "filament_retraction_distances_when_cut", - "filament_retraction_length", - "filament_retraction_minimum_travel", - "filament_retraction_speed", - "filament_shrink", - "filament_shrinkage_compensation_z", - "filament_soluble", - "filament_stamping_distance", - "filament_stamping_loading_speed", - "filament_start_gcode", - "filament_toolchange_delay", - "filament_tower_interface_pre_extrusion_dist", - "filament_tower_interface_pre_extrusion_length", - "filament_tower_interface_print_temp", - "filament_tower_interface_purge_volume", - "filament_tower_ironing_area", - "filament_type", - "filament_unloading_speed", - "filament_unloading_speed_start", - "filament_vendor", - "filament_wipe", - "filament_wipe_distance", - "filament_z_hop", - "filament_z_hop_types", - "first_x_layer_fan_speed", - "full_fan_speed_layer", - "hot_plate_temp", - "hot_plate_temp_initial_layer", - "idle_temperature", - "inherits", - "internal_bridge_fan_speed", - "ironing_fan_speed", - "long_retractions_when_ec", - "nozzle_temperature", - "nozzle_temperature_initial_layer", - "nozzle_temperature_range_high", - "nozzle_temperature_range_low", - "overhang_fan_speed", - "overhang_fan_threshold", - "pellet_flow_coefficient", - "pressure_advance", - "reduce_fan_stop_start_freq", - "required_nozzle_HRC", - "retraction_distances_when_ec", - "slow_down_for_layer_cooling", - "slow_down_layer_time", - "slow_down_min_speed", - "supertack_plate_temp", - "supertack_plate_temp_initial_layer", - "support_material_interface_fan_speed", - "temperature_vitrification", - "textured_cool_plate_temp", - "textured_cool_plate_temp_initial_layer", - "textured_plate_temp", - "textured_plate_temp_initial_layer", - "volumetric_speed_coefficients", -}; - -static const std::vector s_Preset_printer_options = { - "adaptive_bed_mesh_margin", - "auxiliary_fan", - "bbl_use_printhost", - "bed_custom_model", - "bed_custom_texture", - "bed_exclude_area", - "bed_mesh_max", - "bed_mesh_min", - "bed_mesh_probe_distance", - "bed_temperature_formula", - "before_layer_change_gcode", - "best_object_pos", - "change_extrusion_role_gcode", - "change_filament_gcode", - "cooling_tube_length", - "cooling_tube_retraction", - "default_bed_type", - "default_nozzle_volume_type", - "default_print_profile", - "disable_m73", - "emit_machine_limits_to_gcode", - "enable_filament_ramming", - "enable_long_retraction_when_cut", - "enable_power_loss_recovery", - "extra_loading_move", - "extruder_clearance_height_to_lid", - "extruder_clearance_height_to_rod", - "extruder_clearance_radius", - "extruder_printable_area", - "extruder_printable_height", - "extruder_type", - "extruder_variant_list", - "fan_kickstart", - "fan_speedup_overhangs", - "fan_speedup_time", - "file_start_gcode", - "gcode_flavor", - "grab_length", - "head_wrap_detect_zone", - "high_current_on_filament_swap", - "host_type", - "inherits", - "layer_change_gcode", - "long_retractions_when_cut", - "machine_end_gcode", - "machine_load_filament_time", - "machine_pause_gcode", - "machine_start_gcode", - "machine_tool_change_time", - "machine_unload_filament_time", - "manual_filament_change", - "master_extruder_id", - "nozzle_flush_dataset", - "nozzle_height", - "nozzle_hrc", - "nozzle_type", - "nozzle_volume", - "parking_pos_retraction", - "part_cooling_fan_min_pwm", - "pellet_modded_printer", - "physical_extruder_map", - "preferred_orientation", - "print_host", - "print_host_webui", - "printable_area", - "printable_height", - "printer_agent", - "printer_extruder_id", - "printer_extruder_variant", - "printer_model", - "printer_notes", - "printer_structure", - "printer_technology", - "printer_variant", - "printhost_apikey", - "printhost_authorization_type", - "printhost_cafile", - "printhost_password", - "printhost_port", - "printhost_ssl_ignore_revoke", - "printhost_user", - "printing_by_object_gcode", - "purge_in_prime_tower", - "retract_lift_enforce", - "retraction_distances_when_cut", - "scan_first_layer", - "silent_mode", - "single_extruder_multi_material", - "support_air_filtration", - "support_chamber_temp_control", - "support_multi_bed_types", - "support_object_skip_flush", - "template_custom_gcode", - "thumbnails", - "thumbnails_format", - "time_cost", - "time_lapse_gcode", - "tool_change_on_wipe_tower", - "travel_slope", - "upward_compatible_machine", - "use_firmware_retraction", - "use_relative_e_distances", - "wipe_tower_type", - "wrapping_detection_gcode", - "wrapping_detection_layers", - "wrapping_exclude_area", - "z_hop_types", - "z_offset", -}; diff --git a/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp b/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp deleted file mode 100644 index 8d2b375c6c..0000000000 --- a/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp +++ /dev/null @@ -1,5716 +0,0 @@ -// ===== AUTO-GENERATED by tools/config_codegen.py ===== -// DO NOT EDIT MANUALLY. Edit .proto files and re-run codegen. - - def = this->add("filament_end_gcode", coStrings); - def->label = L("End G-code"); - def->category = L("Filament/Advanced"); - def->tooltip = L("End G-code when finishing the printing of this filament."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 120; - def->set_default_value(new ConfigOptionStrings{" "}); - - def = this->add("filament_start_gcode", coStrings); - def->label = L("Start G-code"); - def->category = L("Filament/Advanced"); - def->tooltip = L("Start G-code when starting the printing of this filament."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 12; - def->set_default_value(new ConfigOptionStrings{" "}); - - def = this->add("filament_change_extrusion_role_gcode", coStrings); - def->label = L("Change extrusion role G-code (filament)"); - def->category = L("Filament/Advanced"); - def->tooltip = L("This G-code is inserted when the extrusion role is changed for the active filament."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("enable_overhang_bridge_fan", coBools); - def->label = L("Force cooling for overhangs and bridges"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Enable this option to allow adjustment of the part cooling fan speed for specifically for overhangs, internal and external bridges. Setting the fan speed specifically for these features can improve overall print quality and reduce warping."); - def->set_default_value(new ConfigOptionBools{true}); - - def = this->add("overhang_fan_speed", coInts); - def->label = L("Overhangs and external bridges fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Use this part cooling fan speed when printing bridges or overhang walls with an overhang threshold that exceeds the value set in the 'Overhangs cooling threshold' parameter above. Increasing the cooling specifically for overhangs and bridges can improve the overall print quality of these features.\n\nPlease note, this fan speed is clamped on the lower end by the minimum fan speed threshold set above. It is also adjusted upwards up to the maximum fan speed threshold when the minimum layer time threshold is not met."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{100}); - - def = this->add("overhang_fan_threshold", coEnums); - def->label = L("Overhang cooling activation threshold"); - def->category = L("Filament/Cooling"); - def->tooltip = L("When the overhang exceeds this specified threshold, force the cooling fan to run at the 'Overhang Fan Speed' set below. This threshold is expressed as a percentage, indicating the portion of each line's width that is unsupported by the layer beneath it. Setting this value to 0% forces the cooling fan to run for all outer walls, regardless of the overhang degree."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("0%"); - def->enum_values.push_back("10%"); - def->enum_values.push_back("25%"); - def->enum_values.push_back("50%"); - def->enum_values.push_back("75%"); - def->enum_values.push_back("95%"); - def->enum_labels.push_back(L("0%")); - def->enum_labels.push_back(L("10%")); - def->enum_labels.push_back(L("25%")); - def->enum_labels.push_back(L("50%")); - def->enum_labels.push_back(L("75%")); - def->enum_labels.push_back(L("95%")); - def->set_default_value(new ConfigOptionEnumsGeneric{ (int)Overhang_threshold_bridge }); - - def = this->add("slow_down_for_layer_cooling", coBools); - def->label = L("Slow printing down for better layer cooling"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Enable this option to slow printing speed down to make the final layer time not shorter than the layer time threshold in \"Max fan speed threshold\", so that layer can be cooled for longer time. This can improve the cooling quality for needle and small details."); - def->set_default_value(new ConfigOptionBools{true}); - - def = this->add("activate_air_filtration", coBools); - def->label = L("Activate air filtration"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Activate for better air filtration. G-code command: M106 P3 S(0-255)"); - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("activate_air_filtration_during_print", coBools); - def->full_label = L("During print"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Enable this to override the fan speed set in custom G-code during print."); - def->set_default_value(new ConfigOptionBools{true}); - - def = this->add("activate_air_filtration_on_completion", coBools); - def->full_label = L("On completion"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Enable this to override the fan speed set in custom G-code after print completion."); - def->set_default_value(new ConfigOptionBools{true}); - - def = this->add("during_print_exhaust_fan_speed", coInts); - def->label = L("Fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Speed of exhaust fan during printing. This speed will override the speed in filament custom G-code."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionInts{60}); - - def = this->add("complete_print_exhaust_fan_speed", coInts); - def->label = L("Fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Speed of exhaust fan after printing completes."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionInts{80}); - - def = this->add("close_fan_the_first_x_layers", coInts); - def->label = L("No cooling for the first"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Turn off all cooling fans for the first few layers. This can be used to improve build plate adhesion."); - def->sidetext = L("layers"); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("reduce_fan_stop_start_freq", coBools); - def->label = L("Keep fan always on"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Enabling this setting means that the part cooling fan will never stop completely and will run at least at minimum speed to reduce the frequency of starting and stopping."); - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("dont_slow_down_outer_wall", coBools); - def->label = L("Don't slow down outer walls"); - def->category = L("Filament/Cooling"); - def->tooltip = L("If enabled, this setting will ensure external perimeters are not slowed down to meet the minimum layer time. This is particularly helpful in the below scenarios:\n1. To avoid changes in shine when printing glossy filaments\n2. To avoid changes in external wall speed which may create slight wall artifacts that appear like Z banding\n3. To avoid printing at speeds which cause VFAs (fine artifacts) on the external walls"); - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("fan_cooling_layer_time", coFloats); - def->label = L("Layer time"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Part cooling fan will be enabled for layers of which estimated time is shorter than this value. Fan speed is interpolated between the minimum and maximum fan speeds according to layer printing time."); - def->sidetext = L("s"); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionFloats{60.0f}); - - def = this->add("full_fan_speed_layer", coInts); - def->label = L("Full fan speed at layer"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Fan speed will be ramped up linearly from zero at layer \"close_fan_the_first_x_layers\" to maximum at layer \"full_fan_speed_layer\". \"full_fan_speed_layer\" will be ignored if lower than \"close_fan_the_first_x_layers\", in which case the fan will be running at maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1."); - def->sidetext = L("layer"); - def->min = 0; - def->max = 1000; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("support_material_interface_fan_speed", coInts); - def->label = L("Support interface fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("This part cooling fan speed is applied when printing support interfaces. Setting this parameter to a higher than regular speed reduces the layer binding strength between supports and the supported part, making them easier to separate.\nSet to -1 to disable it.\nThis setting is overridden by disable_fan_first_layers."); - def->sidetext = L("%"); - def->min = -1; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{-1}); - - def = this->add("internal_bridge_fan_speed", coInts); - def->label = L("Internal bridges fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("The part cooling fan speed used for all internal bridges. Set to -1 to use the overhang fan speed settings instead.\n\nReducing the internal bridges fan speed, compared to your regular fan speed, can help reduce part warping due to excessive cooling applied over a large surface for a prolonged period of time."); - def->sidetext = L("%"); - def->min = -1; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{-1}); - - def = this->add("ironing_fan_speed", coInts); - def->label = L("Ironing fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("This part cooling fan speed is applied when ironing. Setting this parameter to a lower than regular speed reduces possible nozzle clogging due to the low volumetric flow rate, making the interface smoother.\nSet to -1 to disable it."); - def->sidetext = L("%"); - def->min = -1; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{-1}); - - def = this->add("fan_max_speed", coFloats); - def->label = L("Fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Part cooling fan speed may be increased when auto cooling is enabled. This is the maximum speed for the part cooling fan."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionFloats{100}); - - def = this->add("fan_min_speed", coFloats); - def->label = L("Fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Minimum speed for part cooling fan."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionFloats{20}); - - def = this->add("additional_cooling_fan_speed", coInts); - def->label = L("Fan speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed during printing except the first several layers which is defined by no cooling layers.\nPlease enable auxiliary_fan in printer settings to use this feature. G-code command: M106 P2 S(0-255)"); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("slow_down_min_speed", coFloats); - def->label = L("Min print speed"); - def->category = L("Filament/Cooling"); - def->tooltip = L("The minimum print speed to which the printer slows down to maintain the minimum layer time defined above when the slowdown for better layer cooling is enabled."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{10.}); - - def = this->add("slow_down_layer_time", coFloats); - def->label = L("Layer time"); - def->category = L("Filament/Cooling"); - def->tooltip = L("The printing speed in exported G-code will be slowed down when the estimated layer time is shorter than this value in order to get better cooling for these layers."); - def->sidetext = L("s"); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionFloats{5.0f}); - - def = this->add("compatible_printers_condition", coString); - def->label = L("Condition"); - def->category = L("Filament/Dependencies"); - def->tooltip = L("A boolean expression using the configuration values of an active printer profile. If this expression evaluates to true, this profile is considered compatible with the active printer profile."); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("compatible_prints_condition", coString); - def->label = L("Condition"); - def->category = L("Filament/Dependencies"); - def->tooltip = L("A boolean expression using the configuration values of an active print profile. If this expression evaluates to true, this profile is considered compatible with the active print profile."); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("supertack_plate_temp", coInts); - def->label = L("Other layers"); - def->full_label = L("Bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Cool Plate SuperTack."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 120; - def->set_default_value(new ConfigOptionInts{35}); - - def = this->add("cool_plate_temp", coInts); - def->label = L("Other layers"); - def->full_label = L("Bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Cool Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{35}); - - def = this->add("textured_cool_plate_temp", coInts); - def->label = L("Other layers"); - def->full_label = L("Bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Textured Cool Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{40}); - - def = this->add("eng_plate_temp", coInts); - def->label = L("Other layers"); - def->full_label = L("Bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Engineering Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{45}); - - def = this->add("hot_plate_temp", coInts); - def->label = L("Other layers"); - def->full_label = L("Bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the High Temp Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{45}); - - def = this->add("textured_plate_temp", coInts); - def->label = L("Other layers"); - def->full_label = L("Bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Textured PEI Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{45}); - - def = this->add("supertack_plate_temp_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Cool Plate SuperTack."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 120; - def->set_default_value(new ConfigOptionInts{35}); - - def = this->add("cool_plate_temp_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Cool Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 120; - def->set_default_value(new ConfigOptionInts{35}); - - def = this->add("textured_cool_plate_temp_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Textured Cool Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 120; - def->set_default_value(new ConfigOptionInts{40}); - - def = this->add("eng_plate_temp_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Engineering Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{45}); - - def = this->add("hot_plate_temp_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the High Temp Plate."); - def->sidetext = L("\u2103"); - def->max = 300; - def->set_default_value(new ConfigOptionInts{45}); - - def = this->add("textured_plate_temp_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer bed temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Bed temperature of the first layer. A value of 0 means the filament does not support printing on the Textured PEI Plate."); - def->sidetext = L("\u2103"); - def->min = 0; - def->max = 300; - def->set_default_value(new ConfigOptionInts{45}); - - def = this->add("filament_flow_ratio", coFloats); - def->label = L("Flow ratio"); - def->category = L("Filament/Filament"); - def->tooltip = L("The material may have volumetric change after switching between molten and crystalline states. This setting changes all extrusion flow of this filament in G-code proportionally. The recommended value range is between 0.95 and 1.05. You may be able to tune this value to get a nice flat surface if there is slight overflow or underflow."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{1.}); - - def = this->add("enable_pressure_advance", coBools); - def->label = L("Enable pressure advance"); - def->category = L("Filament/Filament"); - def->tooltip = L("Enable pressure advance, auto calibration result will be overwritten once enabled."); - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("pressure_advance", coFloats); - def->label = L("Pressure advance"); - def->category = L("Filament/Filament"); - def->tooltip = L("Pressure advance (Klipper) AKA Linear advance factor (Marlin)."); - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.02}); - - def = this->add("adaptive_pressure_advance", coBools); - def->label = L("Enable adaptive pressure advance (beta)"); - def->category = L("Filament/Filament"); - def->tooltip = L("With increasing print speeds (and hence increasing volumetric flow through the nozzle) and increasing accelerations, it has been observed that the effective PA value typically decreases. This means that a single PA value is not always 100% optimal for all features and a compromise value is usually used that does not cause too much bulging on features with lower flow speed and accelerations while also not causing gaps on faster features.\n\nThis feature aims to address this limitation by modeling the response of your printer's extrusion system depending on the volumetric flow speed and acceleration it is printing at. Internally, it generates a fitted model that can extrapolate the needed pressure advance for any given volumetric flow speed and acceleration, which is then emitted to the printer depending on the current print conditions.\n\nWhen enabled, the pressure advance value above is overridden. However, a reasonable default value above is strongly recommended to act as a fallback and for when tool changing.\n\n"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("adaptive_pressure_advance_model", coStrings); - def->label = L("Adaptive pressure advance measurements (beta)"); - def->category = L("Filament/Filament"); - def->tooltip = L("Add sets of pressure advance (PA) values, the volumetric flow speeds and accelerations they were measured at, separated by a comma. One set of values per line. For example\n0.04,3.96,3000\n0.033,3.96,10000\n0.029,7.91,3000\n0.026,7.91,10000\n\nHow to calibrate:\n1. Run the pressure advance test for at least 3 speeds per acceleration value. It is recommended that the test is run for at least the speed of the external perimeters, the speed of the internal perimeters and the fastest feature print speed in your profile (usually its the sparse or solid infill). Then run them for the same speeds for the slowest and fastest print accelerations, and no faster than the recommended maximum acceleration as given by the Klipper input shaper\n2. Take note of the optimal PA value for each volumetric flow speed and acceleration. You can find the flow number by selecting flow from the color scheme drop down and move the horizontal slider over the PA pattern lines. The number should be visible at the bottom of the page. The ideal PA value should be decreasing the higher the volumetric flow is. If it is not, confirm that your extruder is functioning correctly. The slower and with less acceleration you print, the larger the range of acceptable PA values. If no difference is visible, use the PA value from the faster test\n3. Enter the triplets of PA values, Flow and Accelerations in the text box here and save your filament profile."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 15; - def->set_default_value(new ConfigOptionStrings{"0,0,0\n0,0,0"}); - - def = this->add("adaptive_pressure_advance_overhangs", coBools); - def->label = L("Enable adaptive pressure advance for overhangs (beta)"); - def->category = L("Filament/Filament"); - def->tooltip = L("Enable adaptive PA for overhangs as well as when flow changes within the same feature. This is an experimental option, as if the PA profile is not set accurately, it will cause uniformity issues on the external surfaces before and after overhangs.\n"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("adaptive_pressure_advance_bridges", coFloats); - def->label = L("Pressure advance for bridges"); - def->category = L("Filament/Filament"); - def->tooltip = L("Pressure advance value for bridges. Set to 0 to disable.\n\nA lower PA value when printing bridges helps reduce the appearance of slight under extrusion immediately after bridges. This is caused by the pressure drop in the nozzle when printing in the air and a lower PA helps counteract this."); - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.0}); - - def = this->add("default_filament_colour", coStrings); - def->label = L("Default color"); - def->category = L("Filament/Filament"); - def->tooltip = L("Default filament color.\nRight click to reset value to system default."); - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::color; - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("required_nozzle_HRC", coInts); - def->label = L("Required nozzle HRC"); - def->category = L("Filament/Filament"); - def->tooltip = L("Minimum HRC of nozzle required to print the filament. Zero means no checking of nozzle's HRC."); - def->min = 0; - def->max = 500; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("filament_max_volumetric_speed", coFloats); - def->label = L("Max volumetric speed"); - def->category = L("Filament/Filament"); - def->tooltip = L("This setting stands for how much volume of filament can be melted and extruded per second. Printing speed is limited by max volumetric speed, in case of too high and unreasonable speed setting. Can't be zero."); - def->sidetext = L("mm³/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{2.}); - - def = this->add("filament_diameter", coFloats); - def->label = L("Diameter"); - def->category = L("Filament/Filament"); - def->tooltip = L("Filament diameter is used to calculate extrusion in G-code, so it is important and should be accurate."); - def->sidetext = L("mm"); - def->min = 0; - def->set_default_value(new ConfigOptionFloats{1.75}); - - def = this->add("pellet_flow_coefficient", coFloats); - def->label = L("Pellet flow coefficient"); - def->category = L("Filament/Filament"); - def->tooltip = L("Pellet flow coefficient is empirically derived and allows for volume calculation for pellet printers.\n\nInternally it is converted to filament_diameter. All other volume calculations remain the same.\n\nfilament_diameter = sqrt( (4 * pellet_flow_coefficient) / PI )"); - def->min = 0; - def->set_default_value(new ConfigOptionFloats{0.4157}); - - def = this->add("filament_adaptive_volumetric_speed", coBools); - def->label = L("Adaptive volumetric speed"); - def->category = L("Filament/Filament"); - def->tooltip = L("When enabled, the extrusion flow is limited by the smaller of the fitted value (calculated from line width and layer height) and the user-defined maximum flow. When disabled, only the user-defined maximum flow is applied.\n\nNote: Experimental and incomplete feature imported from BBS. Functional for some profiles that already have the variable saved."); - def->mode = comDevelop; - def->nullable = true; - def->set_default_value(new ConfigOptionBoolsNullable{false}); - - def = this->add("filament_shrink", coPercents); - def->label = L("Shrinkage (XY)"); - def->category = L("Filament/Filament"); - def->tooltip = L("Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm). The part will be scaled in XY to compensate. For multi-material prints, ensure filament shrinkage matches across all used filaments\nBe sure to allow enough space between objects, as this compensation is done after the checks."); - def->sidetext = L("%"); - def->min = 50; - def->max = 150; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercents{100}); - - def = this->add("filament_shrinkage_compensation_z", coPercents); - def->label = L("Shrinkage (Z)"); - def->category = L("Filament/Filament"); - def->tooltip = L("Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm). The part will be scaled in Z to compensate."); - def->sidetext = L("%"); - def->min = 50; - def->max = 150; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercents{100}); - - def = this->add("filament_adhesiveness_category", coInts); - def->label = L("Adhesiveness Category"); - def->category = L("Filament/Filament"); - def->tooltip = L("Filament category."); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("filament_density", coFloats); - def->label = L("Density"); - def->category = L("Filament/Filament"); - def->tooltip = L("Filament density. For statistics only."); - def->sidetext = L("g/cm³"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("filament_type", coStrings); - def->label = L("Type"); - def->category = L("Filament/Filament"); - def->tooltip = L("The material type of filament."); - def->gui_type = ConfigOptionDef::GUIType::f_enum_open; - def->gui_flags = "show_value"; - def->set_default_value(new ConfigOptionStrings{"PLA"}); - - def = this->add("filament_soluble", coBools); - def->label = L("Soluble material"); - def->category = L("Filament/Filament"); - def->tooltip = L("Soluble material is commonly used to print supports and support interfaces."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("filament_change_length", coFloats); - def->label = L("Filament ramming length"); - def->category = L("Filament/Filament"); - def->tooltip = L("When changing the extruder, it is recommended to extrude a certain length of filament from the original extruder. This helps minimize nozzle oozing."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{10}); - - def = this->add("filament_is_support", coBools); - def->label = L("Support material"); - def->category = L("Filament/Filament"); - def->tooltip = L("Support material is commonly used to print supports and support interfaces."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("temperature_vitrification", coInts); - def->label = L("Softening temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("The material softens at this temperature, so when the bed temperature is equal to or greater than this, it's highly recommended to open the front door and/or remove the upper glass to avoid clogging."); - def->sidetext = L("\u2103"); - def->set_default_value(new ConfigOptionInts{100}); - - def = this->add("filament_cost", coFloats); - def->label = L("Price"); - def->category = L("Filament/Filament"); - def->tooltip = L("Filament price. For statistics only."); - def->sidetext = L("money/kg"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("filament_vendor", coStrings); - def->label = L("Vendor"); - def->category = L("Filament/Filament"); - def->tooltip = L("Vendor of filament. For show only."); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings{L("(Undefined)")}); - - def = this->add("nozzle_temperature_initial_layer", coInts); - def->label = L("First layer"); - def->full_label = L("First layer nozzle temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Nozzle temperature for printing the first layer when using this filament."); - def->sidetext = L("\u2103"); - def->min = 0; - def->set_default_value(new ConfigOptionInts{200}); - - def = this->add("activate_chamber_temp_control", coBools); - def->label = L("Activate temperature control"); - def->category = L("Filament/Filament"); - def->tooltip = L("Enable this option for automated chamber temperature control. This option activates the emitting of an M191 command before the \"machine_start_gcode\"\n which sets the chamber temperature and waits until it is reached. In addition, it emits an M141 command at the end of the print to turn off the chamber heater, if present.\n\nThis option relies on the firmware supporting the M191 and M141 commands either via macros or natively and is usually used when an active chamber heater is installed."); - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("chamber_temperature", coInts); - def->label = L("Chamber temperature"); - def->full_label = L("Chamber temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("For high-temperature materials like ABS, ASA, PC, and PA, a higher chamber temperature can help suppress or reduce warping and potentially lead to higher interlayer bonding strength. However, at the same time, a higher chamber temperature will reduce the efficiency of air filtration for ABS and ASA.\n\nFor PLA, PETG, TPU, PVA, and other low-temperature materials, this option should be disabled (set to 0) as the chamber temperature should be low to avoid extruder clogging caused by material softening at the heat break.\n\nIf enabled, this parameter also sets a G-code variable named chamber_temperature, which can be used to pass the desired chamber temperature to your print start macro, or a heat soak macro like this: PRINT_START (other variables) CHAMBER_TEMP=[chamber_temperature]. This may be useful if your printer does not support M141/M191 commands, or if you desire to handle heat soaking in the print start macro if no active chamber heater is installed."); - def->sidetext = L("\u2103"); - def->min = 0; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("nozzle_temperature", coInts); - def->label = L("Other layers"); - def->full_label = L("Nozzle temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Nozzle temperature for layers after the initial one."); - def->sidetext = L("\u2103"); - def->min = 0; - def->set_default_value(new ConfigOptionInts{200}); - - def = this->add("nozzle_temperature_range_low", coInts); - def->label = L("Min"); - def->category = L("Filament/Filament"); - def->sidetext = L("\u2103"); - def->min = 0; - def->set_default_value(new ConfigOptionInts{190}); - - def = this->add("nozzle_temperature_range_high", coInts); - def->label = L("Max"); - def->category = L("Filament/Filament"); - def->sidetext = L("\u2103"); - def->min = 0; - def->set_default_value(new ConfigOptionInts{240}); - - def = this->add("idle_temperature", coInts); - def->label = L("Idle temperature"); - def->category = L("Filament/Filament"); - def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups. This is only used when 'Ooze prevention' is active in Print Settings. Set to 0 to disable."); - def->sidetext = L("\u2103"); - def->min = 0; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("filament_loading_speed", coFloats); - def->label = L("Loading speed"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Speed used for loading the filament on the wipe tower."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{28.}); - - def = this->add("filament_loading_speed_start", coFloats); - def->label = L("Loading speed at the start"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Speed used at the very beginning of loading phase."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{3.}); - - def = this->add("filament_unloading_speed", coFloats); - def->label = L("Unloading speed"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect initial part of unloading just after ramming)."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{90.}); - - def = this->add("filament_unloading_speed_start", coFloats); - def->label = L("Unloading speed at the start"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{100.}); - - def = this->add("filament_toolchange_delay", coFloats); - def->label = L("Delay after unloading"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Time to wait after the filament is unloaded. May help to get reliable tool changes with flexible materials that may need more time to shrink to original dimensions."); - def->sidetext = L("s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("filament_cooling_moves", coInts); - def->label = L("Number of cooling moves"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Filament is cooled by being moved back and forth in the cooling tubes. Specify desired number of these moves."); - def->min = 0; - def->max = 20; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{4}); - - def = this->add("filament_stamping_loading_speed", coFloats); - def->label = L("Stamping loading speed"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Speed used for stamping."); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("filament_stamping_distance", coFloats); - def->label = L("Stamping distance measured from the center of the cooling tube"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("If set to non-zero value, filament is moved toward the nozzle between the individual cooling moves (\"stamping\"). This option configures how long this movement should be before the filament is retracted again."); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("filament_cooling_initial_speed", coFloats); - def->label = L("Speed of the first cooling move"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{2.2}); - - def = this->add("filament_minimal_purge_on_wipe_tower", coFloats); - def->label = L("Minimal purge on wipe tower"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside the nozzle may not be known, and the filament pressure is likely not yet stable. Before purging the print head into an infill or a sacrificial object, Orca Slicer will always prime this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably."); - def->sidetext = L("mm³"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{15.}); - - def = this->add("filament_tower_interface_pre_extrusion_dist", coFloats); - def->label = L("Interface layer pre-extrusion distance"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Pre-extrusion distance for prime tower interface layer (where different materials meet)."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{10.}); - - def = this->add("filament_tower_interface_pre_extrusion_length", coFloats); - def->label = L("Interface layer pre-extrusion length"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Pre-extrusion length for prime tower interface layer (where different materials meet)."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("filament_tower_ironing_area", coFloats); - def->label = L("Tower ironing area"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Ironing area for prime tower interface layer (where different materials meet)."); - def->sidetext = L("mm²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{4.}); - - def = this->add("filament_tower_interface_purge_volume", coFloats); - def->label = L("Interface layer purge length"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Purge length for prime tower interface layer (where different materials meet)."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{20.}); - - def = this->add("filament_tower_interface_print_temp", coInts); - def->label = L("Interface layer print temperature"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Print temperature for prime tower interface layer (where different materials meet). If set to -1, use max recommended nozzle temperature."); - def->sidetext = L("\u2103"); - def->min = -1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInts{-1}); - - def = this->add("filament_cooling_final_speed", coFloats); - def->label = L("Speed of the last cooling move"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Cooling moves are gradually accelerating towards this speed."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{3.4}); - - def = this->add("filament_multitool_ramming", coBools); - def->label = L("Enable ramming for multi-tool setups"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Perform ramming when using multi-tool printer (i.e. when the 'Single Extruder Multimaterial' in Printer Settings is unchecked). When checked, a small amount of filament is rapidly extruded on the wipe tower just before the tool change. This option is only used when the wipe tower is enabled."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("filament_multitool_ramming_volume", coFloats); - def->label = L("Multi-tool ramming volume"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("The volume to be rammed before the tool change."); - def->sidetext = L("mm³"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{10.}); - - def = this->add("filament_multitool_ramming_flow", coFloats); - def->label = L("Multi-tool ramming flow"); - def->category = L("Filament/Multimaterial"); - def->tooltip = L("Flow used for ramming the filament before the tool change."); - def->sidetext = L("mm³/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{10.}); - - def = this->add("long_retractions_when_ec", coBools); - def->label = L("Long retraction when extruder change"); - def->category = L("Filament/Multimaterial"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionBoolsNullable{false}); - - def = this->add("retraction_distances_when_ec", coFloats); - def->label = L("Retraction distance when extruder change"); - def->category = L("Filament/Multimaterial"); - def->sidetext = L("mm"); - def->min = 0; - def->max = 10; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{10}); - - def = this->add("filament_notes", coStrings); - def->label = L("Filament notes"); - def->category = L("Filament/Notes"); - def->tooltip = L("You can put your notes regarding the filament here."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 13; - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("filament_ironing_flow", coPercents); - def->label = L("Ironing flow"); - def->category = L("Quality"); - def->tooltip = L("Filament-specific override for ironing flow. This allows you to customize the ironing flow for each filament type. Too high value results in overextrusion on the surface."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionPercentsNullable{ConfigOptionPercentsNullable::nil_value()}); - - def = this->add("filament_ironing_spacing", coFloats); - def->label = L("Ironing line spacing"); - def->category = L("Quality"); - def->tooltip = L("Filament-specific override for ironing line spacing. This allows you to customize the spacing between ironing lines for each filament type."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 1; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{ConfigOptionFloatsNullable::nil_value()}); - - def = this->add("filament_ironing_inset", coFloats); - def->label = L("Ironing inset"); - def->category = L("Quality"); - def->tooltip = L("Filament-specific override for ironing inset. This allows you to customize the distance to keep from the edges when ironing for each filament type."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{ConfigOptionFloatsNullable::nil_value()}); - - def = this->add("filament_ironing_speed", coFloats); - def->label = L("Ironing speed"); - def->category = L("Speed"); - def->tooltip = L("Filament-specific override for ironing speed. This allows you to customize the print speed of ironing lines for each filament type."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{ConfigOptionFloatsNullable::nil_value()}); - - def = this->add("filament_flush_volumetric_speed", coFloats); - def->label = L("Flush volumetric speed"); - def->tooltip = L("Volumetric speed when flushing filament. 0 indicates the max volumetric speed."); - def->sidetext = L("mm³/s"); - def->min = 0; - def->max = 200; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0}); - - def = this->add("filament_cooling_before_tower", coFloats); - def->label = L("Wipe tower cooling"); - def->tooltip = L("Temperature drop before entering filament tower"); - def->sidetext = L("\u2103"); - def->mode = comDevelop; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{10.}); - - def = this->add("filament_printable", coInts); - def->label = L("Filament printable"); - def->tooltip = L("The filament is printable in extruder."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInts{3}); - - def = this->add("close_additional_fan_first_x_layers", coInts); - def->label = L("For the first"); - def->tooltip = L("Set special auxiliary cooling fan for the first certain layers."); - def->sidetext = L("layers"); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("additional_fan_full_speed_layer", coInts); - def->label = L("Full fan speed at layer"); - def->tooltip = L("Auxiliary fan speed will be ramped up linearly from layer \"For the first\" to maximum at layer \"Full fan speed at layer\".\n\"Full fan speed at layer\" will be ignored if lower than \"For the first\", in which case the fan will run at maximum allowed speed at layer \"For the first\" + 1."); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("first_x_layer_fan_speed", coFloats); - def->label = L("Fan speed"); - def->tooltip = L("Special auxiliary cooling fan speed, effective only for the first x layers."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionFloats{0}); - - def = this->add("filament_extruder_variant", coStrings); - def->label = L("Filament's extruder variant"); - def->tooltip = L("Filament's extruder variant."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); - - def = this->add("filament_retraction_length", coFloats); - def->label = L("Length"); - def->full_label = L("Retraction Length"); - def->tooltip = L("Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction."); - def->sidetext = L("mm"); - def->mode = comSimple; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0.8}); - - def = this->add("filament_z_hop", coFloats); - def->label = L("Z-hop height"); - def->tooltip = L("Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. It prevents nozzle from hitting the print when travel move. Using spiral lines to lift Z can prevent stringing."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 5; - def->mode = comSimple; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0.4}); - - def = this->add("filament_z_hop_types", coEnums); - def->label = L("Z-hop type"); - def->tooltip = L("Type of Z-hop."); - def->mode = comAdvanced; - def->nullable = true; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Auto Lift"); - def->enum_values.push_back("Normal Lift"); - def->enum_values.push_back("Slope Lift"); - def->enum_values.push_back("Spiral Lift"); - def->enum_labels.push_back(L("Auto")); - def->enum_labels.push_back(L("Normal")); - def->enum_labels.push_back(L("Slope")); - def->enum_labels.push_back(L("Spiral")); - def->set_default_value(new ConfigOptionEnumsGenericNullable{ ZHopType::zhtSlope }); - - def = this->add("filament_retract_lift_above", coFloats); - def->label = L("Only lift Z above"); - def->tooltip = L("If you set this to a positive value, Z lift will only take place above the specified absolute Z."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0.}); - - def = this->add("filament_retract_lift_below", coFloats); - def->label = L("Only lift Z below"); - def->tooltip = L("If you set this to a positive value, Z lift will only take place below the specified absolute Z."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0.}); - - def = this->add("filament_retract_lift_enforce", coEnums); - def->label = L("On surfaces"); - def->tooltip = L("Enforce Z-Hop behavior. This setting is impacted by the above settings (Only lift Z above/below)."); - def->mode = comAdvanced; - def->nullable = true; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("All Surfaces"); - def->enum_values.push_back("Top Only"); - def->enum_values.push_back("Bottom Only"); - def->enum_values.push_back("Top and Bottom"); - def->enum_labels.push_back(L("All Surfaces")); - def->enum_labels.push_back(L("Top Only")); - def->enum_labels.push_back(L("Bottom Only")); - def->enum_labels.push_back(L("Top and Bottom")); - def->set_default_value(new ConfigOptionEnumsGenericNullable{ RetractLiftEnforceType ::rletAllSurfaces }); - - def = this->add("filament_retraction_speed", coFloats); - def->label = L("Retraction Speed"); - def->full_label = L("Retraction Speed"); - def->tooltip = L("Speed for retracting filament from the nozzle."); - def->sidetext = L("mm/s"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{30.}); - - def = this->add("filament_deretraction_speed", coFloats); - def->label = L("De-retraction Speed"); - def->full_label = L("De-retraction Speed"); - def->tooltip = L("Speed for reloading filament into the nozzle. Zero means same speed of retraction."); - def->sidetext = L("mm/s"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0.}); - - def = this->add("filament_retract_restart_extra", coFloats); - def->label = L("Extra length on restart"); - def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{0.}); - - def = this->add("filament_retraction_minimum_travel", coFloats); - def->label = L("Travel distance threshold"); - def->tooltip = L("Only trigger retraction when the travel distance is longer than this threshold."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{2.}); - - def = this->add("filament_wipe_distance", coFloats); - def->label = L("Wipe Distance"); - def->tooltip = L("Describe how long the nozzle will move along the last path when retracting."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{1.}); - - def = this->add("filament_retract_when_changing_layer", coBools); - def->label = L("Retract when change layer"); - def->tooltip = L("Force a retraction when changes layer."); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionBoolsNullable{false}); - - def = this->add("filament_wipe", coBools); - def->label = L("Wipe while retracting"); - def->tooltip = L("Move nozzle along the last extrusion path when retracting to clean any leaked material on the nozzle. This can minimize blobs when printing a new part after traveling."); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionBoolsNullable{false}); - - def = this->add("filament_retract_before_wipe", coPercents); - def->label = L("Retract amount before wipe"); - def->tooltip = L("The length of fast retraction before wipe, relative to retraction length."); - def->sidetext = L("%"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionPercentsNullable{100}); - - def = this->add("filament_long_retractions_when_cut", coBools); - def->label = L("Long retraction when cut (beta)"); - def->tooltip = L("Experimental feature: Retracting and cutting off the filament at a longer distance during changes to minimize purge. While this reduces flush significantly, it may also raise the risk of nozzle clogs or other printing problems."); - def->mode = comSimple; - def->nullable = true; - def->set_default_value(new ConfigOptionBoolsNullable{false}); - - def = this->add("filament_retraction_distances_when_cut", coFloats); - def->label = L("Retraction distance when cut"); - def->tooltip = L("Experimental feature: Retraction length before cutting off during filament change."); - def->min = 10; - def->max = 18; - def->mode = comSimple; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{18}); - - def = this->add("extruder", coInt); - def->label = L("Extruder"); - def->category = L("Extruders"); - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->enum_labels.push_back(L("default")); - def->enum_labels.push_back(L("1")); - def->enum_labels.push_back(L("2")); - def->enum_labels.push_back(L("3")); - def->enum_labels.push_back(L("4")); - def->enum_labels.push_back(L("5")); - - def = this->add("has_scarf_joint_seam", coBool); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("machine_max_junction_deviation", coFloats); - def->full_label = L("Maximum Junction Deviation"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum junction deviation (M205 J, only apply if JD > 0 for Marlin Firmware\nIf your Marlin 2 printer uses Classic Jerk set this value to 0.)"); - def->sidetext = L("mm"); - def->min = 0; - def->max = 0.3; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.01f}); - - def = this->add("machine_min_extruding_rate", coFloats); - def->full_label = L("Minimum speed for extruding"); - def->category = L("Machine limits"); - def->tooltip = L("Minimum speed for extruding (M205 S)"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloats{0., 0.}); - - def = this->add("machine_min_travel_rate", coFloats); - def->full_label = L("Minimum travel speed"); - def->category = L("Machine limits"); - def->tooltip = L("Minimum travel speed (M205 T)"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloats{0., 0.}); - - def = this->add("machine_max_acceleration_extruding", coFloats); - def->full_label = L("Maximum acceleration for extruding"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration for extruding (M204 P)"); - def->sidetext = L("mm/s²"); - def->min = 0; - def->set_default_value(new ConfigOptionFloats{1500., 1250.}); - - def = this->add("machine_max_acceleration_retracting", coFloats); - def->full_label = L("Maximum acceleration for retracting"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration for retracting (M204 R)"); - def->sidetext = L("mm/s²"); - def->min = 0; - def->set_default_value(new ConfigOptionFloats{1500., 1250.}); - - def = this->add("machine_max_acceleration_travel", coFloats); - def->full_label = L("Maximum acceleration for travel"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration for travel (M204 T), it only applies to Marlin 2."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0., 0.}); - - def = this->add("machine_max_speed_x", coFloats); - def->full_label = L("Maximum speed X"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum speed of X axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{500., 200.}); - - def = this->add("machine_max_acceleration_x", coFloats); - def->full_label = L("Maximum acceleration X"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration of the X axis"); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{1000., 1000.}); - - def = this->add("machine_max_jerk_x", coFloats); - def->full_label = L("Maximum jerk X"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum jerk of the X axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{10., 10.}); - - def = this->add("machine_max_speed_y", coFloats); - def->full_label = L("Maximum speed Y"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum speed of Y axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{500., 200.}); - - def = this->add("machine_max_acceleration_y", coFloats); - def->full_label = L("Maximum acceleration Y"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration of the Y axis"); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{1000., 1000.}); - - def = this->add("machine_max_jerk_y", coFloats); - def->full_label = L("Maximum jerk Y"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum jerk of the Y axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{10., 10.}); - - def = this->add("machine_max_speed_z", coFloats); - def->full_label = L("Maximum speed Z"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum speed of Z axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{12., 12.}); - - def = this->add("machine_max_acceleration_z", coFloats); - def->full_label = L("Maximum acceleration Z"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration of the Z axis"); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{500., 200.}); - - def = this->add("machine_max_jerk_z", coFloats); - def->full_label = L("Maximum jerk Z"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum jerk of the Z axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{0.2, 0.4}); - - def = this->add("machine_max_speed_e", coFloats); - def->full_label = L("Maximum speed E"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum speed of E axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{120., 120.}); - - def = this->add("machine_max_acceleration_e", coFloats); - def->full_label = L("Maximum acceleration E"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum acceleration of the E axis"); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{5000., 5000.}); - - def = this->add("machine_max_jerk_e", coFloats); - def->full_label = L("Maximum jerk E"); - def->category = L("Machine limits"); - def->tooltip = L("Maximum jerk of the E axis"); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comSimple; - def->set_default_value(new ConfigOptionFloats{2.5, 2.5}); - - def = this->add("emit_machine_limits_to_gcode", coBool); - def->label = L("Emit limits to G-code"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("If enabled, the machine limits will be emitted to G-code file.\nThis option will be ignored if the G-code flavor is set to Klipper."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("resonance_avoidance", coBool); - def->label = L("Resonance avoidance"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("By reducing the speed of the outer wall to avoid the resonance zone of the printer, ringing on the surface of the model are avoided.\nPlease turn this option off when testing ringing."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("min_resonance_avoidance_speed", coFloat); - def->label = L("Min"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Minimum speed of resonance avoidance."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(70)); - - def = this->add("max_resonance_avoidance_speed", coFloat); - def->label = L("Max"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Maximum speed of resonance avoidance."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(120)); - - def = this->add("input_shaping_emit", coBool); - def->label = L("Emit input shaping"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Override firmware input shaping settings.\nIf disabled, firmware settings are used."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("input_shaping_type", coEnum); - def->label = L("Input shaper type"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Choose the input shaper algorithm.\nDefault uses the firmware default settings.\nDisable turns off input shaping in the firmware."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->set_default_value(new ConfigOptionEnum(InputShaperType::Default)); - - def = this->add("input_shaping_freq_x", coFloat); - def->label = L("X"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Resonant frequency for the X axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal."); - def->sidetext = L("Hz"); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("input_shaping_freq_y", coFloat); - def->label = L("Y"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Resonant frequency for the Y axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type."); - def->sidetext = L("Hz"); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("input_shaping_damp_x", coFloat); - def->label = L("X"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Damping ratio for the X axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal."); - def->min = 0; - def->max = 1; - def->set_default_value(new ConfigOptionFloat(0.1)); - - def = this->add("input_shaping_damp_y", coFloat); - def->label = L("Y"); - def->category = L("PrinterKinematics/Motion ability"); - def->tooltip = L("Damping ratio for the Y axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type."); - def->min = 0; - def->max = 1; - def->set_default_value(new ConfigOptionFloat(0.1)); - - def = this->add("brim_width", coFloat); - def->label = L("Brim width"); - def->category = L("Print/Others"); - def->tooltip = L("Distance from model to the outermost brim line."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("brim_type", coEnum); - def->label = L("Brim type"); - def->category = L("Print/Others"); - def->tooltip = L("This controls the generation of the brim at outer and/or inner side of models. Auto means the brim width is analyzed and calculated automatically."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("auto_brim"); - def->enum_values.push_back("brim_ears"); - def->enum_values.push_back("painted"); - def->enum_values.push_back("outer_only"); - def->enum_values.push_back("inner_only"); - def->enum_values.push_back("outer_and_inner"); - def->enum_values.push_back("no_brim"); - def->enum_labels.push_back(L("Auto")); - def->enum_labels.push_back(L("Mouse ear")); - def->enum_labels.push_back(L("Painted")); - def->enum_labels.push_back(L("Outer brim only")); - def->enum_labels.push_back(L("Inner brim only")); - def->enum_labels.push_back(L("Outer and inner brim")); - def->enum_labels.push_back(L("No-brim")); - def->set_default_value(new ConfigOptionEnum(btAutoBrim)); - - def = this->add("brim_object_gap", coFloat); - def->label = L("Brim-object gap"); - def->category = L("Print/Others"); - def->tooltip = L("A gap between innermost brim line and object can make brim be removed more easily."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("brim_flow_ratio", coFloat); - def->label = L("Brim flow ratio"); - def->category = L("Print/Others"); - def->tooltip = L("This factor affects the amount of material for brims.\n\nThe actual brim flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.\n\nNote: The resulting value will not be affected by the first-layer flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("brim_use_efc_outline", coBool); - def->label = L("Brim follows compensated outline"); - def->category = L("Print/Others"); - def->tooltip = L("When enabled, the brim is aligned with the first-layer perimeter geometry after Elephant Foot Compensation is applied.\nThis option is intended for cases where Elephant Foot Compensation significantly alters the first-layer footprint.\n\nIf your current setup already works well, enabling it may be unnecessary and can cause the brim to fuse with upper layers."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("combine_brims", coBool); - def->label = L("Combine brims"); - def->category = L("Print/Others"); - def->tooltip = L("Combine multiple brims into one when they are close to each other. This can improve brim adhesion."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("brim_ears_max_angle", coFloat); - def->label = L("Brim ear max angle"); - def->category = L("Print/Others"); - def->tooltip = L("Maximum angle to let a brim ear appear.\nIf set to 0, no brim will be created.\nIf set to ~180, brim will be created on everything but straight sections."); - def->sidetext = L("°"); - def->min = 0; - def->max = 180; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(125)); - - def = this->add("brim_ears_detection_length", coFloat); - def->label = L("Brim ear detection radius"); - def->category = L("Print/Others"); - def->tooltip = L("The geometry will be decimated before detecting sharp angles. This parameter indicates the minimum length of the deviation for the decimation.\n0 to deactivate."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("print_sequence", coEnum); - def->label = L("Print sequence"); - def->category = L("Print/Others"); - def->tooltip = L("Print sequence, layer by layer or object by object."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("by layer"); - def->enum_values.push_back("by object"); - def->enum_labels.push_back(L("By layer")); - def->enum_labels.push_back(L("By object")); - def->set_default_value(new ConfigOptionEnum(PrintSequence::ByLayer)); - - def = this->add("print_order", coEnum); - def->label = L("Intra-layer order"); - def->category = L("Print/Others"); - def->tooltip = L("Print order within a single layer."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("default"); - def->enum_values.push_back("as_obj_list"); - def->enum_labels.push_back(L("Default")); - def->enum_labels.push_back(L("As object list")); - def->set_default_value(new ConfigOptionEnum(PrintOrder::Default)); - - def = this->add("fuzzy_skin", coEnum); - def->label = L("Fuzzy Skin"); - def->category = L("Print/Others"); - def->tooltip = L("Randomly jitter while printing the wall, so that the surface has a rough look. This setting controls the fuzzy position."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("none"); - def->enum_values.push_back("external"); - def->enum_values.push_back("hole"); - def->enum_values.push_back("all"); - def->enum_values.push_back("allwalls"); - def->enum_values.push_back("disabled_fuzzy"); - def->enum_labels.push_back(L("Painted only")); - def->enum_labels.push_back(L("Contour")); - def->enum_labels.push_back(L("Hole")); - def->enum_labels.push_back(L("Contour and hole")); - def->enum_labels.push_back(L("All walls")); - def->enum_labels.push_back(L("Disabled")); - def->set_default_value(new ConfigOptionEnum(FuzzySkinType::Disabled_fuzzy)); - - def = this->add("fuzzy_skin_thickness", coFloat); - def->label = L("Fuzzy skin thickness"); - def->category = L("Print/Others"); - def->tooltip = L("The width within which to jitter. It's advised to be below outer wall line width."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 2; - def->set_default_value(new ConfigOptionFloat(0.2)); - - def = this->add("fuzzy_skin_point_distance", coFloat); - def->label = L("Fuzzy skin point distance"); - def->category = L("Print/Others"); - def->tooltip = L("The average distance between the random points introduced on each line segment."); - def->sidetext = L("mm"); - def->max = 5; - def->set_default_value(new ConfigOptionFloat(0.3f)); - - def = this->add("fuzzy_skin_first_layer", coBool); - def->label = L("Apply fuzzy skin to first layer"); - def->category = L("Print/Others"); - def->tooltip = L("Whether to apply fuzzy skin on the first layer."); - def->set_default_value(new ConfigOptionBool(0)); - - def = this->add("fuzzy_skin_mode", coEnum); - def->label = L("Fuzzy skin generator mode"); - def->category = L("Print/Others"); - def->tooltip = L("Fuzzy skin generation mode. Works only with Arachne!\nDisplacement: Сlassic mode when the pattern is formed by shifting the nozzle sideways from the original path.\nExtrusion: The mode when the pattern formed by the amount of extruded plastic. This is the fast and straight algorithm without unnecessary nozzle shake that gives a smooth pattern. But it is more useful for forming loose walls in the entire they array.\nCombined: Joint mode [Displacement] + [Extrusion]. The appearance of the walls is similar to [Displacement] Mode, but it leaves no pores between the perimeters.\n\nAttention! The [Extrusion] and [Combined] modes works only the fuzzy_skin_thickness parameter not more than the thickness of printed loop. At the same time, the width of the extrusion for a particular layer should also not be below a certain level. It is usually equal 15-25%% of a layer height. Therefore, the maximum fuzzy skin thickness with a perimeter width of 0.4 mm and a layer height of 0.2 mm will be 0.4-(0.2*0.25)=±0.35mm! If you enter a higher parameter than this, the error Flow::spacing() will displayed, and the model will not be sliced. You can choose this number until this error is repeated."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("displacement"); - def->enum_values.push_back("extrusion"); - def->enum_values.push_back("combined"); - def->enum_labels.push_back(L("Displacement")); - def->enum_labels.push_back(L("Extrusion")); - def->enum_labels.push_back(L("Combined")); - def->set_default_value(new ConfigOptionEnum(FuzzySkinMode::Displacement)); - - def = this->add("fuzzy_skin_noise_type", coEnum); - def->label = L("Fuzzy skin noise type"); - def->category = L("Print/Others"); - def->tooltip = L("Noise type to use for fuzzy skin generation:\nClassic: Classic uniform random noise.\nPerlin: Perlin noise, which gives a more consistent texture.\nBillow: Similar to perlin noise, but clumpier.\nRidged Multifractal: Ridged noise with sharp, jagged features. Creates marble-like textures.\nVoronoi: Divides the surface into voronoi cells, and displaces each one by a random amount. Creates a patchwork texture.\nRipple: Uniform ripple pattern that ripples left and right of the original path. Repeating pattern, woven appearance."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("classic"); - def->enum_values.push_back("perlin"); - def->enum_values.push_back("billow"); - def->enum_values.push_back("ridgedmulti"); - def->enum_values.push_back("voronoi"); - def->enum_values.push_back("ripple"); - def->enum_labels.push_back(L("Classic")); - def->enum_labels.push_back(L("Perlin")); - def->enum_labels.push_back(L("Billow")); - def->enum_labels.push_back(L("Ridged Multifractal")); - def->enum_labels.push_back(L("Voronoi")); - def->enum_labels.push_back(L("Ripple")); - def->set_default_value(new ConfigOptionEnum(NoiseType::Classic)); - - def = this->add("fuzzy_skin_scale", coFloat); - def->label = L("Fuzzy skin feature size"); - def->category = L("Print/Others"); - def->tooltip = L("The base size of the coherent noise features, in mm. Higher values will result in larger features."); - def->sidetext = L("mm"); - def->min = 0.1; - def->max = 500; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1.0)); - - def = this->add("fuzzy_skin_octaves", coInt); - def->label = L("Fuzzy Skin Noise Octaves"); - def->category = L("Print/Others"); - def->tooltip = L("The number of octaves of coherent noise to use. Higher values increase the detail of the noise, but also increase computation time."); - def->min = 1; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(4)); - - def = this->add("fuzzy_skin_persistence", coFloat); - def->label = L("Fuzzy skin noise persistence"); - def->category = L("Print/Others"); - def->tooltip = L("The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise."); - def->min = 0.01; - def->max = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.5)); - - def = this->add("fuzzy_skin_ripples_per_layer", coInt); - def->label = L("Number of ripples per layer"); - def->category = L("Print/Others"); - def->tooltip = L("Controls how many full cycles of ripples will be added per layer."); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(15)); - - def = this->add("fuzzy_skin_ripple_offset", coPercent); - def->label = L("Ripple offset"); - def->category = L("Print/Others"); - def->tooltip = L("Shifts the ripple phase forward along the print path by the specified percentage of a wavelength each layer period.\n- 0% keeps every layer identical.\n- 50% shifts the pattern by half a wavelength, effectively inverting the phase.\n- 100% shifts the pattern by a full wavelength, returning to the original phase.\n\nThe shift is applied once every number of layers set by Layers between ripple offset, so layers within the same group are printed identically."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(50)); - - def = this->add("fuzzy_skin_layers_between_ripple_offset", coInt); - def->label = L("Layers between ripple offset"); - def->category = L("Print/Others"); - def->tooltip = L("Specifies how many consecutive layers share the same ripple phase before the offset is applied.\nFor example:\n- 1 = Layer 1 is printed with the base ripple pattern, then layer 2 is shifted by the configured offset, then layer 3 returns to the base pattern, and so on.\n- 3 = Layers 1 to 3 are printed with the base ripple pattern, then layers 4 to 6 are shifted by the configured offset, then layers 7 to 9 return to the base pattern, etc."); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("gcode_add_line_number", coBool); - def->label = L("Add line number"); - def->category = L("Print/Others"); - def->tooltip = L("Enable this to add line number(Nx) at the beginning of each G-code line."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(0)); - - def = this->add("gcode_label_objects", coBool); - def->label = L("Label objects"); - def->category = L("Print/Others"); - def->tooltip = L("Enable this to add comments into the G-code labeling print moves with what object they belong to, which is useful for the Octoprint CancelObject plug-in. This setting is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(1)); - - def = this->add("exclude_object", coBool); - def->label = L("Exclude objects"); - def->category = L("Print/Others"); - def->tooltip = L("Enable this option to add EXCLUDE OBJECT command in G-code."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("gcode_comments", coBool); - def->label = L("Verbose G-code"); - def->category = L("Print/Others"); - def->tooltip = L("Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(0)); - - def = this->add("enable_wrapping_detection", coBool); - def->label = L("Enable clumping detection"); - def->category = L("Print/Others"); - def->tooltip = L("Enable clumping detection"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("notes", coString); - def->label = L("Configuration notes"); - def->category = L("Print/Others"); - def->tooltip = L("You can put here your personal notes. This text will be added to the G-code header comments."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 13; - def->set_default_value(new ConfigOptionString()); - - def = this->add("reduce_infill_retraction", coBool); - def->label = L("Reduce infill retraction"); - def->category = L("Print/Others"); - def->tooltip = L("Don't retract when the travel is entirely within an infill area. That means the oozing can't been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that z-hop is also not performed in areas where retraction is skipped."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("filename_format", coString); - def->label = L("Filename format"); - def->category = L("Print/Others"); - def->tooltip = L("Users can define the project file name when exporting."); - def->mode = comAdvanced; - def->full_width = true; - def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode")); - - def = this->add("post_process", coStrings); - def->label = L("Post-processing Scripts"); - def->category = L("Print/Others"); - def->tooltip = L("If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Orca Slicer config settings by reading environment variables."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 6; - def->gui_flags = "serialized"; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("process_change_extrusion_role_gcode", coString); - def->label = L("Change extrusion role G-code (process)"); - def->category = L("Print/Others"); - def->tooltip = L("This G-code is inserted when the extrusion role is changed. It runs after the machine and filament extrusion role G-code."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString()); - - def = this->add("skirt_distance", coFloat); - def->label = L("Skirt distance"); - def->category = L("Print/Others"); - def->tooltip = L("The distance from the skirt to the brim or the object."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 60; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2)); - - def = this->add("skirt_start_angle", coFloat); - def->label = L("Skirt start point"); - def->category = L("Print/Others"); - def->tooltip = L("Angle from the object center to skirt start point. Zero is the most right position, counter clockwise is positive angle."); - def->sidetext = L("°"); - def->min = -180; - def->max = 180; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(-135)); - - def = this->add("skirt_height", coInt); - def->label = L("Skirt height"); - def->category = L("Print/Others"); - def->tooltip = L("How many layers of skirt. Usually only one layer."); - def->sidetext = L("layers"); - def->max = 10000; - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("single_loop_draft_shield", coBool); - def->label = L("Single loop after first layer"); - def->category = L("Print/Others"); - def->tooltip = L("Limits the skirt/draft shield loops to one wall after the first layer. This is useful, on occasion, to conserve filament but may cause the draft shield/skirt to warp / crack."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("draft_shield", coEnum); - def->label = L("Draft shield"); - def->category = L("Print/Others"); - def->tooltip = L("A draft shield is useful to protect an ABS or ASA print from warping and detaching from print bed due to wind draft. It is usually needed only with open frame printers, i.e. without an enclosure.\n\nEnabled = skirt is as tall as the highest printed object. Otherwise 'Skirt height' is used.\nNote: With the draft shield active, the skirt will be printed at skirt distance from the object. Therefore, if brims are active it may intersect with them. To avoid this, increase the skirt distance value.\n"); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("disabled"); - def->enum_values.push_back("enabled"); - def->enum_labels.push_back(L("Disabled")); - def->enum_labels.push_back(L("Enabled")); - def->set_default_value(new ConfigOptionEnum(dsDisabled)); - - def = this->add("skirt_type", coEnum); - def->label = L("Skirt type"); - def->full_label = L("Skirt type"); - def->category = L("Print/Others"); - def->tooltip = L("Combined - single skirt for all objects, Per object - individual object skirt."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("combined"); - def->enum_values.push_back("perobject"); - def->enum_labels.push_back(L("Combined")); - def->enum_labels.push_back(L("Per object")); - def->set_default_value(new ConfigOptionEnum(stCombined)); - - def = this->add("skirt_loops", coInt); - def->label = L("Skirt loops"); - def->full_label = L("Skirt loops"); - def->category = L("Print/Others"); - def->tooltip = L("Number of loops for the skirt. Zero means disabling skirt."); - def->min = 0; - def->max = 10; - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("skirt_speed", coFloat); - def->label = L("Skirt speed"); - def->full_label = L("Skirt speed"); - def->category = L("Print/Others"); - def->tooltip = L("Speed of skirt, in mm/s. Zero means use default layer extrusion speed."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(50.0)); - - def = this->add("min_skirt_length", coFloat); - def->label = L("Skirt minimum extrusion length"); - def->full_label = L("Skirt minimum extrusion length"); - def->category = L("Print/Others"); - def->tooltip = L("Minimum filament extrusion length in mm when printing the skirt. Zero means this feature is disabled.\n\nUsing a non-zero value is useful if the printer is set up to print without a prime line.\nFinal number of loops is not taking into account while arranging or validating objects distance. Increase loop number in such case."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.0)); - - def = this->add("spiral_mode", coBool); - def->label = L("Spiral vase"); - def->category = L("Print/Others"); - def->tooltip = L("Spiralize smooths out the Z moves of the outer contour. And turns a solid model into a single walled print with solid bottom layers. The final generated model has no seam."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("spiral_mode_smooth", coBool); - def->label = L("Smooth Spiral"); - def->category = L("Print/Others"); - def->tooltip = L("Smooth Spiral smooths out X and Y moves as well, resulting in no visible seam at all, even in the XY directions on walls that are not vertical."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("spiral_mode_max_xy_smoothing", coFloatOrPercent); - def->label = L("Max XY Smoothing"); - def->category = L("Print/Others"); - def->tooltip = L("Maximum distance to move points in XY to try to achieve a smooth spiral. If expressed as a %, it will be computed over nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(200, true)); - - def = this->add("spiral_starting_flow_ratio", coFloat); - def->label = L("Spiral starting flow ratio"); - def->category = L("Print/Others"); - def->tooltip = L("Sets the starting flow ratio while transitioning from the last bottom layer to the spiral. Normally the spiral transition scales the flow ratio from 0% to 100% during the first loop which can in some cases lead to under extrusion at the start of the spiral."); - def->min = 0; - def->max = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("spiral_finishing_flow_ratio", coFloat); - def->label = L("Spiral finishing flow ratio"); - def->category = L("Print/Others"); - def->tooltip = L("Sets the finishing flow ratio while ending the spiral. Normally the spiral transition scales the flow ratio from 100% to 0% during the last loop which can in some cases lead to under extrusion at the end of the spiral."); - def->min = 0; - def->max = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("timelapse_type", coEnum); - def->label = L("Timelapse"); - def->category = L("Print/Others"); - def->tooltip = L("If smooth or traditional mode is selected, a timelapse video will be generated for each print. After each layer is printed, a snapshot is taken with the chamber camera. All of these snapshots are composed into a timelapse video when printing completes. If smooth mode is selected, the toolhead will move to the excess chute after each layer is printed and then take a snapshot. Since the melt filament may leak from the nozzle during the process of taking a snapshot, a prime tower is required for smooth mode to wipe nozzle."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("0"); - def->enum_values.push_back("1"); - def->enum_labels.push_back(L("Traditional")); - def->enum_labels.push_back(L("Smooth")); - def->set_default_value(new ConfigOptionEnum(tlTraditional)); - - def = this->add("slicing_mode", coEnum); - def->label = L("Slicing Mode"); - def->category = L("Print/Others"); - def->tooltip = L("Use \"Even-odd\" for 3DLabPrint airplane models. Use \"Close holes\" to close all holes in the model."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("regular"); - def->enum_values.push_back("even_odd"); - def->enum_values.push_back("close_holes"); - def->enum_labels.push_back(L("Regular")); - def->enum_labels.push_back(L("Even-odd")); - def->enum_labels.push_back(L("Close holes")); - def->set_default_value(new ConfigOptionEnum(SlicingMode::Regular)); - - def = this->add("sparse_infill_filament", coInt); - def->label = L("Infill"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Filament to print internal sparse infill.\n\"Default\" uses the active object/part filament."); - def->min = 0; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("interface_shells", coBool); - def->label = L("Interface shells"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("mmu_segmented_region_max_width", coFloat); - def->label = L("Maximum width of a segmented region"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Maximum width of a segmented region. Zero disables this feature."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("mmu_segmented_region_interlocking_depth", coFloat); - def->label = L("Interlocking depth of a segmented region"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Interlocking depth of a segmented region. It will be ignored if \"mmu_segmented_region_max_width\" is zero or if \"mmu_segmented_region_interlocking_depth\" is bigger than \"mmu_segmented_region_max_width\". Zero disables this feature."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("interlocking_beam", coBool); - def->label = L("Use beam interlocking"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Generate interlocking beam structure at the locations where different filaments touch. This improves the adhesion between filaments, especially models printed in different materials."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("interlocking_beam_width", coFloat); - def->label = L("Interlocking beam width"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The width of the interlocking structure beams."); - def->sidetext = L("mm"); - def->min = 0.01; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.8)); - - def = this->add("interlocking_orientation", coFloat); - def->label = L("Interlocking direction"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Orientation of interlock beams."); - def->sidetext = L("°"); - def->min = 0; - def->max = 360; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(22.5)); - - def = this->add("interlocking_beam_layer_count", coInt); - def->label = L("Interlocking beam layers"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The height of the beams of the interlocking structure, measured in number of layers. Less layers is stronger, but more prone to defects."); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(2)); - - def = this->add("interlocking_depth", coInt); - def->label = L("Interlocking depth"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The distance from the boundary between filaments to generate interlocking structure, measured in cells. Too few cells will result in poor adhesion."); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(2)); - - def = this->add("interlocking_boundary_avoidance", coInt); - def->label = L("Interlocking boundary avoidance"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The distance from the outside of a model where interlocking structures will not be generated, measured in cells."); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(2)); - - def = this->add("ooze_prevention", coBool); - def->label = L("Enable"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("wall_filament", coInt); - def->label = L("Walls"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Filament to print walls.\n\"Default\" uses the active object/part filament."); - def->min = 0; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("solid_infill_filament", coInt); - def->label = L("Solid infill"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Filament to print solid infill.\n\"Default\" uses the active object/part filament."); - def->min = 0; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("standby_temperature_delta", coInt); - def->label = L("Temperature variation"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Temperature difference to be applied when an extruder is not active. The value is not used when 'idle_temperature' in filament settings is set to non-zero value."); - def->sidetext = L("\u2206\u2103"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(-5)); - - def = this->add("preheat_time", coFloat); - def->label = L("Preheat time"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("To reduce the waiting time after tool change, Orca can preheat the next tool while the current tool is still in use. This setting specifies the time in seconds to preheat the next tool. Orca will insert a M104 command to preheat the tool in advance."); - def->sidetext = L("s"); - def->min = 0; - def->max = 120; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(30.0)); - - def = this->add("preheat_steps", coInt); - def->label = L("Preheat steps"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Insert multiple preheat commands (e.g. M104.1). Only useful for Prusa XL. For other printers, please set it to 1."); - def->min = 1; - def->max = 10; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("wipe_tower_no_sparse_layers", coBool); - def->label = L("No sparse layers (beta)"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no tool changes. On layers with a tool change, extruder will travel downward to print the wipe tower. User is responsible for ensuring there is no collision with the print."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("single_extruder_multi_material_priming", coBool); - def->label = L("Prime all printing extruders"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("enable_prime_tower", coBool); - def->label = L("Enable"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The wiping tower can be used to clean up the residue on the nozzle and stabilize the chamber pressure inside the nozzle, in order to avoid appearance defects when printing objects."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("prime_tower_enable_framework", coBool); - def->label = L("Internal ribs"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Enable internal ribs to increase the stability of the prime tower."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("prime_volume", coFloat); - def->label = L("Prime volume"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The volume of material to prime extruder on tower."); - def->sidetext = L("mm³"); - def->min = 1; - def->set_default_value(new ConfigOptionFloat(45.)); - - def = this->add("prime_tower_width", coFloat); - def->label = L("Width"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Width of the prime tower."); - def->sidetext = L("mm"); - def->min = 2; - def->set_default_value(new ConfigOptionFloat(60.)); - - def = this->add("wipe_tower_rotation_angle", coFloat); - def->label = L("Wipe tower rotation angle"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Wipe tower rotation angle with respect to X axis."); - def->sidetext = L("°"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("prime_tower_brim_width", coFloat); - def->label = L("Brim width"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Brim width of prime tower, negative number means auto calculated width based on the height of prime tower."); - def->sidetext = L("mm"); - def->min = -1; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::f_enum_open; - def->enum_values.push_back("-1"); - def->enum_labels.push_back(L("Auto")); - def->set_default_value(new ConfigOptionFloat(3.)); - - def = this->add("wipe_tower_cone_angle", coFloat); - def->label = L("Stabilization cone apex angle"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Angle at the apex of the cone that is used to stabilize the wipe tower. Larger angle means wider base."); - def->sidetext = L("°"); - def->min = 0; - def->max = 90; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(30.0)); - - def = this->add("wipe_tower_max_purge_speed", coFloat); - def->label = L("Maximum wipe tower print speed"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The maximum print speed when purging in the wipe tower and printing the wipe tower sparse layers. When purging, if the sparse infill speed or calculated speed from the filament max volumetric speed is lower, the lowest will be used instead.\n\nWhen printing the sparse layers, if the internal perimeter speed or calculated speed from the filament max volumetric speed is lower, the lowest will be used instead.\n\nIncreasing this speed may affect the tower's stability as well as increase the force with which the nozzle collides with any blobs that may have formed on the wipe tower.\n\nBefore increasing this parameter beyond the default of 90 mm/s, make sure your printer can reliably bridge at the increased speeds and that ooze when tool changing is well controlled.\n\nFor the wipe tower external perimeters the internal perimeter speed is used regardless of this setting."); - def->sidetext = L("mm/s"); - def->min = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(90.)); - - def = this->add("wipe_tower_wall_type", coEnum); - def->label = L("Wall type"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Wipe tower outer wall type.\n1. Rectangle: The default wall type, a rectangle with fixed width and height.\n2. Cone: A cone with a fillet at the bottom to help stabilize the wipe tower.\n3. Rib: Adds four ribs to the tower wall for enhanced stability."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("rectangle"); - def->enum_values.push_back("cone"); - def->enum_values.push_back("rib"); - def->enum_labels.push_back(L("Rectangle")); - def->enum_labels.push_back(L("Cone")); - def->enum_labels.push_back(L("Rib")); - def->set_default_value(new ConfigOptionEnum(wtwRib)); - - def = this->add("wipe_tower_extra_rib_length", coFloat); - def->label = L("Extra rib length"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Positive values can increase the size of the rib wall, while negative values can reduce the size. However, the size of the rib wall can not be smaller than that determined by the cleaning volume."); - def->sidetext = L("mm"); - def->max = 300; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("wipe_tower_rib_width", coFloat); - def->label = L("Rib width"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Rib width is always less than half the prime tower side length."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 300; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(8)); - - def = this->add("wipe_tower_fillet_wall", coBool); - def->label = L("Fillet wall"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The wall of prime tower will fillet."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("wipe_tower_filament", coInt); - def->label = L("Wipe tower"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The extruder to use when printing perimeter of the wipe tower. Set to 0 to use the one that is available (non-soluble would be preferred)."); - def->min = 0; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("prime_tower_skip_points", coBool); - def->label = L("Skip points"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("The wall of prime tower will skip the start points of wipe path."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("enable_tower_interface_features", coBool); - def->label = L("Enable tower interface features"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Enable optimized prime tower interface behavior when different materials meet."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("enable_tower_interface_cooldown_during_tower", coBool); - def->label = L("Cool down from interface boost during prime tower"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("When interface-layer temperature boost is active, set the nozzle back to print temperature at the start of the prime tower so it cools down during the tower."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("prime_tower_infill_gap", coPercent); - def->label = L("Infill gap"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Infill gap."); - def->sidetext = L("%"); - def->min = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(150)); - - def = this->add("flush_into_infill", coBool); - def->label = L("Flush into objects' infill"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Purging after filament change will be done inside objects' infills. This may lower the amount of waste and decrease the print time. If the walls are printed with transparent filament, the mixed color infill will be seen outside. It will not take effect, unless the prime tower is enabled."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("flush_into_support", coBool); - def->label = L("Flush into objects' support"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Purging after filament change will be done inside objects' support. This may lower the amount of waste and decrease the print time. It will not take effect, unless the prime tower is enabled."); - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("flush_into_objects", coBool); - def->label = L("Flush into this object"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("This object will be used to purge the nozzle after a filament change to save filament and decrease the print time. Colors of the objects will be mixed as a result. It will not take effect unless the prime tower is enabled."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("wipe_tower_bridging", coFloat); - def->label = L("Maximal bridging distance"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Maximal distance between supports on sparse infill sections."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(10.)); - - def = this->add("wipe_tower_extra_spacing", coPercent); - def->label = L("Wipe tower purge lines spacing"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Spacing of purge lines on the wipe tower."); - def->sidetext = L("%"); - def->min = 100; - def->max = 300; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(100.)); - - def = this->add("wipe_tower_extra_flow", coPercent); - def->label = L("Extra flow for purging"); - def->category = L("Print/Multimaterial"); - def->tooltip = L("Extra flow used for the purging lines on the wipe tower. This makes the purging lines thicker or narrower than they normally would be. The spacing is adjusted automatically."); - def->sidetext = L("%"); - def->min = 100; - def->max = 300; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(100.)); - - def = this->add("reduce_crossing_wall", coBool); - def->label = L("Avoid crossing walls"); - def->category = L("Print/Quality"); - def->tooltip = L("Detour to avoid traveling across walls, which may cause blobs on the surface."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(INITIAL_REDUCE_CROSSING_WALL)); - - def = this->add("max_travel_detour_distance", coFloatOrPercent); - def->label = L("Avoid crossing walls - Max detour length"); - def->category = L("Print/Quality"); - def->tooltip = L("Maximum detour distance for avoiding crossing wall. Don't detour if the detour distance is larger than this value. Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. Zero to disable."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max_literal = 1000; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("bridge_density", coPercent); - def->label = L("External bridge density"); - def->category = L("Print/Quality"); - def->tooltip = L("Controls the density (spacing) of external bridge lines. Default is 100%.\n\nLower density external bridges can help improve reliability as there is more space for air to circulate around the extruded bridge, improving its cooling speed. Minimum is 10%.\n\nHigher densities can produce smoother bridge surfaces, as overlapping lines provide additional support during printing. Maximum is 120%.\nNote: Bridge density that is too high can cause warping or overextrusion."); - def->sidetext = L("%"); - def->min = 10; - def->max = 120; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(100)); - - def = this->add("internal_bridge_density", coPercent); - def->label = L("Internal bridge density"); - def->category = L("Print/Quality"); - def->tooltip = L("Controls the density (spacing) of internal bridge lines. 100% means solid bridge. Default is 100%.\n\nLower density internal bridges can help reduce top surface pillowing and improve internal bridge reliability as there is more space for air to circulate around the extruded bridge, improving its cooling speed.\n\nThis option works particularly well when combined with the second internal bridge over infill option, further improving internal bridging structure before solid infill is extruded."); - def->sidetext = L("%"); - def->min = 10; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(100)); - - def = this->add("bridge_flow", coFloat); - def->label = L("Bridge flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("Decrease this value slightly (for example 0.9) to reduce the amount of material for bridge, to improve sag.\n\nThe actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("internal_bridge_flow", coFloat); - def->label = L("Internal bridge flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This value governs the thickness of the internal bridge layer. This is the first layer over sparse infill. Decrease this value slightly (for example 0.9) to improve surface quality over sparse infill.\n\nThe actual internal bridge flow used is calculated by multiplying this value with the bridge flow ratio, the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("top_solid_infill_flow_ratio", coFloat); - def->label = L("Top surface flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for top solid infill. You can decrease it slightly to have smooth surface finish.\n\nThe actual top surface flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("bottom_solid_infill_flow_ratio", coFloat); - def->label = L("Bottom surface flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for bottom solid infill.\n\nThe actual bottom solid infill flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("set_other_flow_ratios", coBool); - def->label = L("Set other flow ratios"); - def->category = L("Print/Quality"); - def->tooltip = L("Change flow ratios for other extrusion path types."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("first_layer_flow_ratio", coFloat); - def->label = L("First layer flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material on the first layer for the extrusion path roles listed in this section.\n\nFor the first layer, the actual flow ratio for each path role (does not affect brims and skirts) will be multiplied by this value."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("outer_wall_flow_ratio", coFloat); - def->label = L("Outer wall flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for outer walls.\n\nThe actual outer wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("inner_wall_flow_ratio", coFloat); - def->label = L("Inner wall flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for inner walls.\n\nThe actual inner wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("overhang_flow_ratio", coFloat); - def->label = L("Overhang flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for overhangs.\n\nThe actual overhang flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("sparse_infill_flow_ratio", coFloat); - def->label = L("Sparse infill flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for sparse infill.\n\nThe actual sparse infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("internal_solid_infill_flow_ratio", coFloat); - def->label = L("Internal solid infill flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for internal solid infill.\n\nThe actual internal solid infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("gap_fill_flow_ratio", coFloat); - def->label = L("Gap fill flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for filling the gaps.\n\nThe actual gap filling flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("support_flow_ratio", coFloat); - def->label = L("Support flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for support.\n\nThe actual support flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("support_interface_flow_ratio", coFloat); - def->label = L("Support interface flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for the support interface.\n\nThe actual support interface flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("precise_outer_wall", coBool); - def->label = L("Precise wall"); - def->category = L("Print/Quality"); - def->tooltip = L("Improve shell precision by adjusting outer wall spacing. This also improves layer consistency. NOTE: This option will be ignored for outer-inner or inner-outer-inner wall sequences."); - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("only_one_wall_top", coBool); - def->label = L("Only one wall on top surfaces"); - def->category = L("Print/Quality"); - def->tooltip = L("Use only one wall on flat top surfaces, to give more space to the top infill pattern."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("min_width_top_surface", coFloatOrPercent); - def->label = L("One wall threshold"); - def->category = L("Print/Quality"); - def->tooltip = L("If a top surface has to be printed and it's partially covered by another layer, it won't be considered at a top layer where its width is below this value. This can be useful to not let the 'one perimeter on top' trigger on surface that should be covered only by perimeters. This value can be a mm or a % of the perimeter extrusion width.\nWarning: If enabled, artifacts can be created if you have some thin features on the next layer, like letters. Set this setting to 0 to remove these artifacts."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max_literal = 15; - def->ratio_over = "inner_wall_line_width"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(300, true)); - - def = this->add("only_one_wall_first_layer", coBool); - def->label = L("Only one wall on first layer"); - def->category = L("Print/Quality"); - def->tooltip = L("Use only one wall on first layer, to give more space to the bottom infill pattern."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("extra_perimeters_on_overhangs", coBool); - def->label = L("Extra perimeters on overhangs"); - def->category = L("Print/Quality"); - def->tooltip = L("Create additional perimeter paths over steep overhangs and areas where bridges cannot be anchored."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("overhang_reverse", coBool); - def->label = L("Reverse on even"); - def->full_label = L("Overhang reversal"); - def->category = L("Print/Quality"); - def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on even layers. This alternating pattern can drastically improve steep overhangs.\n\nThis setting can also help reduce part warping due to the reduction of stresses in the part walls."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("overhang_reverse_internal_only", coBool); - def->label = L("Reverse only internal perimeters"); - def->full_label = L("Reverse only internal perimeters"); - def->category = L("Print/Quality"); - def->tooltip = L("Apply the reverse perimeters logic only on internal perimeters.\n\nThis setting greatly reduces part stresses as they are now distributed in alternating directions. This should reduce part warping while also maintaining external wall quality. This feature can be very useful for warp prone material, like ABS/ASA, and also for elastic filaments, like TPU and Silk PLA. It can also help reduce warping on floating regions over supports.\n\nFor this setting to be the most effective, it is recommended to set the Reverse Threshold to 0 so that all internal walls print in alternating directions on even layers irrespective of their overhang degree."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("counterbore_hole_bridging", coEnum); - def->label = L("Bridge counterbore holes"); - def->category = L("Print/Quality"); - def->tooltip = L("This option creates bridges for counterbore holes, allowing them to be printed without support. Available modes include:\n1. None: No bridge is created\n2. Partially Bridged: Only a part of the unsupported area will be bridged\n3. Sacrificial Layer: A full sacrificial bridge layer is created"); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("none"); - def->enum_values.push_back("partiallybridge"); - def->enum_values.push_back("sacrificiallayer"); - def->enum_labels.push_back(L("None")); - def->enum_labels.push_back(L("Partially bridged")); - def->enum_labels.push_back(L("Sacrificial layer")); - def->set_default_value(new ConfigOptionEnum(chbNone)); - - def = this->add("overhang_reverse_threshold", coFloatOrPercent); - def->label = L("Reverse threshold"); - def->full_label = L("Overhang reversal threshold"); - def->category = L("Print/Quality"); - def->tooltip = L("Number of mm the overhang need to be for the reversal to be considered useful. Can be a % of the perimeter width.\nValue 0 enables reversal on every even layers regardless.\nWhen Detect overhang wall is not enabled, this option is ignored and reversal happens on every even layers regardless."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max_literal = 20; - def->ratio_over = "line_width"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(50, true)); - - def = this->add("thick_bridges", coBool); - def->label = L("Thick external bridges"); - def->category = L("Print/Quality"); - def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. If disabled, bridges look better but are reliable just for shorter bridged distances."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("thick_internal_bridges", coBool); - def->label = L("Thick internal bridges"); - def->category = L("Print/Quality"); - def->tooltip = L("If enabled, thick internal bridges will be used. It's usually recommended to have this feature turned on. However, consider turning it off if you are using large nozzles."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("enable_extra_bridge_layer", coEnum); - def->label = L("Extra bridge layers (beta)"); - def->category = L("Print/Quality"); - def->tooltip = L("This option enables the generation of an extra bridge layer over internal and/or external bridges.\n\nExtra bridge layers help improve bridge appearance and reliability, as the solid infill is better supported. This is especially useful in fast printers, where the bridge and solid infill speeds vary greatly. The extra bridge layer results in reduced pillowing on top surfaces, as well as reduced separation of the external bridge layer from its surrounding perimeters.\n\nIt is generally recommended to set this to at least 'External bridge only', unless specific issues with the sliced model are found.\n\nOptions:\n1. Disabled - does not generate second bridge layers. This is the default and is set for compatibility purposes\n2. External bridge only - generates second bridge layers for external-facing bridges only. Please note that small bridges that are shorter or narrower than the set number of perimeters will be skipped as they would not benefit from a second bridge layer. If generated, the second bridge layer will be extruded parallel to the first bridge layer to reinforce the bridge strength\n3. Internal bridge only - generates second bridge layers for internal bridges over sparse infill only. Please note that the internal bridges count towards the top shell layer count of your model. The second internal bridge layer will be extruded as close to perpendicular to the first as possible. If multiple regions in the same island, with varying bridge angles are present, the last region of that island will be selected as the angle reference\n4. Apply to all - generates second bridge layers for both internal and external-facing bridges\n"); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("disabled"); - def->enum_values.push_back("external_bridge_only"); - def->enum_values.push_back("internal_bridge_only"); - def->enum_values.push_back("apply_to_all"); - def->enum_labels.push_back(L("Disabled")); - def->enum_labels.push_back(L("External bridge only")); - def->enum_labels.push_back(L("Internal bridge only")); - def->enum_labels.push_back(L("Apply to all")); - def->set_default_value(new ConfigOptionEnum(eblDisabled)); - - def = this->add("dont_filter_internal_bridges", coEnum); - def->label = L("Filter out small internal bridges"); - def->category = L("Print/Quality"); - def->tooltip = L("This option can help reduce pillowing on top surfaces in heavily slanted or curved models.\nBy default, small internal bridges are filtered out and the internal solid infill is printed directly over the sparse infill. This works well in most cases, speeding up printing without too much compromise on top surface quality.\nHowever, in heavily slanted or curved models, especially where too low a sparse infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\nEnabling limited filtering or no filtering will print internal bridge layer over slightly unsupported internal solid infill. The options below control the sensitivity of the filtering, i.e. they control where internal bridges are created:\n1. Filter - enables this option. This is the default behavior and works well in most cases\n2. Limited filtering - creates internal bridges on heavily slanted surfaces while avoiding unnecessary bridges. This works well for most difficult models\n3. No filtering - creates internal bridges on every potential internal overhang. This option is useful for heavily slanted top surface models; however, in most cases, it creates too many "); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("disabled"); - def->enum_values.push_back("limited"); - def->enum_values.push_back("nofilter"); - def->enum_labels.push_back(L("Filter")); - def->enum_labels.push_back(L("Limited filtering")); - def->enum_labels.push_back(L("No filtering")); - def->set_default_value(new ConfigOptionEnum(ibfDisabled)); - - def = this->add("outer_wall_line_width", coFloatOrPercent); - def->label = L("Outer wall"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width of outer wall. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("wall_sequence", coEnum); - def->label = L("Walls printing order"); - def->category = L("Print/Quality"); - def->tooltip = L("Print sequence of the internal (inner) and external (outer) walls.\n\nUse Inner/Outer for best overhangs. This is because the overhanging walls can adhere to a neighbouring perimeter while printing. However, this option results in slightly reduced surface quality as the external perimeter is deformed by being squashed to the internal perimeter.\n\nUse Inner/Outer/Inner for the best external surface finish and dimensional accuracy as the external wall is printed undisturbed from an internal perimeter. However, overhang performance will reduce as there is no internal perimeter to print the external wall against. This option requires a minimum of 3 walls to be effective as it prints the internal walls from the 3rd perimeter onwards first, then the external perimeter and, finally, the first internal perimeter. This option is recommended against the Outer/Inner option in most cases.\n\nUse Outer/Inner for the same external wall quality and dimensional accuracy benefits of Inner/Outer/Inner option. However, the Z seams will appear less consistent as the first extrusion of a new layer starts on a visible surface."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("inner wall/outer wall"); - def->enum_values.push_back("outer wall/inner wall"); - def->enum_values.push_back("inner-outer-inner wall"); - def->enum_labels.push_back(L("Inner/Outer")); - def->enum_labels.push_back(L("Outer/Inner")); - def->enum_labels.push_back(L("Inner/Outer/Inner")); - def->set_default_value(new ConfigOptionEnum(WallSequence::InnerOuter)); - - def = this->add("is_infill_first", coBool); - def->label = L("Print infill first"); - def->category = L("Print/Quality"); - def->tooltip = L("Order of wall/infill. When the tickbox is unchecked the walls are printed first, which works best in most cases.\n\nPrinting infill first may help with extreme overhangs as the walls have the neighbouring infill to adhere to. However, the infill will slightly push out the printed walls where it is attached to them, resulting in a worse external surface finish. It can also cause the infill to shine through the external surfaces of the part."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("wall_direction", coEnum); - def->label = L("Wall loop direction"); - def->category = L("Print/Quality"); - def->tooltip = L("The direction which the contour wall loops are extruded when looking down from the top.\nHoles are printed in the opposite direction to the contour to maintain alignment with layers whose contour polygons are incomplete and change direction, also partially forming the contour of a hole.\n\nThis option will be disabled if spiral vase mode is enabled."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("ccw"); - def->enum_values.push_back("cw"); - def->enum_labels.push_back(L("Counter clockwise")); - def->enum_labels.push_back(L("Clockwise")); - def->set_default_value(new ConfigOptionEnum(WallDirection::CounterClockwise)); - - def = this->add("print_flow_ratio", coFloat); - def->label = L("Flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("The material may have volumetric change after switching between molten and crystalline states. This setting changes all extrusion flow of this filament in G-code proportionally. The recommended value range is between 0.95 and 1.05. You may be able to tune this value to get a nice flat surface if there is slight overflow or underflow.\n\nThe final object flow ratio is this value multiplied by the filament flow ratio."); - def->min = 0.01; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1.f)); - - def = this->add("line_width", coFloatOrPercent); - def->label = L("Default"); - def->category = L("Print/Quality"); - def->tooltip = L("Default line width if other line widths are set to 0. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); - - def = this->add("initial_layer_line_width", coFloatOrPercent); - def->label = L("First layer"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width of the first layer. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("initial_layer_print_height", coFloat); - def->label = L("First layer height"); - def->category = L("Print/Quality"); - def->tooltip = L("Height of the first layer. Making the first layer height thicker can improve build plate adhesion."); - def->sidetext = L("mm"); - def->min = 0; - def->set_default_value(new ConfigOptionFloat(0.2)); - - def = this->add("precise_z_height", coBool); - def->label = L("Precise Z height"); - def->category = L("Print/Quality"); - def->tooltip = L("Enable this to get precise Z height of object after slicing. It will get the precise object height by fine-tuning the layer heights of the last few layers. Note that this is an experimental parameter."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(0)); - - def = this->add("enable_arc_fitting", coBool); - def->label = L("Arc fitting"); - def->category = L("Print/Quality"); - def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. The fitting tolerance is same as the resolution.\n\nNote: For Klipper machines, this option is recommended to be disabled. Klipper does not benefit from arc commands as these are split again into line segments by the firmware. This results in a reduction in surface quality as line segments are converted to arcs by the slicer and then back to line segments by the firmware."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(0)); - - def = this->add("sparse_infill_line_width", coFloatOrPercent); - def->label = L("Sparse infill"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width of internal sparse infill. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("ironing_type", coEnum); - def->label = L("Ironing Type"); - def->category = L("Print/Quality"); - def->tooltip = L("Ironing is using small flow to print on same height of surface again to make flat surface more smooth. This setting controls which layer being ironed."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("no ironing"); - def->enum_values.push_back("top"); - def->enum_values.push_back("topmost"); - def->enum_values.push_back("solid"); - def->enum_labels.push_back(L("No ironing")); - def->enum_labels.push_back(L("Top surfaces")); - def->enum_labels.push_back(L("Topmost surface")); - def->enum_labels.push_back(L("All solid layers")); - def->set_default_value(new ConfigOptionEnum(IroningType::NoIroning)); - - def = this->add("ironing_pattern", coEnum); - def->label = L("Ironing Pattern"); - def->category = L("Print/Quality"); - def->tooltip = L("The pattern that will be used when ironing."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("concentric"); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Concentric")); - def->set_default_value(new ConfigOptionEnum(ipRectilinear)); - - def = this->add("ironing_flow", coPercent); - def->label = L("Ironing flow"); - def->category = L("Print/Quality"); - def->tooltip = L("The amount of material to extrude during ironing. Relative to flow of normal layer height. Too high value results in overextrusion on the surface."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->ratio_over = "layer_height"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(10)); - - def = this->add("ironing_spacing", coFloat); - def->label = L("Ironing line spacing"); - def->category = L("Print/Quality"); - def->tooltip = L("The distance between the lines of ironing."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.1)); - - def = this->add("ironing_inset", coFloat); - def->label = L("Ironing inset"); - def->category = L("Print/Quality"); - def->tooltip = L("The distance to keep from the edges. A value of 0 sets this to half of the nozzle diameter."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("ironing_angle", coFloat); - def->label = L("Ironing angle offset"); - def->category = L("Print/Quality"); - def->tooltip = L("The angle of ironing lines offset from the top surface."); - def->sidetext = L("°"); - def->min = 0; - def->max = 359; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("ironing_angle_fixed", coBool); - def->label = L("Fixed ironing angle"); - def->category = L("Print/Quality"); - def->tooltip = L("Use a fixed absolute angle for ironing."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("zaa_enabled", coBool); - def->label = L("Z contouring enabled"); - def->category = L("Print/Quality"); - def->tooltip = L("Enable Z-layer contouring (aka Z-layer anti-aliasing)."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("zaa_minimize_perimeter_height", coFloat); - def->label = L("Minimize wall height angle"); - def->category = L("Print/Quality"); - def->tooltip = L("Reduce the height of top-surface perimeters to match the model edge height.\nAffects perimeters with a slope less than this angle (degrees).\nA reasonable value is 35. Set to 0 to disable."); - def->sidetext = L("°"); - def->min = 0; - def->max = 90; - def->set_default_value(new ConfigOptionFloat(35)); - - def = this->add("zaa_dont_alternate_fill_direction", coBool); - def->label = L("Don't alternate fill direction"); - def->category = L("Print/Quality"); - def->tooltip = L("Disable alternating fill direction when using Z contouring."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("zaa_min_z", coFloat); - def->label = L("Minimum z height"); - def->category = L("Print/Quality"); - def->tooltip = L("Minimum Z-layer height.\nAlso controls the slicing plane."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionFloat(0.05)); - - def = this->add("small_area_infill_flow_compensation", coBool); - def->label = L("Small area flow compensation (beta)"); - def->category = L("Print/Quality"); - def->tooltip = L("Enable flow compensation for small infill areas."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("small_area_infill_flow_compensation_model", coStrings); - def->label = L("Flow Compensation Model"); - def->category = L("Print/Quality"); - def->tooltip = L("Flow Compensation Model, used to adjust the flow for small infill areas. The model is expressed as a comma separated pair of values for extrusion length and flow correction factor. Each pair is on a separate line, followed by a semicolon, in the following format: \"1.234, 5.678;\""); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 15; - def->gui_flags = "serialized"; - def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"}); - - def = this->add("make_overhang_printable", coBool); - def->label = L("Make overhangs printable"); - def->category = L("Print/Quality"); - def->tooltip = L("Modify the geometry to print overhangs without support material."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("make_overhang_printable_angle", coFloat); - def->label = L("Make overhangs printable - Maximum angle"); - def->category = L("Print/Quality"); - def->tooltip = L("Maximum angle of overhangs to allow after making more steep overhangs printable.90° will not change the model at all and allow any overhang, while 0 will replace all overhangs with conical material."); - def->sidetext = L("°"); - def->min = 0; - def->max = 90; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(55.)); - - def = this->add("make_overhang_printable_hole_size", coFloat); - def->label = L("Make overhangs printable - Hole area"); - def->category = L("Print/Quality"); - def->tooltip = L("Maximum area of a hole in the base of the model before it's filled by conical material. A value of 0 will fill all the holes in the model base."); - def->sidetext = L("mm²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("detect_overhang_wall", coBool); - def->label = L("Detect overhang walls"); - def->category = L("Print/Quality"); - def->tooltip = L("Detect the overhang percentage relative to line width and use different speed to print. For 100%% overhang, bridge speed is used."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("inner_wall_line_width", coFloatOrPercent); - def->label = L("Inner wall"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width of inner wall. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("resolution", coFloat); - def->label = L("Resolution"); - def->category = L("Print/Quality"); - def->tooltip = L("The G-code path is generated after simplifying the contour of models to avoid too many points and G-code lines. Smaller value means higher resolution and more time to slice."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.01)); - - def = this->add("seam_position", coEnum); - def->label = L("Seam position"); - def->category = L("Print/Quality"); - def->tooltip = L("The start position to print each part of outer wall."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("nearest"); - def->enum_values.push_back("aligned"); - def->enum_values.push_back("aligned_back"); - def->enum_values.push_back("back"); - def->enum_values.push_back("random"); - def->enum_labels.push_back(L("Nearest")); - def->enum_labels.push_back(L("Aligned")); - def->enum_labels.push_back(L("Aligned back")); - def->enum_labels.push_back(L("Back")); - def->enum_labels.push_back(L("Random")); - def->set_default_value(new ConfigOptionEnum(spAligned)); - - def = this->add("staggered_inner_seams", coBool); - def->label = L("Staggered inner seams"); - def->category = L("Print/Quality"); - def->tooltip = L("This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("seam_gap", coFloatOrPercent); - def->label = L("Seam gap"); - def->category = L("Print/Quality"); - def->tooltip = L("In order to reduce the visibility of the seam in a closed loop extrusion, the loop is interrupted and shortened by a specified amount.\nThis amount can be specified in millimeters or as a percentage of the current extruder diameter. The default value for this parameter is 10%."); - def->sidetext = L("mm or %"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(10,true)); - - def = this->add("seam_slope_type", coEnum); - def->label = L("Scarf joint seam (beta)"); - def->category = L("Print/Quality"); - def->tooltip = L("Use scarf joint to minimize seam visibility and increase seam strength."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("none"); - def->enum_values.push_back("external"); - def->enum_values.push_back("all"); - def->enum_labels.push_back(L("None")); - def->enum_labels.push_back(L("Contour")); - def->enum_labels.push_back(L("Contour and hole")); - def->set_default_value(new ConfigOptionEnum(SeamScarfType::None)); - - def = this->add("seam_slope_conditional", coBool); - def->label = L("Conditional scarf joint"); - def->category = L("Print/Quality"); - def->tooltip = L("Apply scarf joints only to smooth perimeters where traditional seams do not conceal the seams at sharp corners effectively."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("scarf_angle_threshold", coInt); - def->label = L("Conditional angle threshold"); - def->category = L("Print/Quality"); - def->tooltip = L("This option sets the threshold angle for applying a conditional scarf joint seam.\nIf the maximum angle within the perimeter loop exceeds this value (indicating the absence of sharp corners), a scarf joint seam will be used. The default value is 155°."); - def->sidetext = L("°"); - def->min = 0; - def->max = 180; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(155)); - - def = this->add("scarf_overhang_threshold", coPercent); - def->label = L("Conditional overhang threshold"); - def->category = L("Print/Quality"); - def->tooltip = L("This option determines the overhang threshold for the application of scarf joint seams. If the unsupported portion of the perimeter is less than this threshold, scarf joint seams will be applied. The default threshold is set at 40% of the external wall's width. Due to performance considerations, the degree of overhang is estimated."); - def->sidetext = L("%"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(40)); - - def = this->add("scarf_joint_speed", coFloatOrPercent); - def->label = L("Scarf joint speed"); - def->category = L("Print/Quality"); - def->tooltip = L("This option sets the printing speed for scarf joints. It is recommended to print scarf joints at a slow speed (less than 100 mm/s). It's also advisable to enable 'Extrusion rate smoothing' if the set speed varies significantly from the speed of the outer or inner walls. If the speed specified here is higher than the speed of the outer or inner walls, the printer will default to the slower of the two speeds. When specified as a percentage (e.g., 80%), the speed is calculated based on the respective outer or inner wall speed. The default value is set to 100%."); - def->sidetext = L("mm/s or %"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("scarf_joint_flow_ratio", coFloat); - def->label = L("Scarf joint flow ratio"); - def->category = L("Print/Quality"); - def->tooltip = L("This factor affects the amount of material for scarf joints."); - def->min = 0; - def->max = 2; - def->set_default_value(new ConfigOptionFloat(1)); - - def = this->add("seam_slope_start_height", coFloatOrPercent); - def->label = L("Scarf start height"); - def->category = L("Print/Quality"); - def->tooltip = L("Start height of the scarf.\nThis amount can be specified in millimeters or as a percentage of the current layer height. The default value for this parameter is 0."); - def->sidetext = L("mm or %"); - def->min = 0; - def->ratio_over = "layer_height"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); - - def = this->add("seam_slope_entire_loop", coBool); - def->label = L("Scarf around entire wall"); - def->category = L("Print/Quality"); - def->tooltip = L("The scarf extends to the entire length of the wall."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("seam_slope_min_length", coFloat); - def->label = L("Scarf length"); - def->category = L("Print/Quality"); - def->tooltip = L("Length of the scarf. Setting this parameter to zero effectively disables the scarf."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(20)); - - def = this->add("seam_slope_steps", coInt); - def->label = L("Scarf steps"); - def->category = L("Print/Quality"); - def->tooltip = L("Minimum number of segments of each scarf."); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(10)); - - def = this->add("seam_slope_inner_walls", coBool); - def->label = L("Scarf joint for inner walls"); - def->category = L("Print/Quality"); - def->tooltip = L("Use scarf joint for inner walls as well."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("role_based_wipe_speed", coBool); - def->label = L("Role base wipe speed"); - def->category = L("Print/Quality"); - def->tooltip = L("The wipe speed is determined by the speed of the current extrusion role. e.g. if a wipe action is executed immediately following an outer wall extrusion, the speed of the outer wall extrusion will be utilized for the wipe action."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("wipe_on_loops", coBool); - def->label = L("Wipe on loops"); - def->category = L("Print/Quality"); - def->tooltip = L("To minimize the visibility of the seam in a closed loop extrusion, a small inward movement is executed before the extruder leaves the loop."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("wipe_before_external_loop", coBool); - def->label = L("Wipe before external loop"); - def->category = L("Print/Quality"); - def->tooltip = L("To minimize visibility of potential overextrusion at the start of an external perimeter when printing with Outer/Inner or Inner/Outer/Inner wall print order, the de-retraction is performed slightly on the inside from the start of the external perimeter. That way any potential over extrusion is hidden from the outside surface.\n\nThis is useful when printing with Outer/Inner or Inner/Outer/Inner wall print order as in these modes it is more likely an external perimeter is printed immediately after a de-retraction move."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("wipe_speed", coFloatOrPercent); - def->label = L("Wipe speed"); - def->category = L("Print/Quality"); - def->tooltip = L("The wipe speed is determined by the speed setting specified in this configuration. If the value is expressed as a percentage (e.g. 80%), it will be calculated based on the travel speed setting above. The default value for this parameter is 80%."); - def->sidetext = L("mm/s or %"); - def->min = 0; - def->ratio_over = "travel_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(80,true)); - - def = this->add("internal_solid_infill_line_width", coFloatOrPercent); - def->label = L("Internal solid infill"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width of internal solid infill. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("slice_closing_radius", coFloat); - def->label = L("Slice gap closing radius"); - def->category = L("Print/Quality"); - def->tooltip = L("Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.049)); - - def = this->add("support_line_width", coFloatOrPercent); - def->label = L("Support"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width of support. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("top_surface_line_width", coFloatOrPercent); - def->label = L("Top surface"); - def->category = L("Print/Quality"); - def->tooltip = L("Line width for top surfaces. If expressed as a %, it will be computed over the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 1000; - def->max_literal = 10; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - - def = this->add("xy_hole_compensation", coFloat); - def->label = L("X-Y hole compensation"); - def->category = L("Print/Quality"); - def->tooltip = L("Holes in objects will expand or contract in the XY plane by the configured value. Positive values make holes bigger, negative values make holes smaller. This function is used to adjust sizes slightly when the objects have assembling issues."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("xy_contour_compensation", coFloat); - def->label = L("X-Y contour compensation"); - def->category = L("Print/Quality"); - def->tooltip = L("Contours of objects will expand or contract in the XY plane by the configured value. Positive values make contours bigger, negative values make contours smaller. This function is used to adjust sizes slightly when the objects have assembling issues."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("hole_to_polyhole", coBool); - def->label = L("Convert holes to polyholes"); - def->category = L("Print/Quality"); - def->tooltip = L("Search for almost-circular holes that span more than one layer and convert the geometry to polyholes. Use the nozzle size and the (biggest) diameter to compute the polyhole.\nSee http://hydraraptor.blogspot.com/2011/02/polyholes.html"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("hole_to_polyhole_threshold", coFloatOrPercent); - def->label = L("Polyhole detection margin"); - def->category = L("Print/Quality"); - def->tooltip = L("Maximum defection of a point to the estimated radius of the circle.\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference. This setting allows you some leeway to broaden the detection.\nIn mm or in % of the radius."); - def->sidetext = L("mm or %"); - def->max_literal = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0.01, false)); - - def = this->add("hole_to_polyhole_twisted", coBool); - def->label = L("Polyhole twist"); - def->category = L("Print/Quality"); - def->tooltip = L("Rotate the polyhole every layer."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("wall_generator", coEnum); - def->label = L("Wall generator"); - def->category = L("Print/Quality"); - def->tooltip = L("Classic wall generator produces walls with constant extrusion width and for very thin areas is used gap-fill. Arachne engine produces walls with variable extrusion width."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("classic"); - def->enum_values.push_back("arachne"); - def->enum_labels.push_back(L("Classic")); - def->enum_labels.push_back(L("Arachne")); - def->set_default_value(new ConfigOptionEnum(PerimeterGeneratorType::Arachne)); - - def = this->add("wall_transition_length", coPercent); - def->label = L("Wall transition length"); - def->category = L("Print/Quality"); - def->tooltip = L("When transitioning between different numbers of walls as the part becomes thinner, a certain amount of space is allotted to split or join the wall segments. It's expressed as a percentage over nozzle diameter."); - def->sidetext = L("%"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(100)); - - def = this->add("wall_transition_filter_deviation", coPercent); - def->label = L("Wall transitioning filter margin"); - def->category = L("Print/Quality"); - def->tooltip = L("Prevent transitioning back and forth between one extra wall and one less. This margin extends the range of extrusion widths which follow to [Minimum wall width - margin, 2 * Minimum wall width + margin]. Increasing this margin reduces the number of transitions, which reduces the number of extrusion starts/stops and travel time. However, large extrusion width variation can lead to under- or overextrusion problems. It's expressed as a percentage over nozzle diameter."); - def->sidetext = L("%"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(25)); - - def = this->add("wall_transition_angle", coFloat); - def->label = L("Wall transitioning threshold angle"); - def->category = L("Print/Quality"); - def->tooltip = L("When to create transitions between even and odd numbers of walls. A wedge shape with an angle greater than this setting will not have transitions and no walls will be printed in the center to fill the remaining space. Reducing this setting reduces the number and length of these center walls, but may leave gaps or overextrude."); - def->sidetext = L("°"); - def->min = 1; - def->max = 59; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(10.)); - - def = this->add("wall_distribution_count", coInt); - def->label = L("Wall distribution count"); - def->category = L("Print/Quality"); - def->tooltip = L("The number of walls, counted from the center, over which the variation needs to be spread. Lower values mean that the outer walls don't change in width."); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("min_feature_size", coPercent); - def->label = L("Minimum feature size"); - def->category = L("Print/Quality"); - def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker than than this value will be widened to the minimum wall width. It's expressed as a percentage over nozzle diameter."); - def->sidetext = L("%"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(25)); - - def = this->add("min_length_factor", coFloat); - def->label = L("Minimum wall length"); - def->category = L("Print/Quality"); - def->tooltip = L("Adjust this value to prevent short, unclosed walls from being printed, which could increase print time. Higher values remove more and longer walls.\n\nNOTE: Bottom and top surfaces will not be affected by this value to prevent visual gaps on the outside of the model. Adjust 'One wall threshold' in the Advanced settings below to adjust the sensitivity of what is considered a top-surface. 'One wall threshold' is only visible if this setting is set above the default value of 0.5, or if single-wall top surfaces is enabled."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 25; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.5)); - - def = this->add("wall_maximum_resolution", coFloat); - def->label = L("Maximum wall resolution"); - def->category = L("Print/Quality"); - def->tooltip = L("This value determines the smallest wall line segment length in mm. The smaller you set this value, the more accurate and precise the walls will be."); - def->sidetext = L("mm"); - def->min = 0.005; - def->max = 0.5; - def->set_default_value(new ConfigOptionFloat(0.5f)); - - def = this->add("wall_maximum_deviation", coFloat); - def->label = L("Maximum wall deviation"); - def->category = L("Print/Quality"); - def->tooltip = L("The maximum deviation allowed when reducing the resolution for the 'Maximum wall resolution' setting. If you increase this, the print will be less accurate, but the G-Code will be smaller. 'Maximum wall deviation' limits 'Maximum wall resolution', so if the two conflict, 'Maximum wall deviation' takes precedence."); - def->sidetext = L("mm"); - def->min = 0.005; - def->max = 0.05; - def->set_default_value(new ConfigOptionFloat(0.025f)); - - def = this->add("initial_layer_min_bead_width", coPercent); - def->label = L("First layer minimum wall width"); - def->category = L("Print/Quality"); - def->tooltip = L("The minimum wall width that should be used for the first layer is recommended to be set to the same size as the nozzle. This adjustment is expected to enhance adhesion."); - def->sidetext = L("%"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(85)); - - def = this->add("min_bead_width", coPercent); - def->label = L("Minimum wall width"); - def->category = L("Print/Quality"); - def->tooltip = L("Width of the wall that will replace thin features (according to the Minimum feature size) of the model. If the Minimum wall width is thinner than the thickness of the feature, the wall will become as thick as the feature itself. It's expressed as a percentage over nozzle diameter."); - def->sidetext = L("%"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(85)); - - def = this->add("enable_overhang_speed", coBool); - def->label = L("Slow down for overhang"); - def->category = L("Print/Speed"); - def->tooltip = L("Enable this option to slow printing down for different overhang degree."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("slowdown_for_curled_perimeters", coBool); - def->label = L("Slow down for curled perimeters"); - def->category = L("Print/Speed"); - def->tooltip = L("Enable this option to slow down printing in areas where perimeters may have curled upwards. For example, additional slowdown will be applied when printing overhangs on sharp corners like the front of the Benchy hull, reducing curling which compounds over multiple layers.\n\nIt is generally recommended to have this option switched on unless your printer cooling is powerful enough or the print speed slow enough that perimeter curling does not happen. If printing with a high external perimeter speed, this parameter may introduce slight artifacts when slowing down due to the large variance in print speeds. If you notice artifacts, ensure your pressure advance is tuned correctly.\n\nNote: When this option is enabled, overhang perimeters are treated like overhangs, meaning the overhang speed is applied even if the overhanging perimeter is part of a bridge. For example, when the perimeters are 100% overhanging, with no wall supporting them from underneath, the 100% overhang speed will be applied."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("overhang_1_4_speed", coFloatOrPercent); - def->label = L("10%"); - def->full_label = L("10%"); - def->category = L("Print/Speed"); - def->sidetext = L("mm/s or %"); - def->min = 0; - def->ratio_over = "outer_wall_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); - - def = this->add("overhang_2_4_speed", coFloatOrPercent); - def->label = L("25%"); - def->full_label = L("25%"); - def->category = L("Print/Speed"); - def->sidetext = L("mm/s or %"); - def->min = 0; - def->ratio_over = "outer_wall_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); - - def = this->add("overhang_3_4_speed", coFloatOrPercent); - def->label = L("50%"); - def->full_label = L("50%"); - def->category = L("Print/Speed"); - def->sidetext = L("mm/s or %"); - def->min = 0; - def->ratio_over = "outer_wall_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); - - def = this->add("overhang_4_4_speed", coFloatOrPercent); - def->label = L("75%"); - def->full_label = L("75%"); - def->category = L("Print/Speed"); - def->sidetext = L("mm/s or %"); - def->min = 0; - def->ratio_over = "outer_wall_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); - - def = this->add("bridge_speed", coFloat); - def->label = L("External"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of the externally visible bridge extrusions.\n\nIn addition, if Slow down for curled perimeters is disabled or Classic overhang mode is enabled, it will be the print speed of overhang walls that are supported by less than 13%, whether they are part of a bridge or an overhang."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(25)); - - def = this->add("internal_bridge_speed", coFloatOrPercent); - def->label = L("Internal"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of internal bridges. If the value is expressed as a percentage, it will be calculated based on the bridge_speed. Default value is 150%."); - def->sidetext = L("mm/s or %"); - def->min = 1; - def->ratio_over = "bridge_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(150, true)); - - def = this->add("default_acceleration", coFloat); - def->label = L("Normal printing"); - def->category = L("Print/Speed"); - def->tooltip = L("The default acceleration of both normal printing and travel except initial layer."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(500.0)); - - def = this->add("outer_wall_speed", coFloat); - def->label = L("Outer wall"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of outer wall which is outermost and visible. It's used to be slower than inner wall speed to get better quality."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60)); - - def = this->add("small_perimeter_speed", coFloatOrPercent); - def->label = L("Small perimeters"); - def->category = L("Print/Speed"); - def->tooltip = L("This separate setting will affect the speed of perimeters having radius <= small_perimeter_threshold (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the outer wall speed setting above. Set to zero for auto."); - def->sidetext = L("mm/s or %"); - def->min = 1; - def->ratio_over = "outer_wall_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(50, true)); - - def = this->add("small_perimeter_threshold", coFloat); - def->label = L("Small perimeters threshold"); - def->category = L("Print/Speed"); - def->tooltip = L("This sets the threshold for small perimeter length. Default threshold is 0mm."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("inner_wall_acceleration", coFloat); - def->label = L("Inner wall"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of inner walls."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(10000)); - - def = this->add("travel_acceleration", coFloat); - def->label = L("Travel"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of travel moves."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(10000)); - - def = this->add("top_surface_acceleration", coFloat); - def->label = L("Top surface"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of top surface infill. Using a lower value may improve top surface quality."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(500)); - - def = this->add("outer_wall_acceleration", coFloat); - def->label = L("Outer wall"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of outer wall. Using a lower value can improve quality."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(500)); - - def = this->add("bridge_acceleration", coFloatOrPercent); - def->label = L("Bridge"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of bridges. If the value is expressed as a percentage (e.g. 50%), it will be calculated based on the outer wall acceleration."); - def->sidetext = L("mm/s² or %"); - def->min = 0; - def->ratio_over = "outer_wall_acceleration"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(50,true)); - - def = this->add("sparse_infill_acceleration", coFloatOrPercent); - def->label = L("Sparse infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of sparse infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration."); - def->sidetext = L("mm/s² or %"); - def->min = 0; - def->ratio_over = "default_acceleration"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("internal_solid_infill_acceleration", coFloatOrPercent); - def->label = L("Internal solid infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of internal solid infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration."); - def->sidetext = L("mm/s² or %"); - def->min = 0; - def->ratio_over = "default_acceleration"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("initial_layer_acceleration", coFloat); - def->label = L("First layer"); - def->category = L("Print/Speed"); - def->tooltip = L("Acceleration of the first layer. Using a lower value can improve build plate adhesion."); - def->sidetext = L("mm/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(300)); - - def = this->add("initial_layer_travel_acceleration", coFloatOrPercent); - def->label = L("First layer travel"); - def->category = L("Print/Speed"); - def->tooltip = L("Travel acceleration of first layer.\nThe percentage value is relative to Travel Acceleration."); - def->sidetext = L("mm/s² or %"); - def->min = 0; - def->ratio_over = "travel_acceleration"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("accel_to_decel_enable", coBool); - def->label = L("Enable accel_to_decel"); - def->category = L("Print/Speed"); - def->tooltip = L("Klipper's max_accel_to_decel will be adjusted automatically."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("accel_to_decel_factor", coPercent); - def->label = L("accel_to_decel"); - def->category = L("Print/Speed"); - def->tooltip = L("Klipper's max_accel_to_decel will be adjusted to this %% of acceleration."); - def->sidetext = L("%"); - def->min = 1; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(50)); - - def = this->add("default_jerk", coFloat); - def->label = L("Default"); - def->category = L("Print/Speed"); - def->tooltip = L("Default jerk."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("default_junction_deviation", coFloat); - def->label = L("Junction Deviation"); - def->category = L("Print/Speed"); - def->tooltip = L("Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting)."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 0.3; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.f)); - - def = this->add("outer_wall_jerk", coFloat); - def->label = L("Outer wall"); - def->category = L("Print/Speed"); - def->tooltip = L("Jerk of outer walls."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(9)); - - def = this->add("inner_wall_jerk", coFloat); - def->label = L("Inner wall"); - def->category = L("Print/Speed"); - def->tooltip = L("Jerk of inner walls."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(9)); - - def = this->add("top_surface_jerk", coFloat); - def->label = L("Top surface"); - def->category = L("Print/Speed"); - def->tooltip = L("Jerk for top surface."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(9)); - - def = this->add("infill_jerk", coFloat); - def->label = L("Infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Jerk for infill."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(9)); - - def = this->add("initial_layer_jerk", coFloat); - def->label = L("First layer"); - def->category = L("Print/Speed"); - def->tooltip = L("Jerk for the first layer."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(9)); - - def = this->add("travel_jerk", coFloat); - def->label = L("Travel"); - def->category = L("Print/Speed"); - def->tooltip = L("Jerk for travel."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(12)); - - def = this->add("initial_layer_travel_jerk", coFloatOrPercent); - def->label = L("First layer travel"); - def->category = L("Print/Speed"); - def->tooltip = L("Travel jerk of first layer.\nThe percentage value is relative to Travel Jerk."); - def->sidetext = L("mm/s or %"); - def->min = 0; - def->ratio_over = "travel_jerk"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("initial_layer_speed", coFloat); - def->label = L("First layer"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of the first layer except the solid infill part."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(30)); - - def = this->add("initial_layer_infill_speed", coFloat); - def->label = L("First layer infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of solid infill part of the first layer."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60.0)); - - def = this->add("initial_layer_travel_speed", coFloatOrPercent); - def->label = L("First layer travel speed"); - def->category = L("Print/Speed"); - def->tooltip = L("Travel speed of the first layer."); - def->sidetext = L("mm/s or %"); - def->min = 1; - def->ratio_over = "travel_speed"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("slow_down_layers", coInt); - def->label = L("Number of slow layers"); - def->category = L("Print/Speed"); - def->tooltip = L("The first few layers are printed slower than normal. The speed is gradually increased in a linear fashion over the specified number of layers."); - def->sidetext = L("layers"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("gap_infill_speed", coFloat); - def->label = L("Gap infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of gap infill. Gap usually has irregular line width and should be printed more slowly."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(30)); - - def = this->add("sparse_infill_speed", coFloat); - def->label = L("Sparse infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of internal sparse infill."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(100)); - - def = this->add("ironing_speed", coFloat); - def->label = L("Ironing speed"); - def->category = L("Print/Speed"); - def->tooltip = L("Print speed of ironing lines."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(20)); - - def = this->add("max_volumetric_extrusion_rate_slope", coFloat); - def->label = L("Extrusion rate smoothing"); - def->category = L("Print/Speed"); - def->tooltip = L("This parameter smooths out sudden extrusion rate changes that happen when the printer transitions from printing a high flow (high speed/larger width) extrusion to a lower flow (lower speed/smaller width) extrusion and vice versa.\n\nIt defines the maximum rate by which the extruded volumetric flow in mm³/s can change over time. Higher values mean higher extrusion rate changes are allowed, resulting in faster speed transitions.\n\nA value of 0 disables the feature.\n\nFor a high speed, high flow direct drive printer (like the Bambu lab or Voron) this value is usually not needed. However it can provide some marginal benefit in certain cases where feature speeds vary greatly. For example, when there are aggressive slowdowns due to overhangs. In these cases a high value of around 300-350 mm³/s² is recommended as this allows for just enough smoothing to assist pressure advance achieve a smoother flow transition.\n\nFor slower printers without pressure advance, the value should be set much lower. A value of 10-15 mm³/s² is a good starting point for direct drive extruders and 5-10 mm³/s² for Bowden style.\n\nThis feature is known as Pressure Equalizer in Prusa slicer.\n\nNote: this parameter disables arc fitting."); - def->sidetext = L("mm³/s²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("max_volumetric_extrusion_rate_slope_segment_length", coFloat); - def->label = L("Smoothing segment length"); - def->category = L("Print/Speed"); - def->tooltip = L("A lower value results in smoother extrusion rate transitions. However, this results in a significantly larger G-code file and more instructions for the printer to process.\n\nDefault value of 3 works well for most cases. If your printer is stuttering, increase this value to reduce the number of adjustments made.\n\nAllowed values: 0.5-5"); - def->sidetext = L("mm"); - def->min = 0.5; - def->max = 5; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(3.0)); - - def = this->add("extrusion_rate_smoothing_external_perimeter_only", coBool); - def->label = L("Apply only on external features"); - def->category = L("Print/Speed"); - def->tooltip = L("Applies extrusion rate smoothing only on external perimeters and overhangs. This can help reduce artefacts due to sharp speed transitions on externally visible overhangs without impacting the print speed of features that will not be visible to the user."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("inner_wall_speed", coFloat); - def->label = L("Inner wall"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of inner wall."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60)); - - def = this->add("internal_solid_infill_speed", coFloat); - def->label = L("Internal solid infill"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of internal solid infill, not the top and bottom surface."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(100)); - - def = this->add("support_interface_speed", coFloat); - def->label = L("Support interface"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of support interface."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(80)); - - def = this->add("support_speed", coFloat); - def->label = L("Support"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of support."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(80)); - - def = this->add("top_surface_speed", coFloat); - def->label = L("Top surface"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of top surface infill which is solid."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(100)); - - def = this->add("travel_speed", coFloat); - def->label = L("Travel"); - def->category = L("Print/Speed"); - def->tooltip = L("Speed of travel which is faster and without extrusion."); - def->sidetext = L("mm/s"); - def->min = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(120)); - - def = this->add("bridge_no_support", coBool); - def->label = L("Don't support bridges"); - def->category = L("Print/Support"); - def->tooltip = L("Don't support the whole bridge area which make support very large. Bridges can usually be printed directly without support if not very long."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("max_bridge_length", coFloat); - def->label = L("Max bridge length"); - def->category = L("Print/Support"); - def->tooltip = L("Max length of bridges that don't need support. Set it to 0 if you want all bridges to be supported, and set it to a very large value if you don't want any bridges to be supported."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(10)); - - def = this->add("raft_contact_distance", coFloat); - def->label = L("Raft contact Z distance"); - def->category = L("Print/Support"); - def->tooltip = L("Z gap between raft and object. If Support Top Z Distance is 0, this value is ignored and the object is printed in direct contact with the raft (no gap)."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.1)); - - def = this->add("raft_first_layer_density", coPercent); - def->label = L("First layer density"); - def->category = L("Print/Support"); - def->tooltip = L("Density of the first raft or support layer."); - def->sidetext = L("%"); - def->min = 10; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(90)); - - def = this->add("raft_first_layer_expansion", coFloat); - def->label = L("First layer expansion"); - def->category = L("Print/Support"); - def->tooltip = L("Expand the first raft or support layer to improve bed plate adhesion."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2.0)); - - def = this->add("raft_layers", coInt); - def->label = L("Raft layers"); - def->category = L("Print/Support"); - def->tooltip = L("Object will be raised by this number of support layers. Use this function to avoid warping when printing ABS."); - def->sidetext = L("layers"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(INITIAL_RAFT_LAYERS)); - - def = this->add("enable_support", coBool); - def->label = L("Enable support"); - def->category = L("Print/Support"); - def->tooltip = L("Enable support generation."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("support_type", coEnum); - def->label = L("Type"); - def->category = L("Print/Support"); - def->tooltip = L("Normal (auto) and Tree (auto) are used to generate support automatically. If Normal (manual) or Tree (manual) is selected, only support enforcers are generated."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("normal(auto)"); - def->enum_values.push_back("tree(auto)"); - def->enum_values.push_back("normal(manual)"); - def->enum_values.push_back("tree(manual)"); - def->enum_labels.push_back(L("Normal (auto)")); - def->enum_labels.push_back(L("Tree (auto)")); - def->enum_labels.push_back(L("Normal (manual)")); - def->enum_labels.push_back(L("Tree (manual)")); - def->set_default_value(new ConfigOptionEnum(stNormalAuto)); - - def = this->add("support_object_xy_distance", coFloat); - def->label = L("Support/object XY distance"); - def->category = L("Print/Support"); - def->tooltip = L("XY separation between an object and its support."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.35)); - - def = this->add("support_object_first_layer_gap", coFloat); - def->label = L("Support/object first layer gap"); - def->category = L("Print/Support"); - def->tooltip = L("XY separation between an object and its support at the first layer."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.2)); - - def = this->add("support_angle", coFloat); - def->label = L("Pattern angle"); - def->category = L("Print/Support"); - def->tooltip = L("Use this setting to rotate the support pattern on the horizontal plane."); - def->sidetext = L("°"); - def->min = 0; - def->max = 359; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("support_on_build_plate_only", coBool); - def->label = L("On build plate only"); - def->category = L("Print/Support"); - def->tooltip = L("Don't create support on model surface, only on build plate."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("support_critical_regions_only", coBool); - def->label = L("Support critical regions only"); - def->category = L("Print/Support"); - def->tooltip = L("Only create support for critical regions including sharp tail, cantilever, etc."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("support_remove_small_overhang", coBool); - def->label = L("Ignore small overhangs"); - def->category = L("Print/Support"); - def->tooltip = L("Ignore small overhangs that possibly don't require support."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("support_top_z_distance", coFloat); - def->label = L("Top Z distance"); - def->category = L("Print/Support"); - def->tooltip = L("Z gap between the support's top and object."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->enum_values.push_back("0"); - def->enum_values.push_back("0.1"); - def->enum_values.push_back("0.2"); - def->enum_labels.push_back(L("0 (soluble)")); - def->enum_labels.push_back(L("0.1 (semi-detachable)")); - def->enum_labels.push_back(L("0.2 (detachable)")); - def->set_default_value(new ConfigOptionFloat(0.2)); - - def = this->add("support_bottom_z_distance", coFloat); - def->label = L("Bottom Z distance"); - def->category = L("Print/Support"); - def->tooltip = L("Z gap between the object and the support bottom. If Support Top Z Distance is 0 and the bottom has interface layers, this value is ignored and the support is printed in direct contact with the object (no gap)."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.2)); - - def = this->add("support_filament", coInt); - def->label = L("Support/raft base"); - def->category = L("Print/Support"); - def->tooltip = L("Filament to print support base and raft. \"Default\" means no specific filament for support and current filament is used."); - def->min = 0; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("support_interface_not_for_body", coBool); - def->label = L("Avoid interface filament for base"); - def->category = L("Print/Support"); - def->tooltip = L("Avoid using support interface filament to print support base if possible."); - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("support_interface_filament", coInt); - def->label = L("Support/raft interface"); - def->category = L("Print/Support"); - def->tooltip = L("Filament to print support interface. \"Default\" means no specific filament for support interface and current filament is used."); - def->min = 0; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("support_interface_top_layers", coInt); - def->label = L("Top interface layers"); - def->category = L("Print/Support"); - def->tooltip = L("Number of top interface layers."); - def->sidetext = L("layers"); - def->min = 0; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->enum_values.push_back("0"); - def->enum_values.push_back("1"); - def->enum_values.push_back("2"); - def->enum_values.push_back("3"); - def->enum_labels.push_back(L("0")); - def->enum_labels.push_back(L("1")); - def->enum_labels.push_back(L("2")); - def->enum_labels.push_back(L("3")); - def->set_default_value(new ConfigOptionInt(3)); - - def = this->add("support_interface_bottom_layers", coInt); - def->label = L("Bottom interface layers"); - def->category = L("Print/Support"); - def->tooltip = L("Number of bottom interface layers."); - def->sidetext = L("layers"); - def->min = -1; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->enum_values.push_back("-1"); - def->enum_labels.push_back(L("Same as top")); - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("support_interface_spacing", coFloat); - def->label = L("Top interface spacing"); - def->category = L("Print/Support"); - def->tooltip = L("Spacing of interface lines. Zero means solid interface.\nForce using solid interface when support ironing is enabled."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.5)); - - def = this->add("support_bottom_interface_spacing", coFloat); - def->label = L("Bottom interface spacing"); - def->category = L("Print/Support"); - def->tooltip = L("Spacing of bottom interface lines. Zero means solid interface."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.5)); - - def = this->add("support_base_pattern", coEnum); - def->label = L("Base pattern"); - def->category = L("Print/Support"); - def->tooltip = L("Line pattern of support.\n\nThe Default option for Tree supports is Hollow, which means no base pattern. For other support types, the Default option is the Rectilinear pattern.\n\nNOTE: For Organic supports, the two walls are supported only with the Hollow/Default base pattern. The Lightning base pattern is supported only by Tree Slim/Strong/Hybrid supports. For the other support types, the Rectilinear will be used instead of Lightning."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("default"); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("rectilinear-grid"); - def->enum_values.push_back("honeycomb"); - def->enum_values.push_back("lightning"); - def->enum_values.push_back("hollow"); - def->enum_labels.push_back(L("Default")); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Rectilinear grid")); - def->enum_labels.push_back(L("Honeycomb")); - def->enum_labels.push_back(L("Lightning")); - def->enum_labels.push_back(L("Hollow")); - def->set_default_value(new ConfigOptionEnum(smpDefault)); - - def = this->add("support_interface_pattern", coEnum); - def->label = L("Interface pattern"); - def->category = L("Print/Support"); - def->tooltip = L("Line pattern of support interface. Default pattern for non-soluble support interface is Rectilinear, while default pattern for soluble support interface is Concentric."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("auto"); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("concentric"); - def->enum_values.push_back("rectilinear_interlaced"); - def->enum_values.push_back("grid"); - def->enum_labels.push_back(L("Default")); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Concentric")); - def->enum_labels.push_back(L("Rectilinear Interlaced")); - def->enum_labels.push_back(L("Grid")); - def->set_default_value(new ConfigOptionEnum(smipAuto)); - - def = this->add("support_base_pattern_spacing", coFloat); - def->label = L("Base pattern spacing"); - def->category = L("Print/Support"); - def->tooltip = L("Spacing between support lines."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2.5)); - - def = this->add("support_expansion", coFloat); - def->label = L("Normal Support expansion"); - def->category = L("Print/Support"); - def->tooltip = L("Expand (+) or shrink (-) the horizontal span of normal support."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("support_style", coEnum); - def->label = L("Style"); - def->category = L("Print/Support"); - def->tooltip = L("Style and shape of the support. For normal support, projecting the supports into a regular grid will create more stable supports (default), while snug support towers will save material and reduce object scarring.\nFor tree support, slim and organic style will merge branches more aggressively and save a lot of material (default organic), while hybrid style will create similar structure to normal support under large flat overhangs."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("default"); - def->enum_values.push_back("grid"); - def->enum_values.push_back("snug"); - def->enum_values.push_back("organic"); - def->enum_values.push_back("tree_slim"); - def->enum_values.push_back("tree_strong"); - def->enum_values.push_back("tree_hybrid"); - def->enum_labels.push_back(L("Default (Grid/Organic)")); - def->enum_labels.push_back(L("Grid")); - def->enum_labels.push_back(L("Snug")); - def->enum_labels.push_back(L("Organic")); - def->enum_labels.push_back(L("Tree Slim")); - def->enum_labels.push_back(L("Tree Strong")); - def->enum_labels.push_back(L("Tree Hybrid")); - def->set_default_value(new ConfigOptionEnum(smsDefault)); - - def = this->add("independent_support_layer_height", coBool); - def->label = L("Independent support layer height"); - def->category = L("Print/Support"); - def->tooltip = L("Support layer uses layer height independent with object layer. This is to support customizing Z-gap and save print time. This option will be invalid when the prime tower is enabled."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("support_threshold_angle", coInt); - def->label = L("Threshold angle"); - def->category = L("Print/Support"); - def->tooltip = L("Support will be generated for overhangs whose slope angle is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support.\nNote: If set to 0, normal supports use the Threshold overlap instead, while tree supports fall back to a default value of 30."); - def->sidetext = L("°"); - def->min = 0; - def->max = 90; - def->set_default_value(new ConfigOptionInt(30)); - - def = this->add("support_threshold_overlap", coFloatOrPercent); - def->label = L("Threshold overlap"); - def->category = L("Print/Support"); - def->tooltip = L("If threshold angle is zero, support will be generated for overhangs whose overlap is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support."); - def->sidetext = L("mm or %"); - def->min = 0; - def->max = 100; - def->max_literal = 0.5; - def->set_default_value(new ConfigOptionFloatOrPercent(50., true)); - - def = this->add("tree_support_branch_angle", coFloat); - def->label = L("Tree support branch angle"); - def->category = L("Print/Support"); - def->tooltip = L("This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther."); - def->sidetext = L("°"); - def->min = 0; - def->max = 60; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(40.)); - - def = this->add("tree_support_branch_angle_organic", coFloat); - def->label = L("Tree support branch angle"); - def->category = L("Print/Support"); - def->tooltip = L("This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther."); - def->sidetext = L("°"); - def->min = 0; - def->max = 60; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(40.)); - - def = this->add("tree_support_angle_slow", coFloat); - def->label = L("Preferred Branch Angle"); - def->category = L("Print/Support"); - def->tooltip = L("The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster."); - def->sidetext = L("°"); - def->min = 10; - def->max = 85; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(25)); - - def = this->add("tree_support_branch_distance", coFloat); - def->label = L("Tree support branch distance"); - def->category = L("Print/Support"); - def->tooltip = L("This setting determines the distance between neighboring tree support nodes."); - def->sidetext = L("mm"); - def->min = 1; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(5.)); - - def = this->add("tree_support_branch_distance_organic", coFloat); - def->label = L("Tree support branch distance"); - def->category = L("Print/Support"); - def->tooltip = L("This setting determines the distance between neighboring tree support nodes."); - def->sidetext = L("mm"); - def->min = 1; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1.)); - - def = this->add("tree_support_top_rate", coPercent); - def->label = L("Branch Density"); - def->category = L("Print/Support"); - def->tooltip = L("Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces instead of a high branch density value if dense interfaces are needed."); - def->sidetext = L("%"); - def->min = 5; - def->max_literal = 35; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(30)); - - def = this->add("tree_support_auto_brim", coBool); - def->label = L("Auto brim width"); - def->category = L("Print/Support"); - def->tooltip = L("Enabling this option means the width of the brim for tree support will be automatically calculated."); - def->set_default_value(new ConfigOptionBool(1)); - - def = this->add("tree_support_brim_width", coFloat); - def->label = L("Tree support brim width"); - def->category = L("Print/Support"); - def->tooltip = L("Distance from tree branch to the outermost brim line."); - def->min = 0; - def->set_default_value(new ConfigOptionFloat(3)); - - def = this->add("tree_support_tip_diameter", coFloat); - def->label = L("Tip Diameter"); - def->category = L("Print/Support"); - def->tooltip = L("Branch tip diameter for organic supports."); - def->sidetext = L("mm"); - def->min = 0.1; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.8)); - - def = this->add("tree_support_branch_diameter", coFloat); - def->label = L("Tree support branch diameter"); - def->category = L("Print/Support"); - def->tooltip = L("This setting determines the initial diameter of support nodes."); - def->sidetext = L("mm"); - def->min = 1; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(5.)); - - def = this->add("tree_support_branch_diameter_angle", coFloat); - def->label = L("Branch Diameter Angle"); - def->category = L("Print/Support"); - def->tooltip = L("The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the organic support."); - def->sidetext = L("°"); - def->min = 0; - def->max = 15; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(5)); - - def = this->add("tree_support_branch_diameter_organic", coFloat); - def->label = L("Tree support branch diameter"); - def->category = L("Print/Support"); - def->tooltip = L("This setting determines the initial diameter of support nodes."); - def->sidetext = L("mm"); - def->min = 1; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2.)); - - def = this->add("tree_support_wall_count", coInt); - def->label = L("Support wall loops"); - def->category = L("Print/Support"); - def->tooltip = L("This setting specifies the count of support walls in the range of [0,2]. 0 means auto."); - def->min = 0; - def->max = 2; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("support_ironing", coBool); - def->label = L("Ironing Support Interface"); - def->category = L("Print/Support"); - def->tooltip = L("Ironing is using small flow to print on same height of support interface again to make it more smooth. This setting controls whether support interface being ironed. When enabled, support interface will be extruded as solid too."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("support_ironing_pattern", coEnum); - def->label = L("Support Ironing Pattern"); - def->category = L("Print/Support"); - def->tooltip = L("The pattern that will be used when ironing."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("concentric"); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Concentric")); - def->set_default_value(new ConfigOptionEnum(ipRectilinear)); - - def = this->add("support_ironing_flow", coPercent); - def->label = L("Support Ironing flow"); - def->category = L("Print/Support"); - def->tooltip = L("The amount of material to extrude during ironing. Relative to flow of normal support interface layer height. Too high value results in overextrusion on the surface."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->ratio_over = "layer_height"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(10)); - - def = this->add("support_ironing_spacing", coFloat); - def->label = L("Support Ironing line spacing"); - def->category = L("Print/Support"); - def->tooltip = L("The distance between the lines of ironing."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 1; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.1)); - - def = this->add("first_layer_sequence_choice", coEnum); - def->label = L("First layer filament sequence"); - def->category = L("Quality"); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Auto"); - def->enum_values.push_back("Customize"); - def->enum_labels.push_back(L("Auto")); - def->enum_labels.push_back(L("Customize")); - def->set_default_value(new ConfigOptionEnum(flsAuto)); - - def = this->add("other_layers_sequence_choice", coEnum); - def->label = L("Other layers filament sequence"); - def->category = L("Quality"); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Auto"); - def->enum_values.push_back("Customize"); - def->enum_labels.push_back(L("Auto")); - def->enum_labels.push_back(L("Customize")); - def->set_default_value(new ConfigOptionEnum(flsAuto)); - - def = this->add("ironing_expansion", coFloat); - def->label = L("Ironing expansion"); - def->category = L("Quality"); - def->tooltip = L("Expand or contract the ironing area."); - def->sidetext = L("mm"); - def->min = -100; - def->max = 100; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("bottom_shell_layers", coInt); - def->label = L("Bottom shell layers"); - def->full_label = L("Bottom shell layers"); - def->category = L("Print/Strength"); - def->tooltip = L("This is the number of solid layers of bottom shell, including the bottom surface layer. When the thickness calculated by this value is thinner than bottom shell thickness, the bottom shell layers will be increased."); - def->sidetext = L("layers"); - def->min = 0; - def->set_default_value(new ConfigOptionInt(3)); - - def = this->add("bottom_shell_thickness", coFloat); - def->label = L("Bottom shell thickness"); - def->full_label = L("Bottom shell thickness"); - def->category = L("Print/Strength"); - def->tooltip = L("The number of bottom solid layers is increased when slicing if the thickness calculated by bottom shell layers is thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that this setting is disabled and thickness of bottom shell is absolutely determined by bottom shell layers."); - def->sidetext = L("mm"); - def->min = 0; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("gap_fill_target", coEnum); - def->label = L("Apply gap fill"); - def->category = L("Print/Strength"); - def->tooltip = L("Enables gap fill for the selected solid surfaces. The minimum gap length that will be filled can be controlled from the filter out tiny gaps option below.\n\nOptions:\n1. Everywhere: Applies gap fill to top, bottom and internal solid surfaces for maximum strength\n2. Top and Bottom surfaces: Applies gap fill to top and bottom surfaces only, balancing print speed, reducing potential over extrusion in the solid infill and making sure the top and bottom surfaces have no pinhole gaps\n3. Nowhere: Disables gap fill for all solid infill areas\n\nNote that if using the classic perimeter generator, gap fill may also be generated between perimeters, if a full width line cannot fit between them. That perimeter gap fill is not controlled by this setting.\n\nIf you would like all gap fill, including the classic perimeter generated one, removed, set the filter out tiny gaps value to a large number, like 999999.\n\nHowever this is not advised, as gap fill between perimeters is contributing to the model's strength. For models where excessive gap fill is generated between perimeters, a better option would be to switch to the arachne wall generator and use this option to control whether the cosmetic top and bottom surface gap fill is generated."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("everywhere"); - def->enum_values.push_back("topbottom"); - def->enum_values.push_back("nowhere"); - def->enum_labels.push_back(L("Everywhere")); - def->enum_labels.push_back(L("Top and bottom surfaces")); - def->enum_labels.push_back(L("Nowhere")); - def->set_default_value(new ConfigOptionEnum(gftNowhere)); - - def = this->add("bridge_angle", coFloat); - def->label = L("External bridge infill direction"); - def->category = L("Print/Strength"); - def->tooltip = L("Bridging angle override. If left to zero, the bridging angle will be calculated automatically. Otherwise the provided angle will be used for external bridges. Use 180° for zero angle."); - def->sidetext = L("°"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("internal_bridge_angle", coFloat); - def->label = L("Internal bridge infill direction"); - def->category = L("Print/Strength"); - def->tooltip = L("Internal bridging angle override. If left to zero, the bridging angle will be calculated automatically. Otherwise the provided angle will be used for internal bridges. Use 180° for zero angle.\n\nIt is recommended to leave it at 0 unless there is a specific model need not to."); - def->sidetext = L("°"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("ensure_vertical_shell_thickness", coEnum); - def->label = L("Ensure vertical shell thickness"); - def->category = L("Print/Strength"); - def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top+bottom solid layers)\nNone: No solid infill will be added anywhere. Caution: Use this option carefully if your model has sloped surfaces\nCritical Only: Avoid adding solid infill for walls\nModerate: Add solid infill for heavily sloping surfaces only\nAll: Add solid infill for all suitable sloping surfaces\nDefault value is All."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("none"); - def->enum_values.push_back("ensure_critical_only"); - def->enum_values.push_back("ensure_moderate"); - def->enum_values.push_back("ensure_all"); - def->enum_labels.push_back(L("None")); - def->enum_labels.push_back(L("Critical Only")); - def->enum_labels.push_back(L("Moderate")); - def->enum_labels.push_back(L("All")); - def->set_default_value(new ConfigOptionEnum(EnsureVerticalShellThickness::evstAll)); - - def = this->add("top_surface_pattern", coEnum); - def->label = L("Top surface pattern"); - def->category = L("Print/Strength"); - def->tooltip = L("Line pattern of top surface infill."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("monotonic"); - def->enum_values.push_back("monotonicline"); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("alignedrectilinear"); - def->enum_values.push_back("concentric"); - def->enum_values.push_back("hilbertcurve"); - def->enum_values.push_back("archimedeanchords"); - def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back(L("Monotonic")); - def->enum_labels.push_back(L("Monotonic line")); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Aligned Rectilinear")); - def->enum_labels.push_back(L("Concentric")); - def->enum_labels.push_back(L("Hilbert Curve")); - def->enum_labels.push_back(L("Archimedean Chords")); - def->enum_labels.push_back(L("Octagram Spiral")); - def->set_default_value(new ConfigOptionEnum(ipMonotonicLine)); - - def = this->add("bottom_surface_pattern", coEnum); - def->label = L("Bottom surface pattern"); - def->category = L("Print/Strength"); - def->tooltip = L("Line pattern of bottom surface infill, not bridge infill."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("monotonic"); - def->enum_values.push_back("monotonicline"); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("alignedrectilinear"); - def->enum_values.push_back("concentric"); - def->enum_values.push_back("hilbertcurve"); - def->enum_values.push_back("archimedeanchords"); - def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back(L("Monotonic")); - def->enum_labels.push_back(L("Monotonic line")); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Aligned Rectilinear")); - def->enum_labels.push_back(L("Concentric")); - def->enum_labels.push_back(L("Hilbert Curve")); - def->enum_labels.push_back(L("Archimedean Chords")); - def->enum_labels.push_back(L("Octagram Spiral")); - def->set_default_value(new ConfigOptionEnum(ipMonotonic)); - - def = this->add("internal_solid_infill_pattern", coEnum); - def->label = L("Internal solid infill pattern"); - def->category = L("Print/Strength"); - def->tooltip = L("Line pattern of internal solid infill. if the detect narrow internal solid infill be enabled, the concentric pattern will be used for the small area."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("monotonic"); - def->enum_values.push_back("monotonicline"); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("alignedrectilinear"); - def->enum_values.push_back("concentric"); - def->enum_values.push_back("hilbertcurve"); - def->enum_values.push_back("archimedeanchords"); - def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back(L("Monotonic")); - def->enum_labels.push_back(L("Monotonic line")); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Aligned Rectilinear")); - def->enum_labels.push_back(L("Concentric")); - def->enum_labels.push_back(L("Hilbert Curve")); - def->enum_labels.push_back(L("Archimedean Chords")); - def->enum_labels.push_back(L("Octagram Spiral")); - def->set_default_value(new ConfigOptionEnum(ipMonotonic)); - - def = this->add("infill_direction", coFloat); - def->label = L("Sparse infill direction"); - def->category = L("Print/Strength"); - def->tooltip = L("Angle for sparse infill pattern, which controls the start or main direction of line."); - def->sidetext = L("°"); - def->min = 0; - def->max = 360; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(45)); - - def = this->add("solid_infill_direction", coFloat); - def->label = L("Solid infill direction"); - def->category = L("Print/Strength"); - def->tooltip = L("Angle for solid infill pattern, which controls the start or main direction of line."); - def->sidetext = L("°"); - def->min = 0; - def->max = 360; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(45)); - - def = this->add("sparse_infill_density", coPercent); - def->label = L("Sparse infill density"); - def->category = L("Print/Strength"); - def->tooltip = L("Density of internal sparse infill, 100% turns all sparse infill into solid infill and internal solid infill pattern will be used."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionPercent(20)); - - def = this->add("align_infill_direction_to_model", coBool); - def->label = L("Align infill direction to model"); - def->category = L("Print/Strength"); - def->tooltip = L("Aligns infill and surface fill directions to follow the model's orientation on the build plate. When enabled, fill directions rotate with the model to maintain optimal strength characteristics."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("extra_solid_infills", coString); - def->label = L("Insert solid layers"); - def->category = L("Print/Strength"); - def->tooltip = L("Insert solid infill at specific layers. Use N to insert every Nth layer, N#K to insert K consecutive solid layers every N layers (K is optional, e.g. '5#' equals '5#1'), or a comma-separated list (e.g. 1,7,9) to insert at explicit layers. Layers are 1-based."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString()); - - def = this->add("fill_multiline", coInt); - def->label = L("Fill Multiline"); - def->category = L("Print/Strength"); - def->tooltip = L("Using multiple lines for the infill pattern, if supported by infill pattern."); - def->min = 1; - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("gyroid_optimized", coBool); - def->label = L("Z-buckling bias optimization (experimental)"); - def->category = L("Print/Strength"); - def->tooltip = L("Tightens the gyroid wave along the Z (vertical) axis at low infill density to shorten the effective vertical column length and improve Z-axis compression buckling resistance. Filament use is preserved. No effect at ~30% sparse infill density and above. Only applies when Sparse infill pattern is set to Gyroid."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("sparse_infill_pattern", coEnum); - def->label = L("Sparse infill pattern"); - def->category = L("Print/Strength"); - def->tooltip = L("Line pattern for internal sparse infill."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("rectilinear"); - def->enum_values.push_back("alignedrectilinear"); - def->enum_values.push_back("zigzag"); - def->enum_values.push_back("crosszag"); - def->enum_values.push_back("lockedzag"); - def->enum_values.push_back("line"); - def->enum_values.push_back("grid"); - def->enum_values.push_back("triangles"); - def->enum_values.push_back("tri-hexagon"); - def->enum_values.push_back("cubic"); - def->enum_values.push_back("adaptivecubic"); - def->enum_values.push_back("quartercubic"); - def->enum_values.push_back("supportcubic"); - def->enum_values.push_back("lightning"); - def->enum_values.push_back("honeycomb"); - def->enum_values.push_back("3dhoneycomb"); - def->enum_values.push_back("lateral-honeycomb"); - def->enum_values.push_back("lateral-lattice"); - def->enum_values.push_back("crosshatch"); - def->enum_values.push_back("tpmsd"); - def->enum_values.push_back("tpmsfk"); - def->enum_values.push_back("gyroid"); - def->enum_values.push_back("concentric"); - def->enum_values.push_back("hilbertcurve"); - def->enum_values.push_back("archimedeanchords"); - def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back(L("Rectilinear")); - def->enum_labels.push_back(L("Aligned Rectilinear")); - def->enum_labels.push_back(L("Zig Zag")); - def->enum_labels.push_back(L("Cross Zag")); - def->enum_labels.push_back(L("Locked Zag")); - def->enum_labels.push_back(L("Line")); - def->enum_labels.push_back(L("Grid")); - def->enum_labels.push_back(L("Triangles")); - def->enum_labels.push_back(L("Tri-hexagon")); - def->enum_labels.push_back(L("Cubic")); - def->enum_labels.push_back(L("Adaptive Cubic")); - def->enum_labels.push_back(L("Quarter Cubic")); - def->enum_labels.push_back(L("Support Cubic")); - def->enum_labels.push_back(L("Lightning")); - def->enum_labels.push_back(L("Honeycomb")); - def->enum_labels.push_back(L("3D Honeycomb")); - def->enum_labels.push_back(L("Lateral Honeycomb")); - def->enum_labels.push_back(L("Lateral Lattice")); - def->enum_labels.push_back(L("Cross Hatch")); - def->enum_labels.push_back(L("TPMS-D")); - def->enum_labels.push_back(L("TPMS-FK")); - def->enum_labels.push_back(L("Gyroid")); - def->enum_labels.push_back(L("Concentric")); - def->enum_labels.push_back(L("Hilbert Curve")); - def->enum_labels.push_back(L("Archimedean Chords")); - def->enum_labels.push_back(L("Octagram Spiral")); - def->set_default_value(new ConfigOptionEnum(ipCrossHatch)); - - def = this->add("lateral_lattice_angle_1", coFloat); - def->label = L("Lateral lattice angle 1"); - def->category = L("Print/Strength"); - def->tooltip = L("The angle of the first set of Lateral lattice elements in the Z direction. Zero is vertical."); - def->sidetext = L("°"); - def->min = -75; - def->max = 75; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(-45)); - - def = this->add("lateral_lattice_angle_2", coFloat); - def->label = L("Lateral lattice angle 2"); - def->category = L("Print/Strength"); - def->tooltip = L("The angle of the second set of Lateral lattice elements in the Z direction. Zero is vertical."); - def->sidetext = L("°"); - def->min = -75; - def->max = 75; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(45)); - - def = this->add("infill_overhang_angle", coFloat); - def->label = L("Infill overhang angle"); - def->category = L("Print/Strength"); - def->tooltip = L("The angle of the infill angled lines. 60° will result in a pure honeycomb."); - def->sidetext = L("°"); - def->min = 15; - def->max = 75; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60)); - - def = this->add("infill_anchor", coFloatOrPercent); - def->label = L("Sparse infill anchor length"); - def->category = L("Print/Strength"); - def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Orca Slicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one side and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max.\nSet this parameter to zero to disable anchoring perimeters connected to a single infill line."); - def->sidetext = L("mm or %"); - def->max_literal = 1000; - def->ratio_over = "sparse_infill_line_width"; - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::f_enum_open; - def->enum_values.push_back("0"); - def->enum_values.push_back("1"); - def->enum_values.push_back("2"); - def->enum_values.push_back("5"); - def->enum_values.push_back("10"); - def->enum_values.push_back("1000"); - def->enum_labels.push_back(L("0 (no open anchors)")); - def->enum_labels.push_back(L("1 mm")); - def->enum_labels.push_back(L("2 mm")); - def->enum_labels.push_back(L("5 mm")); - def->enum_labels.push_back(L("10 mm")); - def->enum_labels.push_back(L("1000 (unlimited)")); - def->set_default_value(new ConfigOptionFloatOrPercent(400, true)); - - def = this->add("infill_anchor_max", coFloatOrPercent); - def->label = L("Maximum length of the infill anchor"); - def->category = L("Print/Strength"); - def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Orca Slicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter.\nIf set to 0, the old algorithm for infill connection will be used, it should create the same result as with 1000 & 0."); - def->mode = comAdvanced; - def->enum_labels.push_back(L("0 (Simple connect)")); - def->enum_labels.push_back(L("1 mm")); - def->enum_labels.push_back(L("2 mm")); - def->enum_labels.push_back(L("5 mm")); - def->enum_labels.push_back(L("10 mm")); - def->enum_labels.push_back(L("1000 (unlimited)")); - def->set_default_value(new ConfigOptionFloatOrPercent(20, false)); - - def = this->add("filter_out_gap_fill", coFloat); - def->label = L("Filter out tiny gaps"); - def->category = L("Print/Strength"); - def->tooltip = L("Don't print gap fill with a length is smaller than the threshold specified (in mm). This setting applies to top, bottom and solid infill and, if using the classic perimeter generator, to wall gap fill."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("infill_combination", coBool); - def->label = L("Infill combination"); - def->category = L("Print/Strength"); - def->tooltip = L("Automatically Combine sparse infill of several layers to print together to reduce time. Wall is still printed with original layer height."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("infill_shift_step", coFloat); - def->label = L("Infill shift step"); - def->category = L("Print/Strength"); - def->tooltip = L("This parameter adds a slight displacement to each layer of infill to create a cross texture."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.4)); - - def = this->add("sparse_infill_rotate_template", coString); - def->label = L("Sparse infill rotation template"); - def->category = L("Print/Strength"); - def->tooltip = L("Rotate the sparse infill direction per layer using a template of angles. Enter comma-separated degrees (e.g., '0,30,60,90'). Angles are applied in order by layer and repeat when the list ends. Advanced syntax is supported: '+5' rotates +5° every layer; '+5#5' rotates +5° every 5 layers. See the Wiki for details. "); - def->sidetext = L("°"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString("")); - - def = this->add("solid_infill_rotate_template", coString); - def->label = L("Solid infill rotation template"); - def->category = L("Print/Strength"); - def->tooltip = L("This parameter adds a rotation of solid infill direction to each layer according to the specified template. The template is a comma-separated list of angles in degrees, e.g. '0,90'. The first angle is applied to the first layer, the second angle to the second layer, and so on. If there are more layers than angles, the angles will be repeated. Note that not all solid infill patterns support rotation."); - def->sidetext = L("°"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString("")); - - def = this->add("skeleton_infill_density", coPercent); - def->label = L("Skeleton infill density"); - def->category = L("Print/Strength"); - def->tooltip = L("The remaining part of the model contour after removing a certain depth from the surface is called the skeleton. This parameter is used to adjust the density of this section. When two regions have the same sparse infill settings but different skeleton densities, their skeleton areas will develop overlapping sections. Default is as same as infill density."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(25)); - - def = this->add("skin_infill_density", coPercent); - def->label = L("Skin infill density"); - def->category = L("Print/Strength"); - def->tooltip = L("The portion of the model's outer surface within a certain depth range is called the skin. This parameter is used to adjust the density of this section. When two regions have the same sparse infill settings but different skin densities, this area will not be split into two separate regions. Default is as same as infill density."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(25)); - - def = this->add("skin_infill_depth", coFloat); - def->label = L("Skin infill depth"); - def->category = L("Print/Strength"); - def->tooltip = L("The parameter sets the depth of skin."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2.0)); - - def = this->add("infill_lock_depth", coFloat); - def->label = L("Infill lock depth"); - def->category = L("Print/Strength"); - def->tooltip = L("The parameter sets the overlapping depth between the interior and skin."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1.0)); - - def = this->add("skin_infill_line_width", coFloatOrPercent); - def->label = L("Skin line width"); - def->category = L("Print/Strength"); - def->tooltip = L("Adjust the line width of the selected skin paths."); - def->sidetext = L("mm"); - def->min = 0; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("skeleton_infill_line_width", coFloatOrPercent); - def->label = L("Skeleton line width"); - def->category = L("Print/Strength"); - def->tooltip = L("Adjust the line width of the selected skeleton paths."); - def->sidetext = L("mm"); - def->min = 0; - def->ratio_over = "nozzle_diameter"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); - - def = this->add("symmetric_infill_y_axis", coBool); - def->label = L("Symmetric infill Y axis"); - def->category = L("Print/Strength"); - def->tooltip = L("If the model has two parts that are symmetric about the Y axis, and you want these parts to have symmetric textures, please click this option on one of the parts."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("infill_combination_max_layer_height", coFloatOrPercent); - def->label = L("Infill combination - Max layer height"); - def->category = L("Print/Strength"); - def->tooltip = L("Maximum layer height for the combined sparse infill.\n\nSet it to 0 or 100% to use the nozzle diameter (for maximum reduction in print time) or a value of ~80% to maximize sparse infill strength.\n\nThe number of layers over which infill is combined is derived by dividing this value with the layer height and rounded down to the nearest decimal.\n\nUse either absolute mm values (eg. 0.32mm for a 0.4mm nozzle) or % values (eg 80%). This value must not be larger than the nozzle diameter."); - def->sidetext = L("mm or %"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatOrPercent(100., true)); - - def = this->add("infill_wall_overlap", coPercent); - def->label = L("Infill/Wall overlap"); - def->category = L("Print/Strength"); - def->tooltip = L("Infill area is enlarged slightly to overlap with wall for better bonding. The percentage value is relative to line width of sparse infill. Set this value to ~10-15% to minimize potential over extrusion and accumulation of material resulting in rough top surfaces."); - def->sidetext = L("%"); - def->ratio_over = "inner_wall_line_width"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(15)); - - def = this->add("top_bottom_infill_wall_overlap", coPercent); - def->label = L("Top/Bottom solid infill/wall overlap"); - def->category = L("Print/Strength"); - def->tooltip = L("Top solid infill area is enlarged slightly to overlap with wall for better bonding and to minimize the appearance of pinholes where the top infill meets the walls. A value of 25-30% is a good starting point, minimizing the appearance of pinholes. The percentage value is relative to line width of sparse infill."); - def->sidetext = L("%"); - def->ratio_over = "inner_wall_line_width"; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercent(25)); - - def = this->add("wall_loops", coInt); - def->label = L("Wall loops"); - def->category = L("Print/Strength"); - def->tooltip = L("Number of walls of every layer."); - def->min = 0; - def->max = 1000; - def->set_default_value(new ConfigOptionInt(2)); - - def = this->add("alternate_extra_wall", coBool); - def->label = L("Alternate extra wall"); - def->category = L("Print/Strength"); - def->tooltip = L("This setting adds an extra wall to every other layer. This way the infill gets wedged vertically between the walls, resulting in stronger prints.\n\nWhen this option is enabled, the ensure vertical shell thickness option needs to be disabled.\n\nUsing lightning infill together with this option is not recommended as there is limited infill to anchor the extra perimeters to."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("minimum_sparse_infill_area", coFloat); - def->label = L("Minimum sparse infill threshold"); - def->category = L("Print/Strength"); - def->tooltip = L("Sparse infill areas smaller than this threshold value are replaced by internal solid infill."); - def->sidetext = L("mm²"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(15)); - - def = this->add("detect_thin_wall", coBool); - def->label = L("Detect thin walls"); - def->category = L("Print/Strength"); - def->tooltip = L("Detect thin walls which can't contain two line widths, and use single line to print. Maybe not printed very well, because it's not a closed loop."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("top_shell_layers", coInt); - def->label = L("Top shell layers"); - def->full_label = L("Top solid layers"); - def->category = L("Print/Strength"); - def->tooltip = L("This is the number of solid layers of top shell, including the top surface layer. When the thickness calculated by this value is thinner than top shell thickness, the top shell layers will be increased."); - def->sidetext = L("layers"); - def->min = 0; - def->set_default_value(new ConfigOptionInt(4)); - - def = this->add("top_shell_thickness", coFloat); - def->label = L("Top shell thickness"); - def->full_label = L("Top shell thickness"); - def->category = L("Print/Strength"); - def->tooltip = L("The number of top solid layers is increased when slicing if the thickness calculated by top shell layers is thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that this setting is disabled and thickness of top shell is absolutely determined by top shell layers."); - def->sidetext = L("mm"); - def->min = 0; - def->set_default_value(new ConfigOptionFloat(0.6)); - - def = this->add("top_surface_density", coPercent); - def->label = L("Top surface density"); - def->category = L("Print/Strength"); - def->tooltip = L("Density of top surface layer. A value of 100% creates a fully solid, smooth top layer. Reducing this value results in a textured top surface, according to the chosen top surface pattern. A value of 0% will result in only the walls on the top layer being created. Intended for aesthetic or functional purposes, not to fix issues such as over-extrusion."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->set_default_value(new ConfigOptionPercent(100)); - - def = this->add("bottom_surface_density", coPercent); - def->label = L("Bottom surface density"); - def->category = L("Print/Strength"); - def->tooltip = L("Density of the bottom surface layer. Intended for aesthetic or functional purposes, not to fix issues such as over-extrusion.\nWARNING: Lowering this value may negatively affect bed adhesion."); - def->sidetext = L("%"); - def->min = 10; - def->max = 100; - def->set_default_value(new ConfigOptionPercent(100)); - - def = this->add("detect_narrow_internal_solid_infill", coBool); - def->label = L("Detect narrow internal solid infills"); - def->category = L("Print/Strength"); - def->tooltip = L("This option will auto-detect narrow internal solid infill areas. If enabled, the concentric pattern will be used for the area to speed up printing. Otherwise, the rectilinear pattern will be used by default."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("brim_ears", coBool); - def->label = L("Brim ears"); - def->category = L("Support"); - def->tooltip = L("Only draw brim over the sharp edges of the model."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("raft_expansion", coFloat); - def->label = L("Raft expansion"); - def->category = L("Support"); - def->tooltip = L("Expand all raft layers in XY plane."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1.5)); - - def = this->add("enforce_support_layers", coInt); - def->category = L("Support"); - def->sidetext = L("layers"); - def->min = 0; - def->max = 5000; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("support_interface_loop_pattern", coBool); - def->label = L("Interface use loop pattern"); - def->category = L("Support"); - def->tooltip = L("Cover the top contact layer of the supports with loops. Disabled by default."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("tree_support_with_infill", coBool); - def->label = L("Tree support with infill"); - def->category = L("Support"); - def->tooltip = L("This setting specifies whether to add infill inside large hollows of tree support."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("curr_bed_type", coEnum); - def->label = L("Bed type"); - def->tooltip = L("Bed types supported by the printer."); - def->enum_keys_map = &s_keys_map_BedType; - def->enum_values.push_back("Cool Plate"); - def->enum_values.push_back("Engineering Plate"); - def->enum_values.push_back("High Temp Plate"); - def->enum_values.push_back("Textured PEI Plate"); - def->enum_values.push_back("Textured Cool Plate"); - def->enum_values.push_back("Supertack Plate"); - def->enum_labels.push_back(L("Smooth Cool Plate")); - def->enum_labels.push_back(L("Engineering Plate")); - def->enum_labels.push_back(L("Smooth High Temp Plate")); - def->enum_labels.push_back(L("Textured PEI Plate")); - def->enum_labels.push_back(L("Textured Cool Plate")); - def->enum_labels.push_back(L("Cool Plate (SuperTack)")); - def->set_default_value(new ConfigOptionEnum(btPC)); - - def = this->add("default_bed_type", coString); - def->label = L("Default bed type"); - def->tooltip = L("Default bed type for the printer (supports both numeric and string format)."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString()); - - def = this->add("first_layer_print_sequence", coInts); - def->label = L("First layer print sequence"); - def->min = 0; - def->max = 16; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("other_layers_print_sequence", coInts); - def->label = L("Other layers print sequence"); - def->min = 0; - def->max = 16; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("other_layers_print_sequence_nums", coInt); - def->label = L("The number of other layers print sequence"); - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("compatible_printers", coStrings); - def->label = L("Select printers"); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("upward_compatible_machine", coStrings); - def->label = L("upward compatible machine"); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("compatible_prints", coStrings); - def->label = L("Select profiles"); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("compatible_machine_expression_group", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("compatible_process_expression_group", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("different_settings_to_system", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("print_compatible_printers", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("default_filament_profile", coStrings); - def->label = L("Default filament profile"); - def->tooltip = L("Default filament profile when switching to this machine profile."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("default_print_profile", coString); - def->label = L("Default process profile"); - def->tooltip = L("Default process profile when switching to this machine profile."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("nozzle_height", coFloat); - def->label = L("Nozzle height"); - def->tooltip = L("The height of nozzle tip."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloat(2.5)); - - def = this->add("grab_length", coFloats); - def->label = L("Grab length"); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloats{0}); - - def = this->add("extruder_colour", coStrings); - def->label = L("Extruder Color"); - def->tooltip = L("Only used as a visual help on UI."); - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::color; - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("extruder_offset", coPoints); - def->label = L("Extruder offset"); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPoints{Vec2d(0,0)}); - - def = this->add("filament_colour", coStrings); - def->label = L("Color"); - def->tooltip = L("Only used as a visual help on UI."); - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::color; - def->set_default_value(new ConfigOptionStrings{"#F2754E"}); - - def = this->add("filament_multi_colour", coStrings); - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("filament_colour_type", coStrings); - def->set_default_value(new ConfigOptionStrings{"1"}); - - def = this->add("filament_map", coInts); - def->label = L("Filament map to extruder"); - def->tooltip = L("Filament map to extruder."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("physical_extruder_map", coInts); - def->label = L("Map the logical extruder to physical extruder"); - def->tooltip = L("Map the logical extruder to physical extruder."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInts{0}); - - def = this->add("filament_map_mode", coEnum); - def->label = L("filament mapping mode"); - def->tooltip = L("Filament mapping mode used as plate param."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Auto For Flush"); - def->enum_values.push_back("Auto For Match"); - def->enum_values.push_back("Manual"); - def->enum_values.push_back("Default"); - def->enum_labels.push_back(L("Auto For Flush")); - def->enum_labels.push_back(L("Auto For Match")); - def->enum_labels.push_back(L("Manual")); - def->enum_labels.push_back(L("Default")); - def->set_default_value(new ConfigOptionEnum(fmmAutoForFlush)); - - def = this->add("enable_filament_dynamic_map", coBool); - def->label = L("Enable filament dynamic map"); - def->tooltip = L("Enable dynamic filament mapping during print."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("has_filament_switcher", coBool); - def->label = L("Has filament switcher"); - def->tooltip = L("Printer has a filament switcher hardware (e.g., AMS)."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("filament_flush_temp", coInts); - def->label = L("Flush temperature"); - def->tooltip = L("Temperature when flushing filament. 0 indicates the upper bound of the recommended nozzle temperature range."); - def->sidetext = L("\u2103"); - def->min = 0; - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionIntsNullable{0}); - - def = this->add("machine_load_filament_time", coFloat); - def->label = L("Filament load time"); - def->tooltip = L("Time to load new filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only."); - def->sidetext = L("s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.0)); - - def = this->add("machine_unload_filament_time", coFloat); - def->label = L("Filament unload time"); - def->tooltip = L("Time to unload old filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only."); - def->sidetext = L("s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.0)); - - def = this->add("machine_tool_change_time", coFloat); - def->label = L("Tool change time"); - def->tooltip = L("Time taken to switch tools. It's usually applicable for tool changers or multi-tool machines. For single-extruder multi-material machines, it's typically 0. For statistics only."); - def->sidetext = L("s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("support_object_skip_flush", coBool); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("bed_temperature_formula", coEnum); - def->label = L("Bed temperature type"); - def->tooltip = L("This option determines how the bed temperature is set during slicing: based on the temperature of the first filament or the highest temperature of the printed filaments."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("by_first_filament"); - def->enum_values.push_back("by_highest_temp"); - def->enum_labels.push_back(L("By First filament")); - def->enum_labels.push_back(L("By Highest Temp")); - def->set_default_value(new ConfigOptionEnum(BedTempFormula::btfHighestTemp)); - - def = this->add("nozzle_flush_dataset", coInts); - def->nullable = true; - def->set_default_value(new ConfigOptionIntsNullable{0}); - - def = this->add("volumetric_speed_coefficients", coStrings); - def->label = L("Max volumetric speed multinomial coefficients"); - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("filament_ramming_parameters", coStrings); - def->label = L("Ramming parameters"); - def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionStrings{"120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|" " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"}); - - def = this->add("filament_settings_id", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings{""}); - - def = this->add("filament_ids", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("adaptive_layer_height", coBool); - - def = this->add("spaghetti_detector", coBool); - - def = this->add("wrapping_detection_layers", coInt); - def->label = L("Clumping detection layers"); - def->tooltip = L("Clumping detection layers."); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInt(20)); - - def = this->add("wrapping_exclude_area", coPoints); - def->label = L("Probing exclude area of clumping"); - def->tooltip = L("Probing exclude area of clumping."); - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::one_string; - def->set_default_value(new ConfigOptionPoints()); - - def = this->add("inherits", coString); - def->label = L("Inherits profile"); - def->tooltip = L("Name of parent profile."); - def->full_width = true; - def->height = 5; - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("inherits_group", coStrings); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("calib_flowrate_topinfill_special_order", coBool); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("silent_mode", coBool); - def->label = L("Supports silent mode"); - def->tooltip = L("Whether the machine supports silent mode in which machine use lower acceleration to print."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("max_layer_height", coFloats); - def->label = L("Max"); - def->tooltip = L("The highest printable layer height for the extruder. Used to limit the maximum layer height when enable adaptive layer height."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("min_layer_height", coFloats); - def->label = L("Min"); - def->tooltip = L("The lowest printable layer height for the extruder. Used to limit the minimum layer height when enable adaptive layer height."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.07}); - - def = this->add("nozzle_diameter", coFloats); - def->label = L("Nozzle diameter"); - def->tooltip = L("The diameter of nozzle."); - def->sidetext = L("mm"); - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.4}); - - def = this->add("host_type", coEnum); - def->label = L("Host Type"); - def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field must contain the kind of the host."); - def->mode = comAdvanced; - def->cli = ConfigOptionDef::nocli; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("prusalink"); - def->enum_values.push_back("prusaconnect"); - def->enum_values.push_back("octoprint"); - def->enum_values.push_back("duet"); - def->enum_values.push_back("flashair"); - def->enum_values.push_back("astrobox"); - def->enum_values.push_back("repetier"); - def->enum_values.push_back("mks"); - def->enum_values.push_back("esp3d"); - def->enum_values.push_back("crealityprint"); - def->enum_values.push_back("obico"); - def->enum_values.push_back("flashforge"); - def->enum_values.push_back("simplyprint"); - def->enum_values.push_back("elegoolink"); - def->enum_labels.push_back(L("PrusaLink")); - def->enum_labels.push_back(L("PrusaConnect")); - def->enum_labels.push_back(L("Octo/Klipper")); - def->enum_labels.push_back(L("Duet")); - def->enum_labels.push_back(L("FlashAir")); - def->enum_labels.push_back(L("AstroBox")); - def->enum_labels.push_back(L("Repetier")); - def->enum_labels.push_back(L("MKS")); - def->enum_labels.push_back(L("ESP3D")); - def->enum_labels.push_back(L("CrealityPrint")); - def->enum_labels.push_back(L("Obico")); - def->enum_labels.push_back(L("Flashforge")); - def->enum_labels.push_back(L("SimplyPrint")); - def->enum_labels.push_back(L("Elegoo Link")); - def->set_default_value(new ConfigOptionEnum(htOctoPrint)); - - def = this->add("nozzle_volume", coFloats); - def->label = L("Nozzle volume"); - def->tooltip = L("Volume of nozzle between the cutter and the end of nozzle."); - def->sidetext = L("mm³"); - def->mode = comAdvanced; - def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable{{0.0}}); - - def = this->add("cooling_tube_retraction", coFloat); - def->label = L("Cooling tube position"); - def->tooltip = L("Distance of the center-point of the cooling tube from the extruder tip."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(91.5)); - - def = this->add("cooling_tube_length", coFloat); - def->label = L("Cooling tube length"); - def->tooltip = L("Length of the cooling tube to limit space for cooling moves inside it."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(5.)); - - def = this->add("high_current_on_filament_swap", coBool); - def->label = L("High extruder current on filament swap"); - def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange sequence to allow for rapid ramming feed rates and to overcome resistance when loading a filament with an ugly shaped tip."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(0)); - - def = this->add("parking_pos_retraction", coFloat); - def->label = L("Filament parking position"); - def->tooltip = L("Distance of the extruder tip from the position where the filament is parked when unloaded. This should match the value in printer firmware."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(92.)); - - def = this->add("extra_loading_move", coFloat); - def->label = L("Extra loading distance"); - def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load is exactly the same as it was moved back during unload. When positive, it is loaded further, if negative, the loading move is shorter than unloading."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(-2.)); - - def = this->add("start_end_points", coPoints); - def->label = L("Start end points"); - def->tooltip = L("The start and end points which is from cutter area to garbage can."); - def->mode = comDevelop; - def->readonly = true; - def->set_default_value(new ConfigOptionPoints{Vec2d(30, -3), Vec2d(54, 245)}); - - def = this->add("printer_model", coString); - def->label = L("Printer type"); - def->tooltip = L("Type of the printer."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("printer_variant", coString); - def->label = L("Printer variant"); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("print_settings_id", coString); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("printer_settings_id", coString); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionString()); - - def = this->add("retraction_minimum_travel", coFloats); - def->label = L("Travel distance threshold"); - def->tooltip = L("Only trigger retraction when the travel distance is longer than this threshold."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{2.}); - - def = this->add("retract_before_wipe", coPercents); - def->label = L("Retract amount before wipe"); - def->tooltip = L("The length of fast retraction before wipe, relative to retraction length."); - def->sidetext = L("%"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPercents{100}); - - def = this->add("retract_when_changing_layer", coBools); - def->label = L("Retract when change layer"); - def->tooltip = L("Force a retraction when changes layer."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("retraction_length", coFloats); - def->label = L("Length"); - def->full_label = L("Retraction Length"); - def->tooltip = L("Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction."); - def->sidetext = L("mm"); - def->set_default_value(new ConfigOptionFloats{0.8}); - - def = this->add("enable_long_retraction_when_cut", coInt); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("long_retractions_when_cut", coBools); - def->label = L("Long retraction when cut (beta)"); - def->tooltip = L("Experimental feature: Retracting and cutting off the filament at a longer distance during changes to minimize purge. While this reduces flush significantly, it may also raise the risk of nozzle clogs or other printing problems."); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("retraction_distances_when_cut", coFloats); - def->label = L("Retraction distance when cut"); - def->tooltip = L("Experimental feature: Retraction length before cutting off during filament change."); - def->min = 10; - def->max = 18; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloats{18}); - - def = this->add("retract_length_toolchange", coFloats); - def->label = L("Length"); - def->full_label = L("Retraction Length (Toolchange)"); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{10.}); - - def = this->add("z_hop", coFloats); - def->label = L("Z-hop height"); - def->tooltip = L("Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. It prevents nozzle from hitting the print when travel move. Using spiral lines to lift Z can prevent stringing."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 5; - def->set_default_value(new ConfigOptionFloats{0.4}); - - def = this->add("retract_lift_above", coFloats); - def->label = L("Only lift Z above"); - def->tooltip = L("If you set this to a positive value, Z lift will only take place above the specified absolute Z."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("retract_lift_below", coFloats); - def->label = L("Only lift Z below"); - def->tooltip = L("If you set this to a positive value, Z lift will only take place below the specified absolute Z."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("z_hop_types", coEnums); - def->label = L("Z-hop type"); - def->tooltip = L("Type of Z-hop."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Auto Lift"); - def->enum_values.push_back("Normal Lift"); - def->enum_values.push_back("Slope Lift"); - def->enum_values.push_back("Spiral Lift"); - def->enum_labels.push_back(L("Auto")); - def->enum_labels.push_back(L("Normal")); - def->enum_labels.push_back(L("Slope")); - def->enum_labels.push_back(L("Spiral")); - def->set_default_value(new ConfigOptionEnumsGeneric{ ZHopType::zhtSlope }); - - def = this->add("travel_slope", coFloats); - def->label = L("Traveling angle"); - def->tooltip = L("Traveling angle for Slope and Spiral Z-hop type. Setting it to 90° results in Normal Lift."); - def->sidetext = L("°"); - def->min = 1; - def->max = 90; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{3}); - - def = this->add("retract_lift_enforce", coEnums); - def->label = L("On surfaces"); - def->tooltip = L("Enforce Z-Hop behavior. This setting is impacted by the above settings (Only lift Z above/below)."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("All Surfaces"); - def->enum_values.push_back("Top Only"); - def->enum_values.push_back("Bottom Only"); - def->enum_values.push_back("Top and Bottom"); - def->enum_labels.push_back(L("All Surfaces")); - def->enum_labels.push_back(L("Top Only")); - def->enum_labels.push_back(L("Bottom Only")); - def->enum_labels.push_back(L("Top and Bottom")); - def->set_default_value(new ConfigOptionEnumsGeneric{ RetractLiftEnforceType ::rletAllSurfaces }); - - def = this->add("extruder_type", coEnums); - def->label = L("Type"); - def->tooltip = L("This setting is only used for initial value of manual calibration of pressure advance. Bowden extruder usually has larger PA value. This setting doesn't influence normal slicing."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Direct Drive"); - def->enum_values.push_back("Bowden"); - def->enum_labels.push_back(L("Direct Drive")); - def->enum_labels.push_back(L("Bowden")); - def->set_default_value(new ConfigOptionEnumsGeneric{ ExtruderType::etDirectDrive }); - - def = this->add("nozzle_volume_type", coEnums); - def->label = L("Nozzle Volume Type"); - def->tooltip = L("Nozzle volume type for extruders."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Standard"); - def->enum_values.push_back("High Flow"); - def->enum_labels.push_back(L("Standard")); - def->enum_labels.push_back(L("High Flow")); - def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard }); - - def = this->add("default_nozzle_volume_type", coEnums); - def->label = L("Default Nozzle Volume Type."); - def->tooltip = L("Default Nozzle volume type for extruders in this printer."); - def->mode = comDevelop; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Standard"); - def->enum_values.push_back("High Flow"); - def->enum_labels.push_back(L("Standard")); - def->enum_labels.push_back(L("High Flow")); - def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard }); - - def = this->add("extruder_variant_list", coStrings); - def->label = L("Extruder variant list"); - def->tooltip = L("Extruder variant list."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); - - def = this->add("extruder_ams_count", coStrings); - def->label = L("Extruder AMS count"); - def->tooltip = L("AMS counts per extruder."); - def->set_default_value(new ConfigOptionStrings()); - - def = this->add("printer_extruder_id", coInts); - def->label = L("Printer extruder id"); - def->tooltip = L("Printer extruder id."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("printer_extruder_variant", coStrings); - def->label = L("Printer's extruder variant"); - def->tooltip = L("Printer's extruder variant."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); - - def = this->add("master_extruder_id", coInt); - def->label = L("Master extruder id"); - def->tooltip = L("Default extruder id to place filament."); - def->set_default_value(new ConfigOptionInt(1)); - - def = this->add("print_extruder_id", coInts); - def->label = L("Print extruder id"); - def->tooltip = L("Print extruder id."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("print_extruder_variant", coStrings); - def->label = L("Print's extruder variant"); - def->tooltip = L("Print's extruder variant."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionStrings{"Direct Drive Standard"}); - - def = this->add("filament_extruder_id", coInts); - def->label = L("Filament extruder id"); - def->tooltip = L("Filament extruder id."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("filament_self_index", coInts); - def->label = L("Filament self index"); - def->tooltip = L("Filament self index."); - def->cli = ConfigOptionDef::nocli; - def->set_default_value(new ConfigOptionInts{1}); - - def = this->add("retract_restart_extra", coFloats); - def->label = L("Extra length on restart"); - def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("retract_restart_extra_toolchange", coFloats); - def->label = L("Extra length on restart"); - def->tooltip = L("When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("retraction_speed", coFloats); - def->label = L("Retraction Speed"); - def->full_label = L("Retraction Speed"); - def->tooltip = L("Speed for retracting filament from the nozzle."); - def->sidetext = L("mm/s"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{30.}); - - def = this->add("deretraction_speed", coFloats); - def->label = L("De-retraction Speed"); - def->full_label = L("De-retraction Speed"); - def->tooltip = L("Speed for reloading filament into the nozzle. Zero means same speed of retraction."); - def->sidetext = L("mm/s"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{0.}); - - def = this->add("bbl_calib_mark_logo", coBool); - def->label = L("Show auto-calibration marks"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("single_extruder_multi_material", coBool); - def->label = L("Single Extruder Multi Material"); - def->tooltip = L("Use single nozzle to print multi filament."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("manual_filament_change", coBool); - def->label = L("Manual Filament Change"); - def->tooltip = L("Enable this option to omit the custom Change filament G-code only at the beginning of the print. The tool change command (e.g., T0) will be skipped throughout the entire print. This is useful for manual multi-material printing, where we use M600/PAUSE to trigger the manual filament change action."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("wipe_tower_type", coEnum); - def->label = L("Wipe tower type"); - def->tooltip = L("Choose the wipe tower implementation for multi-material prints. Type 1 is recommended for Bambu and Qidi printers with a filament cutter. Type 2 offers better compatibility with multi-tool and MMU printers and provide overall better compatibility."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("type1"); - def->enum_values.push_back("type2"); - def->enum_labels.push_back(L("Type 1")); - def->enum_labels.push_back(L("Type 2")); - def->set_default_value(new ConfigOptionEnum(WipeTowerType::Type2)); - - def = this->add("purge_in_prime_tower", coBool); - def->label = L("Purge in prime tower"); - def->tooltip = L("Purge remaining filament into prime tower."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("enable_filament_ramming", coBool); - def->label = L("Enable filament ramming"); - def->tooltip = L("Enable filament ramming"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("tool_change_on_wipe_tower", coBool); - def->label = L("Tool change on wipe tower"); - def->tooltip = L("Force the toolhead to travel to the wipe tower before issuing the tool change command (Tx). Only relevant for multi-extruder (multi-toolhead) printers using a Type 2 wipe tower. By default Orca skips the travel on multi-toolhead machines because the firmware handles the head swap, which can result in the Tx command being issued above the printed part. Enable this option if you want the tool change to always be issued above the wipe tower instead."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("head_wrap_detect_zone", coPoints); - def->label = L("Head wrap detect zone"); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionPoints()); - - def = this->add("travel_speed_z", coFloat); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloat(0.)); - - def = this->add("wipe", coBools); - def->label = L("Wipe while retracting"); - def->tooltip = L("Move nozzle along the last extrusion path when retracting to clean any leaked material on the nozzle. This can minimize blobs when printing a new part after traveling."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBools{false}); - - def = this->add("wipe_distance", coFloats); - def->label = L("Wipe Distance"); - def->tooltip = L("Describe how long the nozzle will move along the last path when retracting.\n\nDepending on how long the wipe operation lasts, how fast and long the extruder/filament retraction settings are, a retraction move may be needed to retract the remaining filament.\n\nSetting a value in the retract amount before wipe setting below will perform any excess retraction before the wipe, else it will be performed after."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloats{1.}); - - def = this->add("flush_volumes_vector", coFloats); - def->label = L("Purging volumes - load/unload volumes"); - def->set_default_value(new ConfigOptionFloats{140.f, 140.f, 140.f, 140.f, 140.f, 140.f, 140.f, 140.f}); - - def = this->add("flush_volumes_matrix", coFloats); - def->label = L("Purging volumes"); - def->set_default_value(new ConfigOptionFloats{0.f, 280.f, 280.f, 280.f, 280.f, 0.f, 280.f, 280.f, 280.f, 280.f, 0.f, 280.f, 280.f, 280.f, 280.f, 0.f}); - - def = this->add("flush_multiplier", coFloats); - def->label = L("Flush multiplier"); - def->tooltip = L("The actual flushing volumes is equal to the flush multiplier multiplied by the flushing volumes in the table."); - def->set_default_value(new ConfigOptionFloats{0.3}); - - def = this->add("wipe_tower_x", coFloats); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloats{15.}); - - def = this->add("wipe_tower_y", coFloats); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloats{220.}); - - def = this->add("wiping_volumes_extruders", coFloats); - def->label = L("Purging volumes - load/unload volumes"); - def->tooltip = L("This vector saves required volumes to change from/to each tool used on the wipe tower. These values are used to simplify creation of the full purging volumes below."); - def->set_default_value(new ConfigOptionFloats{70., 70., 70., 70., 70., 70., 70., 70., 70., 70.}); - - def = this->add("prime_tower_flat_ironing", coBool); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("thumbnails_format", coEnum); - def->label = L("Format of G-code thumbnails"); - def->tooltip = L("Format of G-code thumbnails: PNG for best quality, JPG for smallest size, QOI for low memory firmware."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("PNG"); - def->enum_values.push_back("JPG"); - def->enum_values.push_back("QOI"); - def->enum_values.push_back("BTT_TFT"); - def->enum_values.push_back("COLPIC"); - def->enum_labels.push_back(L("PNG")); - def->enum_labels.push_back(L("JPG")); - def->enum_labels.push_back(L("QOI")); - def->enum_labels.push_back(L("BTT TT")); - def->enum_labels.push_back(L("ColPic")); - def->set_default_value(new ConfigOptionEnum(GCodeThumbnailsFormat::PNG)); - - def = this->add("extruder_clearance_height_to_rod", coFloat); - def->label = L("Height to rod"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Distance of the nozzle tip to the lower rod. Used for collision avoidance in by-object printing."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(40)); - - def = this->add("extruder_clearance_height_to_lid", coFloat); - def->label = L("Height to lid"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Distance of the nozzle tip to the lid. Used for collision avoidance in by-object printing."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(120)); - - def = this->add("extruder_clearance_radius", coFloat); - def->label = L("Radius"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Clearance radius around extruder. Used for collision avoidance in by-object printing."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(40)); - - def = this->add("bed_mesh_min", coPoint); - def->label = L("Bed mesh min"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This option sets the min point for the allowed bed mesh area. Due to the probe's XY offset, most printers are unable to probe the entire bed. To ensure the probe point does not go outside the bed area, the minimum and maximum points of the bed mesh should be set appropriately. OrcaSlicer ensures that adaptive_bed_mesh_min/adaptive_bed_mesh_max values do not exceed these min/max points. This information can usually be obtained from your printer manufacturer. The default setting is (-99999, -99999), which means there are no limits, thus allowing probing across the entire bed."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPoint(Vec2d(-99999, -99999))); - - def = this->add("bed_mesh_max", coPoint); - def->label = L("Bed mesh max"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This option sets the max point for the allowed bed mesh area. Due to the probe's XY offset, most printers are unable to probe the entire bed. To ensure the probe point does not go outside the bed area, the minimum and maximum points of the bed mesh should be set appropriately. OrcaSlicer ensures that adaptive_bed_mesh_min/adaptive_bed_mesh_max values do not exceed these min/max points. This information can usually be obtained from your printer manufacturer. The default setting is (99999, 99999), which means there are no limits, thus allowing probing across the entire bed."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPoint(Vec2d(99999, 99999))); - - def = this->add("bed_mesh_probe_distance", coPoint); - def->label = L("Probe point distance"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This option sets the preferred distance between probe points (grid size) for the X and Y directions, with the default being 50mm for both X and Y."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPoint(Vec2d(50, 50))); - - def = this->add("adaptive_bed_mesh_margin", coFloat); - def->label = L("Mesh margin"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This option determines the additional distance by which the adaptive bed mesh area should be expanded in the XY directions."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("scan_first_layer", coBool); - def->label = L("Scan first layer"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Enable this to enable the camera on printer to check the quality of first layer."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("enable_power_loss_recovery", coEnum); - def->label = L("Power Loss Recovery"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Choose how to control power loss recovery. When set to Printer configuration, the slicer will not emit power loss recovery G-code and will leave the printer's configuration unchanged. Applicable to Bambu Lab or Marlin 2 firmware based printers."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("printer_configuration"); - def->enum_values.push_back("enable"); - def->enum_values.push_back("disable"); - def->enum_labels.push_back(L("Printer configuration")); - def->enum_labels.push_back(L("Enable")); - def->enum_labels.push_back(L("Disable")); - def->set_default_value(new ConfigOptionEnum(PowerLossRecoveryMode::PrinterConfiguration)); - - def = this->add("nozzle_type", coEnums); - def->label = L("Nozzle type"); - def->category = L("Printer/Basic information"); - def->tooltip = L("The metallic material of nozzle. This determines the abrasive resistance of nozzle, and what kind of filament can be printed."); - def->mode = comAdvanced; - def->nullable = true; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("undefine"); - def->enum_values.push_back("hardened_steel"); - def->enum_values.push_back("stainless_steel"); - def->enum_values.push_back("tungsten_carbide"); - def->enum_values.push_back("brass"); - def->enum_labels.push_back(L("Undefine")); - def->enum_labels.push_back(L("Hardened steel")); - def->enum_labels.push_back(L("Stainless steel")); - def->enum_labels.push_back(L("Tungsten carbide")); - def->enum_labels.push_back(L("Brass")); - def->set_default_value(new ConfigOptionEnumsGenericNullable{ ntUndefine }); - - def = this->add("nozzle_hrc", coInt); - def->label = L("Nozzle HRC"); - def->category = L("Printer/Basic information"); - def->tooltip = L("The nozzle's hardness. Zero means no checking for nozzle's hardness during slicing."); - def->sidetext = L("HRC"); - def->min = 0; - def->max = 500; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("printer_structure", coEnum); - def->label = L("Printer structure"); - def->category = L("Printer/Basic information"); - def->tooltip = L("The physical arrangement and components of a printing device."); - def->mode = comDevelop; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("undefine"); - def->enum_values.push_back("corexy"); - def->enum_values.push_back("i3"); - def->enum_values.push_back("hbot"); - def->enum_values.push_back("delta"); - def->enum_labels.push_back(L("Undefine")); - def->enum_labels.push_back(L("CoreXY")); - def->enum_labels.push_back(L("I3")); - def->enum_labels.push_back(L("Hbot")); - def->enum_labels.push_back(L("Delta")); - def->set_default_value(new ConfigOptionEnum(psUndefine)); - - def = this->add("best_object_pos", coPoint); - def->label = L("Best object position"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Best auto arranging position in range [0,1] w.r.t. bed shape."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionPoint(Vec2d(0.5, 0.5))); - - def = this->add("auxiliary_fan", coBool); - def->label = L("Auxiliary part cooling fan"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Enable this option if machine has auxiliary part cooling fan. G-code command: M106 P2 S(0-255)."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("fan_speedup_time", coFloat); - def->category = L("Printer/Basic information"); - def->tooltip = L("Start the fan this number of seconds earlier than its target start time (you can use fractional seconds). It assumes infinite acceleration for this time estimation, and will only take into account G1 and G0 moves (arc fitting is unsupported).\nIt won't move fan commands from custom G-code (they act as a sort of 'barrier').\nIt won't move fan commands into the start G-code if the 'only custom start G-code' is activated.\nUse 0 to deactivate."); - def->sidetext = L("s"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("fan_speedup_overhangs", coBool); - def->label = L("Only overhangs"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Will only take into account the delay for the cooling of overhangs."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("fan_kickstart", coFloat); - def->label = L("Fan kick-start time"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Emit a max fan speed command for this amount of seconds before reducing to target speed to kick-start the cooling fan.\nThis is useful for fans where a low PWM/power may be insufficient to get the fan started spinning from a stop, or to get the fan up to speed faster.\nSet to 0 to deactivate."); - def->sidetext = L("s"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("part_cooling_fan_min_pwm", coInt); - def->label = L("Minimum non-zero part cooling fan speed"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Some part-cooling fans cannot start spinning when commanded below a certain PWM duty cycle. When set above 0, any non-zero part-cooling fan command will be raised to at least this percentage so the fan reliably starts. A fan command of 0 (fan off) is always honoured exactly. This clamp is applied after every other fan calculation (first-layer ramp, layer-time interpolation, overhang/bridge/support-interface/ironing overrides), so scaling still operates within the range [this value, 100%].\nIf your firmware already disables the fan below a threshold (for example Klipper's [fan] off_below: 0.10 shuts the fan off whenever the commanded duty cycle is below 10%), this option and the firmware threshold should ideally be set to the same value. Matching them (e.g. off_below: 0.10 in Klipper and 10% here) guarantees the slicer never emits a non-zero value that the firmware would silently drop, and the fan never receives a value below the one you know it can actually spool at.\nSet to 0 to deactivate."); - def->sidetext = L("%"); - def->min = 0; - def->max = 100; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(0)); - - def = this->add("time_cost", coFloat); - def->label = L("Time cost"); - def->category = L("Printer/Basic information"); - def->tooltip = L("The printer cost per hour."); - def->sidetext = L("money/h"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("support_chamber_temp_control", coBool); - def->label = L("Support control chamber temperature"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This option is enabled if machine support controlling chamber temperature\nG-code command: M141 S(0-255)"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("support_air_filtration", coBool); - def->label = L("Support air filtration"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Enable this if printer support air filtration\nG-code command: M106 P3 S(0-255)"); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("gcode_flavor", coEnum); - def->label = L("G-code flavor"); - def->category = L("Printer/Basic information"); - def->tooltip = L("What kind of G-code the printer is compatible with."); - def->mode = comAdvanced; - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("marlin"); - def->enum_values.push_back("klipper"); - def->enum_values.push_back("reprapfirmware"); - def->enum_values.push_back("repetier"); - def->enum_values.push_back("marlin2"); - def->enum_values.push_back("reprap"); - def->enum_values.push_back("teacup"); - def->enum_values.push_back("makerware"); - def->enum_values.push_back("sailfish"); - def->enum_values.push_back("mach3"); - def->enum_values.push_back("machinekit"); - def->enum_values.push_back("smoothie"); - def->enum_values.push_back("no-extrusion"); - def->enum_labels.push_back(L("Marlin(legacy)")); - def->enum_labels.push_back(L("Klipper")); - def->enum_labels.push_back(L("RepRapFirmware")); - def->enum_labels.push_back(L("Repetier")); - def->enum_labels.push_back(L("Marlin 2")); - def->enum_labels.push_back(L("RepRap/Sprinter")); - def->enum_labels.push_back(L("Teacup")); - def->enum_labels.push_back(L("MakerWare (MakerBot)")); - def->enum_labels.push_back(L("Sailfish (MakerBot)")); - def->enum_labels.push_back(L("Mach3/LinuxCNC")); - def->enum_labels.push_back(L("Machinekit")); - def->enum_labels.push_back(L("Smoothie")); - def->enum_labels.push_back(L("No extrusion")); - def->set_default_value(new ConfigOptionEnum(gcfMarlinLegacy)); - - def = this->add("pellet_modded_printer", coBool); - def->label = L("Pellet Modded Printer"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Enable this option if your printer uses pellets instead of filaments."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("support_multi_bed_types", coBool); - def->label = L("Support multi bed types"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Enable this option if you want to use multiple bed types."); - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("use_firmware_retraction", coBool); - def->label = L("Use firmware retraction"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("disable_m73", coBool); - def->label = L("Disable set remaining print time"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Disable generating of the M73: Set remaining print time in the final G-code."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - - def = this->add("z_offset", coFloat); - def->label = L("Z offset"); - def->category = L("Printer/Basic information"); - def->tooltip = L("This value will be added (or subtracted) from all the Z coordinates in the output G-code. It is used to compensate for bad Z endstop position: for example, if your endstop zero actually leaves the nozzle 0.3mm far from the print bed, set this to -0.3 (or fix your endstop)."); - def->sidetext = L("mm"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); - - def = this->add("thumbnails", coString); - def->label = L("G-code thumbnails"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\""); - def->mode = comAdvanced; - def->gui_type = ConfigOptionDef::GUIType::one_string; - def->set_default_value(new ConfigOptionString("48x48/PNG,300x300/PNG")); - - def = this->add("use_relative_e_distances", coBool); - def->label = L("Use relative E distances"); - def->category = L("Printer/Basic information"); - def->tooltip = L("Relative extrusion is recommended when using \"label_objects\" option. Some extruders work better with this option unchecked (absolute extrusion mode). Wipe tower is only compatible with relative mode. It is recommended on most printers. Default is checked."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("before_layer_change_gcode", coString); - def->label = L("Before layer change G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("This G-code is inserted at every layer change before the Z lift."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString()); - - def = this->add("machine_end_gcode", coString); - def->label = L("End G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("End G-code when finishing the entire print."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 12; - def->set_default_value(new ConfigOptionString("M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n")); - - def = this->add("printing_by_object_gcode", coString); - def->label = L("Between Object G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("Insert G-code between objects. This parameter will only come into effect when you print your models object by object."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 12; - def->set_default_value(new ConfigOptionString()); - - def = this->add("layer_change_gcode", coString); - def->label = L("Layer change G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("This G-code is inserted at every layer change after the Z lift."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString()); - - def = this->add("time_lapse_gcode", coString); - def->label = L("Timelapse G-code"); - def->category = L("Printer/Machine G-code"); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString()); - - def = this->add("wrapping_detection_gcode", coString); - def->label = L("Clumping detection G-code"); - def->category = L("Printer/Machine G-code"); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString("")); - - def = this->add("machine_pause_gcode", coString); - def->label = L("Pause G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("This G-code will be used as a code for the pause print. Users can insert pause G-code in the G-code viewer."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 12; - def->set_default_value(new ConfigOptionString()); - - def = this->add("template_custom_gcode", coString); - def->label = L("Custom G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("This G-code will be used as a custom code."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 12; - def->set_default_value(new ConfigOptionString()); - - def = this->add("file_start_gcode", coString); - def->label = L("File header G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("G-code written at the very top of the output file, before any other content. Useful for adding metadata that printer firmware reads from the first lines of the file (e.g. estimated print time, filament usage). Supports placeholders like {print_time_sec} and {used_filament_length}."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 8; - def->set_default_value(new ConfigOptionString("")); - - def = this->add("machine_start_gcode", coString); - def->label = L("Start G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("Start G-code when starting the entire print."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 12; - def->set_default_value(new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n")); - - def = this->add("change_filament_gcode", coString); - def->label = L("Change filament G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("This G-code is inserted when filament is changed, including T commands to trigger tool change."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString()); - - def = this->add("change_extrusion_role_gcode", coString); - def->label = L("Change extrusion role G-code"); - def->category = L("Printer/Machine G-code"); - def->tooltip = L("This G-code is inserted when the extrusion role is changed."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 5; - def->set_default_value(new ConfigOptionString()); - - def = this->add("printer_notes", coString); - def->label = L("Printer notes"); - def->category = L("Printer/Notes"); - def->tooltip = L("You can put your notes regarding the printer here."); - def->mode = comAdvanced; - def->multiline = true; - def->full_width = true; - def->height = 13; - def->set_default_value(new ConfigOptionString()); diff --git a/src/slic3r/GUI/generated/TabLayout_generated.cpp b/src/slic3r/GUI/generated/TabLayout_generated.cpp deleted file mode 100644 index 3fe01759c2..0000000000 --- a/src/slic3r/GUI/generated/TabLayout_generated.cpp +++ /dev/null @@ -1,973 +0,0 @@ -// ===== AUTO-GENERATED by tools/config_codegen.py from layout.yaml ===== -// DO NOT EDIT MANUALLY. Edit layout.yaml and re-run: python tools/run_codegen.py -// -// Included inside namespace Slic3r::GUI in Tab.cpp after validate_custom_gcode_cb -// forward declaration. No namespace wrapper needed here. - -namespace { constexpr int gcode_field_height = 15; } -namespace { constexpr int notes_field_height = 25; } - -inline void TabPrint_build_quality_layout(TabPrint& tab) -{ - PageShp page = tab.add_options_page(L("Quality"), "custom-gcode_quality"); - { - auto optgroup = page->new_optgroup(L("Layer height")); - optgroup->append_single_option_line("layer_height"); - optgroup->append_single_option_line("initial_layer_print_height"); - } - { - auto optgroup = page->new_optgroup(L("Line width")); - optgroup->append_single_option_line("line_width"); - optgroup->append_single_option_line("initial_layer_line_width"); - optgroup->append_single_option_line("outer_wall_line_width"); - optgroup->append_single_option_line("inner_wall_line_width"); - optgroup->append_single_option_line("top_surface_line_width"); - optgroup->append_single_option_line("sparse_infill_line_width"); - optgroup->append_single_option_line("internal_solid_infill_line_width"); - optgroup->append_single_option_line("support_line_width"); - } - { - auto optgroup = page->new_optgroup(L("Seam")); - optgroup->append_single_option_line("seam_position"); - optgroup->append_single_option_line("staggered_inner_seams"); - optgroup->append_single_option_line("seam_gap"); - optgroup->append_single_option_line("seam_slope_type"); - optgroup->append_single_option_line("seam_slope_conditional"); - optgroup->append_single_option_line("scarf_angle_threshold"); - optgroup->append_single_option_line("scarf_overhang_threshold"); - optgroup->append_single_option_line("scarf_joint_speed"); - optgroup->append_single_option_line("seam_slope_start_height"); - optgroup->append_single_option_line("seam_slope_entire_loop"); - optgroup->append_single_option_line("seam_slope_min_length"); - optgroup->append_single_option_line("seam_slope_steps"); - optgroup->append_single_option_line("scarf_joint_flow_ratio"); - optgroup->append_single_option_line("seam_slope_inner_walls"); - optgroup->append_single_option_line("role_based_wipe_speed"); - optgroup->append_single_option_line("wipe_speed"); - optgroup->append_single_option_line("wipe_on_loops"); - optgroup->append_single_option_line("wipe_before_external_loop"); - } - { - auto optgroup = page->new_optgroup(L("Precision")); - optgroup->append_single_option_line("slice_closing_radius"); - optgroup->append_single_option_line("resolution"); - optgroup->append_single_option_line("enable_arc_fitting"); - optgroup->append_single_option_line("xy_hole_compensation"); - optgroup->append_single_option_line("xy_contour_compensation"); - optgroup->append_single_option_line("elefant_foot_compensation"); - optgroup->append_single_option_line("elefant_foot_layers_density"); - optgroup->append_single_option_line("elefant_foot_compensation_layers"); - optgroup->append_single_option_line("precise_outer_wall"); - optgroup->append_single_option_line("precise_z_height"); - optgroup->append_single_option_line("hole_to_polyhole"); - optgroup->append_single_option_line("hole_to_polyhole_threshold"); - optgroup->append_single_option_line("hole_to_polyhole_twisted"); - } - { - auto optgroup = page->new_optgroup(L("Ironing")); - optgroup->append_single_option_line("ironing_type"); - optgroup->append_single_option_line("ironing_pattern"); - optgroup->append_single_option_line("ironing_flow"); - optgroup->append_single_option_line("ironing_spacing"); - optgroup->append_single_option_line("ironing_inset"); - optgroup->append_single_option_line("ironing_angle"); - optgroup->append_single_option_line("ironing_angle_fixed"); - } - { - auto optgroup = page->new_optgroup(L("Wall generator")); - optgroup->append_single_option_line("wall_generator"); - optgroup->append_single_option_line("wall_transition_angle"); - optgroup->append_single_option_line("wall_transition_filter_deviation"); - optgroup->append_single_option_line("wall_transition_length"); - optgroup->append_single_option_line("wall_distribution_count"); - optgroup->append_single_option_line("initial_layer_min_bead_width"); - optgroup->append_single_option_line("min_bead_width"); - optgroup->append_single_option_line("min_feature_size"); - optgroup->append_single_option_line("min_length_factor"); - optgroup->append_single_option_line("wall_maximum_resolution"); - optgroup->append_single_option_line("wall_maximum_deviation"); - } - { - auto optgroup = page->new_optgroup(L("Walls and surfaces")); - optgroup->append_single_option_line("wall_sequence"); - optgroup->append_single_option_line("is_infill_first"); - optgroup->append_single_option_line("wall_direction"); - optgroup->append_single_option_line("print_flow_ratio"); - optgroup->append_single_option_line("top_solid_infill_flow_ratio"); - optgroup->append_single_option_line("bottom_solid_infill_flow_ratio"); - optgroup->append_single_option_line("set_other_flow_ratios"); - optgroup->append_single_option_line("first_layer_flow_ratio"); - optgroup->append_single_option_line("outer_wall_flow_ratio"); - optgroup->append_single_option_line("inner_wall_flow_ratio"); - optgroup->append_single_option_line("overhang_flow_ratio"); - optgroup->append_single_option_line("sparse_infill_flow_ratio"); - optgroup->append_single_option_line("internal_solid_infill_flow_ratio"); - optgroup->append_single_option_line("gap_fill_flow_ratio"); - optgroup->append_single_option_line("support_flow_ratio"); - optgroup->append_single_option_line("support_interface_flow_ratio"); - optgroup->append_single_option_line("only_one_wall_first_layer"); - optgroup->append_single_option_line("only_one_wall_top"); - optgroup->append_single_option_line("min_width_top_surface"); - optgroup->append_single_option_line("reduce_crossing_wall"); - optgroup->append_single_option_line("max_travel_detour_distance"); - optgroup->append_single_option_line("small_area_infill_flow_compensation"); - optgroup->append_single_option_line("small_area_infill_flow_compensation_model"); - } - { - auto optgroup = page->new_optgroup(L("Bridging")); - optgroup->append_single_option_line("bridge_flow"); - optgroup->append_single_option_line("internal_bridge_flow"); - optgroup->append_single_option_line("bridge_density"); - optgroup->append_single_option_line("internal_bridge_density"); - optgroup->append_single_option_line("thick_bridges"); - optgroup->append_single_option_line("thick_internal_bridges"); - optgroup->append_single_option_line("enable_extra_bridge_layer"); - optgroup->append_single_option_line("dont_filter_internal_bridges"); - optgroup->append_single_option_line("counterbore_hole_bridging"); - } - { - auto optgroup = page->new_optgroup(L("Overhangs")); - optgroup->append_single_option_line("detect_overhang_wall"); - optgroup->append_single_option_line("make_overhang_printable"); - optgroup->append_single_option_line("make_overhang_printable_angle"); - optgroup->append_single_option_line("make_overhang_printable_hole_size"); - optgroup->append_single_option_line("extra_perimeters_on_overhangs"); - optgroup->append_single_option_line("overhang_reverse"); - optgroup->append_single_option_line("overhang_reverse_internal_only"); - optgroup->append_single_option_line("overhang_reverse_threshold"); - } -} - -inline void TabPrint_build_strength_layout(TabPrint& tab) -{ - PageShp page = tab.add_options_page(L("Strength"), "custom-gcode_strength"); - { - auto optgroup = page->new_optgroup(L("Walls")); - optgroup->append_single_option_line("wall_loops"); - optgroup->append_single_option_line("alternate_extra_wall"); - optgroup->append_single_option_line("detect_thin_wall"); - } - { - auto optgroup = page->new_optgroup(L("Top/bottom shells")); - optgroup->append_single_option_line("top_shell_layers"); - optgroup->append_single_option_line("top_shell_thickness"); - optgroup->append_single_option_line("top_surface_density"); - optgroup->append_single_option_line("top_surface_pattern"); - optgroup->append_single_option_line("bottom_shell_layers"); - optgroup->append_single_option_line("bottom_shell_thickness"); - optgroup->append_single_option_line("bottom_surface_density"); - optgroup->append_single_option_line("bottom_surface_pattern"); - optgroup->append_single_option_line("top_bottom_infill_wall_overlap"); - } - { - auto optgroup = page->new_optgroup(L("Infill")); - optgroup->append_single_option_line("sparse_infill_density"); - optgroup->append_single_option_line("fill_multiline"); - optgroup->append_single_option_line("sparse_infill_pattern"); - optgroup->append_single_option_line("infill_direction"); - optgroup->append_single_option_line("sparse_infill_rotate_template"); - optgroup->append_single_option_line("skin_infill_density"); - optgroup->append_single_option_line("skeleton_infill_density"); - optgroup->append_single_option_line("infill_lock_depth"); - optgroup->append_single_option_line("skin_infill_depth"); - optgroup->append_single_option_line("skin_infill_line_width"); - optgroup->append_single_option_line("skeleton_infill_line_width"); - optgroup->append_single_option_line("symmetric_infill_y_axis"); - optgroup->append_single_option_line("infill_shift_step"); - optgroup->append_single_option_line("lateral_lattice_angle_1"); - optgroup->append_single_option_line("lateral_lattice_angle_2"); - optgroup->append_single_option_line("infill_overhang_angle"); - optgroup->append_single_option_line("infill_anchor_max"); - optgroup->append_single_option_line("infill_anchor"); - optgroup->append_single_option_line("internal_solid_infill_pattern"); - optgroup->append_single_option_line("solid_infill_direction"); - optgroup->append_single_option_line("solid_infill_rotate_template"); - optgroup->append_single_option_line("gap_fill_target"); - optgroup->append_single_option_line("filter_out_gap_fill"); - optgroup->append_single_option_line("infill_wall_overlap"); - } - { - auto optgroup = page->new_optgroup(L("Advanced")); - optgroup->append_single_option_line("align_infill_direction_to_model"); - optgroup->append_single_option_line("extra_solid_infills"); - optgroup->append_single_option_line("bridge_angle"); - optgroup->append_single_option_line("internal_bridge_angle"); - optgroup->append_single_option_line("minimum_sparse_infill_area"); - optgroup->append_single_option_line("infill_combination"); - optgroup->append_single_option_line("infill_combination_max_layer_height"); - optgroup->append_single_option_line("detect_narrow_internal_solid_infill"); - optgroup->append_single_option_line("ensure_vertical_shell_thickness"); - } -} - -inline void TabPrint_build_speed_layout(TabPrint& tab) -{ - PageShp page = tab.add_options_page(L("Speed"), "custom-gcode_speed"); - { - auto optgroup = page->new_optgroup(L("First layer speed")); - optgroup->append_single_option_line("initial_layer_speed"); - optgroup->append_single_option_line("initial_layer_infill_speed"); - optgroup->append_single_option_line("initial_layer_travel_speed"); - optgroup->append_single_option_line("slow_down_layers"); - } - { - auto optgroup = page->new_optgroup(L("Other layers speed")); - optgroup->append_single_option_line("outer_wall_speed"); - optgroup->append_single_option_line("inner_wall_speed"); - optgroup->append_single_option_line("small_perimeter_speed"); - optgroup->append_single_option_line("small_perimeter_threshold"); - optgroup->append_single_option_line("sparse_infill_speed"); - optgroup->append_single_option_line("internal_solid_infill_speed"); - optgroup->append_single_option_line("top_surface_speed"); - optgroup->append_single_option_line("gap_infill_speed"); - optgroup->append_single_option_line("ironing_speed"); - optgroup->append_single_option_line("support_speed"); - optgroup->append_single_option_line("support_interface_speed"); - } - { - auto optgroup = page->new_optgroup(L("Overhang speed")); - optgroup->append_single_option_line("enable_overhang_speed"); - optgroup->append_single_option_line("slowdown_for_curled_perimeters"); - { - Line line_{optgroup->get_option("overhang_1_4_speed").opt.label, optgroup->get_option("overhang_1_4_speed").opt.tooltip}; - line_.append_option(optgroup->get_option("overhang_1_4_speed")); - line_.append_option(optgroup->get_option("overhang_2_4_speed")); - line_.append_option(optgroup->get_option("overhang_3_4_speed")); - line_.append_option(optgroup->get_option("overhang_4_4_speed")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("bridge_speed").opt.label, optgroup->get_option("bridge_speed").opt.tooltip}; - line_.append_option(optgroup->get_option("bridge_speed")); - line_.append_option(optgroup->get_option("internal_bridge_speed")); - optgroup->append_line(line_); - } - } - { - auto optgroup = page->new_optgroup(L("Travel speed")); - optgroup->append_single_option_line("travel_speed"); - } - { - auto optgroup = page->new_optgroup(L("Acceleration")); - optgroup->append_single_option_line("default_acceleration"); - optgroup->append_single_option_line("outer_wall_acceleration"); - optgroup->append_single_option_line("inner_wall_acceleration"); - optgroup->append_single_option_line("bridge_acceleration"); - optgroup->append_single_option_line("sparse_infill_acceleration"); - optgroup->append_single_option_line("internal_solid_infill_acceleration"); - optgroup->append_single_option_line("initial_layer_acceleration"); - optgroup->append_single_option_line("initial_layer_travel_acceleration"); - optgroup->append_single_option_line("top_surface_acceleration"); - optgroup->append_single_option_line("travel_acceleration"); - optgroup->append_single_option_line("accel_to_decel_enable"); - optgroup->append_single_option_line("accel_to_decel_factor"); - } - { - auto optgroup = page->new_optgroup(L("Jerk(XY)")); - optgroup->append_single_option_line("default_junction_deviation"); - optgroup->append_single_option_line("default_jerk"); - optgroup->append_single_option_line("outer_wall_jerk"); - optgroup->append_single_option_line("inner_wall_jerk"); - optgroup->append_single_option_line("infill_jerk"); - optgroup->append_single_option_line("top_surface_jerk"); - optgroup->append_single_option_line("initial_layer_jerk"); - optgroup->append_single_option_line("initial_layer_travel_jerk"); - optgroup->append_single_option_line("travel_jerk"); - } - { - auto optgroup = page->new_optgroup(L("Advanced")); - optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope"); - optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_segment_length"); - optgroup->append_single_option_line("extrusion_rate_smoothing_external_perimeter_only"); - } -} - -inline void TabPrint_build_support_layout(TabPrint& tab) -{ - PageShp page = tab.add_options_page(L("Support"), "custom-gcode_support"); - { - auto optgroup = page->new_optgroup(L("Support")); - optgroup->append_single_option_line("enable_support"); - optgroup->append_single_option_line("support_type"); - optgroup->append_single_option_line("support_style"); - optgroup->append_single_option_line("support_threshold_angle"); - optgroup->append_single_option_line("support_threshold_overlap"); - optgroup->append_single_option_line("raft_first_layer_density"); - optgroup->append_single_option_line("raft_first_layer_expansion"); - optgroup->append_single_option_line("support_on_build_plate_only"); - optgroup->append_single_option_line("support_critical_regions_only"); - optgroup->append_single_option_line("support_remove_small_overhang"); - } - { - auto optgroup = page->new_optgroup(L("Raft")); - optgroup->append_single_option_line("raft_layers"); - optgroup->append_single_option_line("raft_contact_distance"); - } - { - auto optgroup = page->new_optgroup(L("Support filament")); - optgroup->append_single_option_line("support_filament"); - optgroup->append_single_option_line("support_interface_filament"); - optgroup->append_single_option_line("support_interface_not_for_body"); - } - { - auto optgroup = page->new_optgroup(L("Support ironing")); - optgroup->append_single_option_line("support_ironing"); - optgroup->append_single_option_line("support_ironing_pattern"); - optgroup->append_single_option_line("support_ironing_flow"); - optgroup->append_single_option_line("support_ironing_spacing"); - } - { - auto optgroup = page->new_optgroup(L("Advanced")); - optgroup->append_single_option_line("support_top_z_distance"); - optgroup->append_single_option_line("support_bottom_z_distance"); - optgroup->append_single_option_line("tree_support_wall_count"); - optgroup->append_single_option_line("support_base_pattern"); - optgroup->append_single_option_line("support_base_pattern_spacing"); - optgroup->append_single_option_line("support_angle"); - optgroup->append_single_option_line("support_interface_top_layers"); - optgroup->append_single_option_line("support_interface_bottom_layers"); - optgroup->append_single_option_line("support_interface_pattern"); - optgroup->append_single_option_line("support_interface_spacing"); - optgroup->append_single_option_line("support_bottom_interface_spacing"); - optgroup->append_single_option_line("support_expansion"); - optgroup->append_single_option_line("support_object_xy_distance"); - optgroup->append_single_option_line("support_object_first_layer_gap"); - optgroup->append_single_option_line("bridge_no_support"); - optgroup->append_single_option_line("max_bridge_length"); - optgroup->append_single_option_line("independent_support_layer_height"); - } - { - auto optgroup = page->new_optgroup(L("Tree supports")); - optgroup->append_single_option_line("tree_support_tip_diameter"); - optgroup->append_single_option_line("tree_support_branch_distance"); - optgroup->append_single_option_line("tree_support_branch_distance_organic"); - optgroup->append_single_option_line("tree_support_top_rate"); - optgroup->append_single_option_line("tree_support_branch_diameter"); - optgroup->append_single_option_line("tree_support_branch_diameter_organic"); - optgroup->append_single_option_line("tree_support_branch_diameter_angle"); - optgroup->append_single_option_line("tree_support_branch_angle"); - optgroup->append_single_option_line("tree_support_branch_angle_organic"); - optgroup->append_single_option_line("tree_support_angle_slow"); - optgroup->append_single_option_line("tree_support_auto_brim"); - optgroup->append_single_option_line("tree_support_brim_width"); - } -} - -inline void TabPrint_build_multimaterial_layout(TabPrint& tab) -{ - PageShp page = tab.add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); - { - auto optgroup = page->new_optgroup(L("Prime tower")); - optgroup->append_single_option_line("enable_prime_tower"); - optgroup->append_single_option_line("prime_tower_skip_points"); - optgroup->append_single_option_line("enable_tower_interface_features"); - optgroup->append_single_option_line("enable_tower_interface_cooldown_during_tower"); - optgroup->append_single_option_line("prime_tower_enable_framework"); - optgroup->append_single_option_line("prime_tower_width"); - optgroup->append_single_option_line("prime_volume"); - optgroup->append_single_option_line("prime_tower_brim_width"); - optgroup->append_single_option_line("prime_tower_infill_gap"); - optgroup->append_single_option_line("wipe_tower_rotation_angle"); - optgroup->append_single_option_line("wipe_tower_bridging"); - optgroup->append_single_option_line("wipe_tower_extra_spacing"); - optgroup->append_single_option_line("wipe_tower_extra_flow"); - optgroup->append_single_option_line("wipe_tower_max_purge_speed"); - optgroup->append_single_option_line("wipe_tower_wall_type"); - optgroup->append_single_option_line("wipe_tower_cone_angle"); - optgroup->append_single_option_line("wipe_tower_extra_rib_length"); - optgroup->append_single_option_line("wipe_tower_rib_width"); - optgroup->append_single_option_line("wipe_tower_fillet_wall"); - optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); - optgroup->append_single_option_line("single_extruder_multi_material_priming"); - } - { - auto optgroup = page->new_optgroup(L("Filament for Features")); - optgroup->append_single_option_line("wall_filament"); - optgroup->append_single_option_line("sparse_infill_filament"); - optgroup->append_single_option_line("solid_infill_filament"); - optgroup->append_single_option_line("wipe_tower_filament"); - } - { - auto optgroup = page->new_optgroup(L("Ooze prevention")); - optgroup->append_single_option_line("ooze_prevention"); - optgroup->append_single_option_line("standby_temperature_delta"); - optgroup->append_single_option_line("preheat_time"); - optgroup->append_single_option_line("preheat_steps"); - } - { - auto optgroup = page->new_optgroup(L("Flush options")); - optgroup->append_single_option_line("flush_into_infill"); - optgroup->append_single_option_line("flush_into_objects"); - optgroup->append_single_option_line("flush_into_support"); - } - { - auto optgroup = page->new_optgroup(L("Advanced")); - optgroup->append_single_option_line("interlocking_beam"); - optgroup->append_single_option_line("interface_shells"); - optgroup->append_single_option_line("mmu_segmented_region_max_width"); - optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth"); - optgroup->append_single_option_line("interlocking_beam_width"); - optgroup->append_single_option_line("interlocking_orientation"); - optgroup->append_single_option_line("interlocking_beam_layer_count"); - optgroup->append_single_option_line("interlocking_depth"); - optgroup->append_single_option_line("interlocking_boundary_avoidance"); - } -} - -inline void TabPrint_build_others_layout(TabPrint& tab) -{ - PageShp page = tab.add_options_page(L("Others"), "custom-gcode_other"); - { - auto optgroup = page->new_optgroup(L("Skirt")); - optgroup->append_single_option_line("skirt_loops"); - optgroup->append_single_option_line("skirt_type"); - optgroup->append_single_option_line("min_skirt_length"); - optgroup->append_single_option_line("skirt_distance"); - optgroup->append_single_option_line("skirt_start_angle"); - optgroup->append_single_option_line("skirt_speed"); - optgroup->append_single_option_line("skirt_height"); - optgroup->append_single_option_line("draft_shield"); - optgroup->append_single_option_line("single_loop_draft_shield"); - } - { - auto optgroup = page->new_optgroup(L("Brim")); - optgroup->append_single_option_line("brim_type"); - optgroup->append_single_option_line("brim_width"); - optgroup->append_single_option_line("brim_object_gap"); - optgroup->append_single_option_line("brim_flow_ratio"); - optgroup->append_single_option_line("brim_use_efc_outline"); - optgroup->append_single_option_line("combine_brims"); - optgroup->append_single_option_line("brim_ears_max_angle"); - optgroup->append_single_option_line("brim_ears_detection_length"); - } - { - auto optgroup = page->new_optgroup(L("Special mode")); - optgroup->append_single_option_line("slicing_mode"); - optgroup->append_single_option_line("print_sequence"); - optgroup->append_single_option_line("print_order"); - optgroup->append_single_option_line("spiral_mode"); - optgroup->append_single_option_line("spiral_mode_smooth"); - optgroup->append_single_option_line("spiral_mode_max_xy_smoothing"); - optgroup->append_single_option_line("spiral_starting_flow_ratio"); - optgroup->append_single_option_line("spiral_finishing_flow_ratio"); - optgroup->append_single_option_line("timelapse_type"); - optgroup->append_single_option_line("enable_wrapping_detection"); - } - { - auto optgroup = page->new_optgroup(L("Fuzzy Skin")); - optgroup->append_single_option_line("fuzzy_skin"); - optgroup->append_single_option_line("fuzzy_skin_mode"); - optgroup->append_single_option_line("fuzzy_skin_noise_type"); - optgroup->append_single_option_line("fuzzy_skin_point_distance"); - optgroup->append_single_option_line("fuzzy_skin_thickness"); - optgroup->append_single_option_line("fuzzy_skin_scale"); - optgroup->append_single_option_line("fuzzy_skin_octaves"); - optgroup->append_single_option_line("fuzzy_skin_persistence"); - optgroup->append_single_option_line("fuzzy_skin_first_layer"); - } - { - auto optgroup = page->new_optgroup(L("G-code output")); - optgroup->append_single_option_line("reduce_infill_retraction"); - optgroup->append_single_option_line("gcode_add_line_number"); - optgroup->append_single_option_line("gcode_comments"); - optgroup->append_single_option_line("gcode_label_objects"); - optgroup->append_single_option_line("exclude_object"); - optgroup->append_single_option_line("filename_format"); - } - { - auto optgroup = page->new_optgroup(L("Change extrusion role G-code")); - optgroup->append_single_option_line("process_change_extrusion_role_gcode"); - } - { - auto optgroup = page->new_optgroup(L("Post-processing Scripts")); - optgroup->append_single_option_line("post_process"); - } - { - auto optgroup = page->new_optgroup(L("Notes")); - optgroup->append_single_option_line("notes"); - } -} - -inline void TabFilament_build_filament_layout(TabFilament& tab) -{ - PageShp page = tab.add_options_page(L("Filament"), "custom-gcode_filament"); - { - auto optgroup = page->new_optgroup(L("Basic information")); - optgroup->append_single_option_line("filament_type"); - optgroup->append_single_option_line("filament_vendor"); - optgroup->append_single_option_line("filament_soluble"); - optgroup->append_single_option_line("filament_is_support"); - optgroup->append_single_option_line("filament_change_length"); - optgroup->append_single_option_line("required_nozzle_HRC"); - optgroup->append_single_option_line("default_filament_colour"); - optgroup->append_single_option_line("filament_diameter"); - optgroup->append_single_option_line("filament_adhesiveness_category"); - optgroup->append_single_option_line("filament_density"); - optgroup->append_single_option_line("filament_shrink"); - optgroup->append_single_option_line("filament_shrinkage_compensation_z"); - optgroup->append_single_option_line("filament_cost"); - optgroup->append_single_option_line("temperature_vitrification"); - optgroup->append_single_option_line("idle_temperature"); - { - Line line_{optgroup->get_option("nozzle_temperature_range_low").opt.label, optgroup->get_option("nozzle_temperature_range_low").opt.tooltip}; - line_.append_option(optgroup->get_option("nozzle_temperature_range_low")); - line_.append_option(optgroup->get_option("nozzle_temperature_range_high")); - optgroup->append_line(line_); - } - } - { - auto optgroup = page->new_optgroup(L("Flow ratio and Pressure Advance")); - optgroup->append_single_option_line("pellet_flow_coefficient"); - optgroup->append_single_option_line("filament_flow_ratio"); - optgroup->append_single_option_line("enable_pressure_advance"); - optgroup->append_single_option_line("pressure_advance"); - optgroup->append_single_option_line("adaptive_pressure_advance"); - optgroup->append_single_option_line("adaptive_pressure_advance_overhangs"); - optgroup->append_single_option_line("adaptive_pressure_advance_bridges"); - optgroup->append_single_option_line("adaptive_pressure_advance_model"); - } - { - auto optgroup = page->new_optgroup(L("Print chamber temperature")); - optgroup->append_single_option_line("activate_chamber_temp_control"); - optgroup->append_single_option_line("chamber_temperature"); - } - { - auto optgroup = page->new_optgroup(L("Print temperature")); - { - Line line_{optgroup->get_option("nozzle_temperature_initial_layer").opt.label, optgroup->get_option("nozzle_temperature_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("nozzle_temperature_initial_layer")); - line_.append_option(optgroup->get_option("nozzle_temperature")); - optgroup->append_line(line_); - } - } - { - auto optgroup = page->new_optgroup(L("Bed temperature")); - { - Line line_{optgroup->get_option("supertack_plate_temp_initial_layer").opt.label, optgroup->get_option("supertack_plate_temp_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("supertack_plate_temp_initial_layer")); - line_.append_option(optgroup->get_option("supertack_plate_temp")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("cool_plate_temp_initial_layer").opt.label, optgroup->get_option("cool_plate_temp_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("cool_plate_temp_initial_layer")); - line_.append_option(optgroup->get_option("cool_plate_temp")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("textured_cool_plate_temp_initial_layer").opt.label, optgroup->get_option("textured_cool_plate_temp_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("textured_cool_plate_temp_initial_layer")); - line_.append_option(optgroup->get_option("textured_cool_plate_temp")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("eng_plate_temp_initial_layer").opt.label, optgroup->get_option("eng_plate_temp_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("eng_plate_temp_initial_layer")); - line_.append_option(optgroup->get_option("eng_plate_temp")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("hot_plate_temp_initial_layer").opt.label, optgroup->get_option("hot_plate_temp_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("hot_plate_temp_initial_layer")); - line_.append_option(optgroup->get_option("hot_plate_temp")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("textured_plate_temp_initial_layer").opt.label, optgroup->get_option("textured_plate_temp_initial_layer").opt.tooltip}; - line_.append_option(optgroup->get_option("textured_plate_temp_initial_layer")); - line_.append_option(optgroup->get_option("textured_plate_temp")); - optgroup->append_line(line_); - } - } - { - auto optgroup = page->new_optgroup(L("Volumetric speed limitation")); - optgroup->append_single_option_line("filament_adaptive_volumetric_speed"); - optgroup->append_single_option_line("filament_max_volumetric_speed"); - } -} - -inline void TabFilament_build_cooling_layout(TabFilament& tab) -{ - PageShp page = tab.add_options_page(L("Cooling"), "custom-gcode_cooling_fan"); - { - auto optgroup = page->new_optgroup(L("Cooling for specific layer")); - optgroup->append_single_option_line("close_fan_the_first_x_layers"); - optgroup->append_single_option_line("full_fan_speed_layer"); - } - { - auto optgroup = page->new_optgroup(L("Part cooling fan")); - { - Line line_{optgroup->get_option("fan_min_speed").opt.label, optgroup->get_option("fan_min_speed").opt.tooltip}; - line_.append_option(optgroup->get_option("fan_min_speed")); - line_.append_option(optgroup->get_option("fan_cooling_layer_time")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("fan_max_speed").opt.label, optgroup->get_option("fan_max_speed").opt.tooltip}; - line_.append_option(optgroup->get_option("fan_max_speed")); - line_.append_option(optgroup->get_option("slow_down_layer_time")); - optgroup->append_line(line_); - } - optgroup->append_single_option_line("reduce_fan_stop_start_freq"); - optgroup->append_single_option_line("slow_down_for_layer_cooling"); - optgroup->append_single_option_line("dont_slow_down_outer_wall"); - optgroup->append_single_option_line("slow_down_min_speed"); - optgroup->append_single_option_line("enable_overhang_bridge_fan"); - optgroup->append_single_option_line("overhang_fan_threshold"); - optgroup->append_single_option_line("overhang_fan_speed"); - optgroup->append_single_option_line("internal_bridge_fan_speed"); - optgroup->append_single_option_line("support_material_interface_fan_speed"); - optgroup->append_single_option_line("ironing_fan_speed"); - } - { - auto optgroup = page->new_optgroup(L("Auxiliary part cooling fan")); - optgroup->append_single_option_line("additional_cooling_fan_speed"); - } - { - auto optgroup = page->new_optgroup(L("Exhaust fan")); - optgroup->append_single_option_line("activate_air_filtration"); - { - Line line_{optgroup->get_option("activate_air_filtration_during_print").opt.label, optgroup->get_option("activate_air_filtration_during_print").opt.tooltip}; - line_.append_option(optgroup->get_option("activate_air_filtration_during_print")); - line_.append_option(optgroup->get_option("during_print_exhaust_fan_speed")); - optgroup->append_line(line_); - } - { - Line line_{optgroup->get_option("activate_air_filtration_on_completion").opt.label, optgroup->get_option("activate_air_filtration_on_completion").opt.tooltip}; - line_.append_option(optgroup->get_option("activate_air_filtration_on_completion")); - line_.append_option(optgroup->get_option("complete_print_exhaust_fan_speed")); - optgroup->append_line(line_); - } - } -} - -inline void TabFilament_build_advanced_layout(TabFilament& tab) -{ - PageShp page = tab.add_options_page(L("Advanced"), "custom-gcode_advanced"); - { - auto optgroup = page->new_optgroup(L("Filament start G-code")); - optgroup->append_single_option_line("filament_start_gcode"); - } - { - auto optgroup = page->new_optgroup(L("Change extrusion role G-code")); - optgroup->append_single_option_line("filament_change_extrusion_role_gcode"); - } - { - auto optgroup = page->new_optgroup(L("Filament end G-code")); - optgroup->append_single_option_line("filament_end_gcode"); - } -} - -inline void TabFilament_build_multimaterial_layout(TabFilament& tab) -{ - PageShp page = tab.add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); - { - auto optgroup = page->new_optgroup(L("Wipe tower parameters")); - optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); - optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_dist"); - optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_length"); - optgroup->append_single_option_line("filament_tower_ironing_area"); - optgroup->append_single_option_line("filament_tower_interface_purge_volume"); - optgroup->append_single_option_line("filament_tower_interface_print_temp"); - } - { - auto optgroup = page->new_optgroup(L("Multi Filament")); - optgroup->append_single_option_line("long_retractions_when_ec"); - optgroup->append_single_option_line("retraction_distances_when_ec"); - } - { - auto optgroup = page->new_optgroup(L("Tool change parameters with single extruder MM printers")); - optgroup->append_single_option_line("filament_loading_speed_start"); - optgroup->append_single_option_line("filament_loading_speed"); - optgroup->append_single_option_line("filament_unloading_speed_start"); - optgroup->append_single_option_line("filament_unloading_speed"); - optgroup->append_single_option_line("filament_toolchange_delay"); - optgroup->append_single_option_line("filament_cooling_moves"); - optgroup->append_single_option_line("filament_cooling_initial_speed"); - optgroup->append_single_option_line("filament_cooling_final_speed"); - optgroup->append_single_option_line("filament_stamping_loading_speed"); - optgroup->append_single_option_line("filament_stamping_distance"); - optgroup->append_single_option_line("filament_ramming_parameters"); - } - { - auto optgroup = page->new_optgroup(L("Tool change parameters with multi extruder MM printers")); - optgroup->append_single_option_line("filament_multitool_ramming"); - optgroup->append_single_option_line("filament_multitool_ramming_volume"); - optgroup->append_single_option_line("filament_multitool_ramming_flow"); - } -} - -inline void TabFilament_build_dependencies_layout(TabFilament& tab) -{ - PageShp page = tab.add_options_page(L("Dependencies"), "advanced"); - { - auto optgroup = page->new_optgroup(L("Compatible printers")); - optgroup->append_single_option_line("compatible_printers"); - optgroup->append_single_option_line("compatible_printers_condition"); - } - { - auto optgroup = page->new_optgroup(L("Compatible process profiles")); - optgroup->append_single_option_line("compatible_prints"); - optgroup->append_single_option_line("compatible_prints_condition"); - } -} - -inline void TabFilament_build_notes_layout(TabFilament& tab) -{ - PageShp page = tab.add_options_page(L("Notes"), "custom-gcode_note"); - { - auto optgroup = page->new_optgroup(L("Notes")); - optgroup->append_single_option_line("filament_notes"); - } -} - -inline void TabPrinter_build_basic_information_layout(TabPrinter& tab) -{ - PageShp page = tab.add_options_page(L("Basic information"), "custom-gcode_object-info"); - { - auto optgroup = page->new_optgroup(L("Printable space"), L"param_printable_space"); - tab.layout_hook_printable_space(optgroup.get()); - } - { - auto optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); - tab.layout_hook_advanced(optgroup.get()); - } - { - auto optgroup = page->new_optgroup(L("Cooling Fan"), L"param_cooling_fan"); - tab.layout_hook_cooling_fan(optgroup.get()); - } - { - auto optgroup = page->new_optgroup(L("Extruder Clearance"), L"param_extruder_clearance"); - optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius"); - optgroup->append_single_option_line("extruder_clearance_height_to_rod", "printer_basic_information_extruder_clearance#height-to-rod"); - optgroup->append_single_option_line("extruder_clearance_height_to_lid", "printer_basic_information_extruder_clearance#height-to-lid"); - } - { - auto optgroup = page->new_optgroup(L("Adaptive bed mesh"), L"param_adaptive_mesh"); - optgroup->append_single_option_line("bed_mesh_min", "printer_basic_information_adaptive_bed_mesh#bed-mesh"); - optgroup->append_single_option_line("bed_mesh_max", "printer_basic_information_adaptive_bed_mesh#bed-mesh"); - optgroup->append_single_option_line("bed_mesh_probe_distance", "printer_basic_information_adaptive_bed_mesh#probe-point-distance"); - optgroup->append_single_option_line("adaptive_bed_mesh_margin", "printer_basic_information_adaptive_bed_mesh#mesh-margin"); - } - { - auto optgroup = page->new_optgroup(L("Accessory"), L"param_accessory"); - optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type"); - optgroup->append_single_option_line("nozzle_hrc", "printer_basic_information_accessory#nozzle-hrc"); - optgroup->append_single_option_line("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan"); - optgroup->append_single_option_line("support_chamber_temp_control", "printer_basic_information_accessory#support-controlling-chamber-temperature"); - optgroup->append_single_option_line("support_air_filtration", "printer_basic_information_accessory#support-air-filtration"); - } -} - -inline void TabPrinter_build_machine_g_code_layout(TabPrinter& tab) -{ - PageShp page = tab.add_options_page(L("Machine G-code"), "custom-gcode_gcode"); - { - auto optgroup = page->new_optgroup(L("File header G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("file_start_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option); - } - } - { - auto optgroup = page->new_optgroup(L("Machine start G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("machine_start_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#machine-start-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Machine end G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("machine_end_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#machine-end-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Printing by object G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("printing_by_object_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#between-object-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Before layer change G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("before_layer_change_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#before-layer-change-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Layer change G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("layer_change_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#after-layer-change-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Timelapse G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("time_lapse_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#time-lapse-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Clumping Detection G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("wrapping_detection_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#clumping-detection-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Change filament G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("change_filament_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#change-filament-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Change extrusion role G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("change_extrusion_role_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#change-extrusion-role-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Pause G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("machine_pause_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#pause-g-code"); - } - } - { - auto optgroup = page->new_optgroup(L("Template Custom G-code"), L"param_gcode", 0); - optgroup->m_on_change = [&tab, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(&tab, optgroup_title, opt_key, value); - }; - optgroup->edit_custom_gcode = [&tab](const t_config_option_key& opt_key) { tab.edit_custom_gcode(opt_key); }; - { - Option option = optgroup->get_option("template_custom_gcode"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = gcode_field_height; - optgroup->append_single_option_line(option, "printer_machine_gcode#template-custom-g-code"); - } - } -} - -inline void TabPrinter_build_notes_layout(TabPrinter& tab) -{ - PageShp page = tab.add_options_page(L("Notes"), "custom-gcode_note"); - { - auto optgroup = page->new_optgroup(L("Notes"), L"note"); - optgroup->append_single_option_line("printer_notes"); - } -} - -// ── Aggregate wrappers (backward-compatible with Tab.cpp call sites) ── - -inline void TabPrint_build_layout(TabPrint& tab) -{ - TabPrint_build_quality_layout(tab); - TabPrint_build_strength_layout(tab); - TabPrint_build_speed_layout(tab); - TabPrint_build_support_layout(tab); - TabPrint_build_multimaterial_layout(tab); - TabPrint_build_others_layout(tab); -} - -inline void TabFilament_build_main_layout(TabFilament& tab) -{ - TabFilament_build_filament_layout(tab); - TabFilament_build_cooling_layout(tab); - TabFilament_build_multimaterial_layout(tab); -} - -inline void TabPrinter_build_basic_info_layout(TabPrinter& tab) -{ - TabPrinter_build_basic_information_layout(tab); -} - -inline void TabPrinter_build_gcode_layout(TabPrinter& tab) -{ - TabPrinter_build_machine_g_code_layout(tab); - TabPrinter_build_notes_layout(tab); -} - From a7c8dcc58d470c3bc29fa0c004ae238fb3406f8b Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 16 Jul 2026 08:42:55 +0200 Subject: [PATCH 03/89] Add texture displacement baking, LSCM unwrap and remesh core --- src/libslic3r/MeshBoolean.cpp | 87 ++ src/libslic3r/MeshBoolean.hpp | 16 + src/libslic3r/TextureDisplacement.cpp | 1296 +++++++++++++++-- src/libslic3r/TextureDisplacement.hpp | 401 ++++- tests/libslic3r/test_texture_displacement.cpp | 99 ++ 5 files changed, 1781 insertions(+), 118 deletions(-) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 8b50681370..478e0b950c 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -26,6 +26,12 @@ #include #include #include +// For parameterize_lscm() +#include +#include +#include +#include +#include // BBS: for boolean using mcut #include "mcut/include/mcut/mcut.h" @@ -249,6 +255,87 @@ indexed_triangle_set cgal_to_indexed_triangle_set(const CGALMesh &cgalmesh) return cgal_to_indexed_triangle_set(cgalmesh.m); } +// ///////////////////////////////////////////////////////////////////////////// +// Isotropic remeshing +// ///////////////////////////////////////////////////////////////////////////// + +indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, unsigned n_iterations) +{ + if (mesh.indices.empty() || target_edge_length <= 0.0) + return mesh; + + _EpicMesh cgal_mesh; + triangle_mesh_to_cgal(mesh.vertices, mesh.indices, cgal_mesh); + if (cgal_mesh.is_empty() || cgal_mesh.number_of_faces() == 0) + return mesh; + + try { + CGALProc::isotropic_remeshing(faces(cgal_mesh), target_edge_length, cgal_mesh, + CGALParams::number_of_iterations(n_iterations)); + } catch (const std::exception &) { + return mesh; // CGAL throws on some non-manifold / degenerate inputs; leave the mesh untouched + } + if (cgal_mesh.number_of_faces() == 0) + return mesh; + return cgal_to_indexed_triangle_set(cgal_mesh); +} + +// ///////////////////////////////////////////////////////////////////////////// +// UV parameterization +// ///////////////////////////////////////////////////////////////////////////// + +std::optional> parameterize_lscm(const indexed_triangle_set &mesh) +{ + namespace SMP = CGAL::Surface_mesh_parameterization; + + if (mesh.indices.empty()) + return std::nullopt; + + _EpicMesh cgal_mesh; + triangle_mesh_to_cgal(mesh.vertices, mesh.indices, cgal_mesh); + + using vertex_descriptor = boost::graph_traits<_EpicMesh>::vertex_descriptor; + using halfedge_descriptor = boost::graph_traits<_EpicMesh>::halfedge_descriptor; + + // LSCM assumes a single topological disk: one connected component, one boundary loop. A patch + // with several disconnected painted islands, or with a hole in it, doesn't qualify -- bail out + // rather than silently parameterizing just one arbitrary piece of it. + { + std::vector component_id(num_faces(cgal_mesh)); + const std::size_t num_components = CGAL::Polygon_mesh_processing::connected_components( + cgal_mesh, CGAL::make_property_map(component_id)); + if (num_components != 1) + return std::nullopt; + } + + const halfedge_descriptor border = CGAL::Polygon_mesh_processing::longest_border(cgal_mesh).first; + if (border == halfedge_descriptor()) + return std::nullopt; // no boundary at all -- a closed patch, which isn't a disk either + + using Point_2 = EpicKernel::Point_2; + using UV_pmap = _EpicMesh::Property_map; + UV_pmap uv_map = cgal_mesh.add_property_map("h:uv", Point_2(0, 0)).first; + + using Parameterizer = SMP::LSCM_parameterizer_3<_EpicMesh>; + const SMP::Error_code err = SMP::parameterize(cgal_mesh, Parameterizer(), border, uv_map); + if (err != SMP::OK) + return std::nullopt; + + // triangle_mesh_to_cgal() adds vertices in the exact same order as mesh.vertices (see above), + // and Surface_mesh assigns indices sequentially on insertion into a fresh mesh, so a + // vertex_descriptor's index here is guaranteed to match the original input vertex index -- + // the same assumption cgal_to_indexed_triangle_set() above already relies on. + std::vector result(mesh.vertices.size(), Vec2f::Zero()); + for (vertex_descriptor vd : vertices(cgal_mesh)) { + const std::size_t idx = std::size_t(vd); + if (idx < result.size()) { + const Point_2 &uv = uv_map[vd]; + result[idx] = Vec2f(float(uv.x()), float(uv.y())); + } + } + return result; +} + // ///////////////////////////////////////////////////////////////////////////// // Boolean operations for CGAL meshes // ///////////////////////////////////////////////////////////////////////////// diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index d9a5d196fb..3fb49cb7c5 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include @@ -73,6 +75,20 @@ bool empty(const CGALMesh &mesh); // Repair a mesh using CGAL. Returns true on success. Optionally returns a summary of repairs and an error string. bool repair(TriangleMesh &mesh, RepairedMeshErrors *repaired_errors = nullptr, std::string *error = nullptr); + +// Real UV unwrap of an open mesh patch via CGAL's LSCM (Least Squares Conformal Maps) surface +// parameterization. Returns one UV coordinate per input vertex (same indexing as `mesh.vertices`), +// or nullopt if `mesh` isn't a single topological disk -- LSCM needs exactly one connected +// component with exactly one boundary loop, true for a typical single brush stroke/patch but not +// guaranteed for multiple disconnected painted islands merged into one mesh. +std::optional> parameterize_lscm(const indexed_triangle_set &mesh); + +// Isotropic remeshing (CGAL): rebuilds the mesh so its triangles are close to a uniform target edge +// length, splitting oversized triangles and collapsing undersized ones. Used to even out a model with +// wildly varying triangle sizes so texture displacement has a consistent vertex density to work with. +// Returns the input unchanged if remeshing fails (e.g. a non-manifold or self-intersecting input). +indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, + unsigned n_iterations = 3); } namespace mcut { diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 54948316ae..50740bc532 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -3,32 +3,65 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include "MeshBoolean.hpp" #include "Model.hpp" #include "PNGReadWrite.hpp" #include "TriangleSelector.hpp" namespace Slic3r { -float DecodedHeightTexture::sample(const Vec2f &uv) const +float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTileMethod tile_method) const { if (empty()) return 0.f; - auto wrap01 = [](float x) { + auto repeat01 = [](float x) { x = std::fmod(x, 1.f); return x < 0.f ? x + 1.f : x; }; + // Standard mirrored-repeat: reflect back and forth every other unit, so tile edges always + // line up with themselves instead of jumping from one edge of the image to the other. + auto mirrored_repeat01 = [](float x) { + x = std::fmod(std::abs(x), 2.f); + return x > 1.f ? 2.f - x : x; + }; - const float fx = wrap01(uv.x()) * float(width); - const float fy = wrap01(uv.y()) * float(height); - int x0 = int(std::floor(fx)) % width; - int y0 = int(std::floor(fy)) % height; - if (x0 < 0) x0 += width; - if (y0 < 0) y0 += height; - const int x1 = (x0 + 1) % width; - const int y1 = (y0 + 1) % height; + if (!tile_enabled && (uv.x() < 0.f || uv.x() >= 1.f || uv.y() < 0.f || uv.y() >= 1.f)) + // Outside the single, non-repeating placement entirely: no texture there, not "smeared + // edge pixel" -- clamping the *coordinate* to [0, 1] would otherwise keep returning the + // border row/column's height forever in every direction, stretching it out to infinity. + return 0.f; + + float u, v; + if (!tile_enabled) { + u = uv.x(); + v = uv.y(); + } else if (tile_method == TextureTileMethod::MirroredRepeat) { + u = mirrored_repeat01(uv.x()); + v = mirrored_repeat01(uv.y()); + } else { + u = repeat01(uv.x()); + v = repeat01(uv.y()); + } + + const float fx = u * float(width); + const float fy = v * float(height); + int x0 = std::clamp(int(std::floor(fx)), 0, width - 1); + int y0 = std::clamp(int(std::floor(fy)), 0, height - 1); + // Neighbour for bilinear filtering: wrap for tiling methods, clamp at the edge otherwise (a + // repeating neighbour would incorrectly blend against the opposite edge of the image). + const int x1 = tile_enabled ? (x0 + 1) % width : std::min(x0 + 1, width - 1); + const int y1 = tile_enabled ? (y0 + 1) % height : std::min(y0 + 1, height - 1); const float tx = fx - std::floor(fx); const float ty = fy - std::floor(fy); @@ -38,58 +71,963 @@ float DecodedHeightTexture::sample(const Vec2f &uv) const return top * (1.f - ty) + bottom * ty; } +namespace { +// Decoding a PNG (zlib inflate + defilter) is real work, and image_data never changes in place +// once assigned to a layer (a new texture always gets a brand new image_data), so the decoded +// result can be cached for the lifetime of that specific image_data allocation. This matters +// because the GUI's live preview calls decode_height_texture() again on every rebuild (every +// paint stroke / parameter tweak), which would otherwise re-decode the same unchanged bytes over +// and over. Keyed by a weak_ptr (not just the raw pointer) so a freed image_data's address being +// reused by an unrelated later allocation can never alias a stale cache entry: a weak_ptr to a +// destroyed object always fails to lock, forcing a correct re-decode instead of a false hit. +struct DecodedTextureCache +{ + std::mutex mutex; + std::unordered_map>, DecodedHeightTexture>> entries; +}; +DecodedTextureCache g_decoded_texture_cache; +} // namespace + +namespace { +// A few passes of a separable box blur approximate a Gaussian, cheaply. `radius` is in pixels; 0 is +// a no-op. Wraps at the edges so a tiling height map stays seamless after smoothing. Operates on the +// grayscale byte buffer in place. +void smooth_height_pixels(std::vector &pixels, int width, int height, int radius) +{ + if (radius <= 0 || width <= 0 || height <= 0 || pixels.size() != size_t(width) * size_t(height)) + return; + + const int window = 2 * radius + 1; + const float inv = 1.f / float(window); + std::vector tmp(pixels.size()); + + for (int pass = 0; pass < 2; ++pass) { // two passes -> smoother than a single box + // Horizontal. + for (int y = 0; y < height; ++y) { + const size_t row = size_t(y) * width; + for (int x = 0; x < width; ++x) { + float sum = 0.f; + for (int k = -radius; k <= radius; ++k) { + int sx = x + k; + sx = (sx % width + width) % width; // wrap + sum += float(pixels[row + size_t(sx)]); + } + tmp[row + size_t(x)] = uint8_t(std::lround(sum * inv)); + } + } + // Vertical. + for (int x = 0; x < width; ++x) + for (int y = 0; y < height; ++y) { + float sum = 0.f; + for (int k = -radius; k <= radius; ++k) { + int sy = y + k; + sy = (sy % height + height) % height; // wrap + sum += float(tmp[size_t(sy) * width + size_t(x)]); + } + pixels[size_t(y) * width + size_t(x)] = uint8_t(std::lround(sum * inv)); + } + } +} +} // namespace + DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer) { DecodedHeightTexture result; if (layer.empty()) return result; - const png::ReadBuf rbuf{ layer.image_data->data(), layer.image_data->size() }; - if (!png::is_png(rbuf)) - // Only 8-bit grayscale PNG height maps are supported. The GUI is responsible for - // converting any imported image (jpg, color png, ...) to that format on import, so this - // code never needs a dependency on wxWidgets/libjpeg to decode arbitrary user images. - return result; + // The raw (unsmoothed) decode is what gets cached, keyed by the image_data allocation -- decoding + // a PNG is the expensive part and never changes for a given image. Smoothing is applied afterwards + // to a throwaway copy, so moving the smoothing slider never invalidates the decode cache. + const void *key = layer.image_data.get(); + bool have_raw = false; + { + std::lock_guard lock(g_decoded_texture_cache.mutex); + auto it = g_decoded_texture_cache.entries.find(key); + if (it != g_decoded_texture_cache.entries.end() && it->second.first.lock() == layer.image_data) { + result = it->second.second; + have_raw = true; + } + } - png::ImageGreyscale img; - if (!png::decode_png(rbuf, img) || img.cols == 0 || img.rows == 0) - return result; + if (!have_raw) { + const png::ReadBuf rbuf{ layer.image_data->data(), layer.image_data->size() }; + if (!png::is_png(rbuf)) + // Only 8-bit grayscale PNG height maps are supported. The GUI is responsible for + // converting any imported image (jpg, color png, ...) to that format on import, so this + // code never needs a dependency on wxWidgets/libjpeg to decode arbitrary user images. + return result; - result.width = int(img.cols); - result.height = int(img.rows); - result.pixels = std::move(img.buf); + png::ImageGreyscale img; + if (!png::decode_png(rbuf, img) || img.cols == 0 || img.rows == 0) + return result; + + result.width = int(img.cols); + result.height = int(img.rows); + result.pixels = std::move(img.buf); + + std::lock_guard lock(g_decoded_texture_cache.mutex); + // Opportunistically drop entries for image_data that no longer exists anywhere, so the + // cache doesn't grow without bound across many add/remove-texture cycles in a long session. + auto &entries = g_decoded_texture_cache.entries; + for (auto it = entries.begin(); it != entries.end();) + it = it->second.first.expired() ? entries.erase(it) : std::next(it); + entries[key] = { std::weak_ptr>(layer.image_data), result }; + } + + // Smoothing radius scales with the texture so the effect is resolution-independent; capped so the + // blur stays affordable on large maps. + if (layer.smoothing > 0.f) { + const int max_radius = std::clamp(int(std::lround(0.02f * std::min(result.width, result.height))), 1, 32); + const int radius = std::max(1, int(std::lround(layer.smoothing * float(max_radius)))); + smooth_height_pixels(result.pixels, result.width, result.height, radius); + } return result; } -Vec2f project_texture_displacement_uv(const Vec3f &position, const Vec3f &normal, const TextureDisplacementLayer &layer) +Vec2f project_planar(const Vec3f &position, const Vec3f &normal) { - // Planar-project onto the two axes orthogonal to the dominant component of `normal`. Using a - // single projection axis for an entire patch (rather than per-vertex normals) avoids visible - // seams where the local normal direction changes. Proper seam-aware UV parametrization is a - // later-phase improvement. + // Planar-project onto the two axes orthogonal to the dominant component of `normal`. Called + // with each vertex's *own* normal (TextureProjectionMethod::Triplanar), this is a standard + // tri-planar/cube projection; a patch spanning several differently-oriented faces gets each + // face projected along its own best-fit axis instead of all faces sharing one axis picked + // from a single averaged normal (which looks correct on one face but visibly distorts on any + // other face in the same patch -- exactly the bug an earlier version of this feature had). const Vec3f n = normal.cwiseAbs(); - Vec2f planar; if (n.x() >= n.y() && n.x() >= n.z()) - planar = Vec2f(position.y(), position.z()); - else if (n.y() >= n.x() && n.y() >= n.z()) - planar = Vec2f(position.x(), position.z()); - else - planar = Vec2f(position.x(), position.y()); + return Vec2f(position.y(), position.z()); + if (n.y() >= n.x() && n.y() >= n.z()) + return Vec2f(position.x(), position.z()); + return Vec2f(position.x(), position.y()); +} +namespace { +// Wrapped around patch_axis, centered at patch_center. u is the arc length (mm) around the axis at +// this point's own radius, v is the signed distance along the axis -- a reasonable approximation +// for roughly cylindrical selections, not an exact fit for arbitrary geometry. +Vec2f project_cylindrical(const Vec3f &position, const Vec3f &patch_center, const Vec3f &patch_axis) +{ + Vec3f up = patch_axis; + up = (up.norm() > 1e-8f) ? Vec3f(up.normalized()) : Vec3f::UnitZ(); + const Vec3f arbitrary = (std::abs(up.dot(Vec3f::UnitZ())) < 0.9f) ? Vec3f::UnitZ() : Vec3f::UnitX(); + const Vec3f right = Vec3f(up.cross(arbitrary).normalized()); + const Vec3f fwd = Vec3f(right.cross(up).normalized()); + + const Vec3f rel = position - patch_center; + const float along_axis = rel.dot(up); + const float x = rel.dot(right); + const float y = rel.dot(fwd); + const float radius = std::sqrt(x * x + y * y); + const float angle = std::atan2(y, x); + + return Vec2f(angle * radius, along_axis); +} + +// Longitude/latitude around patch_center. u/v are scaled by this point's own distance from the +// center so the result is in roughly the same mm-ish units tiling_scale expects, rather than bare +// radians -- again an approximation, not an exact geodesic parametrization. +Vec2f project_spherical(const Vec3f &position, const Vec3f &patch_center) +{ + const Vec3f rel = position - patch_center; + const float radius = rel.norm(); + if (radius < 1e-8f) + return Vec2f::Zero(); + + const Vec3f dir = rel / radius; + const float longitude = std::atan2(dir.y(), dir.x()); + const float latitude = std::asin(std::clamp(dir.z(), -1.f, 1.f)); + return Vec2f(longitude, latitude) * radius; +} + +// CGAL's LSCM parameterizer expects a clean mesh with no isolated (unreferenced) vertices -- but +// `patch` here (from TriangleSelector::get_facets_strict()) carries the *entire* mesh's vertex +// array, only its `indices` filtered to the painted triangles. Build a compacted copy referencing +// only the vertices `patch.indices` actually uses, plus a map back to the original vertex index so +// the resulting per-vertex UVs can be looked up by the caller's own (uncompacted) indexing. +indexed_triangle_set compact_patch_with_map(const indexed_triangle_set &patch, std::vector &original_to_compact) +{ + original_to_compact.assign(patch.vertices.size(), -1); + indexed_triangle_set compact; + compact.vertices.reserve(patch.vertices.size()); + compact.indices.reserve(patch.indices.size()); + for (const stl_triangle_vertex_indices &tri : patch.indices) { + stl_triangle_vertex_indices new_tri; + for (int i = 0; i < 3; ++i) { + const int vi = tri[i]; + if (original_to_compact[vi] < 0) { + original_to_compact[vi] = int(compact.vertices.size()); + compact.vertices.push_back(patch.vertices[vi]); + } + new_tri[i] = original_to_compact[vi]; + } + compact.indices.push_back(new_tri); + } + return compact; +} + +} // namespace + +namespace { + +// Union-find over triangles, used to grow charts. +struct UnionFind +{ + std::vector parent; + explicit UnionFind(size_t n) : parent(n) { std::iota(parent.begin(), parent.end(), 0); } + int find(int x) + { + while (parent[size_t(x)] != x) { + parent[size_t(x)] = parent[size_t(parent[size_t(x)])]; // path halving + x = parent[size_t(x)]; + } + return x; + } + void unite(int a, int b) + { + a = find(a); + b = find(b); + if (a != b) + parent[size_t(b)] = a; + } +}; + +uint64_t undirected_edge_key(int a, int b) +{ + if (a > b) + std::swap(a, b); + return (uint64_t(uint32_t(a)) << 32) | uint32_t(b); +} + +Vec3f face_normal(const indexed_triangle_set &mesh, const stl_triangle_vertex_indices &tri) +{ + const Vec3f n = (mesh.vertices[tri[1]] - mesh.vertices[tri[0]]).cross(mesh.vertices[tri[2]] - mesh.vertices[tri[0]]); + const float len = n.norm(); + return (len > 1e-12f) ? Vec3f(n / len) : Vec3f::UnitZ(); +} + +// Groups triangles into charts: two triangles sharing an edge join the same chart only if the angle +// between their normals is below `seam_angle_deg`. Everything sharper than that is a seam, and the +// unwrap gets cut there instead of being forced to flatten across it. +std::vector segment_into_charts(const indexed_triangle_set &mesh, const std::vector &normals, + float seam_angle_deg, const std::unordered_set &seam_keys, + int &chart_count) +{ + const int n_faces = int(mesh.indices.size()); + const float cos_threshold = std::cos(std::clamp(seam_angle_deg, 0.f, 180.f) * float(M_PI) / 180.f); + + // Each shared edge, with the (up to two) faces on it. + std::unordered_map> edge_faces; + edge_faces.reserve(size_t(n_faces) * 3); + for (int f = 0; f < n_faces; ++f) { + const stl_triangle_vertex_indices &tri = mesh.indices[size_t(f)]; + for (int i = 0; i < 3; ++i) { + const uint64_t key = undirected_edge_key(tri[i], tri[(i + 1) % 3]); + const auto [it, inserted] = edge_faces.emplace(key, std::make_pair(f, -1)); + if (!inserted) + it->second.second = f; + } + } + + // The chart-join test compares a *neighbourhood-averaged* normal per face -- the face plus its + // edge-adjacent neighbours (~5 samples) -- rather than the single face normal. On a finely + // tessellated curved surface this stops one noisy triangle from spuriously cutting (or a lone + // near-flat sliver from wrongly merging) a chart, while a genuine sharp crease, where the whole + // neighbourhood on each side agrees, still cuts. This is the "use 5 points, not one" refinement. + std::vector smoothed(mesh.indices.size()); + for (int f = 0; f < n_faces; ++f) { + Vec3f acc = normals[size_t(f)]; + const stl_triangle_vertex_indices &tri = mesh.indices[size_t(f)]; + for (int i = 0; i < 3; ++i) { + const auto it = edge_faces.find(undirected_edge_key(tri[i], tri[(i + 1) % 3])); + if (it == edge_faces.end()) + continue; + const int nb = (it->second.first == f) ? it->second.second : it->second.first; + if (nb >= 0) + acc += normals[size_t(nb)]; + } + smoothed[size_t(f)] = (acc.norm() > 1e-8f) ? Vec3f(acc.normalized()) : normals[size_t(f)]; + } + + UnionFind uf(mesh.indices.size()); + for (const auto &[key, fp] : edge_faces) { + if (fp.second < 0) + continue; // a boundary edge of the patch, nothing on the far side to join + // A manually/auto marked seam always cuts, whatever the dihedral angle -- that is exactly + // what lets "mark seam" / "cut island" split a chart that is otherwise flat enough to merge. + if (!seam_keys.empty() && seam_keys.count(key)) + continue; + if (smoothed[size_t(fp.first)].dot(smoothed[size_t(fp.second)]) >= cos_threshold) + uf.unite(fp.first, fp.second); + } + + std::vector chart_of(mesh.indices.size(), -1); + std::unordered_map root_to_chart; + chart_count = 0; + for (int f = 0; f < n_faces; ++f) { + const auto [it, inserted] = root_to_chart.emplace(uf.find(f), chart_count); + if (inserted) + ++chart_count; + chart_of[size_t(f)] = it->second; + } + return chart_of; +} + +// Projects a chart onto an orthonormal basis of its own average normal. This is *isometric* for a +// flat chart -- lengths and angles come out exactly right -- which is why a flat chart never needs a +// solve at all, and why this also serves as the fallback for a chart LSCM cannot handle. +std::vector project_to_tangent_plane(const indexed_triangle_set &chart, const Vec3f &normal) +{ + const Vec3f seed = (std::abs(normal.z()) < 0.9f) ? Vec3f::UnitZ() : Vec3f::UnitX(); + const Vec3f u = Vec3f(seed.cross(normal).normalized()); + const Vec3f v = Vec3f(normal.cross(u).normalized()); + + std::vector uvs(chart.vertices.size()); + for (size_t i = 0; i < chart.vertices.size(); ++i) + uvs[i] = Vec2f(chart.vertices[i].dot(u), chart.vertices[i].dot(v)); + return uvs; +} + +float area_3d(const indexed_triangle_set &mesh) +{ + float area = 0.f; + for (const stl_triangle_vertex_indices &t : mesh.indices) + area += 0.5f * (mesh.vertices[t[1]] - mesh.vertices[t[0]]).cross(mesh.vertices[t[2]] - mesh.vertices[t[0]]).norm(); + return area; +} + +float area_2d(const std::vector &uvs, const std::vector &indices) +{ + float area = 0.f; + for (const stl_triangle_vertex_indices &t : indices) { + const Vec2f e0 = uvs[size_t(t[1])] - uvs[size_t(t[0])]; + const Vec2f e1 = uvs[size_t(t[2])] - uvs[size_t(t[0])]; + area += 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x()); + } + return area; +} + +// FNV-1a over the patch's geometry plus the seam angle. The unwrap depends on nothing else about a +// layer -- not depth, tiling, rotation, offset or even which texture is on it -- so keying the cache +// on just this is what lets every one of those sliders be dragged without paying for a re-solve. +uint64_t unwrap_cache_key(const indexed_triangle_set &patch, float seam_angle_deg, float padding_mm, + const std::vector> &seam_edges) +{ + uint64_t h = 1469598103934665603ull; + const auto mix = [&h](const void *data, size_t bytes) { + const unsigned char *p = static_cast(data); + for (size_t i = 0; i < bytes; ++i) { + h ^= p[i]; + h *= 1099511628211ull; + } + }; + mix(patch.vertices.data(), patch.vertices.size() * sizeof(Vec3f)); + mix(patch.indices.data(), patch.indices.size() * sizeof(stl_triangle_vertex_indices)); + mix(&seam_angle_deg, sizeof(seam_angle_deg)); + mix(&padding_mm, sizeof(padding_mm)); + mix(seam_edges.data(), seam_edges.size() * sizeof(std::pair)); + return h; +} + +struct UnwrapCache +{ + std::mutex mutex; + std::unordered_map entries; +}; +UnwrapCache g_unwrap_cache; +const size_t UNWRAP_CACHE_MAX_ENTRIES = 8; + +} // namespace + +PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_angle_deg, float padding_mm, + const std::vector> &seam_edges) +{ + PatchUnwrap result; + if (patch.indices.empty()) + return result; + + const uint64_t key = unwrap_cache_key(patch, seam_angle_deg, padding_mm, seam_edges); + { + std::lock_guard lock(g_unwrap_cache.mutex); + if (auto it = g_unwrap_cache.entries.find(key); it != g_unwrap_cache.entries.end()) + return it->second; + } + + // CGAL wants a mesh with no unreferenced vertices; `patch` carries the whole mesh's vertex array + // (see get_facets_strict()), so compact it and keep the map back to the caller's numbering. + std::vector patch_to_compact; + const indexed_triangle_set compact = compact_patch_with_map(patch, patch_to_compact); + std::vector compact_to_patch(compact.vertices.size(), -1); + for (size_t vi = 0; vi < patch_to_compact.size(); ++vi) + if (patch_to_compact[vi] >= 0) + compact_to_patch[size_t(patch_to_compact[vi])] = int(vi); + + std::vector normals(compact.indices.size()); + for (size_t f = 0; f < compact.indices.size(); ++f) + normals[f] = face_normal(compact, compact.indices[f]); + + // Seam edges arrive in patch (== mesh) vertex space; translate to the compact numbering the + // segmentation runs in. An edge whose endpoints didn't both survive compaction is simply ignored. + std::unordered_set seam_keys; + seam_keys.reserve(seam_edges.size() * 2); + for (const auto &[a, b] : seam_edges) { + if (a < 0 || b < 0 || size_t(a) >= patch_to_compact.size() || size_t(b) >= patch_to_compact.size()) + continue; + const int ca = patch_to_compact[size_t(a)]; + const int cb = patch_to_compact[size_t(b)]; + if (ca >= 0 && cb >= 0) + seam_keys.insert(undirected_edge_key(ca, cb)); + } + + // Once the user has marked seams by hand, those seams define the islands: the automatic + // sharp-angle cutting must not keep splitting faces the user left un-seamed, or islands the user + // meant to be one piece never merge (the "seam angle overrides it" the user hit). So when any + // manual seam exists, the angle threshold is dropped to 180 degrees - nothing is cut except the + // marked seams. With no manual seams, the seam angle behaves exactly as before. + const float effective_seam_angle = seam_keys.empty() ? seam_angle_deg : 180.f; + int chart_count = 0; + const std::vector chart_of = segment_into_charts(compact, normals, effective_seam_angle, seam_keys, chart_count); + + struct FlatChart + { + std::vector uvs; + std::vector to_patch; // chart vertex -> patch vertex + std::vector indices; // chart-local + Vec2f min = Vec2f::Zero(); + Vec2f size = Vec2f::Zero(); + }; + // static_cast, not size_t(...): the latter is a most-vexing-parse and declares a function. + std::vector charts(static_cast(chart_count)); + + for (int c = 0; c < chart_count; ++c) { + FlatChart &chart = charts[size_t(c)]; + + // Build the chart's own sub-mesh. Each chart gets its *own* compact->local vertex map, which + // is exactly what duplicates a seam vertex: it appears once in each chart that touches it, + // free to hold a different UV in each. + indexed_triangle_set chart_mesh; + std::vector compact_to_local(compact.vertices.size(), -1); + Vec3f normal_sum = Vec3f::Zero(); + for (size_t f = 0; f < compact.indices.size(); ++f) { + if (chart_of[f] != c) + continue; + const stl_triangle_vertex_indices &tri = compact.indices[f]; + // Area-weighted, so a chart's average normal isn't dragged around by slivers. + normal_sum += (compact.vertices[tri[1]] - compact.vertices[tri[0]]).cross(compact.vertices[tri[2]] - compact.vertices[tri[0]]); + + stl_triangle_vertex_indices local_tri; + for (int i = 0; i < 3; ++i) { + const int cv = tri[i]; + if (compact_to_local[size_t(cv)] < 0) { + compact_to_local[size_t(cv)] = int(chart_mesh.vertices.size()); + chart_mesh.vertices.push_back(compact.vertices[size_t(cv)]); + chart.to_patch.push_back(compact_to_patch[size_t(cv)]); + } + local_tri[i] = compact_to_local[size_t(cv)]; + } + chart_mesh.indices.push_back(local_tri); + } + if (chart_mesh.indices.empty()) + continue; + + const Vec3f chart_normal = (normal_sum.norm() > 1e-12f) ? Vec3f(normal_sum.normalized()) : Vec3f::UnitZ(); + + // Is the chart flat? Charts are grown by a *pairwise* angle threshold, so a chart can still + // curve gradually across many triangles -- being merged is not the same as being planar. But + // when it is planar (a cube face, and after seam-cutting that is the common case), the + // tangent-plane projection is already the exact answer, and skipping the solve is the single + // biggest speed-up here. + bool planar = true; + for (size_t f = 0; f < compact.indices.size() && planar; ++f) + if (chart_of[f] == c) + planar = normals[f].dot(chart_normal) >= 0.9998f; // ~1 degree + + // Measured before chart_mesh.indices is moved out from under it, below -- area_3d() iterates + // those indices, so taking it afterwards silently measures an empty mesh and returns 0. + const float mesh_area_3d = area_3d(chart_mesh); + + std::optional> uvs; + if (!planar) + uvs = MeshBoolean::cgal::parameterize_lscm(chart_mesh); + // Flat chart, or one LSCM refused (not a topological disk -- closed, or holed). + chart.uvs = uvs ? std::move(*uvs) : project_to_tangent_plane(chart_mesh, chart_normal); + chart.indices = std::move(chart_mesh.indices); + + // LSCM's output is only defined up to a similarity, so charts come back at arbitrary and + // mutually inconsistent scales. Rescale each to its true surface area, so `Tile size (mm)` + // means the same thing on every chart and the texture doesn't change density across a seam. + const float uv_area = area_2d(chart.uvs, chart.indices); + if (uv_area > 1e-12f && mesh_area_3d > 1e-12f) { + const float scale = std::sqrt(mesh_area_3d / uv_area); + for (Vec2f &uv : chart.uvs) + uv *= scale; + } + + Vec2f lo(std::numeric_limits::max(), std::numeric_limits::max()); + Vec2f hi(std::numeric_limits::lowest(), std::numeric_limits::lowest()); + for (const Vec2f &uv : chart.uvs) { + lo = lo.cwiseMin(uv); + hi = hi.cwiseMax(uv); + } + chart.min = lo; + chart.size = hi - lo; + } + + // Shelf-pack the charts side by side so they don't overlap. Overlap would be harmless for a + // repeating texture but wrong for a non-tiled (decal) one, and it makes the UV editor unreadable. + float total_area = 0.f; + float widest = 0.f; + for (const FlatChart &chart : charts) { + total_area = total_area + chart.size.x() * chart.size.y(); + widest = std::max(widest, chart.size.x()); + } + const float shelf_width = std::max(widest, std::sqrt(std::max(total_area, 0.f)) * 1.4f); + // Negative padding means auto: a small fraction of the packed size, which is scale-independent + // and so does something sensible for a 5 mm patch and a 500 mm one alike. + const float margin = (padding_mm >= 0.f) ? padding_mm : std::max(shelf_width * 0.02f, 1e-4f); + + std::vector order(charts.size()); + std::iota(order.begin(), order.end(), 0); + std::sort(order.begin(), order.end(), // tallest first: the usual way to keep shelves tight + [&charts](int a, int b) { return charts[size_t(a)].size.y() > charts[size_t(b)].size.y(); }); + + float cursor_x = 0.f, cursor_y = 0.f, row_height = 0.f; + for (const int c : order) { + FlatChart &chart = charts[size_t(c)]; + if (chart.uvs.empty()) + continue; + if (cursor_x > 0.f && cursor_x + chart.size.x() > shelf_width) { + cursor_x = 0.f; + cursor_y += row_height + margin; + row_height = 0.f; + } + const Vec2f translation = Vec2f(cursor_x, cursor_y) - chart.min; + for (Vec2f &uv : chart.uvs) + uv += translation; + + cursor_x += chart.size.x() + margin; + row_height = std::max(row_height, chart.size.y()); + } + + // Concatenate the packed charts into the flat, duplicated-vertex form PatchUnwrap describes. + result.chart_centroid.assign(size_t(chart_count), Vec2f::Zero()); + for (int c = 0; c < chart_count; ++c) { + FlatChart &chart = charts[size_t(c)]; + const int base = int(result.uvs.size()); + result.uvs.insert(result.uvs.end(), chart.uvs.begin(), chart.uvs.end()); + result.source_vertex.insert(result.source_vertex.end(), chart.to_patch.begin(), chart.to_patch.end()); + result.vertex_chart.insert(result.vertex_chart.end(), chart.uvs.size(), c); + for (const stl_triangle_vertex_indices &tri : chart.indices) + result.indices.emplace_back(tri[0] + base, tri[1] + base, tri[2] + base); + + if (!chart.uvs.empty()) { + Vec2f sum = Vec2f::Zero(); + for (const Vec2f &uv : chart.uvs) + sum += uv; + result.chart_centroid[size_t(c)] = sum / float(chart.uvs.size()); + } + } + result.chart_count = chart_count; + + // Island outlines: an edge used by exactly one triangle. Charts have disjoint vertex sets (seam + // vertices are duplicated), so an edge along a seam shows up once in each chart and is correctly + // reported as a boundary of both. + { + std::unordered_map edge_use; + edge_use.reserve(result.indices.size() * 3); + for (const stl_triangle_vertex_indices &tri : result.indices) + for (int i = 0; i < 3; ++i) + ++edge_use[undirected_edge_key(tri[i], tri[(i + 1) % 3])]; + for (const stl_triangle_vertex_indices &tri : result.indices) + for (int i = 0; i < 3; ++i) { + const int a = tri[i], b = tri[(i + 1) % 3]; + if (edge_use[undirected_edge_key(a, b)] == 1) + result.boundary_edges.emplace_back(a, b); + } + } + + { + std::lock_guard lock(g_unwrap_cache.mutex); + // A patch changes on every paint stroke, so this would grow without bound over a session. + // Nothing here is worth an LRU: the working set is "the patch I am editing right now". + if (g_unwrap_cache.entries.size() >= UNWRAP_CACHE_MAX_ENTRIES) + g_unwrap_cache.entries.clear(); + g_unwrap_cache.entries.emplace(key, result); + } + return result; +} + +Eigen::Matrix island_transform_matrix(int chart, const PatchUnwrap &unwrap, const std::vector &islands) +{ + Eigen::Matrix m; + m << 1.f, 0.f, 0.f, + 0.f, 1.f, 0.f; + if (chart < 0 || size_t(chart) >= islands.size() || size_t(chart) >= unwrap.chart_centroid.size()) + return m; // no hand placement for this island: leave it where the packing put it + + const TextureIsland &island = islands[size_t(chart)]; + const Vec2f centre = unwrap.chart_centroid[size_t(chart)]; + + const float rad = island.rotation_deg * float(M_PI) / 180.f; + const float s = island.scale; + const float cs = std::cos(rad) * s; + const float sn = std::sin(rad) * s; + + // uv -> centre + R*S*(uv - centre) + offset, with the linear part and the constant part split + // out so this can be handed to a shader as-is. + Eigen::Matrix2f linear; + linear << cs, -sn, + sn, cs; + const Vec2f translation = centre + island.offset - linear * centre; + + m.block<2, 2>(0, 0) = linear; + m.col(2) = translation; + return m; +} + +Vec2f apply_island_transform(const Vec2f &uv, int chart, const PatchUnwrap &unwrap, const std::vector &islands) +{ + const Eigen::Matrix m = island_transform_matrix(chart, unwrap, islands); + return m.block<2, 2>(0, 0) * uv + m.col(2); +} + +namespace { +// One chart's copy of a shared mesh edge: the uv-vertex indices of its two endpoints, ordered so that +// .first is always the lower-numbered base vertex (so two charts' copies line up by base vertex). +struct ChartEdge { int chart = -1; int uv_lo = -1; int uv_hi = -1; }; + +// base edge (lo,hi mesh vertex) -> every chart that has it on its boundary. A mesh edge shared by two +// charts is a seam between them and appears once in each chart's boundary (charts have disjoint uv +// vertices), so its entry lists both charts. +std::map, std::vector> build_shared_edges(const PatchUnwrap &u) +{ + std::map, std::vector> edges; + for (const auto &[ua, ub] : u.boundary_edges) { + if (ua < 0 || ub < 0 || size_t(ua) >= u.source_vertex.size() || size_t(ub) >= u.source_vertex.size()) + continue; + const int ba = u.source_vertex[size_t(ua)], bb = u.source_vertex[size_t(ub)]; + if (ba < 0 || bb < 0 || ba == bb) + continue; + const int chart = (size_t(ua) < u.vertex_chart.size()) ? u.vertex_chart[size_t(ua)] : -1; + if (chart < 0) + continue; + const std::pair key{ std::min(ba, bb), std::max(ba, bb) }; + // Order the uv endpoints to match the base-vertex order in the key. + ChartEdge e{ chart, ua, ub }; + if (u.source_vertex[size_t(ua)] != key.first) + std::swap(e.uv_lo, e.uv_hi); + edges[key].push_back(e); + } + return edges; +} + +// Solve the placement that maps child's raw uv edge (rc0->rc1) onto the parent's placed edge +// (pp0->pp1): a rigid rotation about the child chart's centroid plus an offset, scale kept at 1 so the +// texel density is unchanged. cen is the child chart's centroid. +TextureIsland solve_edge_alignment(const Vec2f &rc0, const Vec2f &rc1, const Vec2f &pp0, const Vec2f &pp1, const Vec2f &cen) +{ + const Vec2f dP = pp1 - pp0, dC = rc1 - rc0; + const float rho = std::atan2(dP.y(), dP.x()) - std::atan2(dC.y(), dC.x()); + const float cs = std::cos(rho), sn = std::sin(rho); + const auto rot = [&](const Vec2f &v) { return Vec2f(v.x() * cs - v.y() * sn, v.x() * sn + v.y() * cs); }; + + TextureIsland island; + island.scale = 1.f; + island.rotation_deg = rho * 180.f / float(M_PI); + island.offset = pp0 - (rot(rc0 - cen) + cen); // island_transform_matrix places rc0 exactly here + return island; +} +} // namespace + +bool join_chart_placement(const PatchUnwrap &unwrap, const std::vector &islands, int child, int parent, + TextureIsland &out_child) +{ + if (child < 0 || parent < 0 || child == parent || size_t(child) >= unwrap.chart_centroid.size()) + return false; + const auto edges = build_shared_edges(unwrap); + for (const auto &[base_edge, list] : edges) { + const ChartEdge *pe = nullptr; + const ChartEdge *ce = nullptr; + for (const ChartEdge &e : list) { + if (e.chart == parent) pe = &e; + if (e.chart == child) ce = &e; + } + if (pe == nullptr || ce == nullptr) + continue; + // Parent's *placed* edge; child's *raw* edge, matched endpoint-to-endpoint by base vertex. + const Vec2f pp0 = apply_island_transform(unwrap.uvs[size_t(pe->uv_lo)], parent, unwrap, islands); + const Vec2f pp1 = apply_island_transform(unwrap.uvs[size_t(pe->uv_hi)], parent, unwrap, islands); + out_child = solve_edge_alignment(unwrap.uvs[size_t(ce->uv_lo)], unwrap.uvs[size_t(ce->uv_hi)], pp0, pp1, + unwrap.chart_centroid[size_t(child)]); + return true; + } + return false; +} + +std::vector compute_connected_net(const PatchUnwrap &unwrap) +{ + std::vector islands(size_t(std::max(unwrap.chart_count, 0))); + if (unwrap.chart_count <= 1) + return islands; + + // Chart adjacency, with one representative shared edge per adjacent pair. + const auto edges = build_shared_edges(unwrap); + struct PairEdge { ChartEdge a, b; }; + std::map, PairEdge> pair_edge; + std::vector> adj(size_t(unwrap.chart_count)); + for (const auto &[base_edge, list] : edges) { + for (size_t i = 0; i < list.size(); ++i) + for (size_t j = i + 1; j < list.size(); ++j) { + const int c1 = list[i].chart, c2 = list[j].chart; + if (c1 == c2 || c1 < 0 || c2 < 0 || c1 >= unwrap.chart_count || c2 >= unwrap.chart_count) + continue; + const std::pair pk{ std::min(c1, c2), std::max(c1, c2) }; + if (pair_edge.count(pk)) + continue; // keep the first shared edge as the fold line for this pair + pair_edge[pk] = (c1 < c2) ? PairEdge{ list[i], list[j] } : PairEdge{ list[j], list[i] }; + adj[size_t(pk.first)].push_back(pk.second); + adj[size_t(pk.second)].push_back(pk.first); + } + } + + std::vector placed(size_t(unwrap.chart_count), false); + std::vector pmin(size_t(unwrap.chart_count)), pmax(size_t(unwrap.chart_count)); + const auto chart_bbox = [&](int c, Vec2f &lo, Vec2f &hi) { + lo = Vec2f(std::numeric_limits::max(), std::numeric_limits::max()); + hi = Vec2f(std::numeric_limits::lowest(), std::numeric_limits::lowest()); + for (size_t i = 0; i < unwrap.uvs.size(); ++i) + if (unwrap.vertex_chart[i] == c) { + const Vec2f p = apply_island_transform(unwrap.uvs[i], c, unwrap, islands); + lo = lo.cwiseMin(p); + hi = hi.cwiseMax(p); + } + }; + const auto overlaps_placed = [&](int c, const Vec2f &lo, const Vec2f &hi) { + // A small inset, so charts that merely share an edge (touching bboxes) aren't judged to overlap. + const Vec2f eps = 0.02f * (hi - lo).cwiseAbs(); + for (int o = 0; o < unwrap.chart_count; ++o) + if (placed[size_t(o)] && o != c && + lo.x() + eps.x() < pmax[size_t(o)].x() && hi.x() - eps.x() > pmin[size_t(o)].x() && + lo.y() + eps.y() < pmax[size_t(o)].y() && hi.y() - eps.y() > pmin[size_t(o)].y()) + return true; + return false; + }; + + // BFS from chart 0 (kept at its packed position), unfolding each newly reached chart onto its parent. + std::queue q; + placed[0] = true; + chart_bbox(0, pmin[0], pmax[0]); + q.push(0); + while (!q.empty()) { + const int p = q.front(); + q.pop(); + for (const int c : adj[size_t(p)]) { + if (placed[size_t(c)]) + continue; + const std::pair pk{ std::min(p, c), std::max(p, c) }; + const auto it = pair_edge.find(pk); + if (it == pair_edge.end()) + continue; + const ChartEdge &pe = (p < c) ? it->second.a : it->second.b; + const ChartEdge &ce = (p < c) ? it->second.b : it->second.a; + const Vec2f pp0 = apply_island_transform(unwrap.uvs[size_t(pe.uv_lo)], p, unwrap, islands); + const Vec2f pp1 = apply_island_transform(unwrap.uvs[size_t(pe.uv_hi)], p, unwrap, islands); + islands[size_t(c)] = solve_edge_alignment(unwrap.uvs[size_t(ce.uv_lo)], unwrap.uvs[size_t(ce.uv_hi)], pp0, + pp1, unwrap.chart_centroid[size_t(c)]); + + Vec2f lo, hi; + chart_bbox(c, lo, hi); + if (overlaps_placed(c, lo, hi)) { + islands[size_t(c)] = TextureIsland{}; // would collide: leave it where the packing put it + continue; // and don't unfold its subtree off a packed chart + } + placed[size_t(c)] = true; + pmin[size_t(c)] = lo; + pmax[size_t(c)] = hi; + q.push(c); + } + } + return islands; +} + +void average_island_scales(std::vector &islands) +{ + if (islands.empty()) + return; + float sum = 0.f; + for (const TextureIsland &island : islands) + sum += island.scale; + const float mean = sum / float(islands.size()); + for (TextureIsland &island : islands) + island.scale = mean; +} + +std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) +{ + // Padding disabled (0), matching the UV editor: the packed islands the editor shows and the ones the + // bake/preview samples must be laid out identically, or a hand placement made in the editor would + // land somewhere else in the baked result. + const PatchUnwrap unwrap = compute_patch_unwrap(patch, layer.lscm_seam_angle_deg, 0.f, layer.lscm_seam_edges); + if (unwrap.empty()) + return {}; + + // Manual per-vertex UV edits (UV editor Vertex/Edge modes) override the automatic raw unwrap + // coordinate for a mesh vertex, before the island transform -- so the edit rides along with any + // island move/rotate exactly like the rest of the island. See TextureDisplacementLayer:: + // lscm_uv_overrides. Small (hand edits), so a plain map is ample. + std::map overrides; + for (const auto &[mv, uv] : layer.lscm_uv_overrides) + overrides[mv] = uv; + + // One UV per patch vertex: a seam vertex has several (one per chart it touches) and has to + // settle on one, since it can only be displaced to a single position. See compute_lscm_uvs()'s + // header comment -- the surface stays watertight regardless. + std::vector per_vertex(patch.vertices.size(), Vec2f::Zero()); + std::vector assigned(patch.vertices.size(), false); + for (size_t i = 0; i < unwrap.uvs.size(); ++i) { + const int pv = unwrap.source_vertex[i]; + if (pv >= 0 && !assigned[size_t(pv)]) { + Vec2f raw = unwrap.uvs[i]; + const auto it = overrides.find(pv); + if (it != overrides.end()) + raw = it->second; + per_vertex[size_t(pv)] = apply_island_transform(raw, unwrap.vertex_chart[i], unwrap, layer.islands); + assigned[size_t(pv)] = true; + } + } + return per_vertex; +} + +Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer) +{ const float scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f; - planar *= scale; + const Vec2f scaled = planar * scale; const float rad = layer.rotation_deg * float(M_PI) / 180.f; const float cs = std::cos(rad); const float sn = std::sin(rad); - const Vec2f rotated(planar.x() * cs - planar.y() * sn, planar.x() * sn + planar.y() * cs); + const Vec2f rotated(scaled.x() * cs - scaled.y() * sn, scaled.x() * sn + scaled.y() * cs); return rotated + layer.offset; } -// Area-weighted vertex normals computed from the patch's own (pre-displacement) connectivity. -// Since the patch's vertices have not moved yet at the point this is called, these are identical -// to the surface normals of the mesh the patch was extracted from. +float blend_displacement(float accumulated, float value, TextureBlendMode mode) +{ + switch (mode) { + case TextureBlendMode::Subtract: return accumulated - value; + // Multiply/Divide scale rather than offset, so they take `value` as a factor relative to 1 mm + // (see TextureBlendMode): a 1 mm-deep layer sampling a white texel is then exactly neutral. + case TextureBlendMode::Multiply: return accumulated * value; + case TextureBlendMode::Divide: { + // Every height map has black regions, and a black texel samples to *exactly* zero -- so this + // divisor really does hit zero in ordinary use, not just in some contrived edge case. Floor + // its magnitude: an unbounded 1/0 would not merely look wrong, it would fling vertices + // thousands of mm away and poison the mesh's bounding box (and with it every plate/print + // volume check downstream). The floor doubles as a cap on how far Divide can ever amplify + // the relief beneath it -- at most 1/0.05 = 20x. + constexpr float min_divisor = 0.05f; + const float divisor = (std::abs(value) < min_divisor) ? std::copysign(min_divisor, value < 0.f ? -1.f : 1.f) : + value; + return accumulated / divisor; + } + case TextureBlendMode::Add: + default: return accumulated + value; + } +} + +float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisplacementLayer &layer, + const Vec3f &position, const Vec3f &normal, + const Vec3f &patch_center, const Vec3f &patch_axis, const Vec2f *lscm_uv) +{ + if (texture.empty()) + return 0.f; + + auto sample_at = [&](const Vec2f &planar) { + return texture.sample(apply_uv_transform(planar, layer), layer.tile_enabled, layer.tile_method); + }; + + // Precomputed per-patch LSCM solve wins over the layer's own method (see the header): the + // caller only passes it when the patch actually parameterized successfully, so a patch that + // failed to unwrap falls through to the analytic methods below as its documented fallback. + if (lscm_uv != nullptr) + return sample_at(*lscm_uv); + + switch (layer.projection_method) { + case TextureProjectionMethod::Cylindrical: + return sample_at(project_cylindrical(position, patch_center, patch_axis)); + case TextureProjectionMethod::Spherical: + return sample_at(project_spherical(position, patch_center)); + case TextureProjectionMethod::ViewProjected: + // Flat projection onto the captured projector plane. Single-valued per point, so unlike + // blended triplanar it is one sample, and it is what "project from view" places. + return sample_at(Vec2f(position.dot(layer.view_project_right), position.dot(layer.view_project_up))); + case TextureProjectionMethod::LSCM: // no usable unwrap for this patch -- fall back to Triplanar + case TextureProjectionMethod::Triplanar: + default: break; + } + + // Blended tri-planar: sample all three axis-aligned planes and cross-fade between them by the + // normal's own components, instead of hard-switching to whichever single axis dominates. The + // hard switch is what produced a visible seam wherever the dominant axis flips (see + // TextureProjectionMethod::Triplanar); a weighted blend is continuous across that transition + // by construction, since the weight of the axis being left behind falls smoothly to zero. + Vec3f w = normal.cwiseAbs(); + w = Vec3f(std::pow(w.x(), TRIPLANAR_BLEND_SHARPNESS), std::pow(w.y(), TRIPLANAR_BLEND_SHARPNESS), + std::pow(w.z(), TRIPLANAR_BLEND_SHARPNESS)); + const float w_sum = w.x() + w.y() + w.z(); + if (w_sum < 1e-8f) + // Degenerate normal: no axis is meaningfully dominant, so no blend is meaningful either. + return sample_at(Vec2f(position.x(), position.y())); + w /= w_sum; + + // Each plane drops the axis it is named for, matching project_planar()'s own convention (which + // the GUI's on-canvas placement gizmo also relies on). + return w.x() * sample_at(Vec2f(position.y(), position.z())) + + w.y() * sample_at(Vec2f(position.x(), position.z())) + + w.z() * sample_at(Vec2f(position.x(), position.y())); +} + +indexed_triangle_set extract_painted_patch(const indexed_triangle_set &base_mesh, + const TriangleSelector::TriangleSplittingData &facet_data) +{ + if (facet_data.triangles_to_split.empty()) + return {}; + + const TriangleMesh selector_mesh(base_mesh); + TriangleSelector selector(selector_mesh); + selector.deserialize(facet_data, false); + return selector.get_facets_strict(EnforcerBlockerType::ENFORCER); +} + +bool compute_layer_paint_anchor(const indexed_triangle_set &base_mesh, + const TriangleSelector::TriangleSplittingData &facet_data, + Vec3f &anchor_pos, + Vec3f &anchor_normal) +{ + const indexed_triangle_set patch = extract_painted_patch(base_mesh, facet_data); + if (patch.indices.empty()) + return false; + + Vec3f centroid_sum = Vec3f::Zero(); + Vec3f normal_sum = Vec3f::Zero(); + for (const stl_triangle_vertex_indices &tri : patch.indices) { + const Vec3f &a = patch.vertices[tri[0]]; + const Vec3f &b = patch.vertices[tri[1]]; + const Vec3f &c = patch.vertices[tri[2]]; + // Cross product magnitude is twice the face area, so this area-weights both sums the same + // way texture_displacement_vertex_normals() does above. + normal_sum += (b - a).cross(c - a); + centroid_sum += (a + b + c) / 3.f; + } + + anchor_pos = centroid_sum / float(patch.indices.size()); + anchor_normal = (normal_sum.norm() > 1e-8f) ? Vec3f(normal_sum.normalized()) : Vec3f::UnitZ(); + return true; +} + +// Area-weighted vertex normals of the undisplaced mesh. build_texture_displacement() computes +// these once, up front, and every layer both projects and displaces along them -- so a vertex +// covered by several layers is pushed along one single, well-defined direction rather than along +// whatever direction the surface happened to be pointing partway through the stack. static std::vector texture_displacement_vertex_normals(const indexed_triangle_set &its) { std::vector normals(its.vertices.size(), Vec3f::Zero()); @@ -111,16 +1049,67 @@ static std::vector texture_displacement_vertex_normals(const indexed_tria return normals; } +namespace { +// Shortest along-surface distance from every patch vertex to the patch boundary (the pinned vertices +// shared with the untouched surface), by a multi-source Dijkstra over the patch edges. Used for edge +// smoothing: the displacement is faded out as this distance goes to zero. +std::vector patch_boundary_distance(const indexed_triangle_set &patch, const std::vector &is_boundary) +{ + const size_t n = patch.vertices.size(); + std::vector>> adj(n); + for (const stl_triangle_vertex_indices &tri : patch.indices) + for (int i = 0; i < 3; ++i) { + const int a = tri[i], b = tri[(i + 1) % 3]; + if (a < 0 || b < 0 || size_t(a) >= n || size_t(b) >= n) + continue; + const float w = (patch.vertices[size_t(a)] - patch.vertices[size_t(b)]).norm(); + adj[size_t(a)].push_back({ b, w }); + adj[size_t(b)].push_back({ a, w }); + } + + std::vector dist(n, std::numeric_limits::infinity()); + using QN = std::pair; + std::priority_queue, std::greater> pq; + for (size_t v = 0; v < n; ++v) + if (v < is_boundary.size() && is_boundary[v]) { + dist[v] = 0.f; + pq.push({ 0.f, int(v) }); + } + while (!pq.empty()) { + const auto [d, u] = pq.top(); + pq.pop(); + if (d > dist[size_t(u)]) + continue; + for (const auto &[w, ew] : adj[size_t(u)]) { + const float nd = d + ew; + if (nd < dist[size_t(w)]) { + dist[size_t(w)] = nd; + pq.push({ nd, w }); + } + } + } + return dist; +} +} // namespace + indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data) { - const indexed_triangle_set original_mesh = base_mesh; - indexed_triangle_set working_mesh = original_mesh; - bool mesh_modified = false; + indexed_triangle_set mesh = base_mesh; + // TriangleSelector's vertex array starts with the mesh's own vertices (any extra ones, created + // where a brush stroke split a triangle, are appended after them), and get_facets_strict() + // emits exactly the *referenced* ones, in order. So selector vertex index i is our vertex i -- + // but only if every vertex of `mesh` is referenced by some triangle, which is precisely what + // this call establishes. It is a no-op (indices untouched) for any mesh that already is, which + // in practice is all of them; it exists so an input carrying stray unreferenced vertices can't + // silently shift the indexing and displace the wrong vertices. + its_compactify_vertices(mesh); + if (mesh.vertices.empty() || mesh.indices.empty()) + return mesh; - // Layers behave like stacked image-editor layers: applied in slot order, each one sculpting - // the surface left by the layers before it. This is what makes overlapping layers "blend". + // Layers are combined in slot order, like stacked layers in an image editor: each one folds its + // own displacement into the running total via its blend mode (see TextureBlendMode). std::vector ordered_layers; for (const TextureDisplacementLayer &layer : layers) if (!layer.empty() && layer.slot >= 0 && size_t(layer.slot) < TEXTURE_DISPLACEMENT_MAX_LAYERS) @@ -128,90 +1117,138 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set std::sort(ordered_layers.begin(), ordered_layers.end(), [](const TextureDisplacementLayer *a, const TextureDisplacementLayer *b) { return a->slot < b->slot; }); + // Every layer measures its displacement against the *original* surface -- normals included -- + // rather than against whatever the previous layer left behind. That is what lets all the layers + // be evaluated independently and merged per vertex, instead of having to re-mesh and remap the + // paint masks between them (see the header for why that earlier design was dropped). + const std::vector vertex_normals = texture_displacement_vertex_normals(mesh); + + std::vector displacement(mesh.vertices.size(), 0.f); + std::vector displaced(mesh.vertices.size(), false); + bool any_displacement = false; + + const TriangleMesh selector_mesh(mesh); for (const TextureDisplacementLayer *layer : ordered_layers) { - const TriangleSelector::TriangleSplittingData &stored_data = facets_data[size_t(layer->slot)]; - if (stored_data.triangles_to_split.empty()) + const TriangleSelector::TriangleSplittingData &data = facets_data[size_t(layer->slot)]; + if (data.triangles_to_split.empty()) continue; const DecodedHeightTexture height = decode_height_texture(*layer); if (height.empty()) continue; - TriangleSelector::TriangleSplittingData data = stored_data; - if (mesh_modified) { - // The stored paint mask is relative to the volume's original (unbaked) mesh; remap it - // onto the working mesh, which earlier layers may have already displaced/subdivided. - data = TriangleSelector::remap_painting(original_mesh, data, working_mesh, Transform3d::Identity(), - std::optional>{}); - if (data.bitstream.empty()) - continue; - } - - const TriangleMesh selector_mesh(working_mesh); - TriangleSelector selector(selector_mesh); + TriangleSelector selector(selector_mesh); selector.deserialize(data, false); const indexed_triangle_set patch = selector.get_facets_strict(EnforcerBlockerType::ENFORCER); if (patch.indices.empty()) continue; - // get_facets_strict() returns the *entire* mesh's vertex array regardless of which state - // was asked for (only the returned triangle indices are filtered by state) -- so `patch` - // and `rest` share the exact same vertex indexing. That is what lets the weld below be a - // plain index check instead of a position-based lookup. - indexed_triangle_set rest = selector.get_facets_strict(EnforcerBlockerType::NONE); - assert(rest.vertices.size() == patch.vertices.size()); + // get_facets_strict() returns the same vertex array whichever state is asked for (only the + // triangles are filtered), so `patch` and `rest` share one indexing -- and, per the + // compactify above, it is our own. + const indexed_triangle_set rest = selector.get_facets_strict(EnforcerBlockerType::NONE); - // A vertex that is also used by at least one *unpainted* triangle is a boundary vertex: - // its final position is ambiguous (it belongs to both the painted and untouched surface), - // so it must never be displaced -- otherwise the baked patch would tear away from the - // rest of the mesh. Only vertices used exclusively by painted triangles ("interior" to the - // patch) are eligible for displacement. This is what keeps the bake seamless without any - // remeshing/hole-filling at the boundary. - std::vector is_boundary_vertex(rest.vertices.size(), false); + // A vertex used by even one *unpainted* triangle sits on this layer's boundary: it belongs + // to both the painted patch and the untouched surface, so displacing it would tear the two + // apart. Pinning it is what keeps the result seamless with no remeshing at the seam. + std::vector is_boundary(patch.vertices.size(), false); for (const stl_triangle_vertex_indices &tri : rest.indices) for (int i = 0; i < 3; ++i) - is_boundary_vertex[tri[i]] = true; - - const std::vector vertex_normals = texture_displacement_vertex_normals(patch); + is_boundary[tri[i]] = true; + // Only the Cylindrical/Spherical methods need these; Triplanar blends each vertex's own + // normal and LSCM solves the patch globally. Vec3f average_normal = Vec3f::Zero(); + Vec3f patch_centroid = Vec3f::Zero(); + int patch_vertex_count = 0; for (const stl_triangle_vertex_indices &tri : patch.indices) - for (int i = 0; i < 3; ++i) - average_normal += vertex_normals[tri[i]]; - average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); - - const float sign = layer->invert ? -1.f : 1.f; - std::vector interior_vertex_remap(rest.vertices.size(), -1); - indexed_triangle_set working_mesh_next = std::move(rest); - for (const stl_triangle_vertex_indices &tri : patch.indices) { - stl_triangle_vertex_indices final_tri; for (int i = 0; i < 3; ++i) { - const int vi = tri[i]; - if (is_boundary_vertex[vi]) { - // Shared with the untouched surface: reuse the existing, undisplaced vertex. - final_tri[i] = vi; - continue; + average_normal += vertex_normals[tri[i]]; + patch_centroid += patch.vertices[tri[i]]; + ++patch_vertex_count; + } + average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); + patch_centroid = (patch_vertex_count > 0) ? Vec3f(patch_centroid / float(patch_vertex_count)) : Vec3f::Zero(); + + // Cylinder axis auto-picked as the world axis *least* aligned with the average normal + // (perpendicular to the outward radial normal, as a cylinder's own axis would be). + Vec3f patch_axis = Vec3f::UnitZ(); + const Vec3f an = average_normal.cwiseAbs(); + if (an.x() <= an.y() && an.x() <= an.z()) + patch_axis = Vec3f::UnitX(); + else if (an.y() <= an.x() && an.y() <= an.z()) + patch_axis = Vec3f::UnitY(); + + // A real unwrap of the whole patch, computed once here rather than per vertex -- it is a + // per-chart solve over the whole patch, not a per-point formula. Cached, so repeating this + // for every slider tweak costs a hash rather than a re-solve (see compute_patch_unwrap()). + const std::vector lscm_uvs = (layer->projection_method == TextureProjectionMethod::LSCM) ? + compute_lscm_uvs(patch, *layer) : + std::vector{}; + + // Edge smoothing: a per-vertex weight in [0, 1] that fades the displacement to zero toward the + // patch boundary. amount->0 leaves only the very edge softened; amount->1 fades the whole patch + // flat. k = (1-a)/a turns the normalized boundary distance into that weight (see the header). + std::vector edge_weight; + if (layer->edge_smoothing && layer->edge_smoothing_amount > 0.f) { + const std::vector bdist = patch_boundary_distance(patch, is_boundary); + float max_d = 0.f; + for (const float d : bdist) + if (std::isfinite(d)) + max_d = std::max(max_d, d); + edge_weight.assign(patch.vertices.size(), 1.f); + if (max_d > 1e-6f) { + const float a = std::clamp(layer->edge_smoothing_amount, 0.f, 1.f); + const float k = (a < 1.f) ? (1.f - a) / std::max(a, 1e-3f) : 0.f; + for (size_t v = 0; v < bdist.size() && v < edge_weight.size(); ++v) { + const float d = std::isfinite(bdist[v]) ? bdist[v] : max_d; + edge_weight[v] = std::clamp((d / max_d) * k, 0.f, 1.f); } - if (interior_vertex_remap[vi] < 0) { - const Vec2f uv = project_texture_displacement_uv(patch.vertices[vi], average_normal, *layer); - const float h = height.sample(uv); - const Vec3f displaced = patch.vertices[vi] + vertex_normals[vi] * (h * layer->depth_mm * sign); - interior_vertex_remap[vi] = int(working_mesh_next.vertices.size()); - working_mesh_next.vertices.push_back(displaced); - } - final_tri[i] = interior_vertex_remap[vi]; } - working_mesh_next.indices.push_back(final_tri); } - working_mesh = std::move(working_mesh_next); - mesh_modified = true; + const float sign = layer->invert ? -1.f : 1.f; + // A vertex may be reached by several of the patch's triangles; each must fold into the + // running total exactly once, or a Multiply/Subtract layer would apply two or three times + // over depending on how many painted triangles happen to share the vertex. + std::vector visited(patch.vertices.size(), false); + for (const stl_triangle_vertex_indices &tri : patch.indices) + for (int i = 0; i < 3; ++i) { + const int vi = tri[i]; + // Split vertices the brush introduced live past the end of our own vertex array; + // they carry no displacement of their own and are not part of the output mesh. + if (vi >= int(mesh.vertices.size()) || is_boundary[vi] || visited[vi]) + continue; + visited[vi] = true; + + const Vec2f *lscm_uv = lscm_uvs.empty() ? nullptr : &lscm_uvs[size_t(vi)]; + const float h = sample_layer_height(height, *layer, mesh.vertices[vi], vertex_normals[vi], + patch_centroid, patch_axis, lscm_uv); + + // midlevel is the height that means "stay put", so anything below it displaces + // *inwards* -- see TextureDisplacementLayer::midlevel. At the default of 0 this is + // exactly the old outward-only behaviour. + const float edge_w = edge_weight.empty() ? 1.f : edge_weight[size_t(vi)]; + const float signed_height = (h - layer->midlevel) * layer->depth_mm * sign * edge_w; + displacement[size_t(vi)] = blend_displacement(displacement[size_t(vi)], signed_height, + displaced[size_t(vi)] ? layer->blend_mode : TextureBlendMode::Add); + // The first layer to reach a vertex has nothing underneath it to blend with, so it + // always starts the total off additively -- a Multiply/Divide against an implicit + // zero base would otherwise annihilate (or blow up) it, which is never what the + // user means by putting a mask on the bottom of the stack. + displaced[size_t(vi)] = true; + any_displacement = true; + } } - if (mesh_modified) - its_compactify_vertices(working_mesh); + if (!any_displacement) + return mesh; - return working_mesh; + for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) + if (displaced[vi]) + mesh.vertices[vi] += vertex_normals[vi] * displacement[vi]; + + return mesh; } indexed_triangle_set build_texture_displacement(const ModelVolume &volume) @@ -223,4 +1260,61 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume) return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data); } +indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, float max_edge_length_mm, int max_iterations) +{ + indexed_triangle_set current = mesh; + const float max_edge_sq = max_edge_length_mm * max_edge_length_mm; + + for (int iter = 0; iter < max_iterations; ++iter) { + // One vertex-index pair (always stored low-index-first) -> the midpoint vertex already + // created for it in this pass, so the two triangles sharing that edge both get the exact + // same new vertex instead of two separate, coincident-but-distinct ones (which would leave + // the mesh non-manifold even though it looks fine). + std::unordered_map midpoint_cache; + auto edge_key = [](int a, int b) -> uint64_t { + if (a > b) + std::swap(a, b); + return (uint64_t(uint32_t(a)) << 32) | uint32_t(b); + }; + auto get_midpoint = [&](int a, int b) -> int { + const uint64_t key = edge_key(a, b); + auto it = midpoint_cache.find(key); + if (it != midpoint_cache.end()) + return it->second; + const int idx = int(current.vertices.size()); + current.vertices.push_back((current.vertices[a] + current.vertices[b]) * 0.5f); + midpoint_cache.emplace(key, idx); + return idx; + }; + + std::vector new_indices; + new_indices.reserve(current.indices.size()); + bool any_split = false; + for (const stl_triangle_vertex_indices &tri : current.indices) { + const Vec3f &a = current.vertices[tri[0]]; + const Vec3f &b = current.vertices[tri[1]]; + const Vec3f &c = current.vertices[tri[2]]; + if ((b - a).squaredNorm() <= max_edge_sq && (c - b).squaredNorm() <= max_edge_sq && (a - c).squaredNorm() <= max_edge_sq) { + new_indices.push_back(tri); + continue; + } + + any_split = true; + const int m01 = get_midpoint(tri[0], tri[1]); + const int m12 = get_midpoint(tri[1], tri[2]); + const int m20 = get_midpoint(tri[2], tri[0]); + new_indices.push_back(stl_triangle_vertex_indices(tri[0], m01, m20)); + new_indices.push_back(stl_triangle_vertex_indices(m01, tri[1], m12)); + new_indices.push_back(stl_triangle_vertex_indices(m20, m12, tri[2])); + new_indices.push_back(stl_triangle_vertex_indices(m01, m12, m20)); + } + + current.indices = std::move(new_indices); + if (!any_split) + break; + } + + return current; +} + } // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 4097bcd946..f6c34e93a2 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -25,10 +25,121 @@ class ModelVolume; // is also the number of independent EnforcerBlockerType selectors kept per volume. static constexpr size_t TEXTURE_DISPLACEMENT_MAX_LAYERS = 8; +// How a layer's height texture is sampled outside its [0, 1) tile when tiling is enabled. Ignored +// (always clamp) when TextureDisplacementLayer::tile_enabled is false. +enum class TextureTileMethod : int +{ + Repeat = 0, // wrap around, tile i and tile i+1 are identical (default) + MirroredRepeat = 1, // wrap around, every other tile is mirrored (no visible seam at tile edges) +}; + +// How a layer's texture is mapped onto the mesh. +enum class TextureProjectionMethod : int +{ + // Standard *blended* tri-planar projection: the texture is sampled once per world axis (the + // XY, XZ and YZ planes) and the three samples are blended per vertex, weighted by that + // vertex's own normal raised to TRIPLANAR_BLEND_SHARPNESS. + // + // Earlier versions instead *hard-picked* the single axis most aligned with the normal. That + // has a real, visible failure mode at any edge where the dominant axis flips: on a +X face the + // planar coordinate is (y, z), on a -Y face it is (x, z), so at the shared edge u jumps from + // y_edge to x_edge. On a box centred near the origin those two agree at the (+,+) and (-,-) + // corners (making them look fine) but differ by the full corner width at the (+,-) and (-,+) + // corners, which is exactly the "two bad corners, two good ones" seam that was reported. + // Blending across the transition removes that hard discontinuity by construction. + Triplanar = 0, + // Wrapped around an axis running through the patch's centroid. The axis itself is picked + // automatically as the world axis *least* aligned with the patch's average normal (since a + // cylinder's own axis is perpendicular to its outward radial normal) -- a reasonable default + // for roughly cylindrical selections, not a precise fit for arbitrary geometry. + Cylindrical = 1, + // Wrapped around the patch's centroid using longitude/latitude -- reasonable for roughly + // spherical/rounded selections, again an approximation rather than an exact geodesic map. + Spherical = 2, + // Real UV unwrap of the painted patch -- a proper low-distortion flattening rather than a + // planar/cylindrical/spherical approximation. The patch is first cut into charts along its + // sharp edges and each chart is flattened on its own (see compute_patch_unwrap()), so a patch + // that is not a single developable surface still unwraps sensibly. Falls back to Triplanar for + // any chart that cannot be flattened at all. + LSCM = 3, + // Flat projection along a fixed direction captured from the 3D camera ("project from view"): the + // texture is laid onto the painted area as seen from that angle, like a decal projector. Single + // planar map (no per-face axis switch), so it can smear on faces turned away from the projector -- + // that is inherent to view projection and is the user's call, not a bug. The projector's two + // in-plane axes live in TextureDisplacementLayer::view_project_right/up. + ViewProjected = 4, +}; + +// Dihedral angle (degrees) above which an edge between two painted triangles becomes a chart seam +// -- i.e. the unwrap is cut there rather than being forced to flatten across it. +// +// The whole point of this being a threshold rather than "flatten everything as one piece": three +// faces meeting at a cube corner are not developable, so a single-chart solve has to distort them +// badly to lie flat (they splay out into a fan, which is what "it merges all the edges into a +// triangle" describes). Cutting at the 90-degree edges instead lets each face flatten exactly. +// Meanwhile a smoothly curved surface -- a subdivided sphere, say -- has only small angles between +// neighbouring triangles, stays a single chart, and unwraps as one piece the way it should. +static constexpr float LSCM_DEFAULT_SEAM_ANGLE_DEG = 30.f; + +// Exponent the tri-planar blend weights are raised to (see TextureProjectionMethod::Triplanar). +// Higher means a tighter, more "hard-edged" transition between the three axis projections; lower +// means a wider cross-fade. 4 is the usual default: tight enough that a flat face is sampled +// almost purely along its own axis, wide enough that a 90-degree edge has no visible hard seam. +static constexpr float TRIPLANAR_BLEND_SHARPNESS = 4.f; + +// How a layer's displacement combines with the displacement accumulated by the layers below it +// (i.e. those in lower slots), evaluated per vertex. Analogous to an image editor's layer blend +// modes, except the quantity being blended is a signed displacement distance in mm rather than a +// pixel value. +// +// Add/Subtract are in mm and need no further explanation. Multiply/Divide are *scaling* operations +// and therefore need a unit convention: they treat the layer's own value as a unitless factor +// relative to 1 mm. That makes `depth_mm` act as a gain -- a layer with depth 1 mm and a white +// (1.0) texel multiplies the accumulated relief by exactly 1, i.e. leaves it unchanged -- which is +// the behaviour that makes a Multiply layer usable as a mask over the layers beneath it. +enum class TextureBlendMode : int +{ + Add = 0, // acc + value (default; several layers pile their relief up together) + Subtract = 1, // acc - value (carve this layer's relief out of the layers below) + Multiply = 2, // acc * (value / 1mm) (mask/modulate the layers below by this layer) + Divide = 3, // acc / (value / 1mm) (inverse mask; guarded against a zero/near-zero divisor) +}; + +// Combines one layer's signed displacement `value` (mm) into `accumulated` (mm) per `mode`. +// Shared by the bake/preview path and exposed for tests. +float blend_displacement(float accumulated, float value, TextureBlendMode mode); + +// Where one unwrap island (chart) sits in UV space, on top of wherever compute_patch_unwrap() first +// packed it. This is what the UV editor's drag/rotate gestures write to, so a user can lay the +// islands out by hand -- move them, rotate them, overlap them -- rather than being stuck with the +// automatic packing. +// +// Indexed by chart id, which compute_patch_unwrap() assigns in first-encountered-triangle order. That +// is stable for a given patch and seam angle, but *not* across a change to either: repainting the +// patch, or moving the seam-angle slider, can renumber the charts and so leave a hand-placed island +// applied to a different one. Accepted deliberately -- the alternative is a persistent chart identity +// that survives arbitrary re-segmentation, which is a much larger problem than this feature warrants. +struct TextureIsland +{ + Vec2f offset = Vec2f::Zero(); // in the unwrap's own mm space + float rotation_deg = 0.f; // about the island's own centroid + // About the island's own centroid too. 1 = the size compute_patch_unwrap() gave it, which is + // already its true surface area in mm -- so scaling an island away from 1 deliberately makes its + // texel density differ from its neighbours'. See average_island_scales(). + float scale = 1.f; + + template void serialize(Archive &ar) { ar(offset, rotation_deg, scale); } +}; + +// Sets every island's scale to the mean of the current ones (Blender's "Average Islands Scale"). +// Only meaningful after islands have been scaled by hand: compute_patch_unwrap() already sizes every +// chart to its true mm area, so a freshly unwrapped patch has uniform texel density to begin with. +void average_island_scales(std::vector &islands); + // One texture asset plus its projection/displacement parameters. Several layers may be painted -// onto overlapping areas of the same volume: they are applied in slot order (like layers in an -// image editor), each one displacing the surface that resulted from the layers before it. This -// is what "layered/blended" texture displacement means in this feature. +// onto overlapping areas of the same volume: their displacements are combined per vertex, in slot +// order, each layer folding into the total via its own TextureBlendMode (see +// build_texture_displacement()). This is what "layered/blended" texture displacement means here. struct TextureDisplacementLayer { // Index into ModelVolume::texture_displacement_facets, assigned once when the layer is @@ -53,18 +164,113 @@ struct TextureDisplacementLayer Vec2f offset = Vec2f::Zero(); bool invert = false; + // The height value that means "don't move this vertex". The sampled height (0..1) has this + // subtracted before being scaled by depth_mm, so with the default of 0 the surface only ever + // moves *outwards* (the classic height-map convention), while 0.5 makes mid-grey neutral and + // lets darker texels cut *into* the surface -- an engraved-and-embossed result from one map. + // + // Cutting inward is not free: vertices move along their own normals, which converge inside a + // concave corner and inside a thin wall, so a large depth_mm against a small feature really can + // fold the surface through itself. There is no cheap way to detect that here (it needs a full + // self-intersection test on the displaced mesh), so the GUI warns rather than promising safety. + float midlevel = 0.f; + + // Optional blur applied to the decoded height map before it is sampled, in [0, 1]: 0 is the raw + // texture, 1 the strongest blur. Softens the relief (rounds hard edges, removes speckle) without + // needing a pre-blurred source image. Applied in decode_height_texture(), so it feeds the true + // preview, the UV editor backdrop and the bake identically. + float smoothing = 0.f; + + // Optional feathering of the displacement toward the edge of the painted patch. When enabled, the + // displacement is scaled down as a vertex approaches the patch boundary, so the relief blends + // smoothly into the surrounding surface instead of ending abruptly. `edge_smoothing_amount` in + // (0, 1] sets how far the fade reaches into the patch: small values only soften a thin band at the + // very edge, 1 fades the whole patch to nothing (the painted face comes out flat). Off by default. + bool edge_smoothing = false; + float edge_smoothing_amount = 0.5f; + + // Only used by TextureProjectionMethod::LSCM: when set, a fresh unwrap is laid out as a connected + // net (adjacent charts unfolded edge-to-edge along a spanning tree, see compute_connected_net()) + // rather than as separately packed islands. On by default. Hand-moving an island overrides its + // placement until the next re-unwrap. + bool auto_connect_islands = true; + + // When false, the texture is sampled once (clamped to its edge pixels outside [0, 1)) instead + // of being repeated -- useful for a single decal-like placement rather than a repeating tile. + bool tile_enabled = true; + TextureTileMethod tile_method = TextureTileMethod::Repeat; + + TextureProjectionMethod projection_method = TextureProjectionMethod::Triplanar; + // Only used by TextureProjectionMethod::LSCM. See LSCM_DEFAULT_SEAM_ANGLE_DEG. + float lscm_seam_angle_deg = LSCM_DEFAULT_SEAM_ANGLE_DEG; + // Only used by TextureProjectionMethod::LSCM: gap left between islands by the automatic packing, + // in the unwrap's mm space. Negative means "auto" (a small fraction of the packed size), which is + // what a patch that has never had the slider touched gets. + float island_padding_mm = -1.f; + // Only used by TextureProjectionMethod::LSCM: edges the unwrap is forced to cut along, on top of + // whatever the seam angle already cuts. Each pair is an undirected edge in *mesh vertex index* + // space (first < second). This is what "mark seam" (manual) and "cut island" (auto) both write to. + // Mesh-index space, so like the paint masks these are dropped on any topology change. + std::vector> lscm_seam_edges; + + // Only used by TextureProjectionMethod::ViewProjected: the projector's in-plane axes, in the + // volume's *local* space, captured from the camera when the user hits "Project from view". A point + // projects to Vec2f(dot(pos, right), dot(pos, up)) before the usual tiling/rotation/offset. + Vec3f view_project_right = Vec3f::UnitX(); + Vec3f view_project_up = Vec3f::UnitY(); + // Only used by TextureProjectionMethod::LSCM: hand placement of the unwrap's islands, indexed by + // chart id (see TextureIsland). Shorter than the chart count simply means the missing ones are + // still where the automatic packing put them. + std::vector islands; + + // Only used by TextureProjectionMethod::LSCM: persistent "join" groups, indexed by chart id. Charts + // that share a group id move together as one in the UV editor -- this is what the explicit "Join" + // command records (over and above placing the child next to its parent). An entry of -1, or an index + // past the end of the vector, means the chart is its own singleton group (moves alone). Empty means + // every chart is a singleton. Same chart-renumbering caveat as `islands`: a re-unwrap can reshuffle + // chart ids, so this is meaningful only against the unwrap it was made on. + std::vector island_groups; + + // Only used by TextureProjectionMethod::LSCM: manual per-vertex UV edits made in the UV editor's + // Vertex/Edge select modes. Each pair is (mesh vertex index, its overriding raw-unwrap coordinate in + // mm) -- the *raw* unwrap position, i.e. before the island transform, so the edited vertex still + // moves and rotates with its island. In compute_lscm_uvs() this replaces the automatic unwrap + // coordinate for that vertex; in the editor it edits the displayed geometry directly. Keyed in mesh- + // vertex space like lscm_seam_edges (dropped on a topology change). The raw coordinate is only + // meaningful against the current unwrap, so a re-unwrap clears these. A mesh vertex shared by several + // charts (a seam vertex) settles on one, matching compute_lscm_uvs()'s single-UV-per-vertex rule. + std::vector> lscm_uv_overrides; + + // How this layer folds into the displacement accumulated by the layers below it. Ignored for + // the lowest-slot painted layer, which has nothing beneath it to combine with (the GUI shows + // it as the "Base" layer and hides the control). + TextureBlendMode blend_mode = TextureBlendMode::Add; + bool empty() const { return !image_data || image_data->empty(); } template void save(Archive &ar) const { std::string blob = image_data ? std::string(image_data->begin(), image_data->end()) : std::string(); - ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert); + ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert, tile_enabled, + static_cast(tile_method), static_cast(projection_method), lscm_seam_angle_deg, islands, + static_cast(blend_mode), midlevel, island_padding_mm, lscm_seam_edges, view_project_right, + view_project_up, smoothing, edge_smoothing, edge_smoothing_amount, auto_connect_islands, island_groups, + lscm_uv_overrides); } template void load(Archive &ar) { std::string blob; - ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert); - image_data = blob.empty() ? nullptr : std::make_shared>(blob.begin(), blob.end()); + int tile_method_int = 0; + int projection_method_int = 0; + int blend_mode_int = 0; + ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert, tile_enabled, + tile_method_int, projection_method_int, lscm_seam_angle_deg, islands, blend_mode_int, midlevel, + island_padding_mm, lscm_seam_edges, view_project_right, view_project_up, smoothing, edge_smoothing, + edge_smoothing_amount, auto_connect_islands, island_groups, lscm_uv_overrides); + image_data = blob.empty() ? nullptr : std::make_shared>(blob.begin(), blob.end()); + tile_method = static_cast(tile_method_int); + projection_method = static_cast(projection_method_int); + blend_mode = static_cast(blend_mode_int); } }; @@ -77,33 +283,176 @@ struct DecodedHeightTexture int height = 0; bool empty() const { return width <= 0 || height <= 0 || pixels.empty(); } - // Bilinearly sampled height in [0, 1] at a tiling (wrapping) normalized uv coordinate. - float sample(const Vec2f &uv) const; + // Bilinearly sampled height in [0, 1] at a normalized uv coordinate. When tile_enabled is + // false, uv is clamped to the texture's edge instead of being wrapped/repeated. + float sample(const Vec2f &uv, bool tile_enabled = true, TextureTileMethod tile_method = TextureTileMethod::Repeat) const; }; // Decode a layer's raw image bytes into sampleable grayscale height data. Returns an empty // DecodedHeightTexture if image_data is empty or is not an 8-bit grayscale PNG. DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer); -// Project a mesh-local position onto a layer's height texture, honouring its tiling scale, -// rotation and offset. Uses a simple dominant-axis planar projection of the given normal: proper -// seam-aware UV parametrization is a later-phase improvement (see project plan). -Vec2f project_texture_displacement_uv(const Vec3f &position, const Vec3f &normal, const TextureDisplacementLayer &layer); +// Raw dominant-axis planar projection of `position` (in mm, not yet scaled/rotated/offset by any +// layer), dropping the axis position that best aligns with `normal`. Exposed on its own (rather +// than only inline inside project_texture_displacement_uv()) so GUI code -- the on-canvas +// "adjust texture placement" gizmo -- can map a dragged 3D point into the exact same 2D space +// tiling_scale/rotation_deg/offset operate in, without duplicating the axis-selection logic. +Vec2f project_planar(const Vec3f &position, const Vec3f &normal); + +// Applies a layer's tiling_scale/rotation_deg/offset to an already-projected planar coordinate +// (in mm, dominant-axis planar, cylindrical, spherical, or CGAL LSCM output -- any of them, all +// share this same final step). Exposed separately so build_texture_displacement() can route CGAL +// LSCM's per-patch UV solve through the same scale/rotate/offset controls as every other +// projection method, without going through project_texture_displacement_uv()'s own dispatch +// (which only knows how to compute the *analytic* methods from a single vertex + normal). +Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer); + +// Sample a layer's height texture at a mesh-local position, honouring the layer's projection +// method, tiling scale, rotation, offset and tiling mode. Returns a height in [0, 1]. +// +// This returns a *height* rather than a UV because TextureProjectionMethod::Triplanar is a blend +// of three separate axis projections and therefore takes three texture samples per vertex -- there +// is no single UV that represents it. The other methods do map to one UV internally. +// - `normal` is this specific vertex's own normal; used only by Triplanar (for its blend weights). +// - `patch_center`/`patch_axis` describe the painted patch as a whole (its centroid, and -- for +// Cylindrical only -- the wrap axis); used only by the Cylindrical/Spherical methods. +// - `lscm_uv`, when non-null, is this vertex's precomputed LSCM coordinate and takes precedence +// over `layer.projection_method` (LSCM is a single per-patch solve, not a per-vertex formula, +// so build_texture_displacement() computes it once up front and passes it in here). +// patch_center/patch_axis are cheap to compute once per patch and passed through unchanged for +// every vertex rather than being re-derived per call. +float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisplacementLayer &layer, + const Vec3f &position, const Vec3f &normal, + const Vec3f &patch_center = Vec3f::Zero(), const Vec3f &patch_axis = Vec3f::UnitZ(), + const Vec2f *lscm_uv = nullptr); + +// Area-weighted centroid and average normal of a layer's currently painted patch, in mesh-local +// coordinates -- the same measurements build_texture_displacement() uses to pick its dominant +// projection axis. Used by the GUI to anchor the on-canvas "adjust texture placement" gizmo to +// wherever the layer is actually painted. Returns false (leaving the outputs untouched) if the +// layer has nothing painted yet. +bool compute_layer_paint_anchor(const indexed_triangle_set &base_mesh, + const TriangleSelector::TriangleSplittingData &facet_data, + Vec3f &anchor_pos, + Vec3f &anchor_normal); + +// Extracts the currently painted patch from a volume's base mesh + stored facet data -- the same +// extraction build_texture_displacement() and compute_layer_paint_anchor() each do internally via +// TriangleSelector::get_facets_strict(ENFORCER). Returns an empty mesh if nothing is painted. +// Exposed so GUI code (the LSCM "UV editor" preview pane) can get the same patch build_texture_ +// displacement() would act on, without duplicating the deserialize/get_facets_strict boilerplate. +indexed_triangle_set extract_painted_patch(const indexed_triangle_set &base_mesh, + const TriangleSelector::TriangleSplittingData &facet_data); + +// A patch flattened into 2D. The patch is first split into charts along edges sharper than +// `seam_angle_deg` (see LSCM_DEFAULT_SEAM_ANGLE_DEG), each chart is flattened independently, the +// charts are scaled to their true mm size and packed side by side. +// +// A vertex sitting on a seam belongs to several charts at once and therefore has a *different* UV +// in each of them, so this cannot be a plain "one UV per patch vertex" array: seam vertices are +// duplicated, once per chart touching them. `indices` is the patch's own triangle list re-indexed +// onto that duplicated vertex set, and `source_vertex` maps each duplicate back to the patch vertex +// it came from. +struct PatchUnwrap +{ + std::vector uvs; // one per unwrapped vertex, in mm + std::vector source_vertex; // unwrapped vertex -> index into patch.vertices + std::vector vertex_chart; // unwrapped vertex -> chart (island) id + std::vector indices; // patch triangles, re-indexed into `uvs` + // Per chart, the centroid of its uvs -- the point a TextureIsland's rotation turns about. + std::vector chart_centroid; + // Edges belonging to exactly one triangle: the outline of each island. Indices into `uvs`. This + // is what the UV editor draws highlighted, so the boundaries the seam angle cut are visible. + std::vector> boundary_edges; + int chart_count = 0; + + bool empty() const { return indices.empty(); } +}; + +// Applies an island's hand placement (scale + rotation about its own centroid, then offset) to one +// unwrapped UV. A chart with no entry in `islands` is left exactly where the packing put it. +Vec2f apply_island_transform(const Vec2f &uv, int chart, const PatchUnwrap &unwrap, const std::vector &islands); + +// The same transform as a 2x3 affine matrix (columns: x basis, y basis, translation), for callers +// that would otherwise apply it to every vertex of an island one at a time. The UV editor renders +// each island through this as a uniform, which is what lets a drag move an island without touching +// its vertex buffer at all. +Eigen::Matrix island_transform_matrix(int chart, const PatchUnwrap &unwrap, const std::vector &islands); + +// Lays the unwrap's charts out as a connected net: charts that share a mesh edge are unfolded so +// their shared edge coincides (a cube -> its six faces joined along a spanning tree of edges, the rest +// left as free borders). Charts stay separate islands, so their borders still show and any of them can +// still be moved by hand afterwards. A chart whose unfold would overlap one already placed is left +// where the packing put it. Returns one placement per chart. See the gizmo's auto-connect option. +std::vector compute_connected_net(const PatchUnwrap &unwrap); + +// The placement that unfolds `child` onto `parent` along their shared mesh edge, honouring `parent`'s +// current placement in `islands`. Returns false if the two charts share no edge. Backs the manual +// "Join" command; compute_connected_net() does the same thing across a whole spanning tree. +bool join_chart_placement(const PatchUnwrap &unwrap, const std::vector &islands, + int child, int parent, TextureIsland &out_child); + +// Unwraps `patch` as described above. Charts that are flat (within a degree) are projected onto +// their own tangent plane directly, which is both exact and far cheaper than a solve; only genuinely +// curved charts go through CGAL's LSCM parameterizer (MeshBoolean::cgal::parameterize_lscm()). A +// chart that LSCM cannot flatten at all (it is not a topological disk -- closed, or with a hole) +// falls back to that same tangent-plane projection. +// +// `padding_mm` is the gap the packing leaves between islands; negative means auto (see +// TextureDisplacementLayer::island_padding_mm). `seam_edges` are extra edges to cut along regardless +// of angle (manual/auto seams), in the patch's own vertex-index space (which is the mesh's, since the +// patch carries the whole vertex array -- see get_facets_strict()). +// +// Results are cached, keyed on the patch's geometry, the seam angle, the padding and the seam edges: +// nothing else about a layer (depth, tiling, rotation, offset, texture, island placement) changes the +// unwrap, so dragging any of those sliders must not pay for a re-solve. +PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_angle_deg = LSCM_DEFAULT_SEAM_ANGLE_DEG, + float padding_mm = -1.f, const std::vector> &seam_edges = {}); + +// One UV per patch vertex, for displacement. Displacement is inherently per-vertex -- a vertex has +// exactly one position, so it can only be pushed out by one height -- which means a seam vertex has +// to settle on a single one of its charts' UVs (the first, arbitrarily). That is not a compromise +// in the result: the surface stays watertight either way, since neighbouring vertices each move +// along their own normals and nothing depends on the UVs agreeing across the seam. It is only the +// *display* in the UV editor that needs the duplicated-vertex form above. +// +// Returns an empty vector if the patch has no triangles. Takes the whole layer because it applies +// both the layer's seam angle and its hand-placed islands. +std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer); // One paint mask (as stored by ModelVolume::texture_displacement_facets) per possible layer slot. using TextureDisplacementFacetsData = std::array; // Bake all painted texture-displacement layers into `base_mesh`'s geometry, restricted to the -// painted area(s) only (the rest of the mesh is left untouched). Layers are applied in slot -// order, each measuring its displacement from the surface left by the previous one. Returns the -// mesh unchanged if nothing is painted or no layer has a usable texture. +// painted area(s) only (the rest of the mesh is left untouched). Returns the mesh unchanged if +// nothing is painted or no layer has a usable texture. +// +// **Topology-preserving**: the returned mesh has exactly `base_mesh`'s vertices and triangles, in +// the same order -- only the positions of displaced vertices differ. Every layer's paint mask is +// evaluated against `base_mesh` directly, and each vertex accumulates a single signed displacement +// (in mm) that all the layers covering it fold into, in slot order, via their TextureBlendMode. +// The vertex is then moved once, along its base-mesh normal, by that accumulated total. +// +// This replaced an earlier design that instead applied the layers *sequentially*, re-meshing after +// each one and carrying the next layer's paint mask onto the result with +// TriangleSelector::remap_painting(). That was the cause of a real "the second texture is never +// applied" bug: remapping a mask onto a mesh whose vertices had just been displaced out from under +// it routinely produced an empty bitstream, and the layer was then silently skipped. It is also +// what forced the per-layer vertex duplication and the final its_compactify_vertices() pass. The +// accumulate-then-displace formulation has neither problem, is substantially faster (no remap, no +// welding, one pass over the mesh), and -- because the output keeps the input's exact vertex +// indexing -- lets the GUI overlay a preview on the base mesh without any index translation. +// +// A vertex used by even one *unpainted* triangle of a layer's mask is that layer's boundary: its +// displacement is pinned to zero, so the patch never tears away from the surrounding surface. Only +// vertices used exclusively by painted triangles move. // // Takes plain copied data rather than a ModelVolume reference so it is safe to call from a // background thread (e.g. a bake Job's process() method) on a snapshot captured on the main // thread, without touching the live Model concurrently with the UI. // -// Known Phase 1-3 limitation: this does not attempt to remap texture-displacement paint data -// across topology-changing operations performed outside this gizmo (e.g. ModelObject::split(), +// Known limitation: this does not attempt to remap texture-displacement paint data across +// topology-changing operations performed outside this gizmo (e.g. ModelObject::split(), // mesh-boolean ops) the way TriangleSelector::remap_painting() does for the other paint channels. // Such operations will silently drop any unbaked texture-displacement paint on the affected // volume. This is an explicit extension point for a later phase, not an oversight. @@ -115,6 +464,24 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set // and forwards to the overload above. indexed_triangle_set build_texture_displacement(const ModelVolume &volume); +// Uniformly subdivides `mesh` (every triangle recursively split into 4 via edge midpoints, using a +// shared cache so a midpoint is computed once and reused by both triangles on either side of that +// edge) until every edge is at or below max_edge_length_mm, or max_iterations passes have run, +// whichever comes first (bounding the worst-case triangle-count explosion on a very fine target). +// +// This exists so a low-poly input model can still get fine-grained texture displacement detail -- +// build_texture_displacement() can only ever move existing vertices, so a patch with only a +// handful of vertices to begin with cannot show much detail no matter the texture's resolution. +// +// Deliberately whole-mesh and uniform, not limited to a painted patch: subdividing only part of a +// mesh while leaving the rest untouched creates a classic T-junction/cracking problem where the +// denser and sparser regions meet (the finer side has edge midpoints the coarser side doesn't +// know about). Uniform, whole-mesh subdivision has no such seam and stays manifold, at the cost of +// applying everywhere rather than just where texture detail is actually wanted -- meant to be run +// once, deliberately, before painting (see the gizmo's "Subdivide model" button), not automatically +// during baking. +indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, float max_edge_length_mm, int max_iterations = 6); + } // namespace Slic3r #endif // slic3r_TextureDisplacement_hpp_ diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index a12ac9c1af..b7a9566e3f 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -88,6 +88,105 @@ TEST_CASE("TextureDisplacement: fully painting a mesh displaces every vertex alo } } +// Paints every facet of `mesh` into a serialized mask, the way "Select whole model" does. +static TriangleSelector::TriangleSplittingData paint_whole_mesh(const indexed_triangle_set &mesh) +{ + const TriangleMesh tm(mesh); + TriangleSelector selector(tm); + for (int f = 0; f < int(mesh.indices.size()); ++f) + selector.set_facet(f, EnforcerBlockerType::ENFORCER); + return selector.serialize(); +} + +// Regression test for the bug this feature shipped with: with two layers painted over the same +// area, the second one was silently dropped (its paint mask was remapped onto the mesh the first +// layer had already displaced, which routinely produced an empty bitstream). Every layer is now +// evaluated against the original mesh instead, so both must show up in the total. +TEST_CASE("TextureDisplacement: a second layer over the same area is applied too", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(cube); + facets[1] = facets[0]; // both layers cover the whole cube + + TextureDisplacementLayer base; + base.slot = 0; + base.depth_mm = 1.0f; + base.tiling_scale = 5.0f; + base.image_data = make_flat_gray_png(255); // height 1.0 everywhere + + TextureDisplacementLayer second = base; + second.slot = 1; + second.depth_mm = 0.5f; + second.blend_mode = TextureBlendMode::Add; + + const indexed_triangle_set result = build_texture_displacement(cube, {base, second}, facets); + + // Topology is preserved exactly, so vertices can be compared 1:1 with the input. + REQUIRE(result.vertices.size() == cube.vertices.size()); + REQUIRE(result.indices.size() == cube.indices.size()); + for (size_t i = 0; i < cube.vertices.size(); ++i) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(1.5f, 1e-3f)); // 1.0 + 0.5, not just 1.0 +} + +TEST_CASE("TextureDisplacement: blend modes combine a layer with the ones below it", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(cube); + facets[1] = facets[0]; + + TextureDisplacementLayer base; + base.slot = 0; + base.depth_mm = 2.0f; + base.tiling_scale = 5.0f; + base.image_data = make_flat_gray_png(255); // -> contributes exactly +2.0 mm + + TextureDisplacementLayer second = base; + second.slot = 1; + second.depth_mm = 0.5f; // -> its own value is 0.5 mm + + // Expected total displacement for each mode, given base = 2.0 mm and second = 0.5 mm. Multiply + // and Divide treat the layer's value as a factor relative to 1 mm (see TextureBlendMode). + const auto expected = GENERATE(table({ + { TextureBlendMode::Add, 2.5f }, // 2.0 + 0.5 + { TextureBlendMode::Subtract, 1.5f }, // 2.0 - 0.5 + { TextureBlendMode::Multiply, 1.0f }, // 2.0 * 0.5 + { TextureBlendMode::Divide, 4.0f }, // 2.0 / 0.5 + })); + second.blend_mode = std::get<0>(expected); + + const indexed_triangle_set result = build_texture_displacement(cube, {base, second}, facets); + + REQUIRE(result.vertices.size() == cube.vertices.size()); + for (size_t i = 0; i < cube.vertices.size(); ++i) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(std::get<1>(expected), 1e-3f)); +} + +TEST_CASE("TextureDisplacement: the lowest layer ignores its blend mode", "[TextureDisplacement]") +{ + // Multiply against the implicit zero base would annihilate the only layer present; the first + // layer to reach a vertex always starts the total off additively instead. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(cube); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 2.0f; + layer.tiling_scale = 5.0f; + layer.blend_mode = TextureBlendMode::Multiply; + layer.image_data = make_flat_gray_png(255); + + const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets); + + for (size_t i = 0; i < cube.vertices.size(); ++i) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(2.0f, 1e-3f)); +} + TEST_CASE("TextureDisplacement: boundary vertices shared with unpainted triangles are pinned", "[TextureDisplacement]") { // A small triangle fan around a central vertex O, with 4 outer points A/B/C/D forming 4 From a393b216429abdb6bdd60069834a92fbb428c710 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 16 Jul 2026 08:43:08 +0200 Subject: [PATCH 04/89] Add texture displacement bump and UV-check shaders --- .../shaders/110/texture_displacement_bump.fs | 62 ++++++++- .../shaders/110/texture_displacement_bump.vs | 18 +-- .../110/texture_displacement_uvcheck.fs | 70 +++++++++++ .../110/texture_displacement_uvcheck.vs | 31 +++++ .../shaders/140/texture_displacement_bump.fs | 118 ++++++++++++++++-- .../shaders/140/texture_displacement_bump.vs | 24 +++- .../140/texture_displacement_uvcheck.fs | 83 ++++++++++++ .../140/texture_displacement_uvcheck.vs | 36 ++++++ src/slic3r/GUI/GLShadersManager.cpp | 3 + 9 files changed, 415 insertions(+), 30 deletions(-) create mode 100644 resources/shaders/110/texture_displacement_uvcheck.fs create mode 100644 resources/shaders/110/texture_displacement_uvcheck.vs create mode 100644 resources/shaders/140/texture_displacement_uvcheck.fs create mode 100644 resources/shaders/140/texture_displacement_uvcheck.vs diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index e39a484e3a..a3c44413b5 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -30,11 +30,33 @@ uniform float tiling_scale; uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; +uniform bool use_vertex_uv; +// 2x3 affine (lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the dragged island's uv; see the +// 140 variant. Identity when nothing is dragged. +uniform vec4 island_delta_lin; +uniform vec2 island_delta_tr; varying vec3 clipping_planes_dots; varying vec4 model_pos; varying vec4 world_pos; varying float weight; +varying float active; +varying vec2 vertex_uv; + +void projection_axes(vec3 n, out vec3 t, out vec3 b) +{ + vec3 an = abs(n); + if (an.x >= an.y && an.x >= an.z) { // planar = p.yz + t = vec3(0.0, 1.0, 0.0); + b = vec3(0.0, 0.0, 1.0); + } else if (an.y >= an.x && an.y >= an.z) { // planar = p.xz + t = vec3(1.0, 0.0, 0.0); + b = vec3(0.0, 0.0, 1.0); + } else { // planar = p.xy + t = vec3(1.0, 0.0, 0.0); + b = vec3(0.0, 1.0, 0.0); + } +} vec2 project_uv(vec3 p, vec3 n) { @@ -55,15 +77,45 @@ void main() if (volume_mirrored) triangle_normal = -triangle_normal; - if (weight > 0.0) { - vec2 uv = project_uv(model_pos.xyz, triangle_normal); + if (use_vertex_uv) { + // Mikkelsen surface-gradient bump; see the 140 variant for the full rationale. Scale-exact + // for a conformal LSCM map (no global 1/tiling assumption), and gated by the paint weight + // via a multiply so the branch stays uniform (use_vertex_uv is a uniform). + vec2 uv = (active > 0.5) + ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr + : vertex_uv; + float h = texture2D(height_tex, uv).r; + float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); + vec3 sigmaS = dFdx(model_pos.xyz); + vec3 sigmaT = dFdy(model_pos.xyz); + vec3 R1 = cross(sigmaT, triangle_normal); + vec3 R2 = cross(triangle_normal, sigmaS); + float det = dot(sigmaS, R1); + float dHdx = k * dFdx(h); + float dHdy = k * dFdy(h); + if (abs(det) > 1e-12) + triangle_normal = normalize(triangle_normal - (dHdx * R1 + dHdy * R2) / det); + } else if (weight > 0.0) { + vec2 uv = project_uv(model_pos.xyz, triangle_normal); + vec3 t, b; + projection_axes(triangle_normal, t, b); + float hL = texture2D(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; float hR = texture2D(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; float hD = texture2D(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; float hU = texture2D(height_tex, uv + vec2(0.0, height_tex_texel.y)).r; - float sign_mul = invert ? -1.0 : 1.0; - vec3 bumped_normal = normalize(triangle_normal + sign_mul * depth_mm * vec3(hL - hR, hD - hU, 0.0)); - triangle_normal = normalize(mix(triangle_normal, bumped_normal, clamp(weight, 0.0, 1.0))); + + vec2 dh_duv = vec2((hR - hL) / (2.0 * height_tex_texel.x), (hU - hD) / (2.0 * height_tex_texel.y)); + float inv_tiling = (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; + float amplitude = (invert ? -1.0 : 1.0) * depth_mm * inv_tiling * clamp(weight, 0.0, 1.0); + + float cs = cos(rotation_rad); + float sn = sin(rotation_rad); + vec2 slope = amplitude * vec2(dh_duv.x * cs + dh_duv.y * sn, -dh_duv.x * sn + dh_duv.y * cs); + + vec3 gradient = slope.x * t + slope.y * b; + gradient -= triangle_normal * dot(triangle_normal, gradient); + triangle_normal = normalize(triangle_normal - gradient); } vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); diff --git a/resources/shaders/110/texture_displacement_bump.vs b/resources/shaders/110/texture_displacement_bump.vs index 14a42ac0b1..293129cbb8 100644 --- a/resources/shaders/110/texture_displacement_bump.vs +++ b/resources/shaders/110/texture_displacement_bump.vs @@ -1,22 +1,24 @@ #version 110 +// See resources/shaders/140/texture_displacement_bump.vs for full documentation; this is the +// GLSL 1.10 compatibility variant. + uniform mat4 view_model_matrix; uniform mat4 projection_matrix; - uniform mat4 volume_world_matrix; -// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. uniform vec2 z_range; -// Clipping plane - general orientation. Used by the SLA gizmo. uniform vec4 clipping_plane; -attribute vec3 v_position; -// Per-vertex paint weight for the currently active texture-displacement layer, 0..1. -attribute float v_weight; +attribute vec3 v_position; +attribute vec3 v_normal; // .x = paint weight (0/1); .y = 1 for the dragged island's vertices +attribute vec2 v_tex_coord; // precomputed texture uv, used only when use_vertex_uv is set varying vec3 clipping_planes_dots; varying vec4 model_pos; varying vec4 world_pos; varying float weight; +varying float active; +varying vec2 vertex_uv; void main() { @@ -26,5 +28,7 @@ void main() gl_Position = projection_matrix * view_model_matrix * model_pos; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); - weight = v_weight; + weight = v_normal.x; + active = v_normal.y; + vertex_uv = v_tex_coord; } diff --git a/resources/shaders/110/texture_displacement_uvcheck.fs b/resources/shaders/110/texture_displacement_uvcheck.fs new file mode 100644 index 0000000000..fbfa6a0517 --- /dev/null +++ b/resources/shaders/110/texture_displacement_uvcheck.fs @@ -0,0 +1,70 @@ +#version 110 + +// See resources/shaders/140/texture_displacement_uvcheck.fs; GLSL 1.10 compatibility variant. + +#define INTENSITY_CORRECTION 0.6 +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +#define INTENSITY_AMBIENT 0.3 +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform mat3 view_normal_matrix; +uniform bool volume_mirrored; + +uniform int mode; +uniform float checker_freq; +uniform float tiling_scale; +uniform float rotation_rad; +uniform vec2 uv_offset; +uniform bool use_vertex_uv; + +varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying vec4 world_pos; +varying float distortion; +varying vec2 vertex_uv; + +vec2 project_uv(vec3 p, vec3 n) +{ + vec3 an = abs(n); + vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; + float cs = cos(rotation_rad); + float sn = sin(rotation_rad); + return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; +} + +vec3 heatmap(float t) +{ + t = clamp(t, 0.0, 1.0); + return clamp(vec3(1.5 - abs(4.0 * t - 3.0), + 1.5 - abs(4.0 * t - 2.0), + 1.5 - abs(4.0 * t - 1.0)), 0.0, 1.0); +} + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + if (volume_mirrored) + triangle_normal = -triangle_normal; + + vec3 base; + if (mode == 1) { + base = heatmap(distortion); + } else { + vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal); + vec2 c = floor(uv * checker_freq); + float check = mod(c.x + c.y, 2.0); + base = (check < 0.5) ? vec3(0.22, 0.23, 0.26) : vec3(0.82, 0.83, 0.86); + } + + vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); + float intensity = INTENSITY_AMBIENT + max(dot(eye_normal, LIGHT_TOP_DIR), 0.0) * LIGHT_TOP_DIFFUSE + + max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0) * LIGHT_FRONT_DIFFUSE; + gl_FragColor = vec4(base * intensity, 1.0); +} diff --git a/resources/shaders/110/texture_displacement_uvcheck.vs b/resources/shaders/110/texture_displacement_uvcheck.vs new file mode 100644 index 0000000000..06c59e5c61 --- /dev/null +++ b/resources/shaders/110/texture_displacement_uvcheck.vs @@ -0,0 +1,31 @@ +#version 110 + +// See resources/shaders/140/texture_displacement_uvcheck.vs; GLSL 1.10 compatibility variant. + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat4 volume_world_matrix; +uniform vec2 z_range; +uniform vec4 clipping_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; // .x = per-vertex uv distortion +attribute vec2 v_tex_coord; // precomputed texture uv, used only when use_vertex_uv is set + +varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying vec4 world_pos; +varying float distortion; +varying vec2 vertex_uv; + +void main() +{ + model_pos = vec4(v_position, 1.0); + world_pos = volume_world_matrix * model_pos; + + gl_Position = projection_matrix * view_model_matrix * model_pos; + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + distortion = v_normal.x; + vertex_uv = v_tex_coord; +} diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index e023e43d74..5f762c4f32 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -1,14 +1,33 @@ #version 140 // Fast, geometry-free preview of texture displacement: perturbs the *shading* normal from the -// height texture's local gradient (a bump map), faded out by the per-vertex paint weight. Used -// while the brush is actively dragging (see project plan, "Preview shaders" section); the true, -// exact result is what "Bake" produces via libslic3r/TextureDisplacement.cpp on the CPU. +// height texture's local gradient (a bump map), faded out by the per-vertex paint weight. The +// true, exact result is what "Bake" produces via libslic3r/TextureDisplacement.cpp on the CPU. // -// NOTE: the gradient-to-normal conversion below assumes the two planar-projection axes chosen by -// project_uv() are reasonably aligned with the surface here; it is a cheap approximation (no -// tangent/bitangent basis is reconstructed on the GPU), acceptable for a live preview but not -// intended to be pixel-exact versus the CPU-side bake. +// The bake displaces each surface point along its normal by H = +/- depth_mm * (h(uv) - midlevel), +// with uv from the layer's projection. The perturbed normal is the analytic +// +// N' = normalize(N - (dH/da) * T - (dH/db) * B) +// +// over any orthonormal surface tangent pair (T, B), where the two slopes are real mm-per-mm +// derivatives. Two things have to be right for the preview's apparent depth to match the bake's: +// the tangent frame the gradient is expressed in, and the uv->mm scale that turns a texel +// difference into a slope. Getting the scale wrong is a uniform flattening (a raw texel difference +// is dh over one texel step, not over one mm); getting the frame wrong tilts the bump along the +// wrong axes. +// +// Two projection paths: +// * Triplanar (use_vertex_uv = 0): uv and the tangent axes are derived in-shader from the dominant +// normal axis, mirroring libslic3r's project_planar()/apply_uv_transform(), and the slope is +// formed analytically (there is a closed-form uv, so 1 uv unit is exactly tiling_scale mm). +// * Precomputed uv (use_vertex_uv = 1, used for LSCM): uv comes per-vertex from the CPU (the LSCM +// unwrap with island placement + tiling/rotation/offset already folded in), and the perturbed +// normal is built with Mikkelsen's method -- the surface gradient taken straight from the +// screen-space derivatives of the sampled height and position. This makes no uv->mm scale +// assumption, which matters because an LSCM map is conformal, not isometric: the local mm-per-uv +// varies across the chart, so a single global 1/tiling factor (what an earlier version used) got +// the apparent depth wrong. This path is also what makes the fast preview follow the UV editor: +// move an island and its uv -- hence its bump -- moves with it. #define INTENSITY_CORRECTION 0.6 @@ -39,14 +58,41 @@ uniform float tiling_scale; uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; +uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM) +// A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of +// the island currently being dragged in the UV editor (active > 0.5). Identity when nothing is +// dragged, so this whole path is a no-op then. Lets a UV island drag move the bump on the model with +// only a uniform update -- no mesh rebuild -- exactly the way Adjust placement moves the whole texture. +uniform vec4 island_delta_lin; +uniform vec2 island_delta_tr; in vec3 clipping_planes_dots; in vec4 model_pos; in vec4 world_pos; in float weight; +in float active; +in vec2 vertex_uv; out vec4 out_color; +// The two model-space axes the triplanar planar coordinate is read off, per dominant normal +// component -- same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y +// along b. +void projection_axes(vec3 n, out vec3 t, out vec3 b) +{ + vec3 an = abs(n); + if (an.x >= an.y && an.x >= an.z) { // planar = p.yz + t = vec3(0.0, 1.0, 0.0); + b = vec3(0.0, 0.0, 1.0); + } else if (an.y >= an.x && an.y >= an.z) { // planar = p.xz + t = vec3(1.0, 0.0, 0.0); + b = vec3(0.0, 0.0, 1.0); + } else { // planar = p.xy + t = vec3(1.0, 0.0, 0.0); + b = vec3(0.0, 1.0, 0.0); + } +} + vec2 project_uv(vec3 p, vec3 n) { vec3 an = abs(n); @@ -66,15 +112,63 @@ void main() if (volume_mirrored) triangle_normal = -triangle_normal; - if (weight > 0.0) { - vec2 uv = project_uv(model_pos.xyz, triangle_normal); + if (use_vertex_uv) { + // Precomputed-uv (LSCM) path -- Mikkelsen's surface-gradient bump ("Bump Mapping + // Unparametrized Surfaces on the GPU"). The perturbed normal is derived straight from the + // screen-space derivatives of the *sampled height* and the position, so it is scale-exact + // with no uv->mm assumption at all -- which is the whole point here: an LSCM map is conformal, + // not isometric, so the local mm-per-uv varies across the chart and the earlier "one global + // 1/tiling factor" got the depth visibly wrong. dFdx(h) captures the true on-screen rate of + // change however the chart is stretched or however fine the tiling is. + // + // use_vertex_uv is a uniform, so this whole branch is uniform control flow and the texture + // derivatives are well defined; the paint weight gates the result by a plain multiply (k) + // rather than a per-fragment branch, keeping it that way. + // The dragged island's uv rides a uniform affine so its bump moves without a rebuild; every + // other vertex (active == 0) samples its baked uv unchanged. + vec2 uv = (active > 0.5) + ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr + : vertex_uv; + float h = texture(height_tex, uv).r; + float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); + vec3 sigmaS = dFdx(model_pos.xyz); + vec3 sigmaT = dFdy(model_pos.xyz); + vec3 R1 = cross(sigmaT, triangle_normal); + vec3 R2 = cross(triangle_normal, sigmaS); + float det = dot(sigmaS, R1); + float dHdx = k * dFdx(h); + float dHdy = k * dFdy(h); + if (abs(det) > 1e-12) + triangle_normal = normalize(triangle_normal - (dHdx * R1 + dHdy * R2) / det); + } else if (weight > 0.0) { + // Triplanar path: uv and the tangent axes are reconstructed in-shader from the dominant + // normal component (see header). The gradient is expressed analytically because there is a + // closed-form uv here, unlike the LSCM case. + vec2 uv = project_uv(model_pos.xyz, triangle_normal); + vec3 t, b; + projection_axes(triangle_normal, t, b); + float hL = texture(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; float hR = texture(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; float hD = texture(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; float hU = texture(height_tex, uv + vec2(0.0, height_tex_texel.y)).r; - float sign_mul = invert ? -1.0 : 1.0; - vec3 bumped_normal = normalize(triangle_normal + sign_mul * depth_mm * vec3(hL - hR, hD - hU, 0.0)); - triangle_normal = normalize(mix(triangle_normal, bumped_normal, clamp(weight, 0.0, 1.0))); + + // Central difference, per uv unit (not per texel). + vec2 dh_duv = vec2((hR - hL) / (2.0 * height_tex_texel.x), (hU - hD) / (2.0 * height_tex_texel.y)); + + // uv -> mm is 1/tiling_scale for the triplanar projection, so this turns the uv-space + // gradient into a real surface slope. + float inv_tiling = (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; + float amplitude = (invert ? -1.0 : 1.0) * depth_mm * inv_tiling * clamp(weight, 0.0, 1.0); + // uv was rotated by project_uv() while t/b are the unrotated model axes, so rotate the + // gradient back into the axes' frame. + float cs = cos(rotation_rad); + float sn = sin(rotation_rad); + vec2 slope = amplitude * vec2(dh_duv.x * cs + dh_duv.y * sn, -dh_duv.x * sn + dh_duv.y * cs); + + vec3 gradient = slope.x * t + slope.y * b; + gradient -= triangle_normal * dot(triangle_normal, gradient); + triangle_normal = normalize(triangle_normal - gradient); } vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); diff --git a/resources/shaders/140/texture_displacement_bump.vs b/resources/shaders/140/texture_displacement_bump.vs index 636cdf525c..4518fb6687 100644 --- a/resources/shaders/140/texture_displacement_bump.vs +++ b/resources/shaders/140/texture_displacement_bump.vs @@ -9,16 +9,26 @@ uniform vec2 z_range; // Clipping plane - general orientation. Used by the SLA gizmo. uniform vec4 clipping_plane; -in vec3 v_position; -// Per-vertex paint weight for the currently active texture-displacement layer, 0..1 (see -// TriangleSelectorWeighted... note: Phase 1 stores this as a plain enforced/not-enforced mask, -// so this is 0.0 or 1.0 today; a continuous value is a natural later-phase extension). -in float v_weight; +in vec3 v_position; +// GLModel's P3N3T2 layout (position + normal + texcoord), reused so this mesh builds and renders +// like any other GLModel rather than needing a bespoke vertex buffer. The two spare channels carry +// what the bump preview actually needs per vertex: +// v_normal.x -- the active layer's paint weight, 0 (untouched) or 1 (painted). +// v_normal.y -- 1 for a vertex of the island currently being dragged in the UV editor, else 0. +// The fragment shader applies island_delta to those vertices' uv, so a UV drag is a +// single uniform update rather than a whole-mesh rebuild (like Adjust placement). +// v_tex_coord -- the precomputed texture uv for this vertex, valid only when use_vertex_uv is set +// (i.e. the LSCM projection, where uv can't be reconstructed in the shader). The +// triplanar path ignores it and projects in the fragment shader instead. +in vec3 v_normal; +in vec2 v_tex_coord; out vec3 clipping_planes_dots; out vec4 model_pos; out vec4 world_pos; out float weight; +out float active; +out vec2 vertex_uv; void main() { @@ -28,5 +38,7 @@ void main() gl_Position = projection_matrix * view_model_matrix * model_pos; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); - weight = v_weight; + weight = v_normal.x; + active = v_normal.y; + vertex_uv = v_tex_coord; } diff --git a/resources/shaders/140/texture_displacement_uvcheck.fs b/resources/shaders/140/texture_displacement_uvcheck.fs new file mode 100644 index 0000000000..323a49a4ba --- /dev/null +++ b/resources/shaders/140/texture_displacement_uvcheck.fs @@ -0,0 +1,83 @@ +#version 140 + +// UV-check overlay for the texture-displacement gizmo, drawn over the painted patch so the LSCM +// unwrap can be sanity-checked on the real 3D surface (mode set by the `mode` uniform): +// mode 0 -- Checker: a procedural checkerboard sampled at the layer's uv. Even squares that stay +// square everywhere on the model mean the unwrap is low-distortion; squares that smear or +// shear reveal exactly where it stretches. Same uv the bake samples, so what you see is +// where the texture actually lands. +// mode 1 -- Distortion heatmap: the per-vertex area-distortion carried in `distortion`, blue +// (compressed) -> green (ideal) -> red (stretched). +// Both are lit with the same cheap two-light diffuse the bump preview uses, so the surface still +// reads as 3D. + +#define INTENSITY_CORRECTION 0.6 +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +#define INTENSITY_AMBIENT 0.3 +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform mat3 view_normal_matrix; +uniform bool volume_mirrored; + +uniform int mode; // 0 checker, 1 distortion +uniform float checker_freq; // checker squares per uv unit (one uv unit == one texture tile) +uniform float tiling_scale; +uniform float rotation_rad; +uniform vec2 uv_offset; +uniform bool use_vertex_uv; + +in vec3 clipping_planes_dots; +in vec4 model_pos; +in vec4 world_pos; +in float distortion; +in vec2 vertex_uv; + +out vec4 out_color; + +vec2 project_uv(vec3 p, vec3 n) +{ + vec3 an = abs(n); + vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; + float cs = cos(rotation_rad); + float sn = sin(rotation_rad); + return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; +} + +// Blue -> cyan -> green -> yellow -> red over t in [0,1]. +vec3 heatmap(float t) +{ + t = clamp(t, 0.0, 1.0); + return clamp(vec3(1.5 - abs(4.0 * t - 3.0), + 1.5 - abs(4.0 * t - 2.0), + 1.5 - abs(4.0 * t - 1.0)), 0.0, 1.0); +} + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + if (volume_mirrored) + triangle_normal = -triangle_normal; + + vec3 base; + if (mode == 1) { + base = heatmap(distortion); + } else { + vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal); + vec2 c = floor(uv * checker_freq); + float check = mod(c.x + c.y, 2.0); + // Two distinct greys, plus a faint tint on one set so orientation is readable at a glance. + base = (check < 0.5) ? vec3(0.22, 0.23, 0.26) : vec3(0.82, 0.83, 0.86); + } + + vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); + float intensity = INTENSITY_AMBIENT + max(dot(eye_normal, LIGHT_TOP_DIR), 0.0) * LIGHT_TOP_DIFFUSE + + max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0) * LIGHT_FRONT_DIFFUSE; + out_color = vec4(base * intensity, 1.0); +} diff --git a/resources/shaders/140/texture_displacement_uvcheck.vs b/resources/shaders/140/texture_displacement_uvcheck.vs new file mode 100644 index 0000000000..86a7dd13e9 --- /dev/null +++ b/resources/shaders/140/texture_displacement_uvcheck.vs @@ -0,0 +1,36 @@ +#version 140 + +// Vertex stage for the UV-check overlay (checker / distortion heatmap) drawn over the painted patch +// by GLGizmoTextureDisplacement. Reuses GLModel's P3N3T2 layout so it needs no bespoke buffer: +// v_normal.x -- per-vertex UV distortion (uv-area / surface-area ratio, remapped so 0.5 = ideal); +// only the distortion mode reads it. +// v_tex_coord -- precomputed texture uv, valid only when use_vertex_uv is set (LSCM); the checker +// mode reconstructs uv in the fragment shader otherwise. + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat4 volume_world_matrix; +uniform vec2 z_range; +uniform vec4 clipping_plane; + +in vec3 v_position; +in vec3 v_normal; +in vec2 v_tex_coord; + +out vec3 clipping_planes_dots; +out vec4 model_pos; +out vec4 world_pos; +out float distortion; +out vec2 vertex_uv; + +void main() +{ + model_pos = vec4(v_position, 1.0); + world_pos = volume_world_matrix * model_pos; + + gl_Position = projection_matrix * view_model_matrix * model_pos; + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + distortion = v_normal.x; + vertex_uv = v_tex_coord; +} diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index d602ca1e06..5e85154677 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -102,6 +102,9 @@ std::pair GLShadersManager::init() valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }); // Fast bump-map preview for the texture displacement gizmo (see libslic3r/TextureDisplacement.hpp). valid &= append_shader("texture_displacement_bump", { prefix + "texture_displacement_bump.vs", prefix + "texture_displacement_bump.fs" }); + // UV-check overlay for the same gizmo: a procedural checker or a distortion heatmap over the + // painted patch, to sanity-check the unwrap. + valid &= append_shader("texture_displacement_uvcheck", { prefix + "texture_displacement_uvcheck.vs", prefix + "texture_displacement_uvcheck.fs" }); return { valid, error }; } From 05083bb6ab127006e109057642542bfe2307c994 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 16 Jul 2026 08:43:21 +0200 Subject: [PATCH 05/89] Add texture displacement gizmo and UV editor --- src/slic3r/CMakeLists.txt | 6 + src/slic3r/GUI/GLTexture.cpp | 54 +- src/slic3r/GUI/GLTexture.hpp | 5 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 3013 ++++++++++++++++- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 386 +++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 5 +- src/slic3r/GUI/ImGuiWrapper.cpp | 6 +- .../Jobs/TextureDisplacementPreviewJob.cpp | 29 + .../Jobs/TextureDisplacementPreviewJob.hpp | 52 + src/slic3r/GUI/Plater.cpp | 56 + src/slic3r/GUI/Plater.hpp | 7 + src/slic3r/GUI/TextureLibrary.cpp | 208 ++ src/slic3r/GUI/TextureLibrary.hpp | 50 + src/slic3r/GUI/UVEditorCanvas.cpp | 1314 +++++++ src/slic3r/GUI/UVEditorCanvas.hpp | 316 ++ 15 files changed, 5405 insertions(+), 102 deletions(-) create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp create mode 100644 src/slic3r/GUI/TextureLibrary.cpp create mode 100644 src/slic3r/GUI/TextureLibrary.hpp create mode 100644 src/slic3r/GUI/UVEditorCanvas.cpp create mode 100644 src/slic3r/GUI/UVEditorCanvas.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4b498a3185..38bd235ff2 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -301,6 +301,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/SLAImportJob.hpp GUI/Jobs/TextureDisplacementBakeJob.cpp GUI/Jobs/TextureDisplacementBakeJob.hpp + GUI/Jobs/TextureDisplacementPreviewJob.cpp + GUI/Jobs/TextureDisplacementPreviewJob.hpp GUI/Jobs/ThreadSafeQueue.hpp GUI/Jobs/UpgradeNetworkJob.cpp GUI/Jobs/UpgradeNetworkJob.hpp @@ -470,6 +472,8 @@ set(SLIC3R_GUI_SOURCES GUI/TaskManager.hpp GUI/TextLines.cpp GUI/TextLines.hpp + GUI/TextureLibrary.cpp + GUI/TextureLibrary.hpp GUI/TickCode.cpp GUI/TickCode.hpp GUI/TroubleshootDialog.cpp @@ -486,6 +490,8 @@ set(SLIC3R_GUI_SOURCES GUI/UserManager.hpp GUI/UserNotification.cpp GUI/UserNotification.hpp + GUI/UVEditorCanvas.cpp + GUI/UVEditorCanvas.hpp GUI/WebDownPluginDlg.cpp GUI/WebDownPluginDlg.hpp GUI/WebGuideDialog.cpp diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index d670181b1b..113b9c13dc 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -171,7 +171,8 @@ bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps return false; } -bool GLTexture::load_from_raw_data(std::vector data, unsigned int w, unsigned int h, bool apply_anisotropy) +bool GLTexture::load_from_raw_data(std::vector data, unsigned int w, unsigned int h, bool apply_anisotropy, + bool use_mipmaps) { m_width = w; m_height = h; @@ -195,18 +196,51 @@ bool GLTexture::load_from_raw_data(std::vector data, unsigned int glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - bool use_mipmaps = true; if (use_mipmaps) { - // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards - int lod_w = m_width; - int lod_h = m_height; + // We generate the mipmap chain ourselves rather than calling glGenerateMipmap(), which this + // codebase has historically considered unreliable on some graphics cards. + // + // Each level is a 2x2 box filter of the level above it. Note this used to re-upload the + // *level-0* buffer at every level instead, which does not downscale anything -- it just + // reinterprets the image's first lod_w * lod_h texels as the whole smaller level, i.e. every + // level below 0 held a crop of the top-left corner. It went unnoticed for as long as every + // caller drew these textures at roughly their native size (where only level 0 is ever + // sampled); it shows up the moment one is drawn small enough to select a lower level, as a + // texture that visibly turns into something else as it shrinks. + std::vector scratch; + const std::vector *src = &data; + int src_w = m_width; + int src_h = m_height; GLint level = 0; - while (lod_w > 1 || lod_h > 1) { + while (src_w > 1 || src_h > 1) { ++level; - lod_w = std::max(lod_w / 2, 1); - lod_h = std::max(lod_h / 2, 1); - n_pixels = lod_w * lod_h; - glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + const int lod_w = std::max(src_w / 2, 1); + const int lod_h = std::max(src_h / 2, 1); + + std::vector lod(size_t(lod_w) * size_t(lod_h) * 4); + for (int y = 0; y < lod_h; ++y) { + // min() rather than a plain 2*y+1: an odd source extent leaves the last output texel + // with only one source row/column to average, not two. + const int y0 = std::min(2 * y, src_h - 1); + const int y1 = std::min(2 * y + 1, src_h - 1); + for (int x = 0; x < lod_w; ++x) { + const int x0 = std::min(2 * x, src_w - 1); + const int x1 = std::min(2 * x + 1, src_w - 1); + for (int c = 0; c < 4; ++c) { + const unsigned int sum = (*src)[(size_t(y0) * size_t(src_w) + size_t(x0)) * 4 + size_t(c)] + + (*src)[(size_t(y0) * size_t(src_w) + size_t(x1)) * 4 + size_t(c)] + + (*src)[(size_t(y1) * size_t(src_w) + size_t(x0)) * 4 + size_t(c)] + + (*src)[(size_t(y1) * size_t(src_w) + size_t(x1)) * 4 + size_t(c)]; + lod[(size_t(y) * size_t(lod_w) + size_t(x)) * 4 + size_t(c)] = (unsigned char)(sum / 4); + } + } + } + glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)lod.data())); + + scratch = std::move(lod); + src = &scratch; + src_w = lod_w; + src_h = lod_h; } glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level)); diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index 1bb63651f0..ca1f4db17d 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -100,7 +100,10 @@ namespace GUI { bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy); bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px); //BBS load GLTexture from raw pixel data - bool load_from_raw_data(std::vector data, unsigned int w, unsigned int h, bool apply_anisotropy = false); + // `data` is RGBA, w * h * 4 bytes. With use_mipmaps, a real box-filtered mipmap chain is + // built, so the texture may safely be drawn smaller than its pixel size. + bool load_from_raw_data(std::vector data, unsigned int w, unsigned int h, bool apply_anisotropy = false, + bool use_mipmaps = true); // meanings of states: (std::pair) // first field (int): // 0 -> no changes diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 021d6c31a0..a8dcbaf7c4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -1,29 +1,129 @@ #include "GLGizmoTextureDisplacement.hpp" -#include - -#include -#include +#include +#include "libslic3r/MeshBoolean.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/PNGReadWrite.hpp" +#include "libslic3r/Utils.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/OpenGLManager.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/TextureLibrary.hpp" +#include "slic3r/GUI/UVEditorCanvas.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp" +#include "slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "GLGizmoUtils.hpp" #include #include +#include +#include +#include +#include +#include namespace Slic3r::GUI { +namespace { +// ImGuiWrapper::slider_float()'s trailing `power` parameter is forwarded straight into +// ImGui::SliderFloat()'s ImGuiSliderFlags argument - this ImGui (1.83) replaced the old float +// "power" curve API with a flags word but kept the parameter in the same position, and +// ImGuiWrapper never caught up with the rename. Passing the Logarithmic flag through it is +// therefore how a log-scaled slider gets requested here. +// +// Depth and tile size both want it: they span two to three orders of magnitude, and the values +// that need the most precision (a few hundredths of a millimetre of relief, a very fine tile) are +// all bunched into the very bottom of a linear range, where a single pixel of slider travel is a +// bigger jump than the whole useful region. +constexpr float ImGuiLogSlider = float(ImGuiSliderFlags_Logarithmic); + +// Uploads decoded 8-bit grayscale height data as an RGBA GPU texture for display in the panel. +// Shared by the per-layer thumbnail and the picker's library entries, which differ only in where +// their pixels came from. +// +// The source is downscaled to THUMBNAIL_MAX_PX first, with a box filter, and uploaded *without* +// mipmaps. Both halves of that matter, and together they are the fix for thumbnails rendering as +// garbage: +// - GLTexture's mipmap levels are not real downscales (see load_from_raw_data()'s header) - every +// level re-uploads the full-resolution buffer reinterpreted at a smaller size. A 512px texture +// drawn into a ~48px row is minified ~10x, which is exactly the regime where OpenGL picks those +// broken levels, so the thumbnail showed a scrambled crop of the image rather than the image. +// Turning mipmaps off makes it sample level 0, which is the real picture. +// - But level 0 at 512px sampled down to 48px with plain GL_LINEAR (a 2x2 tap) would alias badly +// on exactly the high-frequency patterns these textures are (knurl, hexagons, weave). Box- +// filtering down to roughly twice the displayed size first is what actually removes the +// frequencies that would alias, and it cuts the VRAM these hold by ~16x as a bonus. +constexpr int THUMBNAIL_MAX_PX = 128; + +std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &decoded) +{ + if (decoded.empty()) + return nullptr; + + // Preserve aspect; never upscale a texture that is already small. + const int scale = std::max(1, (std::max(decoded.width, decoded.height) + THUMBNAIL_MAX_PX - 1) / THUMBNAIL_MAX_PX); + const int w = std::max(1, decoded.width / scale); + const int h = std::max(1, decoded.height / scale); + + std::vector rgba(size_t(w) * size_t(h) * 4); + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) { + // Average the source block this destination pixel covers. + const int x0 = x * decoded.width / w, x1 = std::max(x0 + 1, (x + 1) * decoded.width / w); + const int y0 = y * decoded.height / h, y1 = std::max(y0 + 1, (y + 1) * decoded.height / h); + unsigned int sum = 0, n = 0; + for (int sy = y0; sy < y1 && sy < decoded.height; ++sy) + for (int sx = x0; sx < x1 && sx < decoded.width; ++sx, ++n) + sum += decoded.pixels[size_t(sy) * size_t(decoded.width) + size_t(sx)]; + + const unsigned char gray = (n > 0) ? static_cast(sum / n) : 0; + const size_t di = (size_t(y) * size_t(w) + size_t(x)) * 4; + rgba[di + 0] = gray; + rgba[di + 1] = gray; + rgba[di + 2] = gray; + rgba[di + 3] = 255; + } + + auto texture = std::make_unique(); + if (!texture->load_from_raw_data(std::move(rgba), (unsigned int) w, (unsigned int) h, false, /* use_mipmaps */ false)) + return nullptr; + return texture; +} + +// Intersects the camera ray through `mouse_pos` (screen coords) with the plane passing through +// `plane_point_local`/`plane_normal_local` (mesh-local coords, transformed to world by `trafo`). +// Returns false if the ray is parallel to the plane or the plane is behind the camera. +bool ray_plane_hit(const Camera &camera, const Vec2d &mouse_pos, const Transform3d &trafo, + const Vec3f &plane_point_local, const Vec3f &plane_normal_local, Vec3d &out_world_hit) +{ + Vec3d ray_origin, ray_dir; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, ray_origin, ray_dir); + + const Vec3d plane_point_world = trafo * plane_point_local.cast(); + const Vec3d plane_normal_world = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * plane_normal_local.cast()).normalized(); + + const double denom = ray_dir.dot(plane_normal_world); + if (std::abs(denom) < 1e-8) + return false; + + const double t = (plane_point_world - ray_origin).dot(plane_normal_world) / denom; + if (t < 0.0) + return false; + + out_world_hit = ray_origin + ray_dir * t; + return true; +} +} // namespace + GLGizmoTextureDisplacement::GLGizmoTextureDisplacement(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoPainterBase(parent, icon_filename, sprite_id) { @@ -34,7 +134,7 @@ bool GLGizmoTextureDisplacement::on_init() m_desc["cursor_size"] = _L("Brush size"); m_desc["circle"] = _L("Circle"); m_desc["sphere"] = _L("Sphere"); - m_desc["add_texture"] = _L("Add texture..."); + m_desc["add_texture"] = _L("Add layer"); m_desc["remove_layer"] = _L("Remove"); m_desc["bake"] = _L("Bake"); m_desc["remove_all"] = _L("Erase all"); @@ -49,6 +149,34 @@ std::string GLGizmoTextureDisplacement::on_get_name() const void GLGizmoTextureDisplacement::on_shutdown() { m_parent.toggle_model_objects_visibility(true); + m_preview_glmodel.reset(); + m_bump_preview_glmodel.reset(); + m_uvcheck_glmodel.reset(); + m_wireframe_overlay_glmodel.reset(); + m_wireframe_overlay_vcount = 0; + m_seam_glmodel.reset(); + m_seam_hover_glmodel.reset(); + m_seam_hover_edge = { -1, -1 }; + m_seam_hover_vertex = -1; + m_seam_anchor_glmodel.reset(); + m_seam_path_mode = false; + m_seam_path_anchor = -1; + m_subdivide_editing = false; + m_subdivide_preview_count = -1; + m_subdivide_preview_glmodel.reset(); + m_bump_active_chart = -1; + m_bump_active_vertex.clear(); + m_bump_island_delta = Eigen::Matrix::Identity(); + m_island_drag_active = false; + m_island_move_set.clear(); + m_adjust_texture_mode = false; + m_seam_edit_mode = false; + m_adjust_drag_handle = AdjustHandle::None; + m_adjust_anchor_valid = false; + // The pane is request-only: closing the gizmo drops the request, so reopening it later doesn't + // silently reopen the pane too. + m_show_uv_editor = false; + wxGetApp().plater()->show_uv_editor(false); } PainterGizmoType GLGizmoTextureDisplacement::get_painter_type() const @@ -68,19 +196,1806 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - // Phase 1 reuses the standard paint-mask overlay (as every other paint gizmo does) to show - // which area of the active layer is painted. A dedicated bump-map preview shader - // (resources/shaders/*/texture_displacement_bump.*) is registered and ready to use, but - // wiring it into this render path is deferred to a later phase -- see the project plan. - // "Bake" already shows the true, real-geometry result, which is the most important preview. - render_triangles(selection); + // Once anything is painted, m_preview_glmodel holds the true displaced result (same algorithm + // Bake uses). The untouched original topology (what render_triangles() draws) coincides + // exactly with it everywhere except the painted/displaced area, so both are drawn: the real + // preview geometry first, then the usual selection-highlight overlay with a small depth bias + // so it wins the depth test on the coincident (unpainted) surface - keeping the familiar + // enforcer/blocker highlight for precise brush editing there. Where the surface has actually + // been displaced, the raised preview geometry legitimately occludes the flat overlay - that + // visible bump is itself the "this is painted" indicator in that area. + // + // The bump preview is different: it never actually moves geometry (it's a shading trick), so + // its depth is identical to the overlay's *everywhere*, not just in the unpainted area - the + // depth-biased overlay would win the depth test across the whole surface and hide the bump + // shading entirely. So the overlay is skipped for it; the bump shading itself is the only + // feedback in that mode (still fine for painting, since render_cursor() below shows the brush). + // Coalesced bump rebuild from an in-progress UV island drag (see on_island_edited): done here, at + // most once per drawn frame, rather than synchronously in the UV canvas's mouse-move handler. + if (m_use_bump_preview && m_bump_preview_dirty) { + rebuild_bump_preview_mesh(); + m_bump_preview_dirty = false; + } + const bool use_bump = m_use_bump_preview && m_bump_preview_glmodel.is_initialized(); + if (use_bump) { + m_parent.toggle_model_objects_visibility(true); + if (ModelVolume *mv = texture_volume()) + m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), + m_c->selection_info()->get_active_instance(), mv); + render_bump_preview_mesh(); + } else if (m_preview_glmodel.is_initialized()) { + m_parent.toggle_model_objects_visibility(true); + if (ModelVolume *mv = texture_volume()) + m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), + m_c->selection_info()->get_active_instance(), mv); + render_preview_mesh(); + + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(-1.0f, -1.0f)); + render_triangles(selection); + glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); + } else { + render_triangles(selection); + } + + // Diagnostic overlays, drawn on top of whatever preview is active (both pull toward the camera + // with a polygon offset so they win the depth test against the coincident surface). + if (m_uv_check_mode != UVCheckMode::None) + render_uvcheck_mesh(); + // While previewing a subdivision, its wireframe stands in for the mesh wireframe - it shows the + // density the model *would* have. The normal wireframe toggle is left untouched underneath, so it + // returns to whatever it was once the preview ends (which is what keeps an already-on wireframe on). + if (m_subdivide_editing) + render_subdivide_preview(); + else if (m_wireframe_overlay) + render_wireframe_overlay(); + // Marked seams are always shown for an LSCM layer, so existing cuts are visible before entering + // seam-edit mode - but they matter most while marking. + render_seam_overlay(); + m_c->object_clipper()->render_cut(); m_c->instances_hider()->render_cut(); - render_cursor(); + if (m_adjust_texture_mode) + render_adjust_texture_gizmo(); + else if (!m_seam_edit_mode) // the brush cursor is meaningless while marking seams + render_cursor(); glsafe(::glDisable(GL_BLEND)); } +bool GLGizmoTextureDisplacement::on_mouse(const wxMouseEvent &mouse_event) +{ + if (m_seam_edit_mode) + return on_mouse_seam(mouse_event); + if (m_adjust_texture_mode) + return on_mouse_adjust_texture(mouse_event); + return GLGizmoPainterBase::on_mouse(mouse_event); +} + +bool GLGizmoTextureDisplacement::on_mouse_seam(const wxMouseEvent &mouse_event) +{ + // Hold Ctrl to orbit/pan the camera while in seam mode, exactly as the base painter lets you do + // while painting: with Ctrl down we consume nothing, so the canvas gets the drag and moves the + // view. Without this, seam mode swallowed every left-drag and the camera couldn't be rotated. + if (mouse_event.CmdDown()) + return false; + + // Left-click marks/unmarks an edge; swallow the rest of the left-button stream so a drag doesn't + // paint, but let everything else through so the camera still orbits/pans/zooms normally. + if (mouse_event.LeftDown()) { + const Vec2d pos(mouse_event.GetX(), mouse_event.GetY()); + if (m_seam_path_mode) { + // Two-click shortest-path seam: first click sets the start vertex, the next seams the whole + // path to it and becomes the new start (so a seam line chains click by click). + const int v = seam_vertex_at(pos); + if (v >= 0) { + if (m_seam_path_anchor < 0) + m_seam_path_anchor = v; + else { + mark_seam_path(m_seam_path_anchor, v); + m_seam_path_anchor = v; + } + rebuild_seam_anchor_overlay(); + m_parent.set_as_dirty(); + } + } else { + toggle_seam_at(pos); + } + return true; + } + // Live hover: highlight what a click would pick, so the clickable target is obvious (the "I don't + // know how it works" the user hit). In normal mode that is an edge; in shortest-path mode it is a + // vertex. Don't swallow the motion - the camera still needs it. + if (mouse_event.Moving()) { + const Vec2d pos(mouse_event.GetX(), mouse_event.GetY()); + if (m_seam_path_mode) { + const int v = seam_vertex_at(pos); + if (v != m_seam_hover_vertex) { + m_seam_hover_vertex = v; + m_seam_hover_edge = { -1, -1 }; + rebuild_seam_hover_overlay(); + m_parent.set_as_dirty(); + } + } else { + const std::pair edge = seam_edge_at(pos); + if (edge != m_seam_hover_edge) { + m_seam_hover_edge = edge; + m_seam_hover_vertex = -1; + rebuild_seam_hover_overlay(); + m_parent.set_as_dirty(); + } + } + } + if (mouse_event.LeftUp() || (mouse_event.Dragging() && mouse_event.LeftIsDown())) + return true; + return false; +} + +std::pair GLGizmoTextureDisplacement::seam_edge_at(const Vec2d &mouse_pos) const +{ + const ModelVolume *mv = texture_volume(); + const ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) + return { -1, -1 }; + + // The mesh raycasters are built one per model-part volume, in that order; find this volume's slot. + int idx = -1, count = 0; + for (const ModelVolume *v : mo->volumes) { + if (!v->is_model_part()) + continue; + if (v == mv) { idx = count; break; } + ++count; + } + const auto &raycasters = m_c->raycaster()->raycasters(); + if (idx < 0 || idx >= int(raycasters.size())) + return { -1, -1 }; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + Vec3f hit = Vec3f::Zero(), normal = Vec3f::Zero(); + size_t facet = 0; + if (!raycasters[size_t(idx)]->unproject_on_mesh(mouse_pos, trafo, camera, hit, normal, + m_c->object_clipper()->get_clipping_plane(), &facet)) + return { -1, -1 }; + + const indexed_triangle_set &its = mv->mesh().its; + if (facet >= its.indices.size()) + return { -1, -1 }; + const stl_triangle_vertex_indices &tri = its.indices[facet]; + + // The facet edge nearest the hit point (point-to-segment distance in mesh space). + const auto seg_dist = [](const Vec3f &p, const Vec3f &a, const Vec3f &b) { + const Vec3f ab = b - a; + const float l2 = ab.squaredNorm(); + const float t = (l2 > 1e-12f) ? std::clamp((p - a).dot(ab) / l2, 0.f, 1.f) : 0.f; + return (p - (a + ab * t)).norm(); + }; + int best_i = 0; + float best_d = std::numeric_limits::max(); + for (int i = 0; i < 3; ++i) { + const float d = seg_dist(hit, its.vertices[tri[i]], its.vertices[tri[(i + 1) % 3]]); + if (d < best_d) { best_d = d; best_i = i; } + } + const int a = tri[best_i], b = tri[(best_i + 1) % 3]; + return { std::min(a, b), std::max(a, b) }; +} + +void GLGizmoTextureDisplacement::toggle_seam_at(const Vec2d &mouse_pos) +{ + TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr) + return; + const std::pair edge = seam_edge_at(mouse_pos); + if (edge.first < 0) + return; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Mark texture seam"), UndoRedo::SnapshotType::GizmoAction); + auto &seams = layer->lscm_seam_edges; + if (const auto it = std::find(seams.begin(), seams.end(), edge); it != seams.end()) + seams.erase(it); + else + seams.push_back(edge); + rebuild_preview(); +} + +int GLGizmoTextureDisplacement::seam_vertex_at(const Vec2d &mouse_pos) const +{ + const ModelVolume *mv = texture_volume(); + const ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) + return -1; + int idx = -1, count = 0; + for (const ModelVolume *v : mo->volumes) { + if (!v->is_model_part()) + continue; + if (v == mv) { idx = count; break; } + ++count; + } + const auto &raycasters = m_c->raycaster()->raycasters(); + if (idx < 0 || idx >= int(raycasters.size())) + return -1; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + Vec3f hit = Vec3f::Zero(), normal = Vec3f::Zero(); + size_t facet = 0; + if (!raycasters[size_t(idx)]->unproject_on_mesh(mouse_pos, trafo, camera, hit, normal, + m_c->object_clipper()->get_clipping_plane(), &facet)) + return -1; + const indexed_triangle_set &its = mv->mesh().its; + if (facet >= its.indices.size()) + return -1; + const stl_triangle_vertex_indices &tri = its.indices[facet]; + int best = tri[0]; + float best_d = std::numeric_limits::max(); + for (int i = 0; i < 3; ++i) { + const float d = (hit - its.vertices[tri[i]]).squaredNorm(); + if (d < best_d) { best_d = d; best = tri[i]; } + } + return best; +} + +void GLGizmoTextureDisplacement::mark_seam_path(int v_from, int v_to) +{ + TextureDisplacementLayer *layer = active_layer(); + const ModelVolume *mv = texture_volume(); + if (layer == nullptr || mv == nullptr || v_from < 0 || v_to < 0 || v_from == v_to) + return; + const indexed_triangle_set &its = mv->mesh().its; + const size_t n = its.vertices.size(); + if (size_t(v_from) >= n || size_t(v_to) >= n) + return; + + // Shortest path over the mesh's edge graph (Dijkstra, edge weight = length). Built on demand; one + // pass per click is fine even on a dense mesh. + std::vector>> adj(n); + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) { + const int a = tri[i], b = tri[(i + 1) % 3]; + const float w = (its.vertices[size_t(a)] - its.vertices[size_t(b)]).norm(); + adj[size_t(a)].push_back({ b, w }); + adj[size_t(b)].push_back({ a, w }); + } + + std::vector dist(n, std::numeric_limits::infinity()); + std::vector prev(n, -1); + using QN = std::pair; + std::priority_queue, std::greater> pq; + dist[size_t(v_from)] = 0.f; + pq.push({ 0.f, v_from }); + while (!pq.empty()) { + const auto [d, u] = pq.top(); + pq.pop(); + if (d > dist[size_t(u)]) + continue; + if (u == v_to) + break; + for (const auto &[w, ew] : adj[size_t(u)]) { + const float nd = d + ew; + if (nd < dist[size_t(w)]) { + dist[size_t(w)] = nd; + prev[size_t(w)] = u; + pq.push({ nd, w }); + } + } + } + if (prev[size_t(v_to)] < 0) + return; // unreachable (disconnected components) + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Mark texture seam path"), UndoRedo::SnapshotType::GizmoAction); + auto &seams = layer->lscm_seam_edges; + for (int v = v_to; v != v_from && v >= 0; v = prev[size_t(v)]) { + const int p = prev[size_t(v)]; + if (p < 0) + break; + const std::pair e{ std::min(v, p), std::max(v, p) }; + if (std::find(seams.begin(), seams.end(), e) == seams.end()) + seams.push_back(e); + } + rebuild_preview(); +} + +void GLGizmoTextureDisplacement::rebuild_seam_anchor_overlay() +{ + m_seam_anchor_glmodel.reset(); + const ModelVolume *mv = texture_volume(); + if (!m_seam_edit_mode || !m_seam_path_mode || mv == nullptr || m_seam_path_anchor < 0) + return; + const indexed_triangle_set &its = mv->mesh().its; + if (size_t(m_seam_path_anchor) >= its.vertices.size()) + return; + + // The anchor's incident edges, so the path's start vertex is visible on the model. + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + unsigned nn = 0; + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) { + const int a = tri[i], b = tri[(i + 1) % 3]; + if (a == m_seam_path_anchor || b == m_seam_path_anchor) { + init_data.add_vertex(its.vertices[size_t(a)]); + init_data.add_vertex(its.vertices[size_t(b)]); + init_data.add_line(nn, nn + 1); + nn += 2; + } + } + if (!init_data.is_empty()) + m_seam_anchor_glmodel.init_from(std::move(init_data)); +} + +void GLGizmoTextureDisplacement::rebuild_seam_hover_overlay() +{ + m_seam_hover_glmodel.reset(); + const ModelVolume *mv = texture_volume(); + if (!m_seam_edit_mode || mv == nullptr) + return; + const indexed_triangle_set &its = mv->mesh().its; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + + if (m_seam_path_mode) { + // Highlight the hovered vertex as its ring of incident edges, so the click target is legible on + // a dense mesh (matching the green anchor's style, in the hover yellow render_seam_overlay uses). + if (m_seam_hover_vertex < 0 || size_t(m_seam_hover_vertex) >= its.vertices.size()) + return; + unsigned nn = 0; + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) { + const int a = tri[i], b = tri[(i + 1) % 3]; + if (a == m_seam_hover_vertex || b == m_seam_hover_vertex) { + init_data.add_vertex(its.vertices[size_t(a)]); + init_data.add_vertex(its.vertices[size_t(b)]); + init_data.add_line(nn, nn + 1); + nn += 2; + } + } + if (!init_data.is_empty()) + m_seam_hover_glmodel.init_from(std::move(init_data)); + return; + } + + if (m_seam_hover_edge.first < 0 || size_t(m_seam_hover_edge.first) >= its.vertices.size() || + size_t(m_seam_hover_edge.second) >= its.vertices.size()) + return; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + init_data.add_vertex(its.vertices[size_t(m_seam_hover_edge.first)]); + init_data.add_vertex(its.vertices[size_t(m_seam_hover_edge.second)]); + init_data.add_line(0, 1); + m_seam_hover_glmodel.init_from(std::move(init_data)); +} + +void GLGizmoTextureDisplacement::rebuild_seam_overlay() +{ + m_seam_glmodel.reset(); + const ModelVolume *mv = texture_volume(); + const TextureDisplacementLayer *layer = active_layer(); + if (mv == nullptr || layer == nullptr || layer->lscm_seam_edges.empty()) + return; + const indexed_triangle_set &its = mv->mesh().its; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(layer->lscm_seam_edges.size() * 2); + init_data.reserve_indices(layer->lscm_seam_edges.size() * 2); + unsigned n = 0; + for (const auto &[a, b] : layer->lscm_seam_edges) { + if (a < 0 || b < 0 || size_t(a) >= its.vertices.size() || size_t(b) >= its.vertices.size()) + continue; + init_data.add_vertex(its.vertices[size_t(a)]); + init_data.add_vertex(its.vertices[size_t(b)]); + init_data.add_line(n, n + 1); + n += 2; + } + if (!init_data.is_empty()) + m_seam_glmodel.init_from(std::move(init_data)); +} + +void GLGizmoTextureDisplacement::render_seam_overlay() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + const bool have_marked = m_seam_glmodel.is_initialized(); + const bool have_hover = m_seam_edit_mode && m_seam_hover_glmodel.is_initialized(); + const bool have_anchor = m_seam_edit_mode && m_seam_anchor_glmodel.is_initialized(); + if (mo == nullptr || mv == nullptr || (!have_marked && !have_hover && !have_anchor)) + return; + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + glsafe(::glEnable(GL_POLYGON_OFFSET_LINE)); + glsafe(::glPolygonOffset(-2.0f, -2.0f)); // pull further forward than the wireframe so seams read on top + // A seam edge is geometrically the same line as a wireframe edge, so a mere polygon offset is a + // fragile way to make the red seam beat the white wireframe -- drivers apply GL_POLYGON_OFFSET_LINE + // inconsistently, and the two lines then z-fight and the wireframe wins. When the wireframe is on, + // or while actively marking, just draw the seams with depth testing off so they are unconditionally + // on top -- being visible is the one thing this overlay has to guarantee. + const bool seams_on_top = m_wireframe_overlay || m_seam_edit_mode; + if (seams_on_top) + glsafe(::glDisable(GL_DEPTH_TEST)); +#if !SLIC3R_OPENGL_ES + const bool wide = !OpenGLManager::get_gl_info().is_core_profile(); + if (wide) + glsafe(::glLineWidth(4.0f)); +#endif // !SLIC3R_OPENGL_ES + if (have_marked) { + m_seam_glmodel.set_color(ColorRGBA(1.0f, 0.15f, 0.15f, 1.0f)); // Blender's seam red + m_seam_glmodel.render(); + } + // The edge a click would toggle, in yellow and pulled the furthest forward, so it is unmistakable + // which edge is being targeted while marking seams. + if (have_hover) { + glsafe(::glPolygonOffset(-3.0f, -3.0f)); + m_seam_hover_glmodel.set_color(ColorRGBA(1.0f, 0.9f, 0.15f, 1.0f)); + m_seam_hover_glmodel.render(); + } + // The shortest-path start vertex, shown as its ring of incident edges in green. + if (have_anchor) { + glsafe(::glPolygonOffset(-3.0f, -3.0f)); + m_seam_anchor_glmodel.set_color(ColorRGBA(0.2f, 1.0f, 0.4f, 1.0f)); + m_seam_anchor_glmodel.render(); + } +#if !SLIC3R_OPENGL_ES + if (wide) + glsafe(::glLineWidth(1.0f)); +#endif // !SLIC3R_OPENGL_ES + glsafe(::glDisable(GL_POLYGON_OFFSET_LINE)); + if (seams_on_top) + glsafe(::glEnable(GL_DEPTH_TEST)); + shader->stop_using(); +} + +void GLGizmoTextureDisplacement::render_preview_mesh() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + + auto *shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + shader->start_using(); + const Camera &camera = wxGetApp().plater()->get_camera(); + const Transform3d &view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_preview_glmodel.render(); + shader->stop_using(); +} + +std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const indexed_triangle_set &patch, + const TextureDisplacementLayer &layer) const +{ + if (layer.projection_method == TextureProjectionMethod::LSCM) + return compute_lscm_uvs(patch, layer); // one final uv per patch vertex (0 where unassigned) + if (layer.projection_method == TextureProjectionMethod::ViewProjected) { + std::vector uv(patch.vertices.size()); + for (size_t vi = 0; vi < patch.vertices.size(); ++vi) { + const Vec2f planar(patch.vertices[vi].dot(layer.view_project_right), + patch.vertices[vi].dot(layer.view_project_up)); + uv[vi] = apply_uv_transform(planar, layer); + } + return uv; + } + return {}; // Triplanar / Cylindrical / Spherical: the shader projects on its own +} + +void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() +{ + m_bump_preview_glmodel.reset(); + + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || m_triangle_selectors.empty()) + return; + + // Uses the *live* selector (not the flushed model facet data), so this reflects an in-progress + // stroke immediately rather than only once it ends - the point of this preview mode is to be + // the fast, no-CPU-meshing one. + const indexed_triangle_set patch = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER); + if (patch.indices.empty()) + return; + + const indexed_triangle_set &base = mv->mesh().its; + if (base.vertices.size() != patch.vertices.size()) + return; // shouldn't happen: get_facets_strict() always returns the full vertex array + + std::vector is_painted(patch.vertices.size(), false); + for (const stl_triangle_vertex_indices &tri : patch.indices) + for (int i = 0; i < 3; ++i) + is_painted[tri[i]] = true; + + // For LSCM we hand the shader the finished per-vertex texture uv (island placement + tiling/ + // rotation/offset already folded in, exactly what the bake samples), because it cannot be + // reconstructed in the fragment shader the way a triplanar projection can. This is also what + // makes the fast preview follow the UV editor: the uvs move when an island is dragged, so this + // mesh rebuilds (on drag end) with them. The other projections keep projecting in-shader. + const TextureDisplacementLayer *active = active_layer(); + std::vector vertex_uv = active != nullptr ? compute_layer_vertex_uvs(patch, *active) : std::vector{}; + m_bump_preview_uses_vertex_uv = vertex_uv.size() == base.vertices.size(); + if (!m_bump_preview_uses_vertex_uv) + vertex_uv.clear(); + + GLModel::Geometry init_data; + // P3N3T2: normal.x carries the paint weight, tex_coord carries the precomputed uv (see the vertex + // shader). Reuses a standard GLModel layout rather than a bespoke vertex buffer. + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; + init_data.reserve_vertices(base.vertices.size()); + init_data.reserve_indices(base.indices.size() * 3); + // Every triangle of the whole mesh is included (not just the painted ones) so the surrounding, + // un-bumped surface still renders - the per-vertex weight (0 outside the patch) is what fades + // the shader's bump effect to nothing there, exactly like the true-displacement preview fades + // to the untouched surface at its boundary. + // normal.y flags the island currently dragged in the UV editor, so the shader can move just that + // island through the island_delta uniform (see the bump shaders / on_island_edited). + const bool have_active = m_bump_active_chart >= 0 && !m_bump_active_vertex.empty(); + for (size_t vi = 0; vi < base.vertices.size(); ++vi) { + const float active = (have_active && vi < m_bump_active_vertex.size() && m_bump_active_vertex[vi]) ? 1.f : 0.f; + const Vec3f weight_normal(is_painted[vi] ? 1.f : 0.f, active, 0.f); + const Vec2f uv = m_bump_preview_uses_vertex_uv ? vertex_uv[vi] : Vec2f::Zero(); + init_data.add_vertex(base.vertices[vi], weight_normal, uv); + } + for (const stl_triangle_vertex_indices &tri : base.indices) + init_data.add_triangle(unsigned(tri[0]), unsigned(tri[1]), unsigned(tri[2])); + + m_bump_preview_glmodel.init_from(std::move(init_data)); + // GLModel::render() unconditionally re-sets the shader's "uniform_color" from this internal + // color field right before drawing (see GLModel.cpp) - setting the uniform manually in + // render_bump_preview_mesh() would just get overwritten by it, so it must be set here instead. + // GLModel::Geometry defaults to BLACK, which is exactly what showed up before this was added. + m_bump_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); + + // The mesh now reflects the islands' current placement, so any live drag delta is measured from + // here: reset it to identity and record the dragged island's baked transform. + m_bump_island_delta = Eigen::Matrix::Identity(); + const TextureDisplacementLayer *al = active_layer(); + if (m_bump_active_chart >= 0 && al != nullptr) { + const std::vector> xf = uv_editor_island_transforms(*al); + m_bump_baked_active_xf = (size_t(m_bump_active_chart) < xf.size()) ? xf[size_t(m_bump_active_chart)] + : Eigen::Matrix::Identity(); + } else { + m_bump_baked_active_xf = Eigen::Matrix::Identity(); + } +} + +void GLGizmoTextureDisplacement::compute_bump_active_vertices(const std::vector &charts) +{ + m_bump_active_vertex.clear(); + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || charts.empty()) + return; + const PatchUnwrap &u = m_uv_editor_unwrap; + m_bump_active_vertex.assign(mv->mesh().its.vertices.size(), 0); + // Flag the base vertices of every chart being moved. For a group/multi move that is more than one + // chart, but since such a move is a pure translation the shader applies the same delta to them all + // (see on_island_edited) -- exactly the "joined islands move together" behaviour. + for (size_t i = 0; i < u.uvs.size(); ++i) { + if (i >= u.vertex_chart.size() || + std::find(charts.begin(), charts.end(), u.vertex_chart[i]) == charts.end()) + continue; + const int sv = (i < u.source_vertex.size()) ? u.source_vertex[i] : -1; + if (sv >= 0 && size_t(sv) < m_bump_active_vertex.size()) + m_bump_active_vertex[size_t(sv)] = 1; + } +} + +int GLGizmoTextureDisplacement::island_group_of(const std::vector &groups, int c) +{ + return (c >= 0 && size_t(c) < groups.size() && groups[size_t(c)] >= 0) ? groups[size_t(c)] : c; +} + +void GLGizmoTextureDisplacement::join_island_groups(std::vector &groups, int a, int b, int chart_count) +{ + if (a < 0 || b < 0 || chart_count <= 0) + return; + // Materialise to a full explicit table first, so singletons (which were implicit) get a concrete id + // that the relabel loop below can match on. + if (int(groups.size()) < chart_count) { + const size_t old = groups.size(); + groups.resize(size_t(chart_count)); + for (size_t i = old; i < groups.size(); ++i) + groups[i] = int(i); + } + for (size_t i = 0; i < groups.size(); ++i) + if (groups[i] < 0) + groups[i] = int(i); + const int ga = groups[size_t(a)], gb = groups[size_t(b)]; + if (ga == gb) + return; + const int g = std::min(ga, gb); + for (int &x : groups) + if (x == ga || x == gb) + x = g; +} + +std::vector GLGizmoTextureDisplacement::build_island_move_set(const TextureDisplacementLayer &layer, int primary) const +{ + std::vector set; + const int chart_count = std::max(m_uv_editor_unwrap.chart_count, 0); + + // Seed with the pane's multi-selection (falling back to just the primary if the canvas has none). + std::vector seeds; + if (const UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas()) + seeds = canvas->selected_islands(); + if (seeds.empty() && primary >= 0) + seeds.push_back(primary); + + const auto add = [&set](int c) { + if (c >= 0 && std::find(set.begin(), set.end(), c) == set.end()) + set.push_back(c); + }; + for (int s : seeds) { + add(s); + // Pull in every chart sharing s's join group, so a joined pair moves as one. + const int gs = island_group_of(layer.island_groups, s); + for (int c = 0; c < chart_count; ++c) + if (island_group_of(layer.island_groups, c) == gs) + add(c); + } + add(primary); // never leave the primary out, whatever the selection state + return set; +} + +void GLGizmoTextureDisplacement::render_bump_preview_mesh() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr || !m_bump_preview_glmodel.is_initialized()) + return; + + const TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr || layer->empty()) + return; + + // Reuses the layer-list panel's already-decoded, already-uploaded GPU thumbnail (smoothing-aware), + // whose grayscale value lives in the R channel exactly as the shader samples it. Its width/height + // are read straight off the texture -- decoding the PNG here every frame would re-run the smoothing + // blur on every camera move, which is what tanked the frame rate at high smoothing. + GLTexture *tex = get_layer_thumbnail(*layer); + if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0) + return; + + GLShaderProgram *shader = wxGetApp().get_shader("texture_displacement_bump"); + if (shader == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("volume_world_matrix", trafo_matrix); + const ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); + shader->set_uniform("clipping_plane", clp_data.clp_dataf); + shader->set_uniform("z_range", clp_data.z_range); + const Matrix3d view_normal_matrix = + camera.get_view_matrix().matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, tex->get_id())); + shader->set_uniform("height_tex", 0); + shader->set_uniform("height_tex_texel", Vec2f(1.f / float(tex->get_width()), 1.f / float(tex->get_height()))); + shader->set_uniform("depth_mm", layer->depth_mm); + shader->set_uniform("tiling_scale", layer->tiling_scale); + shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f); + shader->set_uniform("uv_offset", layer->offset); + shader->set_uniform("invert", layer->invert); + // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than + // projecting; see rebuild_bump_preview_mesh(). + shader->set_uniform("use_vertex_uv", m_bump_preview_uses_vertex_uv); + // The live UV-editor island drag rides this 2x3 affine (identity except mid-drag); only the flagged + // island's vertices apply it, so a drag is a uniform update rather than a mesh rebuild. + const Eigen::Matrix &d = m_bump_island_delta; + shader->set_uniform("island_delta_lin", std::array{ d(0, 0), d(0, 1), d(1, 0), d(1, 1) }); + shader->set_uniform("island_delta_tr", Vec2f(d(0, 2), d(1, 2))); + m_bump_preview_glmodel.render(); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + shader->stop_using(); +} + +void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() +{ + m_uvcheck_glmodel.reset(); + if (m_uv_check_mode == UVCheckMode::None) + return; + + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || m_triangle_selectors.empty()) + return; + const indexed_triangle_set patch = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER); + if (patch.indices.empty()) + return; + const indexed_triangle_set &base = mv->mesh().its; + if (base.vertices.size() != patch.vertices.size()) + return; + const TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr) + return; + + // The checker samples wherever the projection puts it; the projections the shader can't + // reconstruct (LSCM, ViewProjected) get a precomputed per-vertex uv, the rest project in-shader. + std::vector uv = compute_layer_vertex_uvs(patch, *layer); + const bool have_uvs = uv.size() == base.vertices.size(); + m_uvcheck_uses_vertex_uv = have_uvs; + + // Per-vertex area distortion in [0,1] (0.5 == ideal), only when both requested and possible. + std::vector distortion(base.vertices.size(), 0.5f); + if (m_uv_check_mode == UVCheckMode::Distortion && have_uvs) { + std::vector tri_log(patch.indices.size(), 0.f); + for (size_t f = 0; f < patch.indices.size(); ++f) { + const stl_triangle_vertex_indices &t = patch.indices[f]; + const float a3 = 0.5f * (base.vertices[t[1]] - base.vertices[t[0]]).cross(base.vertices[t[2]] - base.vertices[t[0]]).norm(); + const Vec2f e0 = uv[t[1]] - uv[t[0]]; + const Vec2f e1 = uv[t[2]] - uv[t[0]]; + const float a2 = 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x()); + tri_log[f] = (a3 > 1e-12f && a2 > 1e-12f) ? std::log2(a2 / a3) : 0.f; + } + // Centre the heatmap on the patch's own median stretch, so a globally-scaled unwrap reads as + // uniformly "ideal" and only *relative* stretching (the thing that matters) shows up as colour. + std::vector sorted = tri_log; + float median = 0.f; + if (!sorted.empty()) { + std::nth_element(sorted.begin(), sorted.begin() + sorted.size() / 2, sorted.end()); + median = sorted[sorted.size() / 2]; + } + std::vector sum(base.vertices.size(), 0.f); + std::vector cnt(base.vertices.size(), 0); + for (size_t f = 0; f < patch.indices.size(); ++f) { + // +/- 2 stops (4x stretch either way) spans the full blue->red range. + const float d = std::clamp(0.5f + (tri_log[f] - median) / 4.f, 0.f, 1.f); + for (int k = 0; k < 3; ++k) { + sum[patch.indices[f][k]] += d; + ++cnt[patch.indices[f][k]]; + } + } + for (size_t v = 0; v < distortion.size(); ++v) + if (cnt[v] > 0) + distortion[v] = sum[v] / float(cnt[v]); + } + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; + init_data.reserve_vertices(base.vertices.size()); + init_data.reserve_indices(patch.indices.size() * 3); + for (size_t vi = 0; vi < base.vertices.size(); ++vi) + init_data.add_vertex(base.vertices[vi], Vec3f(distortion[vi], 0.f, 0.f), + have_uvs ? uv[vi] : Vec2f::Zero()); + for (const stl_triangle_vertex_indices &tri : patch.indices) + init_data.add_triangle(unsigned(tri[0]), unsigned(tri[1]), unsigned(tri[2])); + + m_uvcheck_glmodel.init_from(std::move(init_data)); +} + +void GLGizmoTextureDisplacement::render_uvcheck_mesh() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr || !m_uvcheck_glmodel.is_initialized()) + return; + const TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr) + return; + GLShaderProgram *shader = wxGetApp().get_shader("texture_displacement_uvcheck"); + if (shader == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("volume_world_matrix", trafo_matrix); + const ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); + shader->set_uniform("clipping_plane", clp_data.clp_dataf); + shader->set_uniform("z_range", clp_data.z_range); + const Matrix3d view_normal_matrix = + camera.get_view_matrix().matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0); + shader->set_uniform("mode", m_uv_check_mode == UVCheckMode::Distortion ? 1 : 0); + shader->set_uniform("checker_freq", 4.f); // squares per texture tile + shader->set_uniform("tiling_scale", layer->tiling_scale); + shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f); + shader->set_uniform("uv_offset", layer->offset); + shader->set_uniform("use_vertex_uv", m_uvcheck_uses_vertex_uv); + + // Coincident with the base surface, so pull it toward the camera to win the depth test. + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(-1.0f, -1.0f)); + m_uvcheck_glmodel.render(); + glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); + shader->stop_using(); +} + +void GLGizmoTextureDisplacement::build_wireframe_from_its(const indexed_triangle_set &its) +{ + m_wireframe_overlay_glmodel.reset(); + m_wireframe_overlay_vcount = its.vertices.size(); + if (its.indices.empty()) + return; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(its.vertices.size()); + init_data.reserve_indices(its.indices.size() * 6); + for (const Vec3f &v : its.vertices) + init_data.add_vertex(v); + // One segment per triangle edge; shared edges drawn twice, harmless for a wireframe and far + // cheaper than deduplicating a million of them. + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) + init_data.add_line(unsigned(tri[i]), unsigned(tri[(i + 1) % 3])); + + if (!init_data.is_empty()) + m_wireframe_overlay_glmodel.init_from(std::move(init_data)); +} + +void GLGizmoTextureDisplacement::rebuild_wireframe_overlay() +{ + if (!m_wireframe_overlay) { + m_wireframe_overlay_glmodel.reset(); + m_wireframe_overlay_vcount = 0; + return; + } + const ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return; + const indexed_triangle_set &its = mv->mesh().its; + if (its.indices.empty()) + return; + + // Building from the base mesh (bump/paint mode); its topology only changes on bake/subdivide, and + // this runs on every rebuild_preview(), so rebuild only when the vertex count actually changes. + if (m_wireframe_overlay_glmodel.is_initialized() && m_wireframe_overlay_vcount == its.vertices.size()) + return; + build_wireframe_from_its(its); +} + +void GLGizmoTextureDisplacement::refresh_wireframe() +{ + if (!m_wireframe_overlay) { + m_wireframe_overlay_glmodel.reset(); + m_wireframe_overlay_vcount = 0; + return; + } + // The wireframe has to sit on whatever mesh is actually on screen. In the true-displacement view + // that is the raised preview geometry (m_preview_its) - drawing the flat base mesh's edges there + // leaves them buried inside the bumps, which is why the wireframe "didn't show in real mode". In + // Fast (bump) mode or with nothing painted, the surface is the undisplaced base mesh. + if (!m_use_bump_preview && !m_preview_its.indices.empty()) + build_wireframe_from_its(m_preview_its); + else + rebuild_wireframe_overlay(); +} + +void GLGizmoTextureDisplacement::render_wireframe_overlay() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr || !m_wireframe_overlay_glmodel.is_initialized()) + return; + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + // Pull the lines toward the camera so they sit on the surface rather than z-fighting into it. + glsafe(::glEnable(GL_POLYGON_OFFSET_LINE)); + glsafe(::glPolygonOffset(-1.0f, -1.0f)); + m_wireframe_overlay_glmodel.set_color(ColorRGBA(1.0f, 1.0f, 1.0f, 0.6f)); // white, so it reads on any material + m_wireframe_overlay_glmodel.render(); + glsafe(::glDisable(GL_POLYGON_OFFSET_LINE)); + shader->stop_using(); +} + +void GLGizmoTextureDisplacement::rebuild_preview() +{ + // Bumped first: any in-flight job's result (captured generation from before this call) will + // now compare unequal to m_preview_generation and be discarded when it completes, even if it + // finishes after the job queued below. + const uint64_t generation = ++m_preview_generation; + update_uv_editor(); + rebuild_bump_preview_mesh(); + rebuild_uvcheck_mesh(); + rebuild_seam_overlay(); + + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || !mv->is_texture_displacement_painted()) { + m_preview_glmodel.reset(); + m_preview_its = indexed_triangle_set{}; // no displaced mesh; wireframe falls back to the base + refresh_wireframe(); + return; + } + // In Fast/paint modes the wireframe follows the base mesh and can be built now; the true-displacement + // view's wireframe needs the displaced mesh, which only exists once the job below completes. + if (m_use_bump_preview) + refresh_wireframe(); + + TextureDisplacementPreviewInput input; + input.base_mesh = mv->mesh().its; + input.layers = mv->texture_displacement_layers; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + input.facets_data[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::make_unique(std::move(input), generation, + [this](indexed_triangle_set its, uint64_t result_generation) { + if (result_generation != m_preview_generation) + return; // superseded by a newer edit while this was computing + m_preview_glmodel.reset(); + if (!its.indices.empty()) { + m_preview_glmodel.init_from(its); + m_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); + } + // Keep the displaced mesh so the wireframe overlay can be drawn on it (the true-displacement + // view), then refresh the wireframe from it. + m_preview_its = std::move(its); + refresh_wireframe(); + m_parent.set_as_dirty(); + })); +} + +void GLGizmoTextureDisplacement::update_uv_editor() +{ + Plater *plater = wxGetApp().plater(); + UVEditorCanvas *uv_canvas = plater->get_uv_editor_canvas(); + if (uv_canvas == nullptr) + return; + + const ModelVolume *mv = texture_volume(); + TextureDisplacementLayer *layer = active_layer(); + // The pane is opened only on the user's explicit request (m_show_uv_editor), and only for an LSCM + // layer - never automatically just because something is painted. Keep the cached state/unwrap so + // that switching the toggle back on re-shows instantly (and re-solves if the paint changed while + // it was hidden, via the state comparison below). + if (!m_show_uv_editor || mv == nullptr || layer == nullptr || + layer->projection_method != TextureProjectionMethod::LSCM) { + plater->show_uv_editor(false); + return; + } + + // A vertex/edge edit committing (or an undo reverting one) changes the per-vertex UV overrides + // without going through the Unwrap button. Detect that and force a re-solve, so the pane's geometry + // stays in step with what will bake -- the one exception to "only re-solve on Unwrap". + { + size_t sig = 1469598103934665603ull; // FNV-1a seed + const auto mix = [&sig](uint64_t x) { sig = (sig ^ x) * 1099511628211ull; }; + mix(layer->lscm_uv_overrides.size()); + for (const auto &[v, uv] : layer->lscm_uv_overrides) { + mix(uint64_t(uint32_t(v))); + mix(uint64_t(uint32_t(int32_t(std::llround(uv.x() * 1024.f))))); + mix(uint64_t(uint32_t(int32_t(std::llround(uv.y() * 1024.f))))); + } + if (sig != m_uv_overrides_sig) { + m_uv_overrides_sig = sig; + m_uv_unwrap_pending = true; + } + } + + UVEditorState state; + state.slot = m_active_layer_slot; + state.image_data = layer->image_data.get(); + state.seam_angle = layer->lscm_seam_angle_deg; + state.padding = layer->island_padding_mm; + state.facets = mv->texture_displacement_facet(m_active_layer_slot).get_data(); + state.seam_edges = layer->lscm_seam_edges; + + // The re-solve happens only when the user pressed "Unwrap" (m_uv_unwrap_pending). Every other call + // into here -- a paint stroke ending, a slider release, the check mode changing -- must not pay for + // a fresh LSCM solve; it just re-applies the cheap affine transforms over whatever unwrap already + // exists. If the paint changed underneath but the user hasn't asked to re-unwrap, the pane keeps + // showing the last unwrap on purpose (that is the whole point of making it an explicit action). + bool unwrap_changed = false; + if (m_uv_unwrap_pending) { + m_uv_unwrap_pending = false; + const indexed_triangle_set patch = extract_painted_patch(mv->mesh().its, state.facets); + if (patch.indices.empty()) { + m_uv_editor_state = UVEditorState{}; + m_uv_editor_unwrap = PatchUnwrap{}; + m_uv_editor_distortion_colors.clear(); + plater->show_uv_editor(false); + return; + } + // Padding disabled (0): the user asked to pack islands with no gap between them. + m_uv_editor_unwrap = compute_patch_unwrap(patch, layer->lscm_seam_angle_deg, 0.f, layer->lscm_seam_edges); + // Re-apply any stored per-vertex UV edits onto the fresh unwrap, so the pane shows exactly what + // compute_lscm_uvs() will bake (which applies the same overrides). Keyed by mesh vertex, so every + // unwrapped copy of that vertex gets it -- matching the bake's single-UV-per-vertex settle. + if (!layer->lscm_uv_overrides.empty()) { + std::map ov; + for (const auto &[mv2, uv] : layer->lscm_uv_overrides) + ov[mv2] = uv; + for (size_t i = 0; i < m_uv_editor_unwrap.uvs.size(); ++i) { + const int sv = (i < m_uv_editor_unwrap.source_vertex.size()) ? m_uv_editor_unwrap.source_vertex[i] : -1; + const auto it = ov.find(sv); + if (it != ov.end()) + m_uv_editor_unwrap.uvs[i] = it->second; + } + } + m_uv_editor_state = std::move(state); + unwrap_changed = true; + // Precompute the distortion heatmap now, while the patch is in hand - relative stretch doesn't + // change when islands are only moved, so this need not be redone on a drag. It is fed to the + // canvas below only while the Distortion check mode is on. + compute_uv_editor_distortion_colors(patch); + } + + if (m_uv_editor_unwrap.empty()) { + // Nothing has been unwrapped yet (or the paint was cleared): keep the pane hidden until the user + // presses Unwrap. The panel shows a "Press Unwrap" hint in this state. + plater->show_uv_editor(false); + return; + } + + // The pane background either mirrors the height texture (default) or shows a UV checker (#7), and is + // only re-uploaded when that choice, or the unwrap, actually changes - the height image is large. + const UVBackground desired_bg = (m_uv_check_mode == UVCheckMode::Checker) ? UVBackground::Checker : UVBackground::Height; + const bool bg_smoothing_changed = (desired_bg == UVBackground::Height) && (m_uv_editor_bg_smoothing != layer->smoothing); + if (unwrap_changed || desired_bg != m_uv_editor_bg || bg_smoothing_changed) { + m_uv_editor_bg_smoothing = layer->smoothing; + if (desired_bg == UVBackground::Checker) { + // An even squares-per-axis count so the pattern tiles seamlessly across the UV unit + // boundary (texcoord == position repeats it once per tile). Softened grays, not pure + // black/white, so it doesn't fight the island wires drawn over it. + constexpr int tex = 512, squares = 8, cell = tex / squares; + std::vector checker(size_t(tex) * size_t(tex)); + for (int y = 0; y < tex; ++y) + for (int x = 0; x < tex; ++x) + checker[size_t(y) * tex + x] = ((x / cell + y / cell) & 1) ? 205 : 70; + uv_canvas->set_background_texture(checker, tex, tex); + } else { + const DecodedHeightTexture height = decode_height_texture(*layer); + if (!height.empty()) + uv_canvas->set_background_texture(height.pixels, height.width, height.height); + else + uv_canvas->set_background_texture({}, 0, 0); + } + m_uv_editor_bg = desired_bg; + } + + // Grow (never shrink) the layer's island list to cover every chart. Shrinking would throw away a + // hand placement the moment a stroke temporarily merged two islands, and a stale extra entry is + // harmless - island_transform_matrix() only looks up the charts that actually exist. + if (layer->islands.size() < size_t(m_uv_editor_unwrap.chart_count)) + layer->islands.resize(size_t(m_uv_editor_unwrap.chart_count)); + + // Connected-net layout (on by default): a *fresh* unwrap is unfolded so adjacent charts sit + // edge-to-edge (cube -> a net), rather than as separately packed squares. Only when the user pressed + // Unwrap (m_uv_apply_connected_net) -- a re-segmentation renumbers charts anyway, so any hand + // placement from before is already meaningless. A *refresh* re-solve (a committed vertex edit, or an + // undo) must NOT relayout, or it would throw away every island placement on every vertex edit. + if (unwrap_changed && m_uv_apply_connected_net && layer->auto_connect_islands) { + std::vector net = compute_connected_net(m_uv_editor_unwrap); + if (net.size() == size_t(m_uv_editor_unwrap.chart_count)) { + if (layer->islands.size() < net.size()) + layer->islands.resize(net.size()); + for (size_t i = 0; i < net.size(); ++i) + layer->islands[i] = net[i]; + } + } + if (unwrap_changed) + m_uv_apply_connected_net = false; // consumed; a refresh re-solve leaves placements alone + + // The geometry goes over in the unwrap's *raw* mm coordinates and is only re-uploaded when the + // unwrap itself changed. Everything a slider or a drag can touch - island placement, tiling, + // rotation, offset - is an affine map on top of that, so it goes over as one 2x3 matrix per + // island instead. That is the whole reason dragging an island is now free: a patch of a million + // triangles has a million UVs to re-transform and re-upload otherwise, and it was doing exactly + // that on every single mouse-move event. + if (unwrap_changed) { + UVEditorCanvas::Islands view; + view.uvs = m_uv_editor_unwrap.uvs; + view.indices = m_uv_editor_unwrap.indices; + view.vertex_island = m_uv_editor_unwrap.vertex_chart; + view.boundary_edges = m_uv_editor_unwrap.boundary_edges; + view.island_count = m_uv_editor_unwrap.chart_count; + + uv_canvas->set_island_edit_callback( + [this](int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished) { + on_island_edited(island, offset_delta, rotation_delta, scale_factor, finished); + }); + uv_canvas->set_vertex_edit_callback( + [this](const std::vector> &edits) { on_uv_vertex_edited(edits); }); + uv_canvas->set_command_callback([this](UVEditorCanvas::Command cmd) { on_uv_command(int(cmd)); }); + uv_canvas->set_islands(std::move(view)); + } + + uv_canvas->set_select_mode(static_cast(m_uv_select_mode)); + + uv_canvas->set_uv_transform(layer->tiling_scale, layer->rotation_deg, layer->tile_enabled, + layer->tile_method == TextureTileMethod::MirroredRepeat); + uv_canvas->set_island_transforms(uv_editor_island_transforms(*layer)); + // The distortion heatmap tints the island fills only while its check mode is on; otherwise the + // canvas falls back to its default light-green wash. + if (m_uv_check_mode == UVCheckMode::Distortion) + uv_canvas->set_island_fill_colors(m_uv_editor_distortion_colors); + else + uv_canvas->set_island_fill_colors({}); + + plater->show_uv_editor(true); +} + +void GLGizmoTextureDisplacement::compute_uv_editor_distortion_colors(const indexed_triangle_set &patch) +{ + m_uv_editor_distortion_colors.clear(); + const PatchUnwrap &u = m_uv_editor_unwrap; + if (u.empty() || u.chart_count <= 0) + return; + + // log2(uv area / 3D area) per unwrap triangle - the same measure the 3D distortion overlay uses. + std::vector tri_log(u.indices.size(), 0.f); + std::vector tri_chart(u.indices.size(), -1); + for (size_t f = 0; f < u.indices.size(); ++f) { + const stl_triangle_vertex_indices &t = u.indices[f]; + if (t[0] < 0 || size_t(t[0]) >= u.vertex_chart.size()) + continue; + tri_chart[f] = u.vertex_chart[size_t(t[0])]; + const auto p3 = [&](int uv_idx) -> Vec3f { + const int sv = (size_t(uv_idx) < u.source_vertex.size()) ? u.source_vertex[size_t(uv_idx)] : -1; + return (sv >= 0 && size_t(sv) < patch.vertices.size()) ? patch.vertices[size_t(sv)] : Vec3f::Zero(); + }; + const float a3 = 0.5f * (p3(t[1]) - p3(t[0])).cross(p3(t[2]) - p3(t[0])).norm(); + const Vec2f e0 = u.uvs[size_t(t[1])] - u.uvs[size_t(t[0])]; + const Vec2f e1 = u.uvs[size_t(t[2])] - u.uvs[size_t(t[0])]; + const float a2 = 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x()); + tri_log[f] = (a3 > 1e-12f && a2 > 1e-12f) ? std::log2(a2 / a3) : 0.f; + } + + // Centre on the median stretch, so a globally scaled unwrap reads as uniform and only *relative* + // stretching shows up - matching the 3D overlay's convention. + std::vector sorted = tri_log; + float median = 0.f; + if (!sorted.empty()) { + std::nth_element(sorted.begin(), sorted.begin() + sorted.size() / 2, sorted.end()); + median = sorted[sorted.size() / 2]; + } + + std::vector chart_sum(size_t(u.chart_count), 0.0); + std::vector chart_cnt(size_t(u.chart_count), 0); + for (size_t f = 0; f < tri_log.size(); ++f) { + const int c = tri_chart[f]; + if (c >= 0 && c < u.chart_count) { + chart_sum[size_t(c)] += tri_log[f]; + ++chart_cnt[size_t(c)]; + } + } + + // Blue (compressed) -> green (ideal) -> red (stretched), +/- 2 stops spanning the full range. + const auto heat = [](float t) -> ColorRGBA { + t = std::clamp(t, 0.f, 1.f); + const ColorRGBA blue{ 0.15f, 0.35f, 1.0f, 0.5f }, green{ 0.2f, 0.9f, 0.3f, 0.5f }, red{ 1.0f, 0.2f, 0.15f, 0.5f }; + if (t < 0.5f) { const float s = t * 2.f; return blue * (1.f - s) + green * s; } + const float s = (t - 0.5f) * 2.f; return green * (1.f - s) + red * s; + }; + + m_uv_editor_distortion_colors.resize(size_t(u.chart_count), heat(0.5f)); + for (int c = 0; c < u.chart_count; ++c) + if (chart_cnt[size_t(c)] > 0) { + const float avg = float(chart_sum[size_t(c)] / chart_cnt[size_t(c)]); + m_uv_editor_distortion_colors[size_t(c)] = heat(std::clamp(0.5f + (avg - median) / 4.f, 0.f, 1.f)); + } +} + +void GLGizmoTextureDisplacement::on_uv_command(int cmd) +{ + TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr) + return; + + if (cmd == int(UVEditorCanvas::Command::AverageScale)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Average island scale"), UndoRedo::SnapshotType::GizmoAction); + average_island_scales(layer->islands); + rebuild_preview(); + } else if (cmd == int(UVEditorCanvas::Command::CutSelectedIsland)) { + UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas(); + const int chart = canvas != nullptr ? canvas->selected_island() : -1; + if (chart < 0) { + show_error(nullptr, _u8L("Select an island in the UV editor first.")); + return; + } + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Cut texture island"), UndoRedo::SnapshotType::GizmoAction); + cut_island(*layer, chart); + rebuild_preview(); + } else if (cmd == int(UVEditorCanvas::Command::JoinSelected)) { + UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas(); + const int chart = canvas != nullptr ? canvas->selected_island() : -1; + if (chart < 0) { + show_error(nullptr, _u8L("Select an island in the UV editor first.")); + return; + } + // Join to whichever neighbouring island (one it shares an edge with) is currently placed + // nearest -- i.e. the one it was dragged up against. + const Eigen::Matrix sel_m = island_transform_matrix(chart, m_uv_editor_unwrap, layer->islands); + const Vec2f sel_c = sel_m.block<2, 2>(0, 0) * m_uv_editor_unwrap.chart_centroid[size_t(chart)] + sel_m.col(2); + int best_parent = -1; + float best_d2 = std::numeric_limits::max(); + TextureIsland best_place, cand; + for (int p = 0; p < m_uv_editor_unwrap.chart_count; ++p) { + if (p == chart) + continue; + if (!join_chart_placement(m_uv_editor_unwrap, layer->islands, chart, p, cand)) + continue; // not a neighbour + const Eigen::Matrix pm = island_transform_matrix(p, m_uv_editor_unwrap, layer->islands); + const Vec2f pc = pm.block<2, 2>(0, 0) * m_uv_editor_unwrap.chart_centroid[size_t(p)] + pm.col(2); + const float d2 = (pc - sel_c).squaredNorm(); + if (d2 < best_d2) { best_d2 = d2; best_parent = p; best_place = cand; } + } + if (best_parent < 0) { + show_error(nullptr, _u8L("This island has no neighbour it shares an edge with.")); + return; + } + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Join texture island"), UndoRedo::SnapshotType::GizmoAction); + if (layer->islands.size() <= size_t(chart)) + layer->islands.resize(size_t(chart) + 1); + layer->islands[size_t(chart)] = best_place; + // Record the join so the two (and anything already grouped with either) move together from now + // on, not just visually snap once. + join_island_groups(layer->island_groups, chart, best_parent, m_uv_editor_unwrap.chart_count); + rebuild_preview(); + } else if (cmd == int(UVEditorCanvas::Command::UnjoinSelected)) { + UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas(); + const int chart = canvas != nullptr ? canvas->selected_island() : -1; + if (chart < 0 || size_t(chart) >= layer->islands.size()) { + show_error(nullptr, _u8L("Select an island in the UV editor first.")); + return; + } + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Unjoin texture island"), UndoRedo::SnapshotType::GizmoAction); + layer->islands[size_t(chart)] = TextureIsland{}; // back to its own packed position + // Break its join link too, so it stops moving with the others (the rest stay grouped). + if (size_t(chart) < layer->island_groups.size()) + layer->island_groups[size_t(chart)] = chart; + rebuild_preview(); + } + // FrameAll/ToggleSnap are handled inside the canvas; ProjectFromView is not wired yet. +} + +void GLGizmoTextureDisplacement::capture_view_projection(TextureDisplacementLayer &layer) +{ + const ModelVolume *mv = texture_volume(); + const ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) + return; + + const Camera &camera = wxGetApp().plater()->get_camera(); + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + + // The view matrix's rotation rows are the camera axes in world space; bring them into the + // volume's local frame (where the mesh vertices live) so the projector rides along with the part. + const Matrix3d view_rot = camera.get_view_matrix().matrix().block<3, 3>(0, 0); + const Matrix3d trafo_rot = trafo.matrix().block<3, 3>(0, 0); + const Matrix3d world_to_local = trafo_rot.inverse(); + const Vec3d local_right = world_to_local * Vec3d(view_rot.row(0).transpose()); + const Vec3d local_up = world_to_local * Vec3d(view_rot.row(1).transpose()); + + // Unit axes: the projected planar coordinate must stay in mm so tiling_scale keeps meaning mm. + layer.view_project_right = local_right.norm() > 1e-9 ? Vec3f(local_right.normalized().cast()) : Vec3f::UnitX(); + layer.view_project_up = local_up.norm() > 1e-9 ? Vec3f(local_up.normalized().cast()) : Vec3f::UnitY(); +} + +void GLGizmoTextureDisplacement::cut_island(TextureDisplacementLayer &layer, int chart) +{ + const ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return; + const std::vector &verts = mv->mesh().its.vertices; + const PatchUnwrap &u = m_uv_editor_unwrap; + + // Collect the chart's triangles back in mesh-vertex space, and its 3D bounding box. + std::vector> tris; + Vec3f lo(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); + Vec3f hi(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()); + for (const stl_triangle_vertex_indices &t : u.indices) { + if (t[0] < 0 || size_t(t[0]) >= u.vertex_chart.size() || u.vertex_chart[size_t(t[0])] != chart) + continue; + std::array bt{}; + bool ok = true; + for (int k = 0; k < 3; ++k) { + const int uvv = t[k]; + if (uvv < 0 || size_t(uvv) >= u.source_vertex.size()) { ok = false; break; } + const int base = u.source_vertex[size_t(uvv)]; + if (base < 0 || size_t(base) >= verts.size()) { ok = false; break; } + bt[k] = base; + lo = lo.cwiseMin(verts[size_t(base)]); + hi = hi.cwiseMax(verts[size_t(base)]); + } + if (ok) + tris.push_back(bt); + } + if (tris.empty()) + return; + + // Cut perpendicular to the longest axis, through the centroid - a long thin island is split + // across its narrow middle, which is exactly the "islands might be very long" case. + const Vec3f ext = hi - lo; + const int axis = (ext.x() >= ext.y() && ext.x() >= ext.z()) ? 0 : (ext.y() >= ext.z() ? 1 : 2); + const float mid = 0.5f * (lo[axis] + hi[axis]); + + std::set> seams(layer.lscm_seam_edges.begin(), layer.lscm_seam_edges.end()); + for (const std::array &bt : tris) + for (int i = 0; i < 3; ++i) { + const int a = bt[i], b = bt[(i + 1) % 3]; + // Endpoints on opposite sides of the plane -> this edge crosses it -> make it a seam. + if ((verts[size_t(a)][axis] < mid) != (verts[size_t(b)][axis] < mid)) + seams.insert({ std::min(a, b), std::max(a, b) }); + } + layer.lscm_seam_edges.assign(seams.begin(), seams.end()); +} + +// unwrap mm -> texture uv, per island: the island's own hand placement, then the layer's +// tiling/rotation/offset. Both are affine, so they compose into one matrix the canvas can hand +// straight to a shader. +std::vector> +GLGizmoTextureDisplacement::uv_editor_island_transforms(const TextureDisplacementLayer &layer) +{ + const float uv_scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f; + const float rad = layer.rotation_deg * float(M_PI) / 180.f; + const float cs = std::cos(rad) * uv_scale; + const float sn = std::sin(rad) * uv_scale; + + Eigen::Matrix2f uv_linear; + uv_linear << cs, -sn, + sn, cs; + + m_uv_editor_bbox_min = Vec2f(std::numeric_limits::max(), std::numeric_limits::max()); + m_uv_editor_bbox_max = Vec2f(std::numeric_limits::lowest(), std::numeric_limits::lowest()); + + std::vector> transforms(size_t(std::max(m_uv_editor_unwrap.chart_count, 0))); + for (int c = 0; c < m_uv_editor_unwrap.chart_count; ++c) { + const Eigen::Matrix island = island_transform_matrix(c, m_uv_editor_unwrap, layer.islands); + Eigen::Matrix &m = transforms[size_t(c)]; + m.block<2, 2>(0, 0) = uv_linear * island.block<2, 2>(0, 0); + m.col(2) = uv_linear * island.col(2) + layer.offset; + } + + // Only for the panel's readout; the canvas computes its own bounds. + for (size_t i = 0; i < m_uv_editor_unwrap.uvs.size(); ++i) { + const int c = m_uv_editor_unwrap.vertex_chart[i]; + if (c < 0 || size_t(c) >= transforms.size()) + continue; + const Vec2f uv = transforms[size_t(c)].block<2, 2>(0, 0) * m_uv_editor_unwrap.uvs[i] + transforms[size_t(c)].col(2); + m_uv_editor_bbox_min = m_uv_editor_bbox_min.cwiseMin(uv); + m_uv_editor_bbox_max = m_uv_editor_bbox_max.cwiseMax(uv); + } + return transforms; +} + +void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offset_delta, float rotation_delta, + float scale_factor, bool finished) +{ + TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr || island < 0) + return; + if (size_t(island) >= layer->islands.size()) + layer->islands.resize(size_t(island) + 1); + + // A pure translation (no rotation, no scale) is the only gesture that moves a whole group/selection + // together; rotate and scale stay on the primary island alone. This split is what makes flagging a + // group's worth of vertices on the GPU safe: the shared shader delta is a pure translation, so the + // same offset is correct for every flagged island. + const bool is_move = rotation_delta == 0.f && scale_factor == 1.f; + + if (!m_island_drag_active) { + // Taken before the first delta lands, so one undo reverts the whole drag rather than just + // its final mouse-move. + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Move texture island"), UndoRedo::SnapshotType::GizmoAction); + m_island_drag_active = true; + // Decide the moved set once, at drag start: the whole selection + join groups for a move, or + // just the primary for a rotate/scale. + m_island_move_set = is_move ? build_island_move_set(*layer, island) : std::vector{ island }; + // Set up the GPU drag: flag the moved islands' vertices and bake the mesh once (via the dirty + // flag). From then on the drag is a uniform update, no rebuild -- see render_bump_preview_mesh(). + m_bump_active_chart = island; + compute_bump_active_vertices(m_island_move_set); + m_bump_island_delta = Eigen::Matrix::Identity(); + m_bump_preview_dirty = true; + } + + // Apply the edit. A move goes to every island in the moved set (same offset -> they translate as + // one); a rotate/scale goes only to the primary, about its own centroid. + const std::vector single{ island }; + const std::vector &targets = (is_move && !m_island_move_set.empty()) ? m_island_move_set : single; + for (int c : targets) { + if (c < 0) + continue; + if (size_t(c) >= layer->islands.size()) + layer->islands.resize(size_t(c) + 1); + TextureIsland &target = layer->islands[size_t(c)]; + target.offset += offset_delta; + if (c == island) { + target.rotation_deg += rotation_delta; + // Guarded: a scale that reaches zero is unrecoverable (every subsequent factor multiplies it) + // and would collapse the island to a point -- exactly the failure this feature hit once already. + target.scale = std::clamp(target.scale * scale_factor, 0.001f, 1000.f); + } + } + + if (finished) { + m_island_drag_active = false; + m_bump_active_chart = -1; + m_bump_active_vertex.clear(); + m_island_move_set.clear(); + m_bump_island_delta = Eigen::Matrix::Identity(); + rebuild_preview(); // the real displaced geometry moved: recompute it once, at the end + } else { + const std::vector> xf = uv_editor_island_transforms(*layer); + if (UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas()) { + // Live feedback in the pane, and deliberately *just* the transforms: nothing about the + // unwrap changed, so none of the vertex buffers need touching. This is what makes a drag + // interactive on a patch with a million triangles. + uv_canvas->set_island_transforms(xf); + } + // Move the island on the model live through the shader's island_delta uniform -- no mesh + // rebuild. delta = F_current * F_baked^-1 in final-uv space (the bump mesh bakes F_baked; the + // shader applies delta to the flagged island's uv). The one rebuild that bakes the flags is + // scheduled at drag start above and consumed once per frame by render_painter_gizmo(). + if (m_use_bump_preview && m_bump_active_chart == island && size_t(island) < xf.size()) { + Eigen::Matrix3f cur = Eigen::Matrix3f::Identity(); + cur.topRows<2>() = xf[size_t(island)]; + Eigen::Matrix3f bak = Eigen::Matrix3f::Identity(); + bak.topRows<2>() = m_bump_baked_active_xf; + m_bump_island_delta = (cur * bak.inverse()).topRows<2>(); + m_parent.set_as_dirty(); + } + } +} + +void GLGizmoTextureDisplacement::on_uv_vertex_edited(const std::vector> &edits) +{ + TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr || edits.empty() || m_uv_editor_unwrap.empty()) + return; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Edit texture UV"), UndoRedo::SnapshotType::GizmoAction); + + for (const auto &[unwrapped, raw_uv] : edits) { + if (unwrapped < 0 || size_t(unwrapped) >= m_uv_editor_unwrap.source_vertex.size()) + continue; + // Keep the gizmo's own unwrap copy in step, so the pane and the bake agree without a re-solve. + if (size_t(unwrapped) < m_uv_editor_unwrap.uvs.size()) + m_uv_editor_unwrap.uvs[size_t(unwrapped)] = raw_uv; + + const int mesh_v = m_uv_editor_unwrap.source_vertex[size_t(unwrapped)]; + if (mesh_v < 0) + continue; + // Store (or update) the override for this mesh vertex. Small list, linear scan is fine. + auto it = std::find_if(layer->lscm_uv_overrides.begin(), layer->lscm_uv_overrides.end(), + [mesh_v](const std::pair &p) { return p.first == mesh_v; }); + if (it != layer->lscm_uv_overrides.end()) + it->second = raw_uv; + else + layer->lscm_uv_overrides.emplace_back(mesh_v, raw_uv); + } + + rebuild_preview(); // the baked displacement samples the moved uv now +} + +bool GLGizmoTextureDisplacement::update_adjust_anchor() +{ + const ModelVolume *mv = texture_volume(); + m_adjust_anchor_valid = mv != nullptr && + compute_layer_paint_anchor(mv->mesh().its, mv->texture_displacement_facet(m_active_layer_slot).get_data(), + m_adjust_anchor_pos, m_adjust_anchor_normal); + return m_adjust_anchor_valid; +} + +TextureDisplacementLayer *GLGizmoTextureDisplacement::active_layer() +{ + ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return nullptr; + for (TextureDisplacementLayer &l : mv->texture_displacement_layers) + if (l.slot == m_active_layer_slot) + return &l; + return nullptr; +} + +const TextureDisplacementLayer *GLGizmoTextureDisplacement::active_layer() const +{ + return const_cast(this)->active_layer(); +} + +Vec3f GLGizmoTextureDisplacement::adjust_plane_point() const +{ + const ModelVolume *mv = texture_volume(); + const float bbox_size = (mv != nullptr) ? float(mv->mesh().bounding_box().size().norm()) : 1.f; + const float lift = bbox_size * 0.01f + 0.2f; // clear of the surface, to avoid z-fighting + return m_adjust_anchor_pos + m_adjust_anchor_normal * lift; +} + +Vec3f GLGizmoTextureDisplacement::adjust_handle_center(const TextureDisplacementLayer &layer) const +{ + // apply_uv_transform() maps a planar mm coordinate p to uv = R(p / tiling_scale) + offset, and + // on_mouse_adjust_texture() drives offset by offset = offset_start - R(delta / tiling_scale). + // Inverting that, the handle's displacement from the anchor is - R^-1(offset) * tiling_scale -- + // which, substituted into the drag equation, moves the handle by exactly `delta`. So the handle + // follows the cursor precisely, and is back on the anchor exactly when offset is zero. + const float rad = layer.rotation_deg * float(M_PI) / 180.f; + const float cs = std::cos(rad), sn = std::sin(rad); + const Vec2f unrotated(layer.offset.x() * cs + layer.offset.y() * sn, -layer.offset.x() * sn + layer.offset.y() * cs); + const Vec2f planar = -unrotated * layer.tiling_scale; + + Vec3f u_axis, v_axis; + adjust_tangent_basis(u_axis, v_axis); + return adjust_plane_point() + u_axis * planar.x() + v_axis * planar.y(); +} + +void GLGizmoTextureDisplacement::adjust_tangent_basis(Vec3f &u_axis, Vec3f &v_axis) const +{ + // Mirrors project_planar()'s own dominant-axis choice exactly, so the ring's "0 degrees" and + // the offset handle's plane always agree with what project_texture_displacement_uv() does. + const Vec3f n = m_adjust_anchor_normal.cwiseAbs(); + if (n.x() >= n.y() && n.x() >= n.z()) { + u_axis = Vec3f::UnitY(); + v_axis = Vec3f::UnitZ(); + } else if (n.y() >= n.x() && n.y() >= n.z()) { + u_axis = Vec3f::UnitX(); + v_axis = Vec3f::UnitZ(); + } else { + u_axis = Vec3f::UnitX(); + v_axis = Vec3f::UnitY(); + } +} + +void GLGizmoTextureDisplacement::render_adjust_texture_gizmo() +{ + if (!m_adjust_anchor_valid) + return; + + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + const TextureDisplacementLayer *layer = active_layer(); + if (mo == nullptr || mv == nullptr || layer == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + + // Handle sizes scale with the volume so they stay usable on both tiny and huge models. + const float bbox_size = float(mv->mesh().bounding_box().size().norm()); + const float panel_half = bbox_size * 0.04f + 1.f; + const float arrow_length = panel_half * 2.2f; + + // Tracks the layer's offset, so the handle actually travels with the texture as it is dragged. + const Vec3f handle_center_local = adjust_handle_center(*layer); + + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + shader->start_using(); + + const Camera &camera = wxGetApp().plater()->get_camera(); + + Vec3f u_axis, v_axis; + adjust_tangent_basis(u_axis, v_axis); + Transform3d plane_transform = Transform3d::Identity(); + plane_transform.linear().col(0) = u_axis.cast(); + plane_transform.linear().col(1) = v_axis.cast(); + plane_transform.linear().col(2) = m_adjust_anchor_normal.cast(); + plane_transform.translation() = handle_center_local.cast(); + + // Pan panel: a flat square lying in the patch's own tangent plane. Dragging anywhere on it + // moves the texture freely along both axes at once. + if (!m_adjust_panel_glmodel.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + init_data.add_vertex(Vec3f(-1.f, -1.f, 0.f)); + init_data.add_vertex(Vec3f(1.f, -1.f, 0.f)); + init_data.add_vertex(Vec3f(1.f, 1.f, 0.f)); + init_data.add_vertex(Vec3f(-1.f, 1.f, 0.f)); + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(0, 2, 3); + m_adjust_panel_glmodel.init_from(std::move(init_data)); + } + Transform3d view_model_matrix = camera.get_view_matrix() * trafo_matrix * plane_transform * + Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(panel_half, panel_half, panel_half)); + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + ColorRGBA panel_color = m_adjust_drag_handle == AdjustHandle::Pan ? ColorRGBA::YELLOW() : ColorRGBA::ORANGE(); + panel_color.a(0.45f); + m_adjust_panel_glmodel.set_color(panel_color); + m_adjust_panel_glmodel.render(); + + // Axis arrows: a shaft plus a small V-shaped arrowhead, both along local +X. Reused for both + // the U and V axes below by swapping which world direction local +X is transformed to. + if (!m_adjust_arrow_glmodel.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + init_data.add_vertex(Vec3f(0.f, 0.f, 0.f)); + init_data.add_vertex(Vec3f(1.f, 0.f, 0.f)); + init_data.add_vertex(Vec3f(0.82f, 0.08f, 0.f)); + init_data.add_vertex(Vec3f(0.82f, -0.08f, 0.f)); + init_data.add_line(0, 1); + init_data.add_line(1, 2); + init_data.add_line(1, 3); + m_adjust_arrow_glmodel.init_from(std::move(init_data)); + } +#if !SLIC3R_OPENGL_ES + if (!OpenGLManager::get_gl_info().is_core_profile()) + glsafe(::glLineWidth(2.0f)); +#endif // !SLIC3R_OPENGL_ES + + auto render_arrow = [&](const Vec3f &axis, const Vec3f &other_axis, bool is_active) { + Transform3d arrow_transform = Transform3d::Identity(); + arrow_transform.linear().col(0) = axis.cast(); + arrow_transform.linear().col(1) = other_axis.cast(); + arrow_transform.linear().col(2) = m_adjust_anchor_normal.cast(); + arrow_transform.translation() = handle_center_local.cast(); + + const Transform3d vmm = camera.get_view_matrix() * trafo_matrix * arrow_transform * + Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(arrow_length, arrow_length, arrow_length)); + shader->set_uniform("view_model_matrix", vmm); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + m_adjust_arrow_glmodel.set_color(is_active ? ColorRGBA::YELLOW() : ColorRGBA::ORANGE()); + m_adjust_arrow_glmodel.render(); + }; + render_arrow(u_axis, v_axis, m_adjust_drag_handle == AdjustHandle::AxisU); + render_arrow(v_axis, u_axis, m_adjust_drag_handle == AdjustHandle::AxisV); + + shader->stop_using(); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); +} + +bool GLGizmoTextureDisplacement::on_mouse_adjust_texture(const wxMouseEvent &mouse_event) +{ + if (!m_adjust_anchor_valid) + return false; + + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + TextureDisplacementLayer *layer = active_layer(); + if (mv == nullptr || mo == nullptr || layer == nullptr) + return false; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + const float bbox_size = float(mv->mesh().bounding_box().size().norm()); + const float panel_half = bbox_size * 0.04f + 1.f; + const float arrow_length = panel_half * 2.2f; + // Where the handle is drawn (moves with the layer's offset) vs. the plane the drag is measured + // against (fixed at the anchor). Keeping them apart is what stops the handle's own motion from + // feeding back into the delta that produced it. + const Vec3f handle_center_local = adjust_handle_center(*layer); + const Vec3f drag_plane_local = adjust_plane_point(); + const Vec3d handle_center_world = trafo_matrix * handle_center_local.cast(); + const Vec2d mouse_pos(mouse_event.GetX(), mouse_event.GetY()); + + Vec3f u_axis, v_axis; + adjust_tangent_basis(u_axis, v_axis); + + const Point handle_screen = CameraUtils::project(camera, handle_center_world); + const Vec2d handle_screen_d(double(handle_screen.x()), double(handle_screen.y())); + + // Point-to-segment distance in screen space, for the arrow shafts. + auto dist_to_segment_px = [](const Vec2d &p, const Vec2d &a, const Vec2d &b) { + const Vec2d ab = b - a; + const double len2 = ab.squaredNorm(); + const double t = (len2 > 1e-9) ? std::clamp((p - a).dot(ab) / len2, 0.0, 1.0) : 0.0; + return (p - (a + ab * t)).norm(); + }; + + if (mouse_event.LeftDown()) { + const Vec3d u_tip_world = trafo_matrix * (handle_center_local + u_axis * arrow_length).cast(); + const Vec3d v_tip_world = trafo_matrix * (handle_center_local + v_axis * arrow_length).cast(); + const Point u_tip_screen = CameraUtils::project(camera, u_tip_world); + const Point v_tip_screen = CameraUtils::project(camera, v_tip_world); + const Vec2d u_tip_screen_d(double(u_tip_screen.x()), double(u_tip_screen.y())); + const Vec2d v_tip_screen_d(double(v_tip_screen.x()), double(v_tip_screen.y())); + + // Panel screen-space "radius", approximated from one corner (a loose circle around the + // square is close enough for hit-testing purposes). + const Vec3d panel_corner_world = trafo_matrix * (handle_center_local + (u_axis + v_axis) * panel_half).cast(); + const Point panel_corner_screen = CameraUtils::project(camera, panel_corner_world); + const double panel_screen_radius = (Vec2d(double(panel_corner_screen.x()), double(panel_corner_screen.y())) - handle_screen_d).norm(); + + constexpr double pick_tolerance_px = 8.0; + const double dist_to_u = dist_to_segment_px(mouse_pos, handle_screen_d, u_tip_screen_d); + const double dist_to_v = dist_to_segment_px(mouse_pos, handle_screen_d, v_tip_screen_d); + const double dist_to_panel = (handle_screen_d - mouse_pos).norm(); + + // Arrows take priority over the panel (their tips extend past it), then the panel covers + // the broader central area. + if (dist_to_u <= pick_tolerance_px && dist_to_u <= dist_to_v) + m_adjust_drag_handle = AdjustHandle::AxisU; + else if (dist_to_v <= pick_tolerance_px) + m_adjust_drag_handle = AdjustHandle::AxisV; + else if (dist_to_panel <= panel_screen_radius) + m_adjust_drag_handle = AdjustHandle::Pan; + else { + m_adjust_drag_handle = AdjustHandle::None; + return false; + } + + m_adjust_drag_start_offset = layer->offset; + + Vec3d world_hit; + if (ray_plane_hit(camera, mouse_pos, trafo_matrix, drag_plane_local, m_adjust_anchor_normal, world_hit)) { + const Vec3f local_hit = (trafo_matrix.inverse() * world_hit).cast(); + m_adjust_drag_start_planar = project_planar(local_hit, m_adjust_anchor_normal); + } + return true; + } + + if (mouse_event.Dragging() && m_adjust_drag_handle != AdjustHandle::None) { + Vec3d world_hit; + if (!ray_plane_hit(camera, mouse_pos, trafo_matrix, drag_plane_local, m_adjust_anchor_normal, world_hit)) + return true; + const Vec3f local_hit = (trafo_matrix.inverse() * world_hit).cast(); + const Vec2f current_planar = project_planar(local_hit, m_adjust_anchor_normal); + + Vec2f delta_planar = current_planar - m_adjust_drag_start_planar; + // project_planar()'s (x, y) axes are exactly u_axis/v_axis (see adjust_tangent_basis()), + // so zeroing one component constrains the drag to only the other axis. + if (m_adjust_drag_handle == AdjustHandle::AxisU) + delta_planar.y() = 0.f; + else if (m_adjust_drag_handle == AdjustHandle::AxisV) + delta_planar.x() = 0.f; + + const float scale = (layer->tiling_scale > 1e-6f) ? (1.f / layer->tiling_scale) : 1.f; + const Vec2f delta_scaled = delta_planar * scale; + const float rad = layer->rotation_deg * float(M_PI) / 180.f; + const float cs = std::cos(rad), sn = std::sin(rad); + const Vec2f delta_rotated(delta_scaled.x() * cs - delta_scaled.y() * sn, delta_scaled.x() * sn + delta_scaled.y() * cs); + // Increasing `offset` shifts which texel is sampled at a fixed world position, which + // visually slides the pattern the *opposite* way - subtracting is this session's + // best-effort reasoning about the direction that feels like "dragging the texture", + // unverified against an actual render (see header comment). + layer->offset = m_adjust_drag_start_offset - delta_rotated; + + m_preview_params_dirty = true; + m_parent.set_as_dirty(); + return true; + } + + if (mouse_event.LeftUp() && m_adjust_drag_handle != AdjustHandle::None) { + m_adjust_drag_handle = AdjustHandle::None; + rebuild_preview(); + m_preview_params_dirty = false; + return true; + } + + return false; +} + ModelVolume* GLGizmoTextureDisplacement::texture_volume() { ModelObject *mo = m_c->selection_info()->model_object(); @@ -113,6 +2028,7 @@ void GLGizmoTextureDisplacement::update_model_object() const ModelObjectPtrs &mos = wxGetApp().model().objects; wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + rebuild_preview(); } } @@ -136,6 +2052,18 @@ void GLGizmoTextureDisplacement::update_from_model_object(bool first_update) m_triangle_selectors.back()->deserialize(mv->texture_displacement_facet(m_active_layer_slot).get_data(), false); m_triangle_selectors.back()->request_update_render_data(); } + + // Start a freshly opened, never-textured volume with one layer already in place, so the panel is + // ready to paint straight away rather than showing an empty layer list. Only on first open, and + // only when there are none - never during an undo/redo or layer-switch reload (which also come + // through here), where silently adding a layer would be wrong. + if (first_update) + if (ModelVolume *tv = texture_volume(); tv != nullptr && tv->texture_displacement_layers.empty()) { + add_texture_layer(); // takes its own snapshot and rebuilds the preview + return; + } + + rebuild_preview(); } void GLGizmoTextureDisplacement::set_active_layer(int slot) @@ -143,10 +2071,26 @@ void GLGizmoTextureDisplacement::set_active_layer(int slot) if (slot == m_active_layer_slot) return; // Flush edits made while the previous layer was active before switching what the selectors - // reflect -- otherwise they would be silently lost. + // reflect - otherwise they would be silently lost. update_model_object(); m_active_layer_slot = slot; update_from_model_object(false); + // The on-canvas gizmo (if on) is anchored to whichever layer is active - keep it in sync + // instead of leaving it pointing at the previous layer's (now stale) paint patch. + if (m_adjust_texture_mode) + update_adjust_anchor(); + // Refresh every preview/overlay (bump, UV editor, seams, ...) for the newly active layer. + rebuild_preview(); +} + +unsigned int GLGizmoTextureDisplacement::tool_icon_id() +{ + if (!m_tool_icon_tried) { + m_tool_icon_tried = true; + // Runs from the panel render, i.e. with a GL context current, so the upload is safe here. + m_tool_icon.load_from_svg_file(resources_dir() + "/images/toolbar_texture_displacement.svg", false, false, false, 32); + } + return m_tool_icon.get_id(); } void GLGizmoTextureDisplacement::add_texture_layer() @@ -167,65 +2111,176 @@ void GLGizmoTextureDisplacement::add_texture_layer() return; } + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Add texture displacement layer"), UndoRedo::SnapshotType::GizmoAction); + + TextureDisplacementLayer layer; + layer.slot = free_slot; + // Start the layer off on the first library texture rather than on nothing at all: a textureless + // layer looks broken (painting on it appears to do nothing, because there is no height map to + // displace by). The user swaps it for another from the layer's own picker. + const std::vector &library = texture_library(); + if (!library.empty()) + if (const LibraryTexture *tex = get_library_texture(library.front().path)) { + layer.name = library.front().name; + layer.path = library.front().path; + layer.image_data = tex->image_data; + } + mv->texture_displacement_layers.push_back(std::move(layer)); + + set_active_layer(free_slot); + // set_active_layer() is a no-op when the new slot happens to be the one already active (slot 0, + // for the very first layer added), so the preview would not pick the new texture up on its own. + rebuild_preview(); + m_parent.set_as_dirty(); +} + +const GLGizmoTextureDisplacement::LibraryTexture *GLGizmoTextureDisplacement::get_library_texture(const std::string &path) +{ + if (auto it = m_library_textures.find(path); it != m_library_textures.end()) + return it->second.image_data ? &it->second : nullptr; + + LibraryTexture entry; + std::string error; + entry.image_data = load_texture_image_data(path, error); + if (entry.image_data) { + TextureDisplacementLayer probe; + probe.image_data = entry.image_data; + entry.thumbnail = upload_height_thumbnail(decode_height_texture(probe)); + if (!entry.thumbnail) + entry.image_data.reset(); // decoded to nothing usable - treat it as a failed load + } else { + BOOST_LOG_TRIVIAL(error) << "Texture displacement: could not load texture " << path << ": " << error; + } + + // Cached whether it loaded or not: a file that failed is remembered as unusable, so the picker + // does not retry (and re-log) it on every frame it is on screen. + const auto [pos, inserted] = m_library_textures.emplace(path, std::move(entry)); + return pos->second.image_data ? &pos->second : nullptr; +} + +void GLGizmoTextureDisplacement::set_layer_texture(TextureDisplacementLayer &layer, const TextureLibraryEntry &entry) +{ + const LibraryTexture *tex = get_library_texture(entry.path); + if (tex == nullptr) { + show_error(nullptr, _u8L("Could not load the selected texture.")); + return; + } + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Change texture displacement texture"), UndoRedo::SnapshotType::GizmoAction); + layer.name = entry.name; + layer.path = entry.path; + // Handing over the library's own buffer (rather than a copy) is what lets decode_height_texture() + // - whose cache is keyed by exactly this pointer - hit straight away instead of re-decoding + // the PNG the first time the layer is previewed or baked. + layer.image_data = tex->image_data; + + rebuild_preview(); + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::import_custom_texture(TextureDisplacementLayer &layer) +{ const wxString wildcard = "Images (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp"; wxFileDialog dialog(nullptr, _L("Choose a texture image (height map)"), wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() != wxID_OK) return; - const std::string path = into_u8(dialog.GetPath()); - - wxImage image; - if (!image.LoadFile(dialog.GetPath()) || !image.IsOk()) { - show_error(nullptr, _u8L("Could not load the selected image.")); + // Converts to the 8-bit grayscale PNG the bake code understands and copies it into the user's + // own texture folder, so it stays available for later models (and survives an app update, which + // rewrites the shipped folder wholesale). + std::string error; + const std::optional entry = import_texture_to_library(into_u8(dialog.GetPath()), error); + if (!entry) { + show_error(nullptr, error); return; } + set_layer_texture(layer, *entry); +} - // libslic3r's PNG decoder (used by the baking code, which has no wxWidgets dependency) only - // understands true 8-bit grayscale PNG. Convert and re-encode through Slic3r's own PNG writer - // here rather than relying on wxImage's PNG encoder to pick a compatible color type. - const wxImage gray = image.ConvertToGreyscale(); - const int w = gray.GetWidth(); - const int h = gray.GetHeight(); - if (w <= 0 || h <= 0) { - show_error(nullptr, _u8L("The selected image is empty.")); - return; +float GLGizmoTextureDisplacement::texture_row_height() const +{ + return m_imgui->scaled(3.f); +} + +bool GLGizmoTextureDisplacement::texture_row(const char *id, const std::string &name, GLTexture *thumbnail, bool selected, float width) +{ + const float row_h = texture_row_height(); + const ImVec2 start = ImGui::GetCursorPos(); + const bool clicked = ImGui::Selectable(id, selected, 0, ImVec2(width, row_h)); + const ImVec2 after = ImGui::GetCursorPos(); + + // Lay the image and the name back over the Selectable that was just emitted, so the whole row + // - preview included - is the clickable target rather than just a strip of text next to it. + ImGui::SetCursorPos(ImVec2(start.x + ImGui::GetStyle().FramePadding.x, start.y)); + if (thumbnail != nullptr) { + // Fit inside a row_h square without distorting a non-square source image. + const float aspect = (thumbnail->get_height() > 0) ? float(thumbnail->get_width()) / float(thumbnail->get_height()) : 1.f; + const ImVec2 dim = (aspect >= 1.f) ? ImVec2(row_h, row_h / aspect) : ImVec2(row_h * aspect, row_h); + ImGui::Image((ImTextureID) (intptr_t) thumbnail->get_id(), dim); + ImGui::SameLine(); } - std::vector gray_pixels(size_t(w) * size_t(h)); - const unsigned char *rgb = gray.GetData(); - for (size_t i = 0; i < gray_pixels.size(); ++i) - gray_pixels[i] = rgb[i * 3]; + ImGui::SetCursorPosY(start.y + (row_h - ImGui::GetTextLineHeight()) * 0.5f); // centre the name on the image + ImGui::TextUnformatted(name.c_str()); - const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() - / boost::filesystem::unique_path("orca_texdisp_%%%%%%%%.png"); - if (!Slic3r::png::write_gray_to_file(tmp_path.string(), size_t(w), size_t(h), gray_pixels)) { - show_error(nullptr, _u8L("Failed to prepare the texture for use.")); - return; + ImGui::SetCursorPos(after); + return clicked; +} + +void GLGizmoTextureDisplacement::render_texture_picker(TextureDisplacementLayer &layer) +{ + const float row_h = texture_row_height(); + const float import_btn_w = m_imgui->scaled(1.6f); + const float spacing = ImGui::GetStyle().ItemSpacing.x; + const float picker_w = std::max(m_imgui->scaled(10.f), ImGui::GetContentRegionAvail().x - import_btn_w - spacing); + + const ImVec2 start = ImGui::GetCursorPos(); + if (texture_row("##texture_picker", layer.name.empty() ? _u8L("Choose a texture...") : layer.name, + get_layer_thumbnail(layer), false, picker_w)) + ImGui::OpenPopup("##texture_library"); + const ImVec2 after = ImGui::GetCursorPos(); + + // Positioned explicitly rather than with SameLine(): texture_row() draws several widgets and + // then rewinds the cursor, so ImGui's notion of "the previous line" is not the row's own. + ImGui::SetCursorPos(ImVec2(start.x + picker_w + spacing, start.y)); + if (ImGui::Button("+", ImVec2(import_btn_w, row_h))) + import_custom_texture(layer); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Import your own image as a height map. It is converted to the format the slicer " + "bakes from and saved to your personal texture folder, kept separate from the " + "textures shipped with OrcaSlicer."), + m_imgui->scaled(20.f)); + ImGui::SetCursorPos(after); + + if (ImGui::BeginPopup("##texture_library")) { + const std::vector &library = texture_library(); + if (library.empty()) + m_imgui->text(_L("No textures found.")); + + bool shipped_heading = false; + bool user_heading = false; + for (const TextureLibraryEntry &entry : library) { + if (!entry.is_user && !shipped_heading) { + ImGui::TextDisabled("%s", _u8L("Built-in").c_str()); + shipped_heading = true; + } else if (entry.is_user && !user_heading) { + if (shipped_heading) + ImGui::Separator(); + ImGui::TextDisabled("%s", _u8L("My textures").c_str()); + user_heading = true; + } + + ImGui::PushID(entry.path.c_str()); + const LibraryTexture *tex = get_library_texture(entry.path); + if (texture_row("##entry", entry.name, tex != nullptr ? tex->thumbnail.get() : nullptr, + entry.path == layer.path, m_imgui->scaled(14.f))) { + set_layer_texture(layer, entry); + ImGui::CloseCurrentPopup(); + } + ImGui::PopID(); + } + ImGui::EndPopup(); } - std::vector bytes; - { - std::ifstream ifs(tmp_path.string(), std::ios::binary); - bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); - } - boost::system::error_code ec; - boost::filesystem::remove(tmp_path, ec); - - if (bytes.empty()) { - show_error(nullptr, _u8L("Failed to prepare the texture for use.")); - return; - } - - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Add texture displacement layer"), UndoRedo::SnapshotType::GizmoAction); - - TextureDisplacementLayer layer; - layer.slot = free_slot; - layer.name = boost::filesystem::path(path).filename().string(); - layer.path = path; - layer.image_data = std::make_shared>(std::move(bytes)); - mv->texture_displacement_layers.push_back(std::move(layer)); - - set_active_layer(free_slot); - m_parent.set_as_dirty(); } void GLGizmoTextureDisplacement::remove_texture_layer(int slot) @@ -240,15 +2295,197 @@ void GLGizmoTextureDisplacement::remove_texture_layer(int slot) layers.erase(std::remove_if(layers.begin(), layers.end(), [slot](const TextureDisplacementLayer &l) { return l.slot == slot; }), layers.end()); - if (slot >= 0 && slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) + if (slot >= 0 && slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) { mv->texture_displacement_facet(slot).reset(); + m_thumbnails[size_t(slot)].reset(); + m_thumbnail_source[size_t(slot)] = nullptr; + } if (m_active_layer_slot == slot) - update_from_model_object(false); + update_from_model_object(false); // also rebuilds the preview + else + rebuild_preview(); // a non-active layer's contribution to the combined preview changed m_parent.set_as_dirty(); } +void GLGizmoTextureDisplacement::select_whole_model() +{ + ModelObject *mo = m_c->selection_info()->model_object(); + if (!mo) + return; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Select whole model for texture displacement"), UndoRedo::SnapshotType::GizmoAction); + + int idx = -1; + for (const ModelVolume *v : mo->volumes) { + if (!v->is_model_part()) + continue; + ++idx; + const size_t facet_count = v->mesh().its.indices.size(); + for (size_t i = 0; i < facet_count; ++i) + m_triangle_selectors[idx]->set_facet(int(i), EnforcerBlockerType::ENFORCER); + m_triangle_selectors[idx]->request_update_render_data(); + } + update_model_object(); + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::subdivide_model() +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) + return; + + Plater *plater = wxGetApp().plater(); + Plater::TakeSnapshot snapshot(plater, _u8L("Subdivide model for texture displacement"), UndoRedo::SnapshotType::GizmoAction); + + // Same save/replace/restore-painting dance GLGizmoSimplify uses when it re-tessellates a + // volume's mesh: supported/seam/mmu/fuzzy-skin masks get remapped onto the new triangles, + // texture-displacement doesn't (no remap support for it yet) and is dropped instead of being + // left referring to triangle indices that no longer mean the same thing. + std::optional saved_painting = mv->save_painting(); + + // max_edge_length 0 means "no triangle is ever small enough", so every non-degenerate edge is + // split on each of the m_subdivide_count passes - i.e. a plain "subdivide the whole mesh N times". + TriangleMesh new_mesh(subdivide_mesh_uniform(mv->mesh().its, 0.f, m_subdivide_count)); + mv->set_mesh(std::move(new_mesh)); + mv->set_new_unique_id(); + mv->calculate_convex_hull(); + mv->restore_painting(saved_painting); + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), mo); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + + plater->changed_object(*mo); + update_from_model_object(false); // reload selectors/preview against the new mesh + cleared paint + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::remesh_model() +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr || m_remesh_target_edge_mm <= 0.f) + return; + + Plater *plater = wxGetApp().plater(); + + // CGAL isotropic remeshing can be slow on a big mesh; do it before taking the snapshot so a failure + // (it returns the input unchanged) doesn't leave an empty undo step. + indexed_triangle_set remeshed; + { + wxBusyCursor wait; + remeshed = MeshBoolean::cgal::remesh_isotropic(mv->mesh().its, double(m_remesh_target_edge_mm), 3); + } + if (remeshed.indices.empty() || remeshed.vertices.size() == mv->mesh().its.vertices.size()) { + show_error(nullptr, _u8L("Remeshing did not change the model (it may be non-manifold or the target size " + "is already met).")); + return; + } + + Plater::TakeSnapshot snapshot(plater, _u8L("Remesh model for texture displacement"), UndoRedo::SnapshotType::GizmoAction); + // Same save/replace/restore-painting dance as subdivide: texture-displacement paint has no remap + // across a topology change, so it is dropped rather than left pointing at triangles that moved. + std::optional saved_painting = mv->save_painting(); + mv->set_mesh(TriangleMesh(std::move(remeshed))); + mv->set_new_unique_id(); + mv->calculate_convex_hull(); + mv->restore_painting(saved_painting); + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), mo); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + plater->changed_object(*mo); + update_from_model_object(false); + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::rebuild_subdivide_preview() +{ + m_subdivide_preview_glmodel.reset(); + m_subdivide_preview_count = -1; + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || m_subdivide_count < 1) + return; + + // The same subdivision Apply would commit, but kept in a throwaway mesh and shown only as a + // wireframe - the model itself is not touched until Apply. + const indexed_triangle_set its = subdivide_mesh_uniform(mv->mesh().its, 0.f, m_subdivide_count); + if (its.indices.empty()) + return; + m_subdivide_preview_count = m_subdivide_count; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(its.vertices.size()); + init_data.reserve_indices(its.indices.size() * 6); + for (const Vec3f &v : its.vertices) + init_data.add_vertex(v); + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) + init_data.add_line(unsigned(tri[i]), unsigned(tri[(i + 1) % 3])); + if (!init_data.is_empty()) + m_subdivide_preview_glmodel.init_from(std::move(init_data)); +} + +void GLGizmoTextureDisplacement::render_subdivide_preview() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr || !m_subdivide_preview_glmodel.is_initialized()) + return; + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + glsafe(::glEnable(GL_POLYGON_OFFSET_LINE)); + glsafe(::glPolygonOffset(-1.0f, -1.0f)); + m_subdivide_preview_glmodel.set_color(ColorRGBA(0.2f, 0.9f, 1.0f, 0.7f)); // cyan, reads as "preview" + m_subdivide_preview_glmodel.render(); + glsafe(::glDisable(GL_POLYGON_OFFSET_LINE)); + shader->stop_using(); +} + +GLTexture *GLGizmoTextureDisplacement::get_layer_thumbnail(const TextureDisplacementLayer &layer) +{ + if (layer.empty() || layer.slot < 0 || size_t(layer.slot) >= TEXTURE_DISPLACEMENT_MAX_LAYERS) + return nullptr; + + const size_t slot = size_t(layer.slot); + if (m_thumbnails[slot] && m_thumbnail_source[slot] == layer.image_data.get() && + m_thumbnail_smoothing[slot] == layer.smoothing) + return m_thumbnails[slot].get(); + + // Reuses the already-decoded, already-cached grayscale pixels (see decode_height_texture()'s + // own cache in TextureDisplacement.cpp) - only the gray-to-RGBA expansion and GPU upload below are + // new work. Rebuilt when the texture *or the smoothing* changes, so the fast/bump preview - which + // samples this GPU texture directly - reflects the current smoothing rather than the raw image. + std::unique_ptr texture = upload_height_thumbnail(decode_height_texture(layer)); + if (!texture) + return nullptr; + + m_thumbnails[slot] = std::move(texture); + m_thumbnail_source[slot] = layer.image_data.get(); + m_thumbnail_smoothing[slot] = layer.smoothing; + return m_thumbnails[slot].get(); +} + void GLGizmoTextureDisplacement::bake() { ModelVolume *mv = texture_volume(); @@ -265,7 +2502,17 @@ void GLGizmoTextureDisplacement::bake() } m_bake_in_progress = true; - queue_texture_displacement_bake(*mv, [this]() { m_bake_in_progress = false; }); + queue_texture_displacement_bake(*mv, [this]() { + m_bake_in_progress = false; + // Baking replaces the volume's mesh (new id, new topology) without changing the object's + // id or volume count, so GLGizmoPainterBase::data_changed()'s usual change-detection never + // notices it needs to reload - do it explicitly here, otherwise the gizmo keeps painting + // and rendering against the stale, pre-bake TriangleSelectorPatch until the object is + // deselected and reselected. + if (m_state == On && m_c->selection_info() && m_c->selection_info()->model_object()) + update_from_model_object(false); + m_parent.set_as_dirty(); + }); } void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float bottom_limit) @@ -277,25 +2524,177 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const float approx_height = m_imgui->scaled(24.f); y = std::min(y, bottom_limit - approx_height); - GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 1.0f, 0.0f); + + // Docked (the default) the panel is pinned next to the gizmo toolbar and cannot be moved, like + // every other gizmo's. Undocked it becomes an ordinary floating window: a title bar to drag it + // by, and no forced position - this panel is tall enough (layer stack, per-layer controls) that + // it can cover the very part of the model being painted, and being able to shove it aside is the + // point. The position is deliberately *not* seeded on undock, so the window stays exactly where + // it already was and the user just gains the ability to move it from there. + ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse; + if (!m_undocked) { + flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar; + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 1.0f, 0.0f); + } ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); - GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + GizmoImguiBegin(get_name(), flags); - m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(); - ImGui::PushItemWidth(m_imgui->scaled(7.f)); - m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + // Combo drop-downs otherwise inherit ImGui's near-black default popup background; under the light + // theme that leaves the dark item text unreadable ("the dropbox is black"). Pushed only around each + // Combo below (never around a tooltip, whose own near-black default is what makes it readable). + const ImVec4 combo_popup_bg = wxGetApp().dark_mode() ? ImVec4(0.18f, 0.18f, 0.19f, 1.f) + : ImVec4(0.93f, 0.93f, 0.93f, 1.f); + const auto scoped_combo = [&](const char *id, int *v, const char *const items[], int n) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, combo_popup_bg); + const bool changed = ImGui::Combo(id, v, items, n); + ImGui::PopStyleColor(); + return changed; + }; - bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE; - if (ImGui::RadioButton(m_desc.at("circle").ToUTF8().data(), is_circle)) + if (m_imgui->button(m_undocked ? _L("Dock panel") : _L("Undock panel"))) + m_undocked = !m_undocked; + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Detach this panel so it can be dragged anywhere over the 3D view, or dock it " + "back beside the toolbar."), + m_imgui->scaled(20.f)); + ImGui::Separator(); + + // Selection mode: which of TriangleSelector's existing click/brush mechanisms drives painting. + // "Face" and "Connected area" reuse the exact same underlying selection machinery every other + // paint gizmo already has (single-facet click, and angle-limited flood fill respectively) -- + // just exposed here as an alternative to brushing, one triangle/region at a time. + m_imgui->text(_L("Selection mode")); + const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER; + const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER; + const bool is_area_mode = m_tool_type == ToolType::SMART_FILL; + if (ImGui::RadioButton(_u8L("Brush").c_str(), is_brush_mode)) { + m_tool_type = ToolType::BRUSH; m_cursor_type = TriangleSelector::CursorType::CIRCLE; + } ImGui::SameLine(); - if (ImGui::RadioButton(m_desc.at("sphere").ToUTF8().data(), !is_circle)) - m_cursor_type = TriangleSelector::CursorType::SPHERE; + if (ImGui::RadioButton(_u8L("Face").c_str(), is_face_mode)) { + m_tool_type = ToolType::BRUSH; + m_cursor_type = TriangleSelector::CursorType::POINTER; + } + ImGui::SameLine(); + if (ImGui::RadioButton(_u8L("Connected area").c_str(), is_area_mode)) { + m_tool_type = ToolType::SMART_FILL; + m_cursor_type = TriangleSelector::CursorType::POINTER; + } + + if (is_brush_mode) { + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(); + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + ImGui::PopItemWidth(); + + bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE; + if (ImGui::RadioButton(m_desc.at("circle").ToUTF8().data(), is_circle)) + m_cursor_type = TriangleSelector::CursorType::CIRCLE; + ImGui::SameLine(); + if (ImGui::RadioButton(m_desc.at("sphere").ToUTF8().data(), !is_circle)) + m_cursor_type = TriangleSelector::CursorType::SPHERE; + } else if (is_area_mode) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_imgui->slider_float(_u8L("Angle threshold"), &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, "%.0f"); + ImGui::PopItemWidth(); + } + + if (m_imgui->button(_u8L("Select whole model"))) + select_whole_model(); + + // View mode (#: "make View Mode with just icons"): Normal / Fast / Checker / Distortion behave as + // one radio group, Wireframe as an independent toggle. All reuse the one tool icon for now, so the + // tooltip carries the meaning. The underlying state stays m_use_bump_preview + m_uv_check_mode. + { + const unsigned int icon = tool_icon_id(); + const float vsz = m_imgui->scaled(1.5f); + const ImVec4 teal(0.0f, 0.59f, 0.53f, 1.0f); + const int cur_mode = m_use_bump_preview ? 1 : + (m_uv_check_mode == UVCheckMode::Checker ? 2 : + m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0); + int new_mode = cur_mode; + bool wf_toggle = false; + + // One icon button; active state shown by a teal backing and a full-brightness (vs dimmed) icon. + // Falls back to a text toggle if the icon can't load. + const auto icon_toggle = [&](int uid, bool active, const wxString &label, const wxString &tip) -> bool { + bool clicked; + ImGui::PushID(uid); + if (icon != 0) + clicked = m_imgui->image_button((ImTextureID) (intptr_t) icon, ImVec2(vsz, vsz), ImVec2(0, 0), ImVec2(1, 1), + -1, active ? teal : ImVec4(0, 0, 0, 0), + active ? ImVec4(1, 1, 1, 1) : ImVec4(0.65f, 0.65f, 0.65f, 1.f)); + else { + bool v = active; + clicked = ImGui::Checkbox(label.ToUTF8().data(), &v); + } + ImGui::PopID(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(tip, m_imgui->scaled(18.f)); + return clicked; + }; + + m_imgui->text(_L("View")); + ImGui::SameLine(); + if (icon_toggle(701, cur_mode == 0, _L("Normal"), _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0; + ImGui::SameLine(); + if (icon_toggle(702, cur_mode == 1, _L("Fast"), _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1; + ImGui::SameLine(); + if (icon_toggle(703, cur_mode == 2, _L("Checker"), _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2; + ImGui::SameLine(); + if (icon_toggle(704, cur_mode == 3, _L("Distortion"), _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3; + ImGui::SameLine(); + ImGui::Dummy(ImVec2(m_imgui->scaled(0.6f), 0.f)); + ImGui::SameLine(); + if (icon_toggle(705, m_wireframe_overlay, _L("Wireframe"), _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true; + + if (new_mode != cur_mode) { + m_use_bump_preview = (new_mode == 1); + m_uv_check_mode = (new_mode == 2) ? UVCheckMode::Checker : + (new_mode == 3) ? UVCheckMode::Distortion : UVCheckMode::None; + rebuild_uvcheck_mesh(); + if (m_use_bump_preview) + rebuild_bump_preview_mesh(); + refresh_wireframe(); // Normal<->Fast swaps the wireframe between displaced and base mesh + update_uv_editor(); // mirror the checker / distortion heatmap into the UV pane too (#7) + m_parent.set_as_dirty(); + } + if (wf_toggle) { + m_wireframe_overlay = !m_wireframe_overlay; + refresh_wireframe(); + m_parent.set_as_dirty(); + } + } + + if (ImGui::Checkbox(_u8L("Auto update").c_str(), &m_auto_update)) + if (m_auto_update) + rebuild_preview(); // catch up anything that changed while it was off + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Rebuild the displaced geometry as soon as anything changes (painting, textures, " + "sliders). Turn off to only rebuild when you release a slider, on very heavy models."), + m_imgui->scaled(20.f)); ImGui::Separator(); m_imgui->text(_L("Texture layers")); + if (mv != nullptr) { + // Add-layer affordance as an icon beside the heading. It is deliberately *not* right-aligned + // against the window edge: this panel uses ImGuiWindowFlags_AlwaysAutoResize, and positioning + // an item at GetWindowContentRegionMax().x - w feeds the window's own width back into its + // auto-fit, growing it by one item-spacing every frame -- which, with the panel docked and + // anchored by its right edge, walked it left off-screen on hover. A plain SameLine can't do that. + const unsigned int add_icon = tool_icon_id(); + const float sz = m_imgui->scaled(1.3f); + ImGui::SameLine(); + const bool add_clicked = (add_icon != 0) ? m_imgui->image_button((ImTextureID) (intptr_t) add_icon, ImVec2(sz, sz)) + : m_imgui->button(m_desc.at("add_texture")); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Add a texture layer"), m_imgui->scaled(20.f)); + if (add_clicked) + add_texture_layer(); + } if (mv != nullptr) { std::vector ordered; @@ -303,30 +2702,470 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ordered.push_back(&l); std::sort(ordered.begin(), ordered.end(), [](const auto *a, const auto *b) { return a->slot < b->slot; }); - for (TextureDisplacementLayer *layer : ordered) { + // Removing a layer erases it from mv->texture_displacement_layers, which shifts every later + // element down and leaves `ordered` - and `layer` itself - pointing at the wrong element + // (or past the end). Removing mid-loop would therefore keep rendering this row's remaining + // widgets against freed/shifted memory. Defer it to after the loop instead. + int slot_to_remove = -1; + + // The layer stack lives in its own scrolling, tinted region (#10, #12): with eight layers' + // worth of controls the panel otherwise runs off the bottom of the screen, and there was + // nothing to tell "settings that belong to this layer" apart from "settings that belong to + // the tool". + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.f, 1.f, 1.f, 0.04f)); + ImGui::BeginChild("##texture_layers", ImVec2(0.f, m_imgui->scaled(20.f)), true); + + for (size_t li = 0; li < ordered.size(); ++li) { + TextureDisplacementLayer *layer = ordered[li]; ImGui::PushID(layer->slot); const bool is_active = layer->slot == m_active_layer_slot; - if (ImGui::RadioButton("##active", is_active)) + + // Tint the whole active layer's block, not just its header (#: "background color for the + // selected layer ... whole plate"). The block's height isn't known until it is laid out, so + // draw the block into a foreground channel and the backing rectangle into a background one, + // then merge - the standard ImGui "rect behind a group" trick. + ImDrawList *dl = ImGui::GetWindowDrawList(); + const ImVec2 block_min = ImGui::GetCursorScreenPos(); + const float block_rx = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; + if (is_active) { + dl->ChannelsSplit(2); + dl->ChannelsSetCurrent(1); + } + + // Clicking the header - or anywhere in the layer's block, see the group below - makes + // it the active layer (#11, #12). A radio button was doing this before, which worked but + // gave no sense of which block of controls belonged to which layer. + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.f, 0.68f, 0.58f, 0.55f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.f, 0.68f, 0.58f, 0.35f)); + const std::string header = layer->name.empty() ? Slic3r::format(_u8L("Layer %1%"), li + 1) : layer->name; + if (ImGui::Selectable(header.c_str(), is_active, 0, ImVec2(ImGui::GetContentRegionAvail().x - m_imgui->scaled(3.f), 0.f))) set_active_layer(layer->slot); - ImGui::SameLine(); - ImGui::Text("%s", layer->name.empty() ? "texture" : layer->name.c_str()); + ImGui::PopStyleColor(2); ImGui::SameLine(); if (m_imgui->button(m_desc.at("remove_layer"))) - remove_texture_layer(layer->slot); + slot_to_remove = layer->slot; - ImGui::PushItemWidth(m_imgui->scaled(7.f)); - m_imgui->slider_float(_u8L("Depth (mm)"), &layer->depth_mm, 0.02f, 5.f, "%.2f"); - m_imgui->slider_float(_u8L("Tile size (mm)"), &layer->tiling_scale, 0.5f, 100.f, "%.1f"); - m_imgui->slider_float(_u8L("Rotation"), &layer->rotation_deg, 0.f, 360.f, "%.0f"); + // Everything below is this layer's own; the group lets a click anywhere inside it select + // the layer, which is what makes the block feel like one object rather than loose widgets. + ImGui::BeginGroup(); + render_texture_picker(*layer); + + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + // Depth and tile size are logarithmic: see ImGuiLogSlider. + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Depth (mm)"), &layer->depth_mm, 0.01f, 10.f, "%.3f", ImGuiLogSlider); + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Tile size (mm)"), &layer->tiling_scale, 0.2f, 200.f, "%.2f", ImGuiLogSlider); + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Rotation"), &layer->rotation_deg, 0.f, 360.f, "%.0f"); + // Midlevel (#19): the height that means "don't move". At 0 the surface only ever bulges + // outwards; at 0.5 mid-grey is neutral and darker texels cut inwards. + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Midlevel"), &layer->midlevel, 0.f, 10.f, "%.2f"); ImGui::PopItemWidth(); - ImGui::Checkbox(_u8L("Invert").c_str(), &layer->invert); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("The grey level that stays put. At 0 the texture can only push the surface " + "outwards. Raise it and anything darker cuts inwards instead, so one height " + "map both embosses and engraves -0.5 makes mid-grey neutral.\n\n" + "Cutting inwards can fold the surface through itself where normals converge: " + "inside a sharp concave corner, or through a thin wall. Keep Depth small " + "relative to the feature you are cutting into."), + m_imgui->scaled(20.f)); + if (layer->midlevel > 0.f && layer->depth_mm > 1.f) + m_imgui->warning_text(_L("Deep inward displacement may self-intersect.")); + + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Smoothing"), &layer->smoothing, 0.f, 1.f, "%.2f"); + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Blurs the height texture before it displaces the surface, rounding hard edges " + "and removing speckle without needing a softer source image. Affects the preview " + "and the bake alike."), + m_imgui->scaled(20.f)); + + // Edge smoothing: fade the displacement to flat toward the boundary of the painted area. + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Edge smoothing").c_str(), &layer->edge_smoothing); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Fade the relief to flat toward the edge of the painted area, so it blends into " + "the surrounding surface. A small amount only softens a thin band at the very " + "edge; the maximum flattens the whole painted face."), + m_imgui->scaled(20.f)); + if (layer->edge_smoothing) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Edge amount"), &layer->edge_smoothing_amount, + 0.02f, 1.f, "%.2f"); + ImGui::PopItemWidth(); + } + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer->invert); + + // The lowest painted layer has nothing underneath it to combine with - it *is* the + // base - so a blend mode would be meaningless (and Multiply/Divide against an implicit + // zero would annihilate it). build_texture_displacement() forces the first layer to + // reach a given vertex to behave additively regardless; say so rather than offering a + // control that silently does nothing. + if (li == 0) { + ImGui::TextDisabled("%s", _u8L("Base layer").c_str()); + } else { + m_imgui->text(_u8L("Blend")); + ImGui::SameLine(); + const std::string blend_add = _u8L("Add"); + const std::string blend_subtract = _u8L("Subtract"); + const std::string blend_multiply = _u8L("Multiply"); + const std::string blend_divide = _u8L("Divide"); + const char *blend_items[] = { blend_add.c_str(), blend_subtract.c_str(), blend_multiply.c_str(), + blend_divide.c_str() }; + int blend_mode = static_cast(layer->blend_mode); + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (scoped_combo("##blend_mode", &blend_mode, blend_items, IM_ARRAYSIZE(blend_items))) { + layer->blend_mode = static_cast(blend_mode); + m_preview_params_dirty = true; + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How this layer combines with the layers below it, wherever they overlap. " + "Add and Subtract pile relief on or carve it away. Multiply and Divide " + "scale the relief underneath, which makes this layer act as a mask over " + "it - for those two, Depth is a gain, and a depth of 1 mm on a white " + "part of the texture leaves the layers below unchanged."), + m_imgui->scaled(20.f)); + } + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Tile").c_str(), &layer->tile_enabled); + if (layer->tile_enabled) { + ImGui::SameLine(); + const std::string tile_method_repeat = _u8L("Repeat"); + const std::string tile_method_mirrored = _u8L("Mirrored repeat"); + const char *tile_method_items[] = { tile_method_repeat.c_str(), tile_method_mirrored.c_str() }; + int tile_method = static_cast(layer->tile_method); + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (scoped_combo("##tile_method", &tile_method, tile_method_items, IM_ARRAYSIZE(tile_method_items))) { + layer->tile_method = static_cast(tile_method); + m_preview_params_dirty = true; + } + ImGui::PopItemWidth(); + } + + { + m_imgui->text(_u8L("Projection")); + ImGui::SameLine(); + const std::string projection_triplanar = _u8L("Triplanar (blended)"); + const std::string projection_cylindrical = _u8L("Cylindrical"); + const std::string projection_spherical = _u8L("Spherical"); + const std::string projection_lscm = _u8L("Unwrap (LSCM)"); + const std::string projection_view = _u8L("From view"); + const char *projection_items[] = { projection_triplanar.c_str(), projection_cylindrical.c_str(), + projection_spherical.c_str(), projection_lscm.c_str(), + projection_view.c_str() }; + int projection_method = static_cast(layer->projection_method); + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (scoped_combo("##projection_method", &projection_method, projection_items, IM_ARRAYSIZE(projection_items))) { + const auto new_method = static_cast(projection_method); + // Capture the current view the moment "From view" is chosen, so it does something + // sensible immediately rather than projecting from a stale/default direction. + if (new_method == TextureProjectionMethod::ViewProjected && + layer->projection_method != TextureProjectionMethod::ViewProjected) + capture_view_projection(*layer); + layer->projection_method = new_method; + m_preview_params_dirty = true; + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(layer->projection_method == TextureProjectionMethod::LSCM ? + _u8L("Flattens the painted area and maps the texture onto it with as little " + "stretching as possible. The area is cut into pieces at its sharp edges " + "first (see Seam angle), so each piece can lie flat on its own.") : + _u8L("Triplanar projects the texture from all three axes at once and blends " + "between them, so a patch wrapping around a sharp edge has no seam. " + "Cylindrical and Spherical wrap the texture around the painted area's " + "own centre, for round shapes."), + m_imgui->scaled(20.f)); + + if (layer->projection_method == TextureProjectionMethod::LSCM) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_preview_params_dirty |= m_imgui->slider_float(_u8L("Seam angle"), &layer->lscm_seam_angle_deg, + 5.f, 90.f, "%.0f"); + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Edges sharper than this are cut, and the pieces either side of them are " + "flattened separately. Lower it to cut more: each piece then lies flat " + "with less stretching, at the cost of the texture not running continuously " + "across the cut. Raise it to keep more of the area in one piece. Corners " + "of a box are 90 degrees, so the default cuts them apart; a smoothly " + "rounded surface stays whole."), + m_imgui->scaled(20.f)); + + if (ImGui::Checkbox(_u8L("Connect islands").c_str(), &layer->auto_connect_islands)) { + // Apply (or, when turned off, just stop re-applying) right away rather than + // waiting for the next re-unwrap. + if (layer->auto_connect_islands && !m_uv_editor_unwrap.empty()) { + std::vector net = compute_connected_net(m_uv_editor_unwrap); + if (net.size() == size_t(m_uv_editor_unwrap.chart_count)) { + if (layer->islands.size() < net.size()) + layer->islands.resize(net.size()); + for (size_t i = 0; i < net.size(); ++i) + layer->islands[i] = net[i]; + } + } + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Lay the unwrap out as a connected net: pieces that share an edge are " + "unfolded next to each other (a cube becomes a joined net rather than six " + "loose squares). They stay separate islands, so you can still move any of " + "them by hand afterwards."), + m_imgui->scaled(20.f)); + + if (is_active) { + // Explicit unwrap (#: "Add unwrap button so it does not recompute on every + // change"). The LSCM solve runs only when this is pressed -- painting, the seam + // angle slider and seam marking no longer trigger it -- and the pane opens right + // afterwards. Re-press it to fold in any edits made since. + if (m_imgui->button(_u8L("Unwrap"))) { + m_uv_unwrap_pending = true; + m_uv_apply_connected_net = true; // a genuine re-unwrap may relayout the islands + m_show_uv_editor = true; + update_uv_editor(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Flatten the painted area into UV islands and open the UV editor. The " + "unwrap is computed only when you press this, not on every edit -- so " + "paint, change the seam angle or mark seams first, then press Unwrap to " + "see the result. Islands can then be moved, rotated and scaled."), + m_imgui->scaled(20.f)); + + // Select mode for the UV pane: whole islands, single vertices, or single edges. + // Vertex/Edge are free-form UV editing and feed the per-vertex overrides that the + // bake honours; Island is the move/rotate/scale-with-grouping behaviour. + if (!m_uv_editor_unwrap.empty()) { + m_imgui->text(_u8L("Select:")); + ImGui::SameLine(); + int mode = m_uv_select_mode; + ImGui::RadioButton(_u8L("Island").c_str(), &mode, 0); + ImGui::SameLine(); + ImGui::RadioButton(_u8L("Vertex").c_str(), &mode, 1); + ImGui::SameLine(); + ImGui::RadioButton(_u8L("Edge").c_str(), &mode, 2); + if (mode != m_uv_select_mode) { + m_uv_select_mode = mode; + if (UVEditorCanvas *c = wxGetApp().plater()->get_uv_editor_canvas()) + c->set_select_mode(static_cast(mode)); + } + if (!layer->lscm_uv_overrides.empty()) { + ImGui::SameLine(); + if (m_imgui->button(_u8L("Clear UV edits"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture UV edits"), + UndoRedo::SnapshotType::GizmoAction); + layer->lscm_uv_overrides.clear(); + m_uv_unwrap_pending = true; // re-solve so the pane drops the edited coords + update_uv_editor(); + rebuild_preview(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Discard all manual vertex/edge moves and return the unwrap to its " + "automatic shape."), + m_imgui->scaled(20.f)); + } + } + + // Manual seam marking (#9): a click mode that toggles mesh edges as seams. + bool seam_mode = m_seam_edit_mode; + if (ImGui::Checkbox(_u8L("Mark seams").c_str(), &seam_mode)) { + m_seam_edit_mode = seam_mode; + if (seam_mode) + m_adjust_texture_mode = false; // the two click modes are mutually exclusive + else { + m_seam_hover_edge = { -1, -1 }; // drop the hover highlight when leaving the mode + m_seam_hover_vertex = -1; + m_seam_hover_glmodel.reset(); + m_seam_path_anchor = -1; + m_seam_anchor_glmodel.reset(); + } + m_parent.set_as_dirty(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Click edges on the model to cut the unwrap along them, like marking a " + "seam in Blender. The edge under the cursor is highlighted yellow; click " + "to mark it red. Click a marked (red) edge again to unmark it. Hold Ctrl " + "and drag to rotate the view. Painting is paused while this is on."), + m_imgui->scaled(20.f)); + if (m_seam_edit_mode) { + // Shortest-path mode, for dense meshes: click two points, seam the whole path. + ImGui::SameLine(); + bool path_mode = m_seam_path_mode; + if (ImGui::Checkbox(_u8L("Path").c_str(), &path_mode)) { + m_seam_path_mode = path_mode; + m_seam_path_anchor = -1; + m_seam_hover_edge = { -1, -1 }; // hover target type changes with the mode + m_seam_hover_vertex = -1; + m_seam_hover_glmodel.reset(); + m_seam_anchor_glmodel.reset(); + m_parent.set_as_dirty(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Instead of clicking every edge, click a start point and then an end " + "point: the whole shortest path between them is seamed at once (green " + "marks the start). Each click extends the seam from the last point. " + "Best for dense meshes."), + m_imgui->scaled(20.f)); + } + if (!layer->lscm_seam_edges.empty()) { + ImGui::SameLine(); + if (m_imgui->button(_u8L("Clear seams"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture seams"), + UndoRedo::SnapshotType::GizmoAction); + layer->lscm_seam_edges.clear(); + rebuild_preview(); + } + } + + // What the UV editor is actually showing. Cheap, and the only way to tell an + // unwrap that produced nothing apart from one merely framed off-screen. The + // island tools (snap, average scale, cut) and the gesture hints now live in the + // UV pane itself - its toolbar and status line - rather than here (#18). + if (m_uv_editor_unwrap.empty()) + m_imgui->text(_u8L("Press Unwrap to flatten the painted area.")); + else + m_imgui->text(Slic3r::format(_u8L("Unwrap: %1% islands, %2% faces, %3% verts."), + m_uv_editor_unwrap.chart_count, m_uv_editor_unwrap.indices.size(), + m_uv_editor_unwrap.uvs.size())); + } + } + + if (layer->projection_method == TextureProjectionMethod::ViewProjected) { + // Re-capture the projector from wherever the camera is now (#6): orbit the model, + // press this, and the texture is re-laid from the new angle. + if (m_imgui->button(_u8L("Capture current view"))) { + capture_view_projection(*layer); + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Projects the texture straight onto the painted area from the direction " + "you are currently looking, like a slide projector. Faces angled away from " + "that direction will stretch - turn the model to where you want the " + "texture crisp, then capture."), + m_imgui->scaled(20.f)); + } + } + + if (is_active) { + bool adjust_on = m_adjust_texture_mode; + if (ImGui::Checkbox(_u8L("Adjust placement").c_str(), &adjust_on)) { + if (adjust_on) { + update_model_object(); // flush any pending strokes before anchoring + if (update_adjust_anchor()) + m_adjust_texture_mode = true; + else + show_error(nullptr, _u8L("Paint something with this layer first.")); + } else { + m_adjust_texture_mode = false; + m_adjust_drag_handle = AdjustHandle::None; + } + } + } + ImGui::EndGroup(); + // A click that lands on the layer's body (not on a widget, which consumes its own click) + // selects it too, so the whole block reads as one clickable object (#12). + if (!is_active && ImGui::IsItemClicked()) + set_active_layer(layer->slot); + + if (is_active) { + const float pad = m_imgui->scaled(0.25f); + const ImVec2 rmin(block_min.x - pad, block_min.y - pad); + const ImVec2 rmax(block_rx, ImGui::GetCursorScreenPos().y); + dl->ChannelsSetCurrent(0); + dl->AddRectFilled(rmin, rmax, ImGui::GetColorU32(ImVec4(0.0f, 0.59f, 0.53f, 0.22f)), m_imgui->scaled(0.2f)); + dl->ChannelsMerge(); + } + ImGui::PopID(); ImGui::Separator(); } + + ImGui::EndChild(); + ImGui::PopStyleColor(); + + if (slot_to_remove >= 0) + remove_texture_layer(slot_to_remove); // deferred: see slot_to_remove's declaration } - if (m_imgui->button(m_desc.at("add_texture"))) - add_texture_layer(); + // ImGui sliders report "changed" continuously on every frame while being dragged, not just + // once on release - rebuilding the preview (a real CPU mesh recompute) on every one of those + // frames is what made dragging these sliders feel slow. Only rebuild once the mouse button + // that's driving the drag is released, i.e. once per edit instead of dozens of times per drag. + if (m_preview_params_dirty && (m_auto_update || !ImGui::IsMouseDown(ImGuiMouseButton_Left))) { + rebuild_preview(); + m_preview_params_dirty = false; + } + // (The "Add layer" button now lives next to the "Texture layers" heading, as an icon.) + + ImGui::Separator(); + m_imgui->text(_u8L("Not enough vertices for fine detail?")); + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 1, 5)) { + m_subdivide_count = std::clamp(m_subdivide_count, 1, 5); + if (m_subdivide_editing) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " + "triangle count, so there are enough vertices for the height texture to displace."), + m_imgui->scaled(20.f)); + + if (!m_subdivide_editing) { + if (m_imgui->button(_u8L("Preview subdivision"))) { + m_subdivide_editing = true; + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Shows the subdivided mesh as a wireframe without changing the model. Press Apply to " + "commit it, or Done to leave the model as it is."), + m_imgui->scaled(20.f)); + } else { + if (m_imgui->button(_u8L("Apply"))) { + subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) + rebuild_subdivide_preview(); // re-preview against the now-denser mesh + m_parent.set_as_dirty(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " + "paint on it (already-baked bumps are unaffected)."), + m_imgui->scaled(20.f)); + ImGui::SameLine(); + if (m_imgui->button(_u8L("Done"))) { + m_subdivide_editing = false; + m_subdivide_preview_count = -1; + m_subdivide_preview_glmodel.reset(); + m_parent.set_as_dirty(); + } + } + + // Remesh: even out uneven triangle sizes (CGAL isotropic remeshing). GPU Delaunay isn't practical + // here, but this delivers the same goal - a consistent triangle size across the whole model. + m_imgui->text(_u8L("Uneven triangle sizes?")); + if (m_remesh_target_edge_mm <= 0.f && mv != nullptr) { + // Seed the target with the model's current mean edge length, so the default is a sensible + // "make everything about the size it already averages". + const indexed_triangle_set &its = mv->mesh().its; + double sum = 0.0; size_t cnt = 0; + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) { + sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm(); + ++cnt; + } + m_remesh_target_edge_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)), 0.1f, 20.f) : 1.f; + } + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_imgui->slider_float(_u8L("Target edge (mm)"), &m_remesh_target_edge_mm, 0.1f, 20.f, "%.2f", ImGuiLogSlider); + ImGui::PopItemWidth(); + m_imgui->disabled_begin(mv == nullptr); + if (m_imgui->button(_u8L("Remesh"))) + remesh_model(); + m_imgui->disabled_end(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Rebuilds the whole model with triangles close to this edge length - splitting the big " + "ones and merging the small ones - so displacement has an even density to work with. " + "Replaces the geometry and clears any not-yet-baked paint (already-baked bumps are kept)."), + m_imgui->scaled(20.f)); ImGui::Separator(); @@ -352,7 +3191,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->disabled_end(); ImGui::Separator(); - if (m_imgui->button(_L("Done"))) + if (m_imgui->button(_L("Close"))) m_parent.reset_all_gizmos(); GizmoImguiEnd(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index ef9c3f25b2..f81c6bc08a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -3,7 +3,15 @@ #include "GLGizmoPainterBase.hpp" #include "libslic3r/TextureDisplacement.hpp" +#include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/GLTexture.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/TextureLibrary.hpp" + +#include +#include +#include +#include namespace Slic3r::GUI { @@ -22,6 +30,10 @@ public: void render_painter_gizmo() override; + // Intercepts mouse input while "Adjust Texture" mode is on (dragging the on-canvas offset/ + // rotation handles instead of painting); otherwise forwards to the normal painting handling. + bool on_mouse(const wxMouseEvent &mouse_event) override; + protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; @@ -54,6 +66,173 @@ private: void set_active_layer(int slot); // flushes the previous layer's edits, then reloads selectors void bake(); + // Marks every facet of every model-part volume as painted for the currently active layer -- + // "whole model" as an alternative to brushing/clicking every triangle by hand. + void select_whole_model(); + + // Uniformly subdivides the volume's mesh (see libslic3r::subdivide_mesh_uniform()) so a + // low-poly input model has enough vertices to actually show texture-displacement detail. + // A real, committed geometry change (like Bake), so it needs its own snapshot; unlike Bake it + // has no target region, so any not-yet-baked paint on the volume is dropped rather than + // remapped (texture-displacement paint has no remap-across-topology-change support yet). + void subdivide_model(); + + // Returns a cached GPU thumbnail of layer's texture (decoding + uploading it the first time it + // is requested, or whenever its image_data changes), or nullptr if it has no usable texture. + GLTexture *get_layer_thumbnail(const TextureDisplacementLayer &layer); + + // A texture from the picker's library (see slic3r/GUI/TextureLibrary.hpp), read and uploaded + // once and then kept for the gizmo's lifetime. The decoded bytes are held alongside the GPU + // thumbnail so that picking the texture can hand the layer this very same image_data buffer -- + // which both avoids re-reading the file and lets decode_height_texture()'s own cache (keyed by + // exactly this pointer) hit immediately on the first bake/preview. + struct LibraryTexture + { + std::shared_ptr> image_data; + std::unique_ptr thumbnail; + }; + const LibraryTexture *get_library_texture(const std::string &path); + + // The layer's texture chooser: a drop-down whose closed state and every one of whose entries + // shows a large preview image on the left and the texture's name on the right, plus an adjacent + // button that imports an image file from disk into the user texture folder. Shipped and + // user-imported textures are listed under separate headings. + void render_texture_picker(TextureDisplacementLayer &layer); + void set_layer_texture(TextureDisplacementLayer &layer, const TextureLibraryEntry &entry); + void import_custom_texture(TextureDisplacementLayer &layer); + // Draws a picker row (image left, name right) on top of a full-width Selectable, and leaves the + // cursor below it. Shared by the drop-down's closed state and its individual entries so the two + // cannot drift apart. Returns true when the row is clicked. + bool texture_row(const char *id, const std::string &name, GLTexture *thumbnail, bool selected, float width); + float texture_row_height() const; + + // "Adjust Texture" mode: instead of painting, dragging an on-canvas handle changes the active + // layer's offset. The handle is a flat panel lying in the paint patch's own tangent plane + // (a "pan" -- drag anywhere on it for free 2D movement), plus two arrows along the patch's + // own U/V axes that constrain the drag to just that one axis for precise nudging. Anchored to + // the centroid/average-normal of the active layer's current paint patch (see + // libslic3r::compute_layer_paint_anchor()), so nothing is drawn if it has nothing painted yet. + // + // NOTE: the drag direction/sign below is this session's best-effort reasoning about which way + // the texture should appear to move as the handle is dragged -- it could not be visually + // confirmed while writing it (no way to render/see pixels in this environment), so it may + // need a one-line sign flip once actually tested. + bool update_adjust_anchor(); // recomputes m_adjust_anchor_pos/normal; false if nothing painted + bool on_mouse_adjust_texture(const wxMouseEvent &mouse_event); + void render_adjust_texture_gizmo(); + // Mesh-local tangent-plane basis at m_adjust_anchor_normal, matching project_planar()'s + // dominant-axis convention so dragging on-canvas maps consistently onto offset. + void adjust_tangent_basis(Vec3f &u_axis, Vec3f &v_axis) const; + + // The plane a drag is measured against: the paint patch's anchor, lifted clear of the surface. + // Deliberately *fixed* -- independent of the layer's offset -- so that moving the handle cannot + // move the plane the handle's own motion is derived from, which would be a feedback loop. + Vec3f adjust_plane_point() const; + + // Where the handle is actually drawn, in mesh-local coordinates. This is NOT just the patch's + // centroid: the handle *represents the texture's placement*, so it has to travel as `offset` + // changes. Pinning it to the centroid is why dragging it looked broken -- the texture slid but + // the handle stayed put. Undoing apply_uv_transform()'s scale and rotation turns the layer's + // offset back into a displacement in mm within the patch's tangent plane, which is what gets + // added to the anchor here. That is exactly consistent with the drag arithmetic in + // on_mouse_adjust_texture(): the handle then tracks the cursor 1:1, and sits back on the anchor + // precisely when offset is zero. + Vec3f adjust_handle_center(const TextureDisplacementLayer &layer) const; + + // The layer painted by the active slot, or nullptr if that slot has no layer yet. + TextureDisplacementLayer *active_layer(); + const TextureDisplacementLayer *active_layer() const; + + // Recomputes m_preview_glmodel from the volume's current (unbaked) paint state, using the same + // build_texture_displacement() algorithm as Bake. Called whenever the paint mask changes + // (stroke end, layer switch, undo/redo reload, post-bake refresh) rather than every frame -- + // this is real mesh work (PNG sampling, vertex welding), not something to redo per paint stroke + // drag sample or idle repaint. With several painted layers this can be slow, so the actual + // computation runs in a background TextureDisplacementPreviewJob; this function only queues + // it and returns immediately, and m_preview_glmodel is updated later when it completes. + void rebuild_preview(); + void render_preview_mesh(); + + // Alternate, GPU-only preview: perturbs shading normals from the active layer's height texture + // (a classic bump map) instead of actually moving vertices, using the + // resources/shaders/*/texture_displacement_bump.* shader. Faster than the true-displacement + // preview (no CPU meshing at all -- just a per-vertex paint-weight buffer built at the same + // cadence as rebuild_preview()) but only shows the *active* layer, and any bump is a shading + // illusion, not real geometry -- "Bake" always produces the true, exact result either way. + void rebuild_bump_preview_mesh(); + void render_bump_preview_mesh(); + + // Feeds the active layer's painted patch + LSCM unwrap (if it's using that projection method) + // into Plater's docked UV-editor pane and shows it, or hides the pane if the active layer + // isn't using LSCM (or nothing is painted). Called whenever something that could change what + // the pane should show happens: paint changes, layer switch, projection method change, bake, + // and on shutdown (to hide it). + void update_uv_editor(); + + // Applies one island edit reported by the UV editor's drag/rotate gestures to the active layer. + // Deltas are incremental (see UVEditorCanvas::IslandEditFn); `finished` ends the gesture, which + // is when -- and only when -- the 3D preview is rebuilt, since doing that per mouse-move would + // queue a mesh recompute for every pixel of a drag. + void on_island_edited(int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished); + // Applies a committed vertex/edge edit from the UV editor's Vertex/Edge modes: each entry is an + // unwrapped-vertex index and its new raw-unwrap coordinate. Maps the unwrapped index to a mesh + // vertex and stores a per-vertex UV override on the layer (see lscm_uv_overrides), then rebuilds the + // preview so the baked geometry follows. + void on_uv_vertex_edited(const std::vector> &edits); + // UV-editor sub-element select mode, mirrored into the canvas: 0 = Island, 1 = Vertex, 2 = Edge. + int m_uv_select_mode = 0; + // One affine per island (columns: x basis, y basis, translation), mapping the unwrap's raw mm + // coordinates to texture UVs -- the same type as UVEditorCanvas::IslandTransform, spelled out + // here so this header needn't drag in wxGLCanvas/glad. Cheap to recompute (it is per *island*, + // not per vertex), which is what lets an island drag update the pane without re-uploading a + // single vertex. + std::vector> uv_editor_island_transforms(const TextureDisplacementLayer &layer); + // Handles a toolbar command forwarded from the UV pane that needs the layer data the canvas + // doesn't hold (average island scale, cut island). Takes the command as an int (a cast of + // UVEditorCanvas::Command) so this header needn't pull in glad/wxGLCanvas via the canvas header. + void on_uv_command(int cmd); + // Splits one unwrap chart in two by marking the mesh edges that straddle the plane through its + // 3D centroid, perpendicular to its longest axis, as seams (#17). The re-unwrap then separates it. + void cut_island(TextureDisplacementLayer &layer, int chart); + + // Captures the current camera's right/up axes into the layer's projector (#6), transformed into + // the volume's local space so the projection is stable as the object is later moved/rotated. + void capture_view_projection(TextureDisplacementLayer &layer); + + // Manual seam marking (#9): a mode where clicking the model toggles the nearest mesh edge in the + // active layer's lscm_seam_edges, so the unwrap can be cut exactly where the user wants -- the + // Blender "mark seam" workflow. Painting is suppressed while it is on. + bool m_seam_edit_mode = false; + GLModel m_seam_glmodel; // the current seam edges, highlighted on the mesh + bool on_mouse_seam(const wxMouseEvent &mouse_event); + void toggle_seam_at(const Vec2d &mouse_pos); + void rebuild_seam_overlay(); + void render_seam_overlay(); + // The mesh edge nearest the mouse, in the volume's own vertex indices, or {-1,-1} if the ray misses. + // Factored out of toggle_seam_at() so the same pick can drive a live hover highlight (below) that + // shows which edge a click would toggle -- the "I don't know how it works" feedback the user hit. + std::pair seam_edge_at(const Vec2d &mouse_pos) const; + std::pair m_seam_hover_edge{ -1, -1 }; + // The vertex a click would pick in shortest-path mode, so the target is visible on hover the same + // way the edge is in normal mode. -1 when nothing is under the cursor (or not in path mode). + int m_seam_hover_vertex = -1; + GLModel m_seam_hover_glmodel; + void rebuild_seam_hover_overlay(); + + // Shortest-path seam marking, for dense meshes where clicking every single triangle edge is + // tedious: in this sub-mode a click picks the nearest vertex, and the next click marks every edge + // on the shortest surface path between the two as a seam - so a whole seam line is drawn with two + // clicks. The end vertex becomes the next start, so a multi-segment seam chains click by click. + bool m_seam_path_mode = false; + int m_seam_path_anchor = -1; // mesh vertex the path starts from, or -1 + GLModel m_seam_anchor_glmodel; // the anchor's incident edges, highlighted + int seam_vertex_at(const Vec2d &mouse_pos) const; // nearest mesh vertex under the cursor + void mark_seam_path(int v_from, int v_to); // seam every edge on the shortest path + void rebuild_seam_anchor_overlay(); + // Set while an island gesture is in flight, so the undo snapshot is taken once at the start of + // the drag (capturing the state *before* it) rather than on every motion event. + bool m_island_drag_active = false; + // Which of the up to TEXTURE_DISPLACEMENT_MAX_LAYERS paint masks the brush currently writes // into. Always a valid slot index (0 by default) so the base class's per-volume selector // machinery always has something to work with, even before any texture has been added -- @@ -62,7 +241,214 @@ private: int m_active_layer_slot = 0; bool m_bake_in_progress = false; + // When set, the true-displacement geometry is rebuilt on every parameter change (live), instead of + // only once the slider being dragged is released. On by default so painting/added textures show + // straight away without needing to nudge a slider first. + bool m_auto_update = true; + + // Subdivision is now count-based (split the whole mesh 1..5 times) rather than a target edge + // length, and is previewed as a wireframe before it is committed: nothing is written to the model + // until "Apply". While previewing, the would-be subdivided mesh is drawn as a wireframe overlay so + // the added density is visible; "Done" ends the preview without touching the model. The normal + // "Show mesh wireframe" toggle is left alone, so a wireframe the user already had on stays on. + int m_subdivide_count = 1; + bool m_subdivide_editing = false; + int m_subdivide_preview_count = -1; // the count m_subdivide_preview_glmodel was built for + GLModel m_subdivide_preview_glmodel; + void rebuild_subdivide_preview(); + void render_subdivide_preview(); + + // Isotropic remeshing (CGAL) to even out wildly varying triangle sizes so displacement has a + // consistent density to work with. Target edge length in mm; 0 means "not yet initialised", filled + // with the mesh's mean edge length the first time the control is shown. Like subdivide, it replaces + // the geometry and drops not-yet-baked paint (no remap across a topology change). + float m_remesh_target_edge_mm = 0.f; + void remesh_model(); + + // Live, pre-bake preview of the true displaced geometry (built by the same algorithm Bake + // uses). Empty/uninitialized whenever nothing is painted yet, in which case the gizmo falls + // back to the standard paint-mask overlay like every other painting gizmo. + GLModel m_preview_glmodel; + // Set while a layer parameter slider has changed since the last rebuild_preview() call but the + // mouse button driving the drag hasn't been released yet -- see on_render_input_window(). + bool m_preview_params_dirty = false; + + // See rebuild_bump_preview_mesh()/render_bump_preview_mesh(). + bool m_use_bump_preview = false; + // Set from the UV editor's per-move island edits instead of rebuilding the (potentially large) bump + // mesh synchronously inside that mouse handler -- doing the rebuild there stalled both the UV pane + // and the 3D view. The rebuild is instead coalesced to once per 3D frame (render_painter_gizmo). + bool m_bump_preview_dirty = false; + GLModel m_bump_preview_glmodel; + // Whether the current bump mesh carries a precomputed per-vertex uv (LSCM) that the shader + // should sample at directly, rather than projecting in-shader. Set by rebuild_bump_preview_mesh(). + bool m_bump_preview_uses_vertex_uv = false; + + // GPU island drag: while an island is dragged in the UV editor, the bump mesh is baked once (with + // the dragged island's vertices flagged, v_normal.y = 1) and then moved purely through the shader's + // island_delta uniform -- one uniform update per mouse move, no rebuild -- so it tracks the cursor + // as smoothly as Adjust placement. m_bump_active_chart is the dragged island (or -1); + // m_bump_active_vertex flags its base vertices; m_bump_baked_active_xf is that island's placement + // baked into the current mesh, against which the live delta is measured; m_bump_island_delta is the + // resulting final-uv-space affine handed to the shader (identity except mid-drag). + int m_bump_active_chart = -1; + std::vector m_bump_active_vertex; + Eigen::Matrix m_bump_baked_active_xf = Eigen::Matrix::Identity(); + Eigen::Matrix m_bump_island_delta = Eigen::Matrix::Identity(); + void compute_bump_active_vertices(const std::vector &charts); + + // The set of islands the current UV-editor drag moves together: the pane's multi-selection unioned + // with each selected island's join group (see build_island_move_set()). Populated at drag start and + // cleared when it finishes. A move applies the same offset to every island in it; rotate/scale act + // only on the primary. Empty when no move drag is in flight. + std::vector m_island_move_set; + // All islands that must move with `primary`: the pane's multi-selection plus, for each of those, the + // charts sharing its join group in `layer`. Always contains `primary`. + std::vector build_island_move_set(const TextureDisplacementLayer &layer, int primary) const; + // The join-group id of chart `c`: its explicit entry in `groups`, or `c` itself (its own singleton) + // when unset. Two charts move together iff this matches. + static int island_group_of(const std::vector &groups, int c); + // Merges chart `b`'s join group into chart `a`'s (materialising `groups` to `chart_count` first). + static void join_island_groups(std::vector &groups, int a, int b, int chart_count); + // Final per-vertex texture uv for the projections the shader can't reconstruct itself -- LSCM (an + // unwrap) and ViewProjected (a projector plane the shader doesn't know). One entry per patch/base + // vertex, already through apply_uv_transform(). Empty for Triplanar/Cylindrical/Spherical, which + // the shader projects on its own. Shared by the bump preview and the UV-check overlay. + std::vector compute_layer_vertex_uvs(const indexed_triangle_set &patch, + const TextureDisplacementLayer &layer) const; + + // UV-check overlay drawn over the painted patch to sanity-check the unwrap (#13/#14). Built by + // rebuild_uvcheck_mesh(), drawn by render_uvcheck_mesh() with the "texture_displacement_uvcheck" + // shader. Checker works for any projection; Distortion needs the per-vertex LSCM uv. + enum class UVCheckMode { None, Checker, Distortion }; + UVCheckMode m_uv_check_mode = UVCheckMode::None; + GLModel m_uvcheck_glmodel; + bool m_uvcheck_uses_vertex_uv = false; + void rebuild_uvcheck_mesh(); + void render_uvcheck_mesh(); + + // The UV editor pane is opened only on the user's explicit request (this toggle in the panel), + // never automatically just because a patch exists -- auto-popping it whenever there was "a + // selection to process" is exactly what the user asked to stop. update_uv_editor() keeps the pane + // hidden unless this is set. Reset on gizmo shutdown so reopening the gizmo doesn't reopen the pane. + bool m_show_uv_editor = false; + // The unwrap is expensive, so it is recomputed only when the user explicitly asks for it (the + // "Unwrap" button), not on every paint stroke or slider nudge. This is set by that button and + // consumed by the next update_uv_editor() call, which is the only path that re-solves the unwrap; + // every other call merely refreshes the cheap per-island affine transforms over the existing one. + bool m_uv_unwrap_pending = false; + // Set alongside m_uv_unwrap_pending only by the Unwrap button, so the connected-net auto-layout runs + // on a genuine re-unwrap but not on a refresh re-solve (a vertex-edit commit or undo), which must + // leave island placements untouched. + bool m_uv_apply_connected_net = false; + // Signature of the per-vertex UV overrides last reflected in the pane. When it changes without the + // user pressing Unwrap -- a vertex/edge edit committing, or an undo/redo reverting one -- the pane + // is re-solved so its geometry follows, even though a plain edit otherwise never re-solves (#Feat2). + size_t m_uv_overrides_sig = 0; + // What the UV pane's background currently holds, so update_uv_editor() only re-uploads it when the + // choice actually changes (the height texture is large; re-sending it every stroke would be waste). + enum class UVBackground { None, Height, Checker }; + UVBackground m_uv_editor_bg = UVBackground::None; + float m_uv_editor_bg_smoothing = -1.f; // smoothing the height backdrop was uploaded at + // Per-chart distortion heatmap colour for the UV pane (#7/#14), computed once when the unwrap is + // re-solved (relative stretch doesn't change when islands are merely moved), fed to the canvas only + // while the Distortion check mode is on. Empty otherwise. + std::vector m_uv_editor_distortion_colors; + void compute_uv_editor_distortion_colors(const indexed_triangle_set &patch); + + // Plain triangle-edge overlay on the mesh (#8), toggled independently of the check modes. + bool m_wireframe_overlay = false; + GLModel m_wireframe_overlay_glmodel; + size_t m_wireframe_overlay_vcount = 0; // topology signature, so it rebuilds only on a real change + void rebuild_wireframe_overlay(); // from the base mesh (bump/paint mode) + void build_wireframe_from_its(const indexed_triangle_set &its); // from an explicit mesh, no early-out + void refresh_wireframe(); // pick base vs displaced source for the current view + void render_wireframe_overlay(); + // The displaced preview geometry the last preview job produced, kept so the wireframe overlay can be + // drawn on the raised surface actually shown in the true-displacement view (#: "wireframe in real mode"). + indexed_triangle_set m_preview_its; + // Bumped on every rebuild_preview() call; a background TextureDisplacementPreviewJob's result + // is only applied if this hasn't moved on since the job was queued (see rebuild_preview()), + // so a burst of edits can't have an earlier, now-stale job clobber a later one's result. + uint64_t m_preview_generation = 0; + + // Per-slot GPU thumbnail cache for the layer list panel, keyed by the image_data pointer that + // was current the last time each thumbnail was built (see get_layer_thumbnail()). + std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> m_thumbnails; + std::array m_thumbnail_source{}; + // The smoothing each cached thumbnail was built at, so a smoothing change re-uploads it (and the + // fast/bump preview, which samples this texture, actually shows the blur). + std::array m_thumbnail_smoothing{}; + + // Library textures the picker has shown at least once, keyed by file path (see LibraryTexture). + std::map m_library_textures; + + // Everything the *unwrap* depends on. update_uv_editor() runs from rebuild_preview(), i.e. on + // every stroke end and every slider release -- but depth/tiling/rotation/offset/blend change + // none of this, so re-extracting the patch and re-solving on those edits would be pure waste. + // Held as the real values rather than a hash: TriangleSplittingData has an exact operator==, so + // there is no reason to accept a hash's (however unlikely) chance of showing a stale unwrap. + struct UVEditorState + { + int slot = -1; + const void *image_data = nullptr; + float seam_angle = -1.f; + float padding = -2.f; + TriangleSelector::TriangleSplittingData facets; + // Manual/auto seam edges also change the unwrap, so a change here must force a re-solve just + // like the facets do (marking a seam leaves the paint mask untouched). + std::vector> seam_edges; + + bool operator==(const UVEditorState &other) const + { + return slot == other.slot && image_data == other.image_data && seam_angle == other.seam_angle && + padding == other.padding && facets == other.facets && seam_edges == other.seam_edges; + } + }; + UVEditorState m_uv_editor_state; + // Bounds of the UVs last handed to the pane, purely so the panel can show where the unwrap + // actually landed -- it is packed in mm and then divided by the tile size, so it is easy for it + // to end up far outside the texture's first tile without any of that being visible. + Vec2f m_uv_editor_bbox_min = Vec2f::Zero(); + Vec2f m_uv_editor_bbox_max = Vec2f::Zero(); + // The unwrap m_uv_editor_state produced, kept so that changing tiling/rotation/offset only costs + // re-running apply_uv_transform() over it, not another extraction and solve. + PatchUnwrap m_uv_editor_unwrap; + + // When set, the panel is a free-floating window the user can drag anywhere (with a title bar to + // grab), instead of being pinned to the right of the gizmo toolbar. Persisted across gizmo + // open/close within a session, so the choice sticks while working. + bool m_undocked = false; + + // See the "Adjust Texture" block of private methods above. + bool m_adjust_texture_mode = false; + bool m_adjust_anchor_valid = false; + Vec3f m_adjust_anchor_pos = Vec3f::Zero(); // mesh-local + Vec3f m_adjust_anchor_normal = Vec3f::UnitZ(); // mesh-local + + // Pan: free drag anywhere on the flat panel, moves offset along both axes. AxisU/AxisV: drag + // the corresponding arrow, moves offset along only that one axis. + enum class AdjustHandle { None, Pan, AxisU, AxisV }; + AdjustHandle m_adjust_drag_handle = AdjustHandle::None; + Vec2f m_adjust_drag_start_offset = Vec2f::Zero(); + // Anchor-relative planar position (see project_planar()) of the point under the mouse at the + // moment the current drag started; every subsequent frame's delta is measured against this, + // rather than accumulated frame-to-frame, to avoid drift. + Vec2f m_adjust_drag_start_planar = Vec2f::Zero(); + + // Lazily-built unit quad (the pan panel) and unit line-with-arrowhead (reused, rotated, for + // both the U and V axis arrows), transformed into place at render time. + GLModel m_adjust_panel_glmodel; + GLModel m_adjust_arrow_glmodel; + std::map m_desc; + + // The tool's SVG (toolbar_texture_displacement.svg) uploaded once as a GL texture, so it can be + // used as an ImGui image button in the panel (currently the "add layer" affordance next to the + // Texture layers heading). Lazily loaded on first use, when a GL context is guaranteed current. + GLTexture m_tool_icon; + bool m_tool_icon_tried = false; + unsigned int tool_icon_id(); // 0 if the icon could not be loaded }; } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 9803fad2cb..0977e388b5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -217,9 +217,8 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); m_gizmos.emplace_back(new GLGizmoFuzzySkin(m_parent, m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg", EType::FuzzySkin)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmSegmentation)); - // TODO: placeholder icon reused from the fuzzy-skin gizmo; swap in a dedicated - // toolbar_texture_displacement(_dark).svg once one exists. - m_gizmos.emplace_back(new GLGizmoTextureDisplacement(m_parent, m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg", EType::TextureDisplacement)); + // One shared icon (no dedicated dark variant yet); it recolours acceptably in both themes. + m_gizmos.emplace_back(new GLGizmoTextureDisplacement(m_parent, "toolbar_texture_displacement.svg", EType::TextureDisplacement)); m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss)); m_gizmos.emplace_back(new GLGizmoSVG(m_parent)); m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure)); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index d4d08aa84f..94083dacee 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -2554,7 +2554,11 @@ void ImGuiWrapper::push_toolbar_style(const float scale) ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10 ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11 ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, COL_GREEN_LIGHT); // 12 - ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));//13 + // The checkbox/radio frame behind this is drawn fully transparent (see FrameBg above, + // alpha 0), showing the light window background through it -- a white check mark there is + // invisible. Dark mode doesn't have this problem (its window background is dark), so only + // this branch needs a check mark color with real contrast against a light background. + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 156 / 255.f, 136 / 255.f, 1.00f));//13 ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f)); diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp new file mode 100644 index 0000000000..e4a7ca616c --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -0,0 +1,29 @@ +#include "TextureDisplacementPreviewJob.hpp" + +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r::GUI { + +TextureDisplacementPreviewJob::TextureDisplacementPreviewJob(TextureDisplacementPreviewInput &&input, uint64_t generation, + std::function on_finished) + : m_input(std::move(input)), m_generation(generation), m_on_finished(std::move(on_finished)) +{ +} + +void TextureDisplacementPreviewJob::process(Ctl &ctl) +{ + ctl.update_status(0, _u8L("Computing texture displacement preview")); + + // Only ever touches m_input (captured by value before this job was queued) and local state -- + // never the live Model -- so this is safe to run concurrently with the UI thread. + m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data); +} + +void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr &eptr) +{ + if (canceled || eptr || !m_on_finished) + return; + m_on_finished(std::move(m_result), m_generation); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp new file mode 100644 index 0000000000..d024fc2548 --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -0,0 +1,52 @@ +#ifndef slic3r_TextureDisplacementPreviewJob_hpp_ +#define slic3r_TextureDisplacementPreviewJob_hpp_ + +#include +#include +#include + +#include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TriangleMesh.hpp" + +#include "Job.hpp" + +namespace Slic3r::GUI { + +// Everything process() needs, captured by value on the main thread when the job is queued -- +// mirrors TextureDisplacementBakeInput, but a preview never writes back to the Model. +struct TextureDisplacementPreviewInput +{ + indexed_triangle_set base_mesh; + std::vector layers; + TextureDisplacementFacetsData facets_data; +}; + +// Computes the true (unbaked) displaced-mesh preview in the background. With several painted +// layers this is real, non-trivial CPU work (PNG sampling, per-layer vertex welding), which used +// to run synchronously on every paint stroke and parameter tweak and made editing feel slow with +// more than one or two layers. Unlike Bake, this never touches the live Model -- a preview is +// purely informational, there is nothing to commit. +class TextureDisplacementPreviewJob : public Job +{ +public: + // `generation` is an opaque token the caller controls (typically an incrementing counter): + // on_finished should only actually be applied by the caller if it still matches the caller's + // current generation when the job completes, so that a burst of edits queuing several of + // these jobs in a row can't have an earlier, now-stale result clobber a later one that + // finishes first. + TextureDisplacementPreviewJob(TextureDisplacementPreviewInput &&input, uint64_t generation, + std::function on_finished); + + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; + +private: + TextureDisplacementPreviewInput m_input; + uint64_t m_generation; + indexed_triangle_set m_result; + std::function m_on_finished; +}; + +} // namespace Slic3r::GUI + +#endif // slic3r_TextureDisplacementPreviewJob_hpp_ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 26b9217b3f..8deede98ac 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -90,6 +90,7 @@ #include "Selection.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" +#include "UVEditorCanvas.hpp" #include "3DBed.hpp" #include "PartPlate.hpp" #include "Camera.hpp" @@ -4468,6 +4469,13 @@ struct Plater::priv GLToolbar collapse_toolbar; Preview *preview; AssembleView* assemble_view { nullptr }; + // Docked/resizable 2D pane showing GLGizmoTextureDisplacement's LSCM unwrap of a painted + // patch; a sibling AUI pane alongside "sidebar"/"main", not part of the view3D/preview/ + // assemble_view sizer -- see its registration below and Plater::get_uv_editor_canvas(). The + // pane hosts the panel (toolbar + canvas + status line); uv_editor_canvas is its inner canvas, + // cached so the gizmo can reach it directly. + UVEditorPanel* uv_editor_panel { nullptr }; + UVEditorCanvas* uv_editor_canvas { nullptr }; bool first_enter_assemble{ true }; std::unique_ptr notification_manager; @@ -5133,6 +5141,20 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) .BottomDockable(false) .BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit()))); + // UV editor pane for GLGizmoTextureDisplacement's LSCM unwrap preview -- a resizable/dockable + // sibling of "sidebar"/"main" like everything else registered on this same AUI manager, not a + // change to the view3D/preview/assemble_view sizer above. Hidden by default: only relevant + // while that gizmo is active with a layer using the "Unwrap (LSCM)" projection method (see + // Plater::show_uv_editor()), so it stays out of the way of everyone else's window layout. + uv_editor_panel = new UVEditorPanel(q); + uv_editor_canvas = uv_editor_panel->canvas(); + m_aui_mgr.AddPane(uv_editor_panel, wxAuiPaneInfo() + .Name("uv_editor") + .Caption(_L("UV Editor")) + .Right() + .Hide() + .BestSize(wxSize(40 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()))); + auto* panel_sizer = new wxBoxSizer(wxHORIZONTAL); panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0); panel_sizer->Add(preview, 1, wxEXPAND | wxALL, 0); @@ -5162,6 +5184,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) BOOST_LOG_TRIVIAL(info) << "Removed floating AUI state from saved window layout for Wayland"; } + // The UV editor is a transient, gizmo-driven pane (see show_uv_editor()); a saved layout + // from a session that happened to close with it open would otherwise restore it visible on + // startup, with nothing painted in it. Force it hidden here so it only ever appears when the + // texture-displacement gizmo asks for it. + if (wxAuiPaneInfo &uv_pane = m_aui_mgr.GetPane("uv_editor"); uv_pane.IsOk()) + uv_pane.Hide(); + sidebar_layout.is_collapsed = !sidebar.IsShown(); } @@ -17149,6 +17178,33 @@ GLCanvas3D* Plater::get_assmeble_canvas3D() return nullptr; } +UVEditorCanvas* Plater::get_uv_editor_canvas() +{ + return p->uv_editor_canvas; +} + +void Plater::show_uv_editor(bool show) +{ + if (p->uv_editor_panel == nullptr) + return; + const wxAuiPaneInfo &pane = p->m_aui_mgr.GetPane(p->uv_editor_panel); + if (!pane.IsOk() || pane.IsShown() == show) + return; + + // Deferred, because GLGizmoTextureDisplacement calls this from its ImGui panel -- that is, from + // the middle of the 3D canvas's GL frame. Showing an AUI pane re-lays out the window and + // delivers the resulting size/paint events synchronously, and the UV canvas painting itself + // makes its own surface current in the app's *shared* GL context, which mid-frame is the one + // the 3D canvas is drawing into. Doing the layout once the frame is over avoids that entirely. + CallAfter([this, show]() { + wxAuiPaneInfo &deferred_pane = p->m_aui_mgr.GetPane(p->uv_editor_panel); + if (!deferred_pane.IsOk() || deferred_pane.IsShown() == show) + return; + deferred_pane.Show(show); + p->m_aui_mgr.Update(); + }); +} + GLCanvas3D* Plater::get_current_canvas3D(bool exclude_preview) { return p->get_current_canvas3D(exclude_preview); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index c70c5ca7c1..474a6512a7 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -611,6 +611,13 @@ public: GLCanvas3D* get_assmeble_canvas3D(); wxWindow* get_select_machine_dialog(); + // Docked UV-editor pane used by GLGizmoTextureDisplacement's LSCM projection preview (see + // UVEditorCanvas.hpp). Returns nullptr only before the main window is fully constructed. + class UVEditorCanvas* get_uv_editor_canvas(); + // Shows or hides the UV-editor AUI pane, updating its docked layout accordingly. Safe to call + // repeatedly (e.g. every time the gizmo's active layer/projection method changes). + void show_uv_editor(bool show); + void arrange(); void orient(); void find_new_position(const ModelInstancePtrs &instances); diff --git a/src/slic3r/GUI/TextureLibrary.cpp b/src/slic3r/GUI/TextureLibrary.cpp new file mode 100644 index 0000000000..349d82a050 --- /dev/null +++ b/src/slic3r/GUI/TextureLibrary.cpp @@ -0,0 +1,208 @@ +#include "TextureLibrary.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "libslic3r/PNGReadWrite.hpp" +#include "libslic3r/Utils.hpp" + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r::GUI { + +namespace { + +// Extensions the picker will list. The shipped folder only ever contains .png; the rest are here +// so a user who drops a .jpg straight into their own folder still sees it (load_texture_image_data() +// converts anything it can open). +bool is_image_file(const boost::filesystem::path &path) +{ + std::string ext = path.extension().string(); + boost::algorithm::to_lower(ext); + return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp"; +} + +void scan_dir(const boost::filesystem::path &dir, bool is_user, std::vector &out) +{ + boost::system::error_code ec; + if (!boost::filesystem::is_directory(dir, ec)) + return; + + const size_t first = out.size(); + for (boost::filesystem::directory_iterator it(dir, ec), end; it != end && !ec; it.increment(ec)) { + if (!boost::filesystem::is_regular_file(it->path(), ec) || !is_image_file(it->path())) + continue; + out.push_back({ it->path().stem().string(), it->path().string(), is_user }); + } + std::sort(out.begin() + first, out.end(), + [](const TextureLibraryEntry &a, const TextureLibraryEntry &b) { return a.name < b.name; }); +} + +// Slic3r::png only writes PNGs to a file, so the encode round-trips through a temp file rather than +// staying in memory. It happens once per import / per texture pick, not per frame, so the I/O is +// not worth avoiding with a second PNG encoder. +bool encode_gray_png_bytes(const wxImage &image, std::vector &out, std::string &error) +{ + const wxImage gray = image.ConvertToGreyscale(); + const int w = gray.GetWidth(); + const int h = gray.GetHeight(); + if (w <= 0 || h <= 0) { + error = _u8L("The selected image is empty."); + return false; + } + + // wxImage always stores 3 bytes per pixel; after ConvertToGreyscale the three are equal. + std::vector pixels(size_t(w) * size_t(h)); + const unsigned char *rgb = gray.GetData(); + for (size_t i = 0; i < pixels.size(); ++i) + pixels[i] = rgb[i * 3]; + + const boost::filesystem::path tmp = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("orca_texdisp_%%%%%%%%.png"); + if (!Slic3r::png::write_gray_to_file(tmp.string(), size_t(w), size_t(h), pixels)) { + error = _u8L("Failed to prepare the texture for use."); + return false; + } + + { + std::ifstream ifs(tmp.string(), std::ios::binary); + out.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp, ec); + + if (out.empty()) { + error = _u8L("Failed to prepare the texture for use."); + return false; + } + return true; +} + +std::vector read_file_bytes(const std::string &path) +{ + std::ifstream ifs(path, std::ios::binary); + return std::vector(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); +} + +// True if these bytes are already the 8-bit grayscale PNG libslic3r can decode, i.e. can be stored +// on a layer as-is. Mirrors exactly what decode_height_texture() accepts. +bool is_supported_height_map(const std::vector &bytes) +{ + if (bytes.empty()) + return false; + const png::ReadBuf rbuf{ bytes.data(), bytes.size() }; + if (!png::is_png(rbuf)) + return false; + png::ImageGreyscale img; + return png::decode_png(rbuf, img) && img.cols > 0 && img.rows > 0; +} + +std::vector g_library; +bool g_library_scanned = false; + +} // namespace + +std::string user_texture_dir() +{ + const boost::filesystem::path dir = boost::filesystem::path(Slic3r::data_dir()) / "textures" / "displacement"; + boost::system::error_code ec; + boost::filesystem::create_directories(dir, ec); + if (ec) { + BOOST_LOG_TRIVIAL(error) << "Could not create the user texture directory " << dir.string() << ": " << ec.message(); + return {}; + } + return dir.string(); +} + +const std::vector &texture_library(bool force_rescan) +{ + if (g_library_scanned && !force_rescan) + return g_library; + + g_library.clear(); + scan_dir(boost::filesystem::path(Slic3r::resources_dir()) / "textures" / "displacement", false, g_library); + const std::string user_dir = user_texture_dir(); + if (!user_dir.empty()) + scan_dir(boost::filesystem::path(user_dir), true, g_library); + + g_library_scanned = true; + return g_library; +} + +std::optional import_texture_to_library(const std::string &source_path, std::string &error) +{ + const std::string user_dir = user_texture_dir(); + if (user_dir.empty()) { + error = _u8L("Could not create the folder for imported textures."); + return std::nullopt; + } + + wxImage image; + if (!image.LoadFile(from_u8(source_path)) || !image.IsOk()) { + error = _u8L("Could not load the selected image."); + return std::nullopt; + } + + std::vector bytes; + if (!encode_gray_png_bytes(image, bytes, error)) + return std::nullopt; + + // Never overwrite an existing texture (the user's or, if they picked the same name twice, their + // own earlier import) -- uniquify instead. + const std::string stem = boost::filesystem::path(source_path).stem().string(); + boost::filesystem::path dest = boost::filesystem::path(user_dir) / (stem + ".png"); + for (int i = 2; boost::filesystem::exists(dest); ++i) + dest = boost::filesystem::path(user_dir) / (stem + " (" + std::to_string(i) + ").png"); + + { + boost::nowide::ofstream ofs(dest.string(), std::ios::binary); + ofs.write(reinterpret_cast(bytes.data()), std::streamsize(bytes.size())); + if (!ofs.good()) { + error = _u8L("Failed to save the imported texture."); + return std::nullopt; + } + } + + texture_library(true); // pick the new file up + const std::string dest_str = dest.string(); + for (const TextureLibraryEntry &e : g_library) + if (e.path == dest_str) + return e; + + error = _u8L("Failed to save the imported texture."); + return std::nullopt; +} + +std::shared_ptr> load_texture_image_data(const std::string &path, std::string &error) +{ + std::vector bytes = read_file_bytes(path); + if (bytes.empty()) { + error = _u8L("Could not read the texture file."); + return nullptr; + } + if (is_supported_height_map(bytes)) + return std::make_shared>(std::move(bytes)); + + // Not an 8-bit grayscale PNG (a colour image somebody copied into the folder by hand, say): + // convert it the same way an import would, but leave the file on disk alone. + wxImage image; + if (!image.LoadFile(from_u8(path)) || !image.IsOk()) { + error = _u8L("Could not load the selected image."); + return nullptr; + } + std::vector converted; + if (!encode_gray_png_bytes(image, converted, error)) + return nullptr; + return std::make_shared>(std::move(converted)); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/TextureLibrary.hpp b/src/slic3r/GUI/TextureLibrary.hpp new file mode 100644 index 0000000000..acef78d4d9 --- /dev/null +++ b/src/slic3r/GUI/TextureLibrary.hpp @@ -0,0 +1,50 @@ +#ifndef slic3r_TextureLibrary_hpp_ +#define slic3r_TextureLibrary_hpp_ + +#include +#include +#include +#include + +namespace Slic3r::GUI { + +// One selectable height-map texture in the texture-displacement gizmo's texture picker. +struct TextureLibraryEntry +{ + std::string name; // display name (the file's stem, e.g. "Wood Grain") + std::string path; // absolute path on disk + bool is_user; // imported by the user, as opposed to shipped with OrcaSlicer +}; + +// Every available height-map texture: the ones shipped in resources/textures/displacement first, +// then the user's own from /textures/displacement, each group sorted by name. +// +// The two live in separate directories deliberately: an app update replaces the resources tree +// wholesale, so anything the user imported has to sit somewhere that update can never overwrite or +// delete. `is_user` is what the picker uses to show them under separate headings. +// +// Scanned once and cached. Pass force_rescan after an import, or to pick up a file the user dropped +// into either folder by hand while the app was running. +const std::vector &texture_library(bool force_rescan = false); + +// /textures/displacement, created if it does not exist yet. Empty string on failure. +std::string user_texture_dir(); + +// Reads any image format wxWidgets can open, converts it to the 8-bit grayscale PNG that +// libslic3r's decode_height_texture() understands, and saves it into user_texture_dir() (uniquified +// if that name is taken). The conversion has to happen here rather than in libslic3r, which has no +// image toolkit and so only ever handles the one already-normalized format. +// +// Returns the newly imported entry, or nullopt with `error` set. `source_path` is only read. +std::optional import_texture_to_library(const std::string &source_path, std::string &error); + +// Encoded bytes of `path`, ready to hand to TextureDisplacementLayer::image_data. Files already in +// the supported 8-bit grayscale PNG form (everything in the two library folders, by construction) +// are passed through verbatim; anything else -- e.g. a colour PNG the user copied into the folder +// by hand -- is converted on the fly, so a valid image never silently produces a blank layer. +// Returns nullptr with `error` set if the file cannot be read or decoded at all. +std::shared_ptr> load_texture_image_data(const std::string &path, std::string &error); + +} // namespace Slic3r::GUI + +#endif // slic3r_TextureLibrary_hpp_ diff --git a/src/slic3r/GUI/UVEditorCanvas.cpp b/src/slic3r/GUI/UVEditorCanvas.cpp new file mode 100644 index 0000000000..ab7d023f8b --- /dev/null +++ b/src/slic3r/GUI/UVEditorCanvas.cpp @@ -0,0 +1,1314 @@ +#include "UVEditorCanvas.hpp" + +#include +#include +#include +#include + +#include + +#include "3DScene.hpp" +#include "GLShader.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "OpenGLManager.hpp" +#include "Plater.hpp" +#include "wxExtensions.hpp" +#include "libslic3r/AppConfig.hpp" + +namespace Slic3r::GUI { + +namespace { +// Roughly Blender's UV/Image editor palette, which is what this pane is measured against. The UV_ +// prefix is not decoration: COLOR_BACKGROUND (and several other COLOR_*) are Win32 system-colour +// macros from WinUser.h, and an unprefixed name here expands to an integer literal mid-declaration. +const ColorRGBA UV_COLOR_BG = { 0.16f, 0.16f, 0.16f, 1.f }; +const ColorRGBA UV_COLOR_GRID = { 1.f, 1.f, 1.f, 0.10f }; +const ColorRGBA UV_COLOR_TILE_OUTLINE = { 1.f, 1.f, 1.f, 0.45f }; +const ColorRGBA UV_COLOR_WIRE = { 0.85f, 0.85f, 0.85f, 0.35f }; // interior edges, unselected +const ColorRGBA UV_COLOR_BOUNDARY = { 1.f, 0.35f, 0.15f, 0.85f }; // island outline == a seam, so Blender's seam red +// Island fills (#4): unselected a light green kept translucent so the texture/checker/wireframe under +// it still reads (the user asked for both these fills *and* to see the check overlay through them, so +// the alphas stay low); the selected one switches to the app accent teal #009688 and is marked mainly +// by its bold boundary and brighter wire rather than a heavy fill. +const ColorRGBA UV_COLOR_FILL = { 0.55f, 0.85f, 0.45f, 0.40f }; // unselected: light green +const ColorRGBA UV_COLOR_SEL_FILL = { 0.0f, 0.588f, 0.533f, 0.20f }; // selected: #009688 teal +const ColorRGBA UV_COLOR_SEL_WIRE = { 0.75f, 1.f, 0.7f, 0.55f }; +const ColorRGBA UV_COLOR_SEL_BOUNDARY = { 0.2f, 1.f, 0.55f, 1.f }; // bold light-green edge (#7) +const ColorRGBA UV_COLOR_DIAL = { 1.f, 0.85f, 0.2f, 0.9f }; // rotation protractor (#11) + +constexpr float SNAP_PIXELS = 28.f; // how close a boundary vertex has to come before it sticks (#2) + +// The pixel format this canvas is created with has to match the one the app's single shared +// wxGLContext was created against (that of View3D's canvas, from OpenGLManager::create_wxglcanvas()), +// so this mirrors that attribute list *including its multisampling*: WGL requires the HDC passed to +// wglMakeCurrent() to have the same pixel format as the one the context was created with, and a +// differing sample count is a differing pixel format. Getting only the non-multisample half of this +// right still leaves SetCurrent() failing, which is silent - the canvas then just shows whatever +// was last in its backbuffer, i.e. nothing. +std::vector gl_attrib_list() +{ + int antialiasing_samples = 4; + if (const AppConfig *app_config = wxGetApp().app_config; app_config != nullptr) { + const std::string value = app_config->get(SETTING_OPENGL_AA_SAMPLES); + if (value == "0" || value == "2" || value == "4" || value == "8" || value == "16") + antialiasing_samples = ::atoi(value.c_str()); + } + // OpenGLManager's own auto-detection has already run by now (View3D is created before this + // canvas), so this only reads its verdict rather than re-detecting. + if (!OpenGLManager::can_multisample()) + antialiasing_samples = 0; + + return { + WX_GL_RGBA, + WX_GL_DOUBLEBUFFER, + WX_GL_MIN_RED, 8, + WX_GL_MIN_GREEN, 8, + WX_GL_MIN_BLUE, 8, + WX_GL_MIN_ALPHA, 8, + WX_GL_DEPTH_SIZE, 24, + WX_GL_STENCIL_SIZE, 8, + WX_GL_SAMPLE_BUFFERS, antialiasing_samples > 0 ? GL_TRUE : GL_FALSE, + WX_GL_SAMPLES, antialiasing_samples, + 0 + }; +} + +// Wide lines are only *required* to be supported in a compatibility profile; a core-profile driver is +// allowed to reject anything but 1.0 with GL_INVALID_VALUE. In practice every desktop driver we care +// about honours it, so ask and swallow the error rather than giving up thickness everywhere. +void set_line_width(float width) +{ + ::glLineWidth(width); + ::glGetError(); +} + +uint64_t undirected_edge_key(int a, int b) +{ + if (a > b) + std::swap(a, b); + return (uint64_t(uint32_t(a)) << 32) | uint32_t(b); +} + +// Signed-area test, so it works whichever way round the triangle is wound. +bool point_in_triangle(const Vec2f &p, const Vec2f &a, const Vec2f &b, const Vec2f &c) +{ + const auto cross = [](const Vec2f &u, const Vec2f &v) { return u.x() * v.y() - u.y() * v.x(); }; + const float d0 = cross(b - a, p - a); + const float d1 = cross(c - b, p - b); + const float d2 = cross(a - c, p - c); + const bool has_neg = (d0 < 0.f) || (d1 < 0.f) || (d2 < 0.f); + const bool has_pos = (d0 > 0.f) || (d1 > 0.f) || (d2 > 0.f); + return !(has_neg && has_pos); +} + +// Shortest signed difference between two angles, so a rotation gesture crossing +/-pi doesn't jump. +float angle_delta(float from, float to) +{ + float d = to - from; + while (d > float(M_PI)) + d -= 2.f * float(M_PI); + while (d < -float(M_PI)) + d += 2.f * float(M_PI); + return d; +} + +// A 2D affine as the 4x4 the "flat" shader's view_model_matrix wants. +Transform3d to_transform3d(const UVEditorCanvas::IslandTransform &m) +{ + Transform3d t = Transform3d::Identity(); + t(0, 0) = m(0, 0); t(0, 1) = m(0, 1); t(0, 3) = m(0, 2); + t(1, 0) = m(1, 0); t(1, 1) = m(1, 1); t(1, 3) = m(1, 2); + return t; +} +} // namespace + +UVEditorCanvas::UVEditorCanvas(wxWindow *parent) + : wxGLCanvas(parent, wxID_ANY, gl_attrib_list().data(), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS) +{ + // The GL canvas paints its entire surface, so background erasing is unnecessary (and would + // otherwise race with our own rendering) - same setup as OpenGLManager::create_wxglcanvas(). + SetBackgroundStyle(wxBG_STYLE_PAINT); + + // Shares the app's one real GL context (see class comment) rather than creating an + // independent one - this is the same call View3D/Preview/AssembleView make in + // GUI_Preview.cpp, and is what lets this canvas reuse wxGetApp().get_shader(...) and GLModel. + m_context = wxGetApp().init_glcontext(*this); + + Bind(wxEVT_PAINT, &UVEditorCanvas::on_paint, this); + Bind(wxEVT_SIZE, &UVEditorCanvas::on_size, this); + Bind(wxEVT_LEFT_DOWN, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_LEFT_UP, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_RIGHT_DOWN, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_RIGHT_UP, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_MIDDLE_DOWN, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_MIDDLE_UP, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_MOTION, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_MOUSEWHEEL, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_KEY_DOWN, &UVEditorCanvas::on_key, this); + Bind(wxEVT_ERASE_BACKGROUND, &UVEditorCanvas::on_erase_background, this); +} + +// NOTE (applies to set_background_texture() too): these are called from the texture-displacement +// gizmo's ImGui panel, i.e. from *inside* the main 3D canvas's own render pass. They must therefore +// only ever mark state dirty and schedule a repaint - rendering inline from here would make this +// canvas's GL surface current in the middle of the 3D canvas's frame (and did: it was also called +// while this pane was still hidden, where wxGLCanvas::SetCurrent() refuses to switch at all and the +// GL calls that followed simply landed on the 3D canvas instead). +void UVEditorCanvas::set_islands(Islands islands) +{ + // Only frame the view when a patch first appears, not on every stroke that extends one - the + // latter would keep yanking the view out from under a user who has panned or zoomed. + m_needs_fit |= m_islands.indices.empty(); + // Re-uploads that keep the same island/vertex count are just a refresh (e.g. a committed vertex edit + // re-applied), not a re-segmentation, so the selection and the picked sub-element stay valid and are + // worth keeping. A change in either count means the charts were renumbered and both are meaningless. + const bool same_structure = m_islands.island_count == islands.island_count && + m_islands.uvs.size() == islands.uvs.size(); + m_islands = std::move(islands); + if (m_selected_island >= m_islands.island_count) + m_selected_island = -1; + if (!same_structure) { + // A fresh unwrap renumbers charts, so any previous multi-selection is meaningless: drop it, then + // keep the primary (if still valid) as a single-island selection so the highlight is consistent. + m_selection.clear(); + if (m_selected_island >= 0) + m_selection.push_back(m_selected_island); + // Vertex/edge picks index into the old unwrap; drop them too. + m_active_vertex = -1; + m_active_edge = { -1, -1 }; + } + + // Boundary vertices, bucketed per island, for snapping. + m_island_boundary_verts.assign(size_t(std::max(m_islands.island_count, 0)), {}); + std::vector seen(m_islands.uvs.size(), false); + for (const auto &[a, b] : m_islands.boundary_edges) + for (const int v : { a, b }) { + if (v < 0 || size_t(v) >= m_islands.uvs.size() || seen[size_t(v)]) + continue; + seen[size_t(v)] = true; + const int island = m_islands.vertex_island[size_t(v)]; + if (island >= 0 && size_t(island) < m_island_boundary_verts.size()) + m_island_boundary_verts[size_t(island)].push_back(v); + } + + m_mesh_dirty = true; + m_background_quad_dirty = true; // the backdrop is sized to the unwrap, so it moved too + Refresh(); +} + +void UVEditorCanvas::set_island_transforms(std::vector transforms) +{ + m_transforms = std::move(transforms); + m_background_quad_dirty = true; // content_bounds() depends on where the islands ended up + Refresh(); +} + +void UVEditorCanvas::set_island_fill_colors(std::vector colors) +{ + m_island_fill_colors = std::move(colors); + Refresh(); +} + +void UVEditorCanvas::set_uv_transform(float tiling_scale, float rotation_deg, bool tile_enabled, bool tile_mirrored) +{ + m_tiling_scale = (std::abs(tiling_scale) > 1e-6f) ? tiling_scale : 1.f; + m_rotation_deg = rotation_deg; + m_tile_enabled = tile_enabled; + m_tile_mirrored = tile_mirrored; + m_background_quad_dirty = true; +} + +void UVEditorCanvas::set_background_texture(const std::vector &grayscale_pixels, int width, int height) +{ + if (width <= 0 || height <= 0 || grayscale_pixels.size() != size_t(width) * size_t(height)) { + m_background_width = m_background_height = 0; + m_background_pixels.clear(); + } else { + m_background_width = width; + m_background_height = height; + m_background_pixels.assign(size_t(width) * size_t(height) * 4, 255); + for (size_t i = 0; i < grayscale_pixels.size(); ++i) { + const unsigned char g = grayscale_pixels[i]; + m_background_pixels[i * 4 + 0] = g; + m_background_pixels[i * 4 + 1] = g; + m_background_pixels[i * 4 + 2] = g; + m_background_pixels[i * 4 + 3] = 255; + } + } + m_background_dirty = true; + Refresh(); +} + +void UVEditorCanvas::reset_view() +{ + m_needs_fit = true; + Refresh(); +} + +void UVEditorCanvas::run_command(Command cmd) +{ + switch (cmd) { + case Command::FrameAll: reset_view(); return; + case Command::ToggleSnap: m_snap_enabled = !m_snap_enabled; update_status(); return; + // The rest need the layer data the canvas doesn't hold; hand them to the gizmo. + case Command::AverageScale: + case Command::CutSelectedIsland: + case Command::ProjectFromView: + case Command::JoinSelected: + case Command::UnjoinSelected: + if (m_on_command) + m_on_command(cmd); + return; + } +} + +void UVEditorCanvas::update_status() +{ + if (!m_on_status) + return; + + wxString msg; + switch (m_gesture) { + case Gesture::MoveIsland: msg = _L("Moving island | release to drop, Esc to cancel"); break; + case Gesture::RotateIsland: + case Gesture::RotateIslandModal: + msg = wxString::Format(_L("Rotating island: %d° | Shift = snap 15°, click to confirm, Esc to cancel"), + int(std::lround(m_rot_display_deg))); + break; + case Gesture::ScaleIslandModal: msg = _L("Scaling island | click to confirm, Esc to cancel"); break; + case Gesture::MoveVertex: msg = _L("Moving vertex | release to drop"); break; + case Gesture::MoveEdge: msg = _L("Moving edge | release to drop"); break; + case Gesture::Pan: msg = _L("Panning"); break; + case Gesture::None: + default: + if (m_select_mode == SelectMode::Vertex) + msg = _L("Vertex mode: drag a vertex to reshape the unwrap | wheel = zoom, middle-drag = pan, Home = frame"); + else if (m_select_mode == SelectMode::Edge) + msg = _L("Edge mode: drag an island edge to reshape the unwrap | wheel = zoom, middle-drag = pan, Home = frame"); + else if (m_selection.size() > 1) + msg = wxString::Format(_L("%d islands selected | drag = move together, Shift/Ctrl click = add/remove, R/S = rotate/scale primary"), + int(m_selection.size())); + else if (m_selected_island >= 0) + msg = wxString::Format(_L("Island %d selected | drag = move, R = rotate, S = scale, Shift/Ctrl click = multi-select, Home = frame"), + m_selected_island + 1); + else + msg = _L("Click an island to select | Shift/Ctrl click = multi-select, wheel = zoom, middle-drag = pan, Home = frame all"); + if (m_snap_enabled) + msg += _L(" | snap ON"); + break; + } + m_on_status(msg); +} + +Vec2f UVEditorCanvas::island_uv(size_t vertex) const +{ + const Vec2f &raw = m_islands.uvs[vertex]; + const int island = m_islands.vertex_island[vertex]; + if (island < 0 || size_t(island) >= m_transforms.size()) + return raw; + const IslandTransform &m = m_transforms[size_t(island)]; + return m.block<2, 2>(0, 0) * raw + m.col(2); +} + +void UVEditorCanvas::content_bounds(Vec2f &min_uv, Vec2f &max_uv) const +{ + // Always include the texture's first tile, so an unwrap that happens to be tiny, or absent, + // still leaves something sensibly framed on screen. + min_uv = Vec2f(0.f, 0.f); + max_uv = Vec2f(1.f, 1.f); + for (size_t i = 0; i < m_islands.uvs.size(); ++i) { + const Vec2f uv = island_uv(i); + min_uv = min_uv.cwiseMin(uv); + max_uv = max_uv.cwiseMax(uv); + } +} + +void UVEditorCanvas::fit_view_to_content() +{ + Vec2f min_uv, max_uv; + content_bounds(min_uv, max_uv); + + m_pan = 0.5f * (min_uv + max_uv); + m_zoom = std::max(0.5f * (max_uv - min_uv).maxCoeff() * 1.1f, 0.05f); + m_needs_fit = false; +} + +void UVEditorCanvas::view_half_extents(float &half_w, float &half_h) const +{ + const wxSize size = GetSize(); + const float w = float(std::max(1, size.GetWidth())); + const float h = float(std::max(1, size.GetHeight())); + + // m_zoom is the half-extent visible across the *shorter* edge; the longer edge shows + // proportionally more. Both axes have to be driven off the same uniform scale, or the content + // comes out squashed on one of the two pane orientations. + const float aspect = w / h; + half_w = m_zoom * std::max(aspect, 1.f); + half_h = m_zoom * std::max(1.f / aspect, 1.f); +} + +Vec2f UVEditorCanvas::screen_to_uv(const wxPoint &px) const +{ + const wxSize size = GetSize(); + const float w = float(std::max(1, size.GetWidth())); + const float h = float(std::max(1, size.GetHeight())); + + float half_w, half_h; + view_half_extents(half_w, half_h); + + // Inverse of the projection in render(): x maps straight through, y is negated there so that v + // runs down the screen - which means screen-down and v-increasing agree, and this is a plain + // scale on both axes. + return Vec2f(m_pan.x() + (2.f * float(px.x) / w - 1.f) * half_w, + m_pan.y() + (2.f * float(px.y) / h - 1.f) * half_h); +} + +int UVEditorCanvas::island_at(const Vec2f &uv) const +{ + int found = -1; + for (const Vec3i32 &tri : m_islands.indices) { + if (tri.minCoeff() < 0 || size_t(tri.maxCoeff()) >= m_islands.uvs.size()) + continue; + if (!point_in_triangle(uv, island_uv(size_t(tri[0])), island_uv(size_t(tri[1])), island_uv(size_t(tri[2])))) + continue; + + const int island = m_islands.vertex_island[size_t(tri[0])]; + // Islands are allowed to overlap, so a point can be inside several. Keep whichever is already + // selected - otherwise a drag of a partly-covered island would be stolen mid-gesture by the + // one on top of it. + if (is_selected(island)) + return island; + found = island; + } + return found; +} + +void UVEditorCanvas::set_select_mode(SelectMode mode) +{ + if (m_select_mode == mode) + return; + m_select_mode = mode; + m_active_vertex = -1; + m_active_edge = { -1, -1 }; + m_gesture = Gesture::None; + update_status(); + Refresh(); +} + +int UVEditorCanvas::vertex_at(const Vec2f &uv) const +{ + // Screen-space threshold, so the pick feels the same at every zoom. + const wxSize size = GetSize(); + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + const float threshold = SNAP_PIXELS * uv_per_px; + + int best = -1; + float best_d = threshold * threshold; + for (size_t i = 0; i < m_islands.uvs.size(); ++i) { + const float d = (island_uv(i) - uv).squaredNorm(); + if (d < best_d) { + best_d = d; + best = int(i); + } + } + return best; +} + +std::pair UVEditorCanvas::edge_at(const Vec2f &uv) const +{ + const wxSize size = GetSize(); + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + const float threshold = SNAP_PIXELS * uv_per_px; + + // Point-to-segment distance in texture-UV space, against the island outlines (the edges the pane + // exists to show). Interior edges are left alone: the boundary is what a user reshapes. + const auto seg_dist_sq = [](const Vec2f &p, const Vec2f &a, const Vec2f &b) { + const Vec2f ab = b - a; + const float l2 = ab.squaredNorm(); + const float t = (l2 > 1e-12f) ? std::clamp((p - a).dot(ab) / l2, 0.f, 1.f) : 0.f; + return (p - (a + ab * t)).squaredNorm(); + }; + std::pair best{ -1, -1 }; + float best_d = threshold * threshold; + for (const auto &[a, b] : m_islands.boundary_edges) { + if (a < 0 || b < 0 || size_t(a) >= m_islands.uvs.size() || size_t(b) >= m_islands.uvs.size()) + continue; + const float d = seg_dist_sq(uv, island_uv(size_t(a)), island_uv(size_t(b))); + if (d < best_d) { + best_d = d; + best = { a, b }; + } + } + return best; +} + +void UVEditorCanvas::move_vertex_raw(int v, const Vec2f &delta_uv) +{ + if (v < 0 || size_t(v) >= m_islands.uvs.size()) + return; + const int island = m_islands.vertex_island[size_t(v)]; + // The gesture happens in texture-UV space; the stored coordinate is the raw unwrap. Undo the + // island's own linear map (which includes the layer's tiling scale + rotation) to get there. + Eigen::Matrix2f lin = Eigen::Matrix2f::Identity(); + if (island >= 0 && size_t(island) < m_transforms.size()) + lin = m_transforms[size_t(island)].block<2, 2>(0, 0); + const float det = lin.determinant(); + const Vec2f raw_delta = (std::abs(det) > 1e-12f) ? Vec2f(lin.inverse() * delta_uv) : delta_uv; + m_islands.uvs[size_t(v)] += raw_delta; + m_mesh_dirty = true; // the edited raw uv is redrawn from rebuild_island_models() next frame +} + +float UVEditorCanvas::island_rotation_deg(int island) const +{ + // The rotation baked into the island's affine, in the same y-down UV convention the gesture uses. + // First column is scale*(cos, sin); its angle is the island's on-screen orientation. + if (island < 0 || size_t(island) >= m_transforms.size()) + return 0.f; + const IslandTransform &m = m_transforms[size_t(island)]; + return std::atan2(m(1, 0), m(0, 0)) * 180.f / float(M_PI); +} + +Vec2f UVEditorCanvas::island_centroid(int island) const +{ + Vec2f sum = Vec2f::Zero(); + int count = 0; + for (size_t i = 0; i < m_islands.uvs.size(); ++i) + if (m_islands.vertex_island[i] == island) { + sum += island_uv(i); + ++count; + } + return (count > 0) ? Vec2f(sum / float(count)) : Vec2f::Zero(); +} + +Vec2f UVEditorCanvas::uv_delta_to_unwrap(const Vec2f &delta_uv) const +{ + // apply_uv_transform() maps unwrap -> uv as uv = R(unwrap / tiling) + offset, so the inverse of + // a *delta* (the translation drops out) is unwrap = tiling * R^-1(delta_uv). + const float rad = m_rotation_deg * float(M_PI) / 180.f; + const float cs = std::cos(rad), sn = std::sin(rad); + return Vec2f(delta_uv.x() * cs + delta_uv.y() * sn, -delta_uv.x() * sn + delta_uv.y() * cs) * m_tiling_scale; +} + +Vec2f UVEditorCanvas::snap_correction(int island) const +{ + if (!m_snap_enabled || island < 0 || size_t(island) >= m_island_boundary_verts.size()) + return Vec2f::Zero(); + + // A screen-space threshold, so the magnet feels the same at every zoom level. + const wxSize size = GetSize(); + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + const float threshold = SNAP_PIXELS * uv_per_px; + + float best_dist_sq = threshold * threshold; + Vec2f best = Vec2f::Zero(); + for (const int mine : m_island_boundary_verts[size_t(island)]) { + const Vec2f a = island_uv(size_t(mine)); + for (size_t other = 0; other < m_island_boundary_verts.size(); ++other) { + if (int(other) == island) + continue; + for (const int theirs : m_island_boundary_verts[other]) { + const Vec2f d = island_uv(size_t(theirs)) - a; + const float dist_sq = d.squaredNorm(); + if (dist_sq < best_dist_sq) { + best_dist_sq = dist_sq; + best = d; + } + } + } + } + return best; +} + +void UVEditorCanvas::end_gesture() +{ + const bool was_editing = m_gesture == Gesture::MoveIsland || m_gesture == Gesture::RotateIsland || + m_gesture == Gesture::RotateIslandModal || m_gesture == Gesture::ScaleIslandModal; + + // Stick to a neighbour at the end of a move, rather than magnetically fighting the cursor + // throughout it - a snap that keeps re-applying mid-drag is very hard to pull *out* of. + if (m_gesture == Gesture::MoveIsland && m_on_island_edit) { + const Vec2f correction = snap_correction(m_selected_island); + if (!correction.isZero()) + m_on_island_edit(m_selected_island, uv_delta_to_unwrap(correction), 0.f, 1.f, false); + } + + if (was_editing && m_on_island_edit) + m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f, /* finished */ true); + + // Commit a vertex/edge edit once, on release: hand the owner the affected unwrapped vertices and + // their new raw-unwrap coordinates so it can store the overrides and re-solve the bake preview. + if ((m_gesture == Gesture::MoveVertex || m_gesture == Gesture::MoveEdge) && m_vertex_edit_moved && + m_on_vertex_edit) { + std::vector> edits; + const auto add = [&](int v) { + if (v >= 0 && size_t(v) < m_islands.uvs.size()) + edits.emplace_back(v, m_islands.uvs[size_t(v)]); + }; + if (m_gesture == Gesture::MoveVertex) + add(m_active_vertex); + else { + add(m_active_edge.first); + add(m_active_edge.second); + } + if (!edits.empty()) + m_on_vertex_edit(edits); + } + + m_gesture = Gesture::None; + m_rot_raw_deg = 0.f; + m_rot_applied_deg = 0.f; + m_modal_scale_accum = 1.f; + if (HasCapture()) + ReleaseMouse(); +} + +void UVEditorCanvas::on_key(wxKeyEvent &evt) +{ + const int key = evt.GetKeyCode(); + + // Undo/redo while the pane has focus. Island edits already take a Plater snapshot per gesture (see + // GLGizmoTextureDisplacement::on_island_edited), so this just drives the same global history; the + // gizmo re-pushes the restored island transforms into the canvas on the reload that follows. + if (evt.ControlDown() && (key == 'Z' || key == 'z')) { + if (evt.ShiftDown()) wxGetApp().plater()->redo(); + else wxGetApp().plater()->undo(); + return; + } + if (evt.ControlDown() && (key == 'Y' || key == 'y')) { + wxGetApp().plater()->redo(); + return; + } + + if (m_gesture == Gesture::RotateIslandModal || m_gesture == Gesture::ScaleIslandModal) { + if (key == WXK_ESCAPE) { + // Put the island back exactly where the modal gesture found it, then finish. + if (m_on_island_edit) { + if (m_gesture == Gesture::RotateIslandModal && m_rot_applied_deg != 0.f) + m_on_island_edit(m_selected_island, Vec2f::Zero(), -m_rot_applied_deg, 1.f, false); + if (m_gesture == Gesture::ScaleIslandModal && m_modal_scale_accum != 1.f) + m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f / m_modal_scale_accum, false); + } + end_gesture(); + Refresh(); + return; + } + if (key == WXK_RETURN || key == WXK_NUMPAD_ENTER) { + end_gesture(); + Refresh(); + return; + } + } + + // Frame everything. The unwrap is packed in mm and then divided by the layer's tile size, so it + // can easily sit tens of tiles away from the texture's first one - panning back by hand from + // there is hopeless, and without this there would be no way to reach reset_view() at all. + if (key == WXK_HOME || key == 'F' || key == 'f') { + reset_view(); + return; + } + + // Blender's modal transforms: R / S, then the transform follows the mouse until it is confirmed + // with a click or Enter, or abandoned with Esc. + if (m_selected_island >= 0 && m_select_mode == SelectMode::Island && m_gesture == Gesture::None && + (key == 'R' || key == 'r' || key == 'S' || key == 's')) { + const Vec2f uv = screen_to_uv(ScreenToClient(wxGetMousePosition())); + const Vec2f centre = island_centroid(m_selected_island); + const Vec2f rel = uv - centre; + if (key == 'R' || key == 'r') { + m_gesture = Gesture::RotateIslandModal; + m_rot_raw_deg = 0.f; + m_rot_applied_deg = 0.f; + m_rot_base_deg = island_rotation_deg(m_selected_island); + m_rot_display_deg = m_rot_base_deg; + m_gesture_last_angle = std::atan2(rel.y(), rel.x()); + } else { + m_gesture = Gesture::ScaleIslandModal; + m_modal_scale_accum = 1.f; + m_gesture_last_dist = std::max(rel.norm(), 1e-6f); + } + return; + } + + evt.Skip(); +} + +void UVEditorCanvas::on_mouse(wxMouseEvent &evt) +{ + const wxEventType type = evt.GetEventType(); + const wxPoint pos = evt.GetPosition(); + + // Key events (R/S/Home) only arrive if this canvas has focus, and clicking it is the natural way + // to ask for it - the pane is not in the tab order. + if (type == wxEVT_LEFT_DOWN || type == wxEVT_RIGHT_DOWN || type == wxEVT_MIDDLE_DOWN) + SetFocus(); + + // A modal R/S is confirmed by any click, exactly as in Blender. + if ((m_gesture == Gesture::RotateIslandModal || m_gesture == Gesture::ScaleIslandModal) && + (type == wxEVT_LEFT_DOWN || type == wxEVT_RIGHT_DOWN)) { + end_gesture(); + Refresh(); + return; + } + + if (type == wxEVT_LEFT_DOWN) { + const Vec2f uv = screen_to_uv(pos); + m_drag_last_px = pos; + m_gesture_last_uv = uv; + if (m_select_mode == SelectMode::Vertex) { + m_active_vertex = vertex_at(uv); + m_active_edge = { -1, -1 }; + m_vertex_edit_moved = false; + m_gesture = (m_active_vertex >= 0) ? Gesture::MoveVertex : Gesture::Pan; + } else if (m_select_mode == SelectMode::Edge) { + m_active_edge = edge_at(uv); + m_active_vertex = -1; + m_vertex_edit_moved = false; + m_gesture = (m_active_edge.first >= 0) ? Gesture::MoveEdge : Gesture::Pan; + } else { + const int hit = island_at(uv); + if (hit >= 0) { + if (evt.ShiftDown()) { + // Shift adds to the selection (and makes the clicked one the new primary). + if (!is_selected(hit)) + m_selection.push_back(hit); + m_selected_island = hit; + } else if (evt.ControlDown()) { + // Ctrl toggles: clicking a selected island removes it (the requested "deselect"), + // clicking an unselected one adds it. + if (auto it = std::find(m_selection.begin(), m_selection.end(), hit); it != m_selection.end()) { + m_selection.erase(it); + m_selected_island = m_selection.empty() ? -1 : m_selection.back(); + } else { + m_selection.push_back(hit); + m_selected_island = hit; + } + } else { + // Plain click: keep the whole selection if the clicked island is already part of it (so + // a drag moves the group), otherwise collapse to just this one. + if (!is_selected(hit)) + m_selection.assign(1, hit); + m_selected_island = hit; + } + } else if (!evt.ShiftDown() && !evt.ControlDown()) { + // Clicking empty space with no modifier clears the selection and pans. + m_selection.clear(); + m_selected_island = -1; + } + m_gesture = (m_selected_island >= 0) ? Gesture::MoveIsland : Gesture::Pan; + } + CaptureMouse(); + Refresh(); + } else if (type == wxEVT_RIGHT_DOWN && m_selected_island >= 0 && m_select_mode == SelectMode::Island) { + const Vec2f rel = screen_to_uv(pos) - island_centroid(m_selected_island); + m_gesture = Gesture::RotateIsland; + m_rot_raw_deg = 0.f; + m_rot_applied_deg = 0.f; + m_rot_base_deg = island_rotation_deg(m_selected_island); + m_rot_display_deg = m_rot_base_deg; + m_gesture_last_angle = std::atan2(rel.y(), rel.x()); + CaptureMouse(); + } else if (type == wxEVT_MIDDLE_DOWN) { + m_gesture = Gesture::Pan; + m_drag_last_px = pos; + CaptureMouse(); + } else if (type == wxEVT_LEFT_UP || type == wxEVT_RIGHT_UP || type == wxEVT_MIDDLE_UP) { + if (m_gesture != Gesture::RotateIslandModal && m_gesture != Gesture::ScaleIslandModal) { + end_gesture(); + Refresh(); + } + } else if (type == wxEVT_MOTION) { + switch (m_gesture) { + case Gesture::Pan: { + const wxSize size = GetSize(); + const float scale = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + // Both axes point the same way on screen as in UV space (v runs down), so dragging the + // content along with the cursor is a subtraction on both. + m_pan.x() -= float(pos.x - m_drag_last_px.x) * scale; + m_pan.y() -= float(pos.y - m_drag_last_px.y) * scale; + m_drag_last_px = pos; + Refresh(); + break; + } + case Gesture::MoveIsland: { + const Vec2f uv = screen_to_uv(pos); + if (m_on_island_edit) + m_on_island_edit(m_selected_island, uv_delta_to_unwrap(uv - m_gesture_last_uv), 0.f, 1.f, false); + m_gesture_last_uv = uv; + Refresh(); + break; + } + case Gesture::MoveVertex: { + const Vec2f uv = screen_to_uv(pos); + move_vertex_raw(m_active_vertex, uv - m_gesture_last_uv); + m_gesture_last_uv = uv; + m_vertex_edit_moved = true; + Refresh(); + break; + } + case Gesture::MoveEdge: { + const Vec2f uv = screen_to_uv(pos); + const Vec2f delta = uv - m_gesture_last_uv; + move_vertex_raw(m_active_edge.first, delta); + move_vertex_raw(m_active_edge.second, delta); + m_gesture_last_uv = uv; + m_vertex_edit_moved = true; + Refresh(); + break; + } + case Gesture::RotateIsland: + case Gesture::RotateIslandModal: { + const Vec2f rel = screen_to_uv(pos) - island_centroid(m_selected_island); + const float angle = std::atan2(rel.y(), rel.x()); + // Accumulate the raw mouse rotation incrementally so it survives crossing +/-180 degrees. + m_rot_raw_deg += angle_delta(m_gesture_last_angle, angle) * 180.f / float(M_PI); + m_gesture_last_angle = angle; + + // With Shift, quantise to *global* 15-degree marks (0/15/30...), i.e. snap the island's + // absolute on-screen orientation, not 15 degrees relative to wherever it started (#10) -- + // snapping the target rather than each delta is what keeps it from juddering on a step. + constexpr float STEP = 15.f; + const float absolute = m_rot_base_deg + m_rot_raw_deg; + const float target_abs = evt.ShiftDown() ? std::round(absolute / STEP) * STEP : absolute; + const float deg = target_abs - (m_rot_base_deg + m_rot_applied_deg); + if (m_on_island_edit && deg != 0.f) + m_on_island_edit(m_selected_island, Vec2f::Zero(), deg, 1.f, false); + m_rot_applied_deg = target_abs - m_rot_base_deg; + m_rot_display_deg = target_abs; + Refresh(); + break; + } + case Gesture::ScaleIslandModal: { + const Vec2f rel = screen_to_uv(pos) - island_centroid(m_selected_island); + const float dist = std::max(rel.norm(), 1e-6f); + const float factor = dist / m_gesture_last_dist; + if (m_on_island_edit && factor != 1.f) + m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, factor, false); + m_modal_scale_accum *= factor; + m_gesture_last_dist = dist; + Refresh(); + break; + } + default: break; + } + } else if (type == wxEVT_MOUSEWHEEL) { + // Zoom about the cursor, not the view centre - otherwise zooming in on something off to the + // side walks it straight out of the frame. + const Vec2f before = screen_to_uv(pos); + const float factor = std::pow(0.9f, float(evt.GetWheelRotation()) / float(evt.GetWheelDelta())); + m_zoom = std::clamp(m_zoom * factor, 0.001f, 5000.f); + const Vec2f after = screen_to_uv(pos); + m_pan += before - after; + Refresh(); + } +} + +void UVEditorCanvas::rebuild_island_models() +{ + m_mesh_dirty = false; + m_island_wireframe.clear(); + m_island_boundary.clear(); + m_island_fill.clear(); + + const int islands = std::max(m_islands.island_count, 0); + if (islands == 0 || m_islands.uvs.empty() || m_islands.indices.empty()) + return; + + m_island_wireframe.resize(size_t(islands)); + m_island_boundary.resize(size_t(islands)); + m_island_fill.resize(size_t(islands)); + + const int vertex_count = int(m_islands.uvs.size()); + + // Charts have disjoint vertex sets (compute_patch_unwrap() duplicates seam vertices per chart), + // so every vertex belongs to exactly one island and this partition is clean. + std::unordered_map is_boundary; + is_boundary.reserve(m_islands.boundary_edges.size() * 2); + for (const auto &[a, b] : m_islands.boundary_edges) + is_boundary[undirected_edge_key(a, b)] = true; + + // Named, not size_t(islands) inline: the latter is a most-vexing-parse and declares a function + // (a single identifier in the parens reads as a parameter name), which is what every + // "subscript requires array or pointer type" error on these vectors was. + const size_t n_islands = size_t(islands); + + // Global vertex index -> index within its own island's buffers. + std::vector local(m_islands.uvs.size(), -1); + std::vector> island_verts(n_islands); + for (size_t i = 0; i < m_islands.uvs.size(); ++i) { + const int island = m_islands.vertex_island[i]; + if (island < 0 || island >= islands) + continue; + local[i] = int(island_verts[size_t(island)].size()); + island_verts[size_t(island)].push_back(m_islands.uvs[i]); + } + + std::vector wire(n_islands), outline(n_islands), fill(n_islands); + for (int c = 0; c < islands; ++c) { + for (GLModel::Geometry *g : { &wire[size_t(c)], &outline[size_t(c)] }) + g->format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + fill[size_t(c)].format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + for (GLModel::Geometry *g : { &wire[size_t(c)], &outline[size_t(c)], &fill[size_t(c)] }) { + g->reserve_vertices(island_verts[size_t(c)].size()); + for (const Vec2f &uv : island_verts[size_t(c)]) + g->add_vertex(Vec3f(uv.x(), uv.y(), 0.f)); + } + } + + for (const Vec3i32 &tri : m_islands.indices) { + if (tri.minCoeff() < 0 || tri.maxCoeff() >= vertex_count) + continue; + const int island = m_islands.vertex_island[size_t(tri[0])]; + if (island < 0 || island >= islands) + continue; + + fill[size_t(island)].add_triangle(unsigned(local[size_t(tri[0])]), unsigned(local[size_t(tri[1])]), + unsigned(local[size_t(tri[2])])); + // Interior edges only: the island outline is drawn separately, brighter and on top, so drawing + // it here as well would just dim it by blending against itself. + for (int i = 0; i < 3; ++i) { + const int a = tri[i], b = tri[(i + 1) % 3]; + if (!is_boundary.count(undirected_edge_key(a, b))) + wire[size_t(island)].add_line(unsigned(local[size_t(a)]), unsigned(local[size_t(b)])); + } + } + + for (const auto &[a, b] : m_islands.boundary_edges) { + if (a < 0 || b < 0 || a >= vertex_count || b >= vertex_count) + continue; + const int island = m_islands.vertex_island[size_t(a)]; + if (island < 0 || island >= islands) + continue; + outline[size_t(island)].add_line(unsigned(local[size_t(a)]), unsigned(local[size_t(b)])); + } + + for (int c = 0; c < islands; ++c) { + if (!wire[size_t(c)].is_empty()) + m_island_wireframe[size_t(c)].init_from(std::move(wire[size_t(c)])); + if (!outline[size_t(c)].is_empty()) + m_island_boundary[size_t(c)].init_from(std::move(outline[size_t(c)])); + if (!fill[size_t(c)].is_empty()) + m_island_fill[size_t(c)].init_from(std::move(fill[size_t(c)])); + } +} + +void UVEditorCanvas::rebuild_grid() +{ + // One line per UV unit (i.e. per texture tile), plus a subdivision when zoomed in far enough that + // whole tiles would be too coarse to read. + const float span = 2.f * m_zoom; + float step = 1.f; + while (step > 0.01f && span / step > 40.f) + step *= 2.f; + while (span / step < 4.f) + step *= 0.5f; + + // Cover exactly the currently-visible view rectangle (plus a one-step margin), recomputed on every + // render. The old version only rebuilt when `step` changed, so panning at a fixed zoom left the grid + // frozen at wherever it was last built - which is the "not in whole area / not always rendered while + // moving" the user saw. The grid is only a few dozen lines, so rebuilding it per frame is cheap. + float half_w, half_h; + view_half_extents(half_w, half_h); + const Vec2f lo = m_pan - Vec2f(half_w, half_h) - Vec2f(step, step); + const Vec2f hi = m_pan + Vec2f(half_w, half_h) + Vec2f(step, step); + m_grid_step = step; + + const int first_x = int(std::floor(lo.x() / step)), last_x = int(std::ceil(hi.x() / step)); + const int first_y = int(std::floor(lo.y() / step)), last_y = int(std::ceil(hi.y() / step)); + if (last_x - first_x > 4000 || last_y - first_y > 4000) + return; // degenerate zoom; not worth drawing a grid nobody can see + + GLModel::Geometry grid; + grid.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + unsigned index = 0; + const auto line = [&](const Vec2f &a, const Vec2f &b) { + grid.add_vertex(Vec3f(a.x(), a.y(), 0.f)); + grid.add_vertex(Vec3f(b.x(), b.y(), 0.f)); + grid.add_line(index, index + 1); + index += 2; + }; + for (int i = first_x; i <= last_x; ++i) + line(Vec2f(float(i) * step, lo.y()), Vec2f(float(i) * step, hi.y())); + for (int i = first_y; i <= last_y; ++i) + line(Vec2f(lo.x(), float(i) * step), Vec2f(hi.x(), float(i) * step)); + + m_grid_glmodel.reset(); + if (!grid.is_empty()) + m_grid_glmodel.init_from(std::move(grid)); +} + +void UVEditorCanvas::rebuild_rotation_dial() +{ + m_dial_glmodel.reset(); + const bool rotating = (m_gesture == Gesture::RotateIsland || m_gesture == Gesture::RotateIslandModal); + if (!rotating || m_selected_island < 0) + return; + + const wxSize size = GetSize(); + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + const float radius = 90.f * uv_per_px; // ~constant on-screen size regardless of zoom + const Vec2f centre = island_centroid(m_selected_island); + + GLModel::Geometry dial; + dial.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + unsigned index = 0; + const auto line = [&](const Vec2f &a, const Vec2f &b) { + dial.add_vertex(Vec3f(a.x(), a.y(), 0.f)); + dial.add_vertex(Vec3f(b.x(), b.y(), 0.f)); + dial.add_line(index, index + 1); + index += 2; + }; + const auto on_ring = [&](float deg, float r) { + const float a = deg * float(M_PI) / 180.f; + return centre + r * Vec2f(std::cos(a), std::sin(a)); + }; + + // The ring itself. + constexpr int SEG = 72; + for (int i = 0; i < SEG; ++i) + line(on_ring(float(i) * 360.f / SEG, radius), on_ring(float(i + 1) * 360.f / SEG, radius)); + // A tick every 15 degrees (the snap marks), longer on the cardinals. + for (int d = 0; d < 360; d += 15) { + const bool cardinal = (d % 90) == 0; + line(on_ring(float(d), radius * (cardinal ? 0.80f : 0.90f)), on_ring(float(d), radius * (cardinal ? 1.08f : 1.0f))); + } + // The needle at the island's current absolute angle. + line(centre, on_ring(m_rot_display_deg, radius * 1.12f)); + + if (!dial.is_empty()) + m_dial_glmodel.init_from(std::move(dial)); +} + +void UVEditorCanvas::rebuild_background_quad() +{ + m_background_glmodel.reset(); + m_background_quad_dirty = false; + if (m_background_width <= 0 || m_background_height <= 0) + return; + + Vec2f lo, hi; + content_bounds(lo, hi); + if (m_tile_enabled) { + // Snap out to whole tiles. Only cosmetic, but it keeps the backdrop's edge on a tile + // boundary instead of slicing a brick in half at an arbitrary place. + lo = Vec2f(std::floor(lo.x()), std::floor(lo.y())); + hi = Vec2f(std::ceil(hi.x()), std::ceil(hi.y())); + } else { + // Tiling off: the sampler reads 0 outside the first tile and nothing else exists, so the + // backdrop is exactly that one tile (GL_CLAMP_TO_BORDER in render() gives it the same + // black surround, rather than smearing the edge texels outward). + lo = Vec2f(0.f, 0.f); + hi = Vec2f(1.f, 1.f); + } + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + // Texcoord == position: the height sampling maps one whole texture onto each unit square of uv + // (see DecodedHeightTexture::sample()), so uv *is* the texture coordinate. That also puts the + // texture's first pixel row at v = 0, exactly where sample() reads it. + init_data.add_vertex(Vec3f(lo.x(), lo.y(), 0.f), Vec2f(lo.x(), lo.y())); + init_data.add_vertex(Vec3f(hi.x(), lo.y(), 0.f), Vec2f(hi.x(), lo.y())); + init_data.add_vertex(Vec3f(hi.x(), hi.y(), 0.f), Vec2f(hi.x(), hi.y())); + init_data.add_vertex(Vec3f(lo.x(), hi.y(), 0.f), Vec2f(lo.x(), hi.y())); + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(0, 2, 3); + m_background_glmodel.init_from(std::move(init_data)); +} + +void UVEditorCanvas::rebuild_background_texture() +{ + m_background_texture.reset(); + m_background_dirty = false; + if (m_background_width > 0 && m_background_height > 0) + m_background_texture.load_from_raw_data(m_background_pixels, (unsigned int) m_background_width, + (unsigned int) m_background_height, false); + // The quad's extent doesn't depend on the pixels, but it does have to exist alongside them. + m_background_quad_dirty = true; +} + +void UVEditorCanvas::on_paint(wxPaintEvent & /*evt*/) +{ + wxPaintDC dc(this); + render(); +} + +void UVEditorCanvas::on_size(wxSizeEvent &evt) +{ + evt.Skip(); + // Refresh() alone only *schedules* a repaint, which Windows can coalesce/delay until a live + // resize drag ends, leaving stale wrong-aspect-ratio content on screen throughout the drag. + // Update() flushes it immediately - still through the normal paint path (unlike calling + // render() directly), which matters because this canvas shares the app's one GL context with + // the 3D view and must not hijack it outside its own paint. + Refresh(); + Update(); +} + +void UVEditorCanvas::render() +{ + if (m_context == nullptr || !IsShownOnScreen()) + return; + + // wxGLCanvas::SetCurrent() genuinely fails (returns false) on a canvas that isn't shown on + // screen, and can also fail on a pixel-format mismatch with the context - and every GL call + // made afterwards would then run against whichever context *is* current, which here is the main + // 3D canvas mid-frame. Bail instead of corrupting it. + if (!SetCurrent(*m_context)) + return; + + if (m_mesh_dirty) + rebuild_island_models(); + if (m_background_dirty) + rebuild_background_texture(); + if (m_background_quad_dirty) + rebuild_background_quad(); + if (m_needs_fit) + fit_view_to_content(); + rebuild_grid(); + rebuild_rotation_dial(); + + const wxSize size = GetSize(); + glsafe(::glViewport(0, 0, size.GetWidth(), size.GetHeight())); + glsafe(::glClearColor(UV_COLOR_BG.r(), UV_COLOR_BG.g(), UV_COLOR_BG.b(), 1.f)); + glsafe(::glClear(GL_COLOR_BUFFER_BIT)); + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + // Orthographic, centered on m_pan. Y is negated so v runs down the screen, which is what puts + // the texture's first pixel row (v = 0, see rebuild_background_quad()) at the top rather than + // upside down. + float half_w, half_h; + view_half_extents(half_w, half_h); + + Transform3d projection_matrix = Transform3d::Identity(); + projection_matrix(0, 0) = 1.0 / std::max(double(half_w), 1e-6); + projection_matrix(1, 1) = -1.0 / std::max(double(half_h), 1e-6); + Transform3d view_matrix = Transform3d::Identity(); + view_matrix.translate(Vec3d(-double(m_pan.x()), -double(m_pan.y()), 0.0)); + + const auto draw = [&](GLModel &model, const ColorRGBA &color, const Transform3d &model_matrix) { + if (!model.is_initialized()) + return; + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + shader->start_using(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + model.set_color(color); + model.render(); + shader->stop_using(); + }; + const Transform3d identity = Transform3d::Identity(); + + if (m_background_glmodel.is_initialized()) { + if (GLShaderProgram *shader = wxGetApp().get_shader("flat_texture")) { + shader->start_using(); + shader->set_uniform("view_model_matrix", view_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_background_texture.get_id())); + // Repeat exactly the way DecodedHeightTexture::sample() does, so the backdrop under an + // island really is the texels that island will sample. With tiling off, sample() returns + // 0 outside the first tile - a black border, not a smeared edge, which is what + // CLAMP_TO_BORDER reproduces (GLTexture's own default of GL_REPEAT would not). + const GLint wrap = m_tile_enabled ? (m_tile_mirrored ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_BORDER; + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap)); + if (!m_tile_enabled) { + constexpr GLfloat border[4] = { 0.f, 0.f, 0.f, 1.f }; + glsafe(::glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border)); + } + m_background_glmodel.render(); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + shader->stop_using(); + } + } + + set_line_width(1.f); + draw(m_grid_glmodel, UV_COLOR_GRID, identity); + + // The texture's first tile. Always drawn, even with nothing painted, so the pane always has a + // fixed landmark: the unwrap is packed in mm and divided by the tile size, so it is routinely + // many tiles away from here, and without this there is no way to tell "the islands are somewhere + // else" apart from "there are no islands". + if (!m_tile_outline_glmodel.is_initialized()) { + GLModel::Geometry tile; + tile.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + tile.reserve_vertices(4); + tile.reserve_indices(8); + tile.add_vertex(Vec3f(0.f, 0.f, 0.f)); + tile.add_vertex(Vec3f(1.f, 0.f, 0.f)); + tile.add_vertex(Vec3f(1.f, 1.f, 0.f)); + tile.add_vertex(Vec3f(0.f, 1.f, 0.f)); + for (unsigned i = 0; i < 4; ++i) + tile.add_line(i, (i + 1) % 4); + m_tile_outline_glmodel.init_from(std::move(tile)); + } + set_line_width(2.f); + draw(m_tile_outline_glmodel, UV_COLOR_TILE_OUTLINE, identity); + + const auto island_matrix = [this, &identity](int c) { + return (size_t(c) < m_transforms.size()) ? to_transform3d(m_transforms[size_t(c)]) : identity; + }; + + // Island fills first, as a translucent wash under the wires: every island gets a faint one so it + // reads as a solid patch rather than a wire cage, and the selected one gets the accent teal (#7). + // When a distortion heatmap has been supplied (set_island_fill_colors), unselected islands take + // their heatmap colour instead of the default wash; the selected one still wins its highlight. + for (int c = 0; c < int(m_island_fill.size()); ++c) { + ColorRGBA fill = UV_COLOR_FILL; + if (size_t(c) < m_island_fill_colors.size()) + fill = m_island_fill_colors[size_t(c)]; + draw(m_island_fill[size_t(c)], is_selected(c) ? UV_COLOR_SEL_FILL : fill, island_matrix(c)); + } + + set_line_width(1.f); + for (int c = 0; c < int(m_island_wireframe.size()); ++c) + draw(m_island_wireframe[size_t(c)], is_selected(c) ? UV_COLOR_SEL_WIRE : UV_COLOR_WIRE, island_matrix(c)); + + // The island outlines - i.e. exactly the edges the seam angle cut the patch along. Drawn last + // and brightest, because "where does one island end and the next begin" is the single thing this + // pane exists to answer. The selected one gets a bold edge (#7). + for (int c = 0; c < int(m_island_boundary.size()); ++c) { + const bool selected = is_selected(c); + set_line_width(selected ? 3.f : 1.5f); + draw(m_island_boundary[size_t(c)], selected ? UV_COLOR_SEL_BOUNDARY : UV_COLOR_BOUNDARY, island_matrix(c)); + } + set_line_width(1.f); + + // The rotation protractor, on top of everything while a rotation gesture is live (#11). + if (m_dial_glmodel.is_initialized()) { + set_line_width(2.f); + draw(m_dial_glmodel, UV_COLOR_DIAL, identity); + set_line_width(1.f); + } + + // Vertex/Edge mode handles: a small square drawn over the picked vertex (or each endpoint of the + // picked edge), so it is obvious which sub-element a drag will move. The already-drawn boundary + // sits between an edge's two markers, so the pair reads as "this edge". + if (m_select_mode != SelectMode::Island && (m_active_vertex >= 0 || m_active_edge.first >= 0)) { + if (!m_vertex_marker_glmodel.is_initialized()) { + GLModel::Geometry q; + q.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + q.reserve_vertices(4); + q.reserve_indices(6); + q.add_vertex(Vec3f(-0.5f, -0.5f, 0.f)); + q.add_vertex(Vec3f(0.5f, -0.5f, 0.f)); + q.add_vertex(Vec3f(0.5f, 0.5f, 0.f)); + q.add_vertex(Vec3f(-0.5f, 0.5f, 0.f)); + q.add_triangle(0, 1, 2); + q.add_triangle(0, 2, 3); + m_vertex_marker_glmodel.init_from(std::move(q)); + } + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + const float marker = uv_per_px * 10.f; // ~10 px square, constant on screen at any zoom + const auto draw_marker = [&](int v) { + if (v < 0 || size_t(v) >= m_islands.uvs.size()) + return; + const Vec2f p = island_uv(size_t(v)); + Transform3d m = Transform3d::Identity(); + m.translate(Vec3d(double(p.x()), double(p.y()), 0.0)); + m.scale(double(marker)); + draw(m_vertex_marker_glmodel, UV_COLOR_SEL_BOUNDARY, m); + }; + if (m_active_vertex >= 0) + draw_marker(m_active_vertex); + if (m_active_edge.first >= 0) { + draw_marker(m_active_edge.first); + draw_marker(m_active_edge.second); + } + } + + // The GL context is shared with the 3D view; leave the bits we touched as we found them. + glsafe(::glDisable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + SwapBuffers(); + + // Cheap, and render() runs on every gesture change (each Refresh), so the status line stays + // current without threading update_status() through every mouse/key transition. The panel + // guards against relayout when the text is unchanged. + update_status(); +} + +// ---------------------------------------------------------------------------------------------- +// UVEditorPanel +// ---------------------------------------------------------------------------------------------- + +namespace { +enum : int { + ID_UV_FRAME = wxID_HIGHEST + 4200, + ID_UV_SNAP, + ID_UV_AVG_SCALE, + ID_UV_CUT, + ID_UV_PROJECT, + ID_UV_JOIN, + ID_UV_UNJOIN, +}; +} // namespace + +UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) +{ + // Icon + label buttons: the tool icon (toolbar_texture_displacement.svg) is reused on all four as a + // placeholder until dedicated per-tool art exists, and the label is kept alongside it so the + // buttons stay tellable apart while they share one picture. + const wxBitmap icon = create_scaled_bitmap("toolbar_texture_displacement", this, 16); + auto *bar = new wxBoxSizer(wxHORIZONTAL); + const auto add_button = [&](int id, const wxString &label, const wxString &tip) { + auto *b = new wxButton(this, id, label, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + b->SetBitmap(icon); + b->SetToolTip(tip); + bar->Add(b, 0, wxALL, 2); + b->Bind(wxEVT_BUTTON, &UVEditorPanel::on_tool, this); + return b; + }; + add_button(ID_UV_FRAME, _L("Frame"), _L("Frame all islands (Home)")); + m_snap_button = new wxToggleButton(this, ID_UV_SNAP, _L("Snap"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + m_snap_button->SetBitmap(icon); + m_snap_button->SetToolTip(_L("Snap islands together when dragging")); + bar->Add(m_snap_button, 0, wxALL, 2); + m_snap_button->Bind(wxEVT_TOGGLEBUTTON, &UVEditorPanel::on_tool, this); + add_button(ID_UV_AVG_SCALE, _L("Avg scale"), _L("Give every island the same texel density")); + add_button(ID_UV_CUT, _L("Cut"), _L("Split the selected island across its long axis")); + add_button(ID_UV_JOIN, _L("Join"), _L("Unfold the selected island onto its nearest neighbour along their shared edge")); + add_button(ID_UV_UNJOIN, _L("Unjoin"), _L("Send the selected island back to its own packed position")); + + m_canvas = new UVEditorCanvas(this); + m_canvas->set_status_callback([this](const wxString &text) { + if (m_status != nullptr && m_status->GetLabel() != text) { + m_status->SetLabel(text); + m_status->Refresh(); + } + }); + + m_status = new wxStaticText(this, wxID_ANY, wxEmptyString); + m_status->SetForegroundColour(wxColour(180, 180, 180)); + + auto *sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(bar, 0, wxEXPAND); + sizer->Add(m_canvas, 1, wxEXPAND); + sizer->Add(m_status, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, 3); + SetSizer(sizer); +} + +void UVEditorPanel::on_tool(wxCommandEvent &evt) +{ + switch (evt.GetId()) { + case ID_UV_FRAME: m_canvas->run_command(UVEditorCanvas::Command::FrameAll); break; + case ID_UV_SNAP: m_canvas->run_command(UVEditorCanvas::Command::ToggleSnap); break; + case ID_UV_AVG_SCALE: m_canvas->run_command(UVEditorCanvas::Command::AverageScale); break; + case ID_UV_CUT: m_canvas->run_command(UVEditorCanvas::Command::CutSelectedIsland); break; + case ID_UV_JOIN: m_canvas->run_command(UVEditorCanvas::Command::JoinSelected); break; + case ID_UV_UNJOIN: m_canvas->run_command(UVEditorCanvas::Command::UnjoinSelected); break; + default: evt.Skip(); return; + } + // Keep the toggle button's visual state in step with the canvas, which owns the flag. + if (m_snap_button != nullptr) + m_snap_button->SetValue(m_canvas->snap_enabled()); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/UVEditorCanvas.hpp b/src/slic3r/GUI/UVEditorCanvas.hpp new file mode 100644 index 0000000000..87d85d6e2c --- /dev/null +++ b/src/slic3r/GUI/UVEditorCanvas.hpp @@ -0,0 +1,316 @@ +#ifndef slic3r_UVEditorCanvas_hpp_ +#define slic3r_UVEditorCanvas_hpp_ + +#include +#include +#include +#include + +// Must come before wx/glcanvas.h in any translation unit that includes this header: glcanvas.h +// pulls in the platform's real GL/gl.h, and glad/gl.h errors out if that happens first (it wants +// to be the one to define the standard include guards GL/gl.h itself defines). +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/Point.hpp" +#include "GLModel.hpp" +#include "GLTexture.hpp" + +namespace Slic3r::GUI { + +// Standalone 2D viewer/editor for a flattened (UV-unwrapped) mesh patch -- shows the result of +// GLGizmoTextureDisplacement's LSCM projection method as its own resizable pane (see Plater's +// "uv_editor" AUI pane) rather than folding 2D UV-space rendering into the main 3D viewport. +// +// Islands can be laid out by hand, roughly the way Blender's UV editor works: click one to select +// it, drag to move it, right-drag or press R to rotate it, S to scale it, both about its own centre. +// Islands are free to overlap -- nothing re-packs them behind the user's back. The texture underneath +// is always drawn upright and axis-aligned, and it is the islands that move over it, which is what +// makes "rotate this island" a meaningful gesture rather than just spinning the whole texture. +// +// **Geometry is uploaded in the unwrap's own (raw, mm) coordinates, once**, and each island is drawn +// through its own affine matrix passed as a shader uniform. That matters: a patch can easily run to +// a million triangles, and the earlier design -- which pre-transformed every UV on the CPU and +// re-uploaded the whole wireframe on every mouse-move event -- made a drag cost a couple of hundred +// milliseconds per frame. Moving an island now touches a 2x3 matrix and nothing else. +// +// Uses the app's single shared wxGLContext (via wxGetApp().init_glcontext(), the same call +// View3D/Preview/AssembleView each make in GUI_Preview.cpp) rather than an independent context of +// its own, specifically so it can reuse the app's already-registered "flat"/"flat_texture" +// shaders and GLModel as-is -- GLModel::render() looks up its shader via a GUI_App-wide "current +// shader", which only means anything for canvases sharing the app's one real GL context. +class UVEditorCanvas : public wxGLCanvas +{ +public: + explicit UVEditorCanvas(wxWindow *parent); + + // Maps one island's raw unwrap coordinate to a texture UV: the island's own hand placement and + // then the layer's tiling/rotation/offset, composed into a single affine (columns: x basis, + // y basis, translation). + using IslandTransform = Eigen::Matrix; + + // The unwrap to display, in the unwrap's own mm coordinates -- *not* texture UVs. Changing this + // is the expensive path (it rebuilds every vertex buffer), so it must only be called when the + // unwrap itself changes, never merely because an island moved. Pass an empty `indices` to show + // nothing. + struct Islands + { + std::vector uvs; + std::vector indices; + std::vector vertex_island; // per uv + std::vector> boundary_edges; // island outlines, indices into uvs + int island_count = 0; + }; + void set_islands(Islands islands); + + // The cheap path: one transform per island. Safe to call on every mouse-move of a drag. + void set_island_transforms(std::vector transforms); + + // One fill colour per island, overriding the default light-green wash -- used to paint the UV + // distortion heatmap over the islands when the gizmo's "Distortion" check mode is on (#7/#14). + // Pass empty to go back to the default wash. Cheap: it never touches a vertex buffer. + void set_island_fill_colors(std::vector colors); + + // The layer's own tiling scale and rotation. Needed to map a gesture, which happens in texture-UV + // space, back into the unwrap's mm space -- which is where a TextureIsland's offset actually + // lives (see apply_uv_transform()). The tile settings come along because the background has to + // repeat exactly the way the height sampler does, or the pane would stop showing what gets baked. + void set_uv_transform(float tiling_scale, float rotation_deg, bool tile_enabled, bool tile_mirrored); + + // Same 8-bit grayscale pixels build_texture_displacement()'s height sampling uses, shown + // beneath the wireframe (expanded to RGBA on upload) so the unwrap can be checked against the + // texture it will actually sample. Pass width/height <= 0 to clear it. + void set_background_texture(const std::vector &grayscale_pixels, int width, int height); + + // Snap a dragged island's boundary to a neighbouring island's when they come close (#2). Off is + // the honest default for overlap-friendly layouts; the pane toolbar toggles it. + void set_snap_enabled(bool enabled) { m_snap_enabled = enabled; } + bool snap_enabled() const { return m_snap_enabled; } + + // High-level actions the pane toolbar triggers. The canvas handles the view-only ones (framing, + // the snap toggle) itself and forwards the rest to whoever owns the island data (the gizmo), via + // the command callback -- the canvas has the selection and the view, the gizmo has the layer. + enum class Command { FrameAll, ToggleSnap, AverageScale, CutSelectedIsland, ProjectFromView, JoinSelected, UnjoinSelected }; + void run_command(Command cmd); + using CommandFn = std::function; + void set_command_callback(CommandFn fn) { m_on_command = std::move(fn); } + + // Called whenever the one-line status/hint text changes (current gesture + the shortcuts that + // apply right now), so the pane can show it Blender-style along the bottom. + using StatusFn = std::function; + void set_status_callback(StatusFn fn) { m_on_status = std::move(fn); } + + // Reports an island edit as it happens. The deltas are *incremental* (one mouse event's worth) + // and already converted into the units a TextureIsland stores -- unwrap mm, degrees, and a scale + // *factor* to multiply the island's existing scale by. They are incremental on purpose: the owner + // applies them and hands back fresh transforms, and if the gesture tracked geometry rather than + // raw mouse motion that round trip would feed back into itself. `finished` marks the end of a + // gesture, so the owner can rebuild the 3D preview once rather than on every motion event. + using IslandEditFn = + std::function; + void set_island_edit_callback(IslandEditFn fn) { m_on_island_edit = std::move(fn); } + + // What a click grabs: a whole island (move/rotate/scale, groups move together), a single vertex, or + // a single edge (both its endpoints). Vertex/Edge are free-form UV editing -- they move the actual + // unwrap coordinates, which the owner then folds into the layer's per-vertex UV overrides so the + // change is baked, not just shown (see set_vertex_edit_callback). + enum class SelectMode { Island, Vertex, Edge }; + void set_select_mode(SelectMode mode); + SelectMode select_mode() const { return m_select_mode; } + + // Reports a committed vertex/edge edit: the list of (unwrapped-vertex index, its new raw-unwrap + // coordinate in mm). Fired once, on mouse release, since it re-solves the displacement preview; the + // pane shows the edit live from its own geometry in the meantime. The owner maps the unwrapped index + // to a mesh vertex (via the unwrap's source_vertex) and stores the override. + using UVVertexEditFn = std::function> &edits)>; + void set_vertex_edit_callback(UVVertexEditFn fn) { m_on_vertex_edit = std::move(fn); } + + // The primary (last-clicked) island, still the pivot for rotate/scale and the target of the + // single-island toolbar commands (Cut/Join/Unjoin). -1 if nothing is selected. + int selected_island() const { return m_selected_island; } + // The full multi-selection (Shift adds, Ctrl toggles). Always contains m_selected_island when it is + // >= 0. The gizmo reads this to decide which islands a drag moves together, unioned with each + // selected island's join group. + const std::vector &selected_islands() const { return m_selection; } + void reset_view(); + +private: + void on_paint(wxPaintEvent &evt); + void on_size(wxSizeEvent &evt); + void on_mouse(wxMouseEvent &evt); + void on_key(wxKeyEvent &evt); + void on_erase_background(wxEraseEvent &evt) {} // required to avoid flicker on MSW, deliberately a no-op + + void render(); + void rebuild_island_models(); + void rebuild_background_texture(); + void rebuild_background_quad(); + void rebuild_grid(); + // The UV region worth looking at: every island, plus always at least the texture's first tile, so + // there is something sensibly framed even before anything is painted. + void content_bounds(Vec2f &min_uv, Vec2f &max_uv) const; + // Frames content_bounds(). Bound to Home, and run once each time an unwrap first appears. + void fit_view_to_content(); + + // Half-extents of the visible UV region. Split out because both rendering and every mouse + // gesture need them, and they have to agree exactly or picking lands in the wrong place. + void view_half_extents(float &half_w, float &half_h) const; + Vec2f screen_to_uv(const wxPoint &px) const; + // Raw unwrap coordinate -> texture UV, through the island's own transform. + Vec2f island_uv(size_t vertex) const; + // The island under `uv`, or -1. Prefers the current selection when islands overlap, so that + // dragging one that sits under another doesn't hand the drag to its neighbour halfway through. + int island_at(const Vec2f &uv) const; + // Nearest unwrapped vertex to `uv` within a screen-space threshold, or -1 (Vertex mode picking). + int vertex_at(const Vec2f &uv) const; + // Nearest island-boundary edge to `uv` within a screen-space threshold, as its two unwrapped-vertex + // indices, or {-1,-1} (Edge mode picking). + std::pair edge_at(const Vec2f &uv) const; + // Moves one unwrapped vertex by a texture-UV delta, converting it back into the vertex's own raw + // unwrap space through its island's inverse transform, and marks the mesh dirty so it redraws. + void move_vertex_raw(int unwrapped_vertex, const Vec2f &delta_uv); + Vec2f island_centroid(int island) const; + // Converts a delta in texture-UV space into the unwrap's mm space, undoing the layer's scale and + // rotation -- the inverse of what apply_uv_transform() did on the way in. + Vec2f uv_delta_to_unwrap(const Vec2f &delta_uv) const; + // The correction that would bring the selected island's nearest boundary vertex onto a boundary + // vertex of some *other* island, in texture-UV space. Zero if nothing is within reach (#2). + Vec2f snap_correction(int island) const; + void end_gesture(); + // Rebuilds the status line from the current gesture/selection and pushes it to m_on_status. + void update_status(); + + wxGLContext *m_context = nullptr; // owned by OpenGLManager/GUI_App, not by this canvas + + Islands m_islands; + std::vector m_transforms; + // Per-island fill colour override (distortion heatmap); empty means use the default wash (#7). + std::vector m_island_fill_colors; + // Boundary vertices per island, for snapping -- a patch's boundary is a tiny fraction of it, and + // rescanning the whole uv array on every snap test would not be. + std::vector> m_island_boundary_verts; + + bool m_mesh_dirty = true; + // One set of models per island, so an island can be drawn through its own transform. Built once + // per unwrap, never on a drag. + std::vector m_island_wireframe; // interior edges + std::vector m_island_boundary; // outline + std::vector m_island_fill; // filled, for the selected island's wash + + GLModel m_tile_outline_glmodel; // the texture's first tile, [0,1]^2 -- the "you are here" + GLModel m_grid_glmodel; + float m_grid_step = 0.f; // the UV step m_grid_glmodel was built for; 0 = not built + + float m_tiling_scale = 1.f; + float m_rotation_deg = 0.f; + bool m_tile_enabled = true; + bool m_tile_mirrored = false; + bool m_snap_enabled = false; + + std::vector m_background_pixels; // RGBA, expanded from the grayscale input + int m_background_width = 0, m_background_height = 0; + bool m_background_dirty = false; // the pixels need (re)uploading + bool m_background_quad_dirty = true; // only the quad's extent changed + GLTexture m_background_texture; + // Covers content_bounds(), not just [0,1]: the unwrap is packed in mm and then divided by the + // layer's tile size, so it routinely spans many tiles, and a single-unit-square backdrop would + // leave most of the islands sitting over bare background. Texcoord == position, so the GL wrap + // mode repeats it exactly the way DecodedHeightTexture::sample() does. + GLModel m_background_glmodel; + + // 2D pan/zoom. m_pan is the UV-space point at the center of the view, m_zoom half the UV-space + // extent visible across the shorter screen edge. v runs *down* the screen, matching both the + // texture's own row order and every other UV editor's convention. + Vec2f m_pan = Vec2f(0.5f, 0.5f); + float m_zoom = 0.75f; + bool m_needs_fit = true; // fit the view to the next unwrap that arrives + + enum class Gesture + { + None, + Pan, + MoveIsland, + RotateIsland, // right-drag: rotation tracks the mouse, ends when the button is released + RotateIslandModal, // 'R': rotation tracks the mouse until a click confirms or Esc cancels + ScaleIslandModal, // 'S': likewise, distance from the centre drives the scale + MoveVertex, // Vertex mode: drag one unwrapped vertex + MoveEdge, // Edge mode: drag both endpoints of one boundary edge + }; + Gesture m_gesture = Gesture::None; + + SelectMode m_select_mode = SelectMode::Island; + // The sub-element being edited in Vertex/Edge mode (unwrapped-vertex indices), or -1/{-1,-1}. + int m_active_vertex = -1; + std::pair m_active_edge{ -1, -1 }; + // Set once a Vertex/Edge drag actually moves, so a bare click (select without drag) doesn't commit a + // no-op edit and take an undo snapshot for nothing. + bool m_vertex_edit_moved = false; + // Lazily-built small filled square, drawn at an edited/hovered vertex as a handle. + GLModel m_vertex_marker_glmodel; + int m_selected_island = -1; + // The full multi-selection; m_selected_island is its primary (last-clicked) member. Kept as a small + // vector rather than a set because it is tiny and iteration order (primary last) is convenient. + std::vector m_selection; + bool is_selected(int island) const + { + return std::find(m_selection.begin(), m_selection.end(), island) != m_selection.end(); + } + wxPoint m_drag_last_px; + Vec2f m_gesture_last_uv = Vec2f::Zero(); + float m_gesture_last_angle = 0.f; + float m_gesture_last_dist = 0.f; + // Rotation is tracked as two running totals over the gesture: the raw mouse rotation, and how much + // has actually been applied. With Shift held the applied total is quantised to 15-degree steps + // (Blender-style angle snapping), so the two diverge -- and driving the applied total off the raw + // one, rather than snapping each incremental delta, is what makes the snap stable instead of + // juddering. The raw/applied split also survives crossing +/-180 degrees, which a single wrapped + // angle would not. m_rot_applied doubles as the modal-rotate undo amount for Esc. + float m_rot_raw_deg = 0.f; + float m_rot_applied_deg = 0.f; + // The island's absolute on-screen rotation when the gesture began (decoded from its transform), so + // Shift can snap to *global* 15-degree marks (0/15/30...) rather than 15 degrees relative to + // wherever the island happened to start (#10). Also drives the angle read-out and the dial. + float m_rot_base_deg = 0.f; + float m_rot_display_deg = 0.f; // current absolute angle, for the status line and dial needle + float m_modal_scale_accum = 1.f; // so Esc can undo exactly what the modal scale applied, as a factor + + // A protractor drawn around the island while it rotates: a ring, a tick every 15 degrees, and a + // needle at the current angle, so the rotation is legible (#11). Rebuilt each frame during a + // rotation gesture (cheap: a few hundred short lines) and left empty otherwise. + GLModel m_dial_glmodel; + void rebuild_rotation_dial(); + // The island's current absolute rotation in degrees, decoded from its transform's first column. + float island_rotation_deg(int island) const; + + IslandEditFn m_on_island_edit; + UVVertexEditFn m_on_vertex_edit; + CommandFn m_on_command; + StatusFn m_on_status; +}; + +// Hosts a UVEditorCanvas together with a small icon toolbar (frame, snap, average scale, cut, +// project-from-view) and a Blender-style status line along the bottom that names the current gesture +// and the shortcuts in play (#18). This is what actually goes into Plater's "uv_editor" AUI pane; +// the gizmo still talks to the inner canvas, reached via canvas(). +class UVEditorPanel : public wxPanel +{ +public: + explicit UVEditorPanel(wxWindow *parent); + UVEditorCanvas *canvas() { return m_canvas; } + +private: + void on_tool(wxCommandEvent &evt); + + UVEditorCanvas *m_canvas = nullptr; + wxToggleButton *m_snap_button = nullptr; + wxStaticText *m_status = nullptr; +}; + +} // namespace Slic3r::GUI + +#endif // slic3r_UVEditorCanvas_hpp_ From dc5a48bdfd29206687f63ab5176b2a55b1c38fe0 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 16 Jul 2026 08:43:27 +0200 Subject: [PATCH 06/89] Add texture displacement toolbar icon and textures --- .../images/toolbar_texture_displacement.svg | 240 ++++++++++++++++++ resources/textures/displacement/Bricks.png | Bin 0 -> 1671 bytes resources/textures/displacement/Grid.png | Bin 0 -> 1634 bytes resources/textures/displacement/Hexagons.png | Bin 0 -> 7024 bytes resources/textures/displacement/Knurl.png | Bin 0 -> 2581 bytes resources/textures/displacement/Noise.png | Bin 0 -> 150336 bytes resources/textures/displacement/Quilt.png | Bin 0 -> 3691 bytes resources/textures/displacement/Studs.png | Bin 0 -> 2579 bytes resources/textures/displacement/Waves.png | Bin 0 -> 10630 bytes resources/textures/displacement/Weave.png | Bin 0 -> 2561 bytes .../textures/displacement/Wood Grain.png | Bin 0 -> 103929 bytes 11 files changed, 240 insertions(+) create mode 100644 resources/images/toolbar_texture_displacement.svg create mode 100644 resources/textures/displacement/Bricks.png create mode 100644 resources/textures/displacement/Grid.png create mode 100644 resources/textures/displacement/Hexagons.png create mode 100644 resources/textures/displacement/Knurl.png create mode 100644 resources/textures/displacement/Noise.png create mode 100644 resources/textures/displacement/Quilt.png create mode 100644 resources/textures/displacement/Studs.png create mode 100644 resources/textures/displacement/Waves.png create mode 100644 resources/textures/displacement/Weave.png create mode 100644 resources/textures/displacement/Wood Grain.png diff --git a/resources/images/toolbar_texture_displacement.svg b/resources/images/toolbar_texture_displacement.svg new file mode 100644 index 0000000000..c154258c75 --- /dev/null +++ b/resources/images/toolbar_texture_displacement.svg @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/textures/displacement/Bricks.png b/resources/textures/displacement/Bricks.png new file mode 100644 index 0000000000000000000000000000000000000000..f89431370b10112d89cb93ee672ee9766ec27284 GIT binary patch literal 1671 zcmciCUr19?90%~@pYl(bmSIpyDxwde{X<2jO=Dt4PSHxJmL_JoN+ZiHH@9UEX`)3h zDoYnp{>}N2W_fk9(oD$Ae@WB3$uzX=x^9hadcAj@vrq_noj)#@FNgc^Ip=)2rODiQ z5BDAJ0057KBb*cf9AR_>uJ8?py~~0C*vL)b#2y!F2z;PrTP$!Mjw+9=x{V&;+iKs^ zuL|tw-OA0g7Xw0ZUegqZ!wHm6G|sCZOIrmOevL@utR1_5G91R@WK{gHGERr06lw<0 zE|hddb}wm8!H%)MVifG@(dSDB&ri~4)(KC`N|qLV{@qGDsiG<)tbo3=?BEB0Gmrt` zg?upp5gmYC7G;dw`D$7orOfEK(UG@FkS-PN?a7Q6?eEP_5*>WcPZNcG$W0eTB*V?Z zIY92h7ZnjX1aq@eyQpY|VW}1d?=XUiOr2O%q_z_hNBgta&PTQf@mCPP-^Ldsz8LYv zh%ZKb@hbls;+um)v<6;bmu{j>s5X2fCBb|>{c)_1s%a`WJK6QL*Vwjer6b&?m0A<; zb?!p0&Sz1@n9j=jDomLrgSDpY`{POzzY15Gavzo0CQBdLiL>)YH1zM#Zk(My3PUsu z!F&y!U#G;`_54ZN*)Apdm%Y};G*`&CAby37Z$W$u;#&~kg7}tI{w(4f5>IvMFr!f2 znnOx-qcb#jte2{3EHkrC>@>K@)}!!$_p+R~wTIVDV8!@nTop1^L=)Yh%$Ml%l1CGR ze$6~$d>>Xs;4xclGtirV9g}LGv{Q4uLQJZvgFyrXcdU(?JH~@y`~~H| z1RW*8nesbH* literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Grid.png b/resources/textures/displacement/Grid.png new file mode 100644 index 0000000000000000000000000000000000000000..14bd166a2c0cf2f082a366e10a22c3391b4c7e7d GIT binary patch literal 1634 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 N8D2BhE!K9#{2w;?a?Ah# literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Knurl.png b/resources/textures/displacement/Knurl.png new file mode 100644 index 0000000000000000000000000000000000000000..373e2badd6a02b1852db59acbe7f604b42e3c2c3 GIT binary patch literal 2581 zcmaJ@eN+_J9ev|ihCu0<8li%9EV0rAKZ3!cVs;lw6hA;BB++7ED;O|Dh!73NWp|3k zYJo&08qkHMJw2wDPsPNvy6$>XK|p~7MZ{fLVP)~K?652{`!)0Wz?>R_yZ_9YGw;0L zyT5zy@4h)27rV~K%ijwC;IlqDY6}1}(CZA~iJsuQ*#(OLcuuoEDk34bdsrJ1y?tAN zhwcyl5sME+yipgh`282+J>&m)WI=wHSN_c3h=Y$5$-PfS8W)Op?hNwUHSCd5Kp$(4 z^?bB2W4N>r_*fx;I4Azs;;DW z(k~s>`?Ph8#QB$=PVCl%3shH?aNNq7?{mYCZRVbV?=`oh{ZE92Z%(N`9@=fHV&;`6 z?G^S4C3)XKjdJyCnr+O*bsfe`sT-|)8>`V6knW2LE*}F!En>$Sp%uS8O{oiV(CgYu#phP89ta<*tB{in7Ea-Ay*Z%CKpOys` zpYPhgJm9mkpy92?H7D;V{7l5{K-PN}=co3cDS%yYh=lh2dsn4iMiw|6ihR(I{s8 zs|~bXmw@6B0ex>JB|6YR{Bwtpk&3oS>7YZTxJZw8{nmoXi)~=hP6?>b7SLZ8k`<6j zfW52(lzSqi$4T+q2+JU9CuvACBdE{RWgLl?&ts%P3oR|fNy=# z2KF^Qg`k(vf-zRvTJOMr{P730Z?_V^W4Mnv;dJbJb)j#Q|1(8s3a2!JJifd2AX&smy z_V9U`GtF4lQDnUx0y+n&ky=QKf8R>9mz%LM7-ND8o;7=`iMqAOeJ7Cn5F5qI1@tx~ zP+|jdL<0?nS@;t&XtLth{dmIP;IfxH(far^)|d3-KfeI8PE&E?Dw)YFquj`9hl^Oa zS;+X&t{mRpBE4!{bJuC%Oz_ny?3=VA0llw~j628+ySYDKu*;rK7@7__sV3S_UC+dG zZrNerR+#+tW}RzdMX*=TD}I>I$=zZUABe0|6J#BY5T#xBVvZ z%G`duq{xC@Oq0;7$KfYD{G^xpk+%$J=GV`q)Kzk_0YY#(&D&aCKnC$CG1Z6WGqu9k13Eex^7&8RD zIhdvDT?Qz%e-lx+5hdAAVufYSD5?y|S53d7)bFGAUMqNn=R)CH zlz3A|+X}jor=-HzXRU+?)En*0*~jqp5*|J>a5J7}WeysUe;~t7YHYpVy{+N3KA$ef*$5jgu|mFcmudJ+C^)$p$8p|Njao18z*>*f4_qYk<(JjCar6hf|!f zX~wO|tLDaTu_;GY4Qo5u%dYMU-wq<(?tpe^5V-Ll(x=&VjRs=yFJya$Pz2!xhIN$- zzLh?U9UQEJwPUVJAvhzvtZ=HCksc$6RtXZTk(0W~gf~af#BnyMJ7pqN6RhKI>O?+- Zz~h`fjm-TpY2trx{p+z&HId@{{{dN+{A&OJ literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Noise.png b/resources/textures/displacement/Noise.png new file mode 100644 index 0000000000000000000000000000000000000000..c945e9ca8cec92f1db209c2bca0e85168063fda1 GIT binary patch literal 150336 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

g714vx=KJT~{G# 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

3jntS~UR>qC9^TnwAc!NTWGHPVv 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 Id!jS`1CBv0i2wiq literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Studs.png b/resources/textures/displacement/Studs.png new file mode 100644 index 0000000000000000000000000000000000000000..1f9baaa7e8f27f8f34d826b4f8d8fb58cdc5483e GIT binary patch literal 2579 zcmY+DX;f3!8ivClh!|NdwI~s}EecxHcx6ZdNw5wS!&E>KQG-_o6+#>Wgy8^Mfs2S- zMTr_ARE+~N$dn+26H1mKQVa$mkduLk%w%9n2#3H;SKA-%AN$+uUHe_%e%`ge+(3VC z?2(E3v~CO_y0 z#~1$piE1|*&DcPx2*-gJzN6#oZRK_79ySufvSF=W+GsINblk=jldhr-IW1xIZ?H=;7! zvFgviB#;;US9%pV51|G_JoyG)P6(CuUz#H`;E5LTt%;%O+>RG9yb?b@{~NfadnwcBQ=+4z7kl$w0O^HOJDVm^%=g@8|=0MWa>RHOVjH&=9*M&u&cVs>`cR>Es8PS$D`xVJn2&%41nG<@tr*4(74i^8LCAYr)gLPFfk z@Z>c{9L|186SFCUH_LD=W^* zY=Ug4iPaN4)GNCpF}Lwn7ag579}`qaC49+1f?WOfajsR>=t$_&8wSG`kI!?=IGwJT z&&&0;V^t+2Fj;W~9=epky68Qb>m(MKdeeQul-cDT>iU&-9s!q8zLbTgOE}3c+DNi0 zMRVV>!bATr#ss~}^B&2SqixNmXM%fbZLt|TaBfI2Gc!|Vib$rzB7Gby|79j z=!(X4dBU~~t6Q-sUoz4y>r#gaV=FjSo%_cXK}JS|6DJc42wcQuO1o7th=30=l7Zwg zmuK+sNfzDM9dDr#KV?Ww8Urlorn`T!RM1}F>1NJHPzu`10IB(_FiQoLkJk{}pTg>) z9>8qlIC$eBnQ-_h8|{FWQ15j7Gr)h1F~aMs-A8Csw3h{HV&4Hcak2)0>IXpGVjQ9M z5EFgI0ixF2m3I!9^UUtl-IsR?k-RlU%wl;bWT7o=hgVEgDC_05gWKJgcDAIF7u+%n>r{naG2=22s+)tn zG~=>ag#Gzez$sze>B`gdT;THjc&dSGY%9=oaU7KQlcnitjp*;}#ME1b3Vg?P&G6?p z@a;X?zd8OX+YFzcFVuTKfL#yHO5;y5NlmU0i$Is-``@agrXt}WW)0w$Ds#JX+qD)5 zYm$Zi&S%yF$*>GCu*S5a_LCKn@NrgaICoBwr7<}#SxZ{i+l-Rflmi}YPj;Vfeo459 z-8U{pr0?g_4fY<);jE$*tizdJxczPV*JC?8MM<=xbkrEuGi`Ir0lx;)(_MglcL=e8 zREY)9Ezx=fvrEjfp(&t(0L|9OC-9z2d|-RnH29EBAq3gm{sZ!NJ=hXLtkHXgtT0PC zRMDv>3Zh~4WG!HZ8USw;#}VEgWujxydmRG#(M)tKX0PKhVImVv!m-g_Lx(75{`+2k zXYwc|_MW6!CGqN_$+~`POyV~ET2IPh

%%C4_LP!!{kNg>-hoDf8KwiiJ>qkc}ez z`K`wjp(h85^AB9694YA(U&}>tM8Thsk?f;Vb|D4KvTj1C>O>_(*Y*>v>imPE|lX3 z-l>PDvPO*$3;$3@$+Kft;i32HVR?>b)mhPM(%KKN3+1umH6&zDla&PZJlTou7NUu# zB)wA0Rch67jbJu*5;kUJ7%d=?878W4Y?i+r^mJd^)Z%k|!Kbu#?t+wfe34iR`T zmM${ze&gJIui$za_!-}c@yQS!S>!V>rzJV{zd;)y#r+tZo z_{Jc(zC-UlD~I=GQZ^=MLV{GT>jP~hn@>$Hr=$}Bdg&Yq!TC+b zLzFz?N)6fGIdr2ReavxYz$o=L;zys8SN<(*~ZAjWxVV@oi3Av{wvrn-!9I45(s@}AEH zSm(xBR}?+mQ3KzP*Wl<0evF}CZ#FzK(W5bp>$?Ug4)Fn~dK}a(kO@B>YDC*Ph^e>N zD?2;#G+U!)48G(EH1_Sa*LxoWG?x#cxG_$eNMw>YScv-Smb|mSL(Pbbf-k9CFB@(p zM#D9U+}f)QVhlW^=E8=~@z*ffW1_=Bmv$>FWl!>|EN?9hTCdbls_Q?FLd7_GacjxFGdCx#(N^ zLU=)X0$4ni*`-(FPc)J|T*WL~x&r_9XHC@SCvZ^BC%`Rv8hql5{R=0vRfR^jn7Kbu S+(ZAV_#E^ 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 N@4BIOsU|k&e*hqgmSz9| literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Weave.png b/resources/textures/displacement/Weave.png new file mode 100644 index 0000000000000000000000000000000000000000..022c423b813732d6a964fe0670a45b19f12f9609 GIT binary patch literal 2561 zcmb7``&SZqAIAqxEzPV|b6rwuZL_qb+ahT}w@uqFx|!Rl$F!d6u~byNq~)ZRm8luk zK4k@poi3)8?N(~u&;q5+fV@9~h%(8D25O_?DB|<&Ed2rfGH1^Dyg#4s%mCR)0k*6})H$AXid-qutk>I|Rz z`J*C@TimIsNbQQerK=ft%1!B8Lq74OJn<8uKB^XEerGc-$+Egr=9L(3ZEKG#wAc zW2=K?*4^A*i71BQnVNEwUS{mvmT}bjuV+*l1(L6wUkgdb!*h%UgK;F5U@WA3eNm5p9yb^wr&?T1CctS( zC{!>szM^)+`d|zHjI4botxBXClD|sy^rzE`E%M)HxSu=zS$St;QRgeSTOM?p!=cr# zG{>V9GL3lL%ai65L8EJJYVW#e=f1h;s+}iz;I93yzJRQ?eOtIjYxnN4r`G=cQ*Z5j z@iTg!!@|E@^1lD^g=?PU;u80~AC{Dn^N35!*W@j5tMtrsT3+p)=ggZv+xv#zptH%u z&TK*8fG6}(Rm*m)a)1AUUdiiMzxD zNjZ@Cc12)5V?feWCWFae!1&fcyw}5PQUen8^Shm$p8+;??q|b>hl5iigQ|#+NThav zM_|8c?J4H2g@KVl_B(S(TtLW{J9{ngjq$2l`GuMksNauqc0rLSm|Z#l%^i2bf3979v+9`iSZdR z{ZG6veD2RqGK(F!Qh5OzV4-HdpDwvxeETUQ)O@=pv*6<2nk>}(SM6oj^mp3qP>UUd z$ph-1bOar?!KTur`eADZCR|n_BI)`lDSMlw`rmd$VLqNuMWm?_O3KnOss3(ZIF?>i zDk4=(P*Qfby~K}{7K=#DCE)XU7KNqrx3TH#PfGPY&s$YC+-gb+l~IcG!^=dZNEyR0 zBm>iwK(abPs=rpzs$z8l$>OL|+$9M})G`Lo;R~4iA&{bb+_84q`edtU8&*4Dqr9=P zWIU0)(bJ>Lx`o9W0C<#{3-46DT5{FRv)J-s14{x3W%!xj7G+7y?})SgEZ7p(B!G0u z`}|N}M$!zXIoURn452hR{2XQ}@ri2wAsETD?;sn+eEU-xlmyA$D zJXeU@{aDztFeQAag32wThzlOGuw88e{67OqxVePM!zZX*{x*)}h^rWVZjWLeS=c&8 zJzjMZfg@#1TFn$SM7fPaj&&91RM?{p@+|D9sCxW)0s^aLOrAxz*$`9(oFs;J_e2u3 zJ^hdy(+$|@G@*i7adu*O1hQLa2RyX)?B4Rduj6o)$xhWRV4zUShqz+(Aq>|OsBzK2 zJ$#o-I~;OW($2sp>*CvTH=i1&Ivp0yAjxa1;63nJ^ zC?Hx&fWAyaNg3Ca;Vw)dxeX+~@-WOt3nbS)W%_H?G8Jn?Ls3|L0nL7Xy=4h`Ur8i z6HMNL)0}7Q`RG7_5V!l8N!xXh;}=Lon_GqWl5i#bBA&@pOQ{O@3`cUGM+Zcu@Z62YdX$~R6Wo`y0P01vGPYdWatPz4-ISQ5Jv7-$XH(d}`6;;;c) z;6(-Q=>ZEmYsdFo8tp8{Yhd@?s?$jD>&^-!vPVCGoFaVZt;|@oI!SqZ)1FFY;o+D` sW%=3b-pJo~pC=(5ubV0rlxaE^Gwh{Uac}3Bf4l9!?A}_u`Ovxl0}6M7wg3PC literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Wood Grain.png b/resources/textures/displacement/Wood Grain.png new file mode 100644 index 0000000000000000000000000000000000000000..e65ce88d652855b4c189c7cf67196130f9b1820e GIT binary patch literal 103929 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 VB}F{je_j9p002ovPDHLkV1nNbfcpRd literal 0 HcmV?d00001 From 68d754d94613f575489225d06cbb83a41594c324 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 16 Jul 2026 08:43:35 +0200 Subject: [PATCH 07/89] Add texture displacement documentation --- TEXTURE_DISPLACEMENT.md | 604 ++++++++++++++++++++++++++++++++++ TEXTURE_DISPLACEMENT_GUIDE.md | 326 ++++++++++++++++++ 2 files changed, 930 insertions(+) create mode 100644 TEXTURE_DISPLACEMENT.md create mode 100644 TEXTURE_DISPLACEMENT_GUIDE.md diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md new file mode 100644 index 0000000000..02a0c357fa --- /dev/null +++ b/TEXTURE_DISPLACEMENT.md @@ -0,0 +1,604 @@ +# Texture Displacement — Technical Notes + +Branch: `feature/texture_displacement`. This document is a knowledge dump of the whole feature as +it stands: architecture, file map, algorithms, known bugs found and fixed (with root causes worth +remembering), and what's still deferred. Written so a fresh session (or a fresh pair of eyes) can +pick this up without re-deriving everything from scratch. + +## What it does + +A paint-style gizmo (`GLGizmoTextureDisplacement`) that lets you: +- Paint one or more "layers" onto a model's surface, each a height-map texture with its own + depth/tiling/rotation/offset/invert/tile-mode/projection-mode/blend-mode. +- Pick a texture from a shipped library (`resources/textures/displacement/`) or import your own + (saved into `/textures/displacement/`, kept separate so app updates can't clobber it). +- Combine overlapping layers with image-editor-style blend modes (Add/Subtract/Multiply/Divide). +- Preview the true displaced result live, before baking (background job, not on the UI thread). +- Optionally preview via a fast GPU bump-map shader instead (no real geometry movement, just + shading) for a lighter-weight alternative. +- Bake into real mesh geometry on demand, restricted to the painted area only. +- Subdivide a low-poly model first so there are enough vertices to show fine detail. +- Unwrap a painted patch with a real CGAL LSCM parameterization and view it in a dedicated, + dockable 2D "UV Editor" pane. + +## Architecture + +### Data model (per `ModelVolume`) + +Each of up to `TEXTURE_DISPLACEMENT_MAX_LAYERS` (8) layers gets its **own independent +`FacetsAnnotation`** paint mask — the exact same `TriangleSelector`/`FacetsAnnotation` machinery +every other paint gizmo (FdmSupports, Seam, MMU, FuzzySkin) already uses, just one full instance +per layer slot instead of one per volume. This is what makes "layered/blended" painting work for +free: the same triangle can be `ENFORCER` in layer 2's mask and layer 5's mask simultaneously, and +at bake/preview time each layer displaces the surface left by the previous one (image-editor-layer +semantics). + +**Important gotcha**: `ModelVolume` does **not** hold `std::array`. It holds +8 individually-named fields (`texture_displacement_facets_0` .. `_7`) plus a +`texture_displacement_facet(int slot)` accessor. Reason: `FacetsAnnotation`'s ctor is private, +friended only to `ModelVolume`; `std::array`'s own implicitly-generated special member functions +are generated with **`std::array`'s** access rights, not the enclosing class's, so friendship does +not propagate through the array wrapper. This is a real MSVC C2280 if you try it — confirmed by +attempting it. `TextureDisplacementFacetsData` (a `std::array`, +used to carry paint-mask *data* around, e.g. into the bake job) is fine as a real `std::array` +since `TriangleSplittingData` has an ordinary public ctor — only the `FacetsAnnotation` object +itself has the friend-ctor problem. + +Plus `std::vector texture_displacement_layers;` — the plain-data layer +definitions (texture bytes + params), ordinary public ctor, safe in a vector. + +Touch points that had to mirror the existing `FacetsAnnotation` pattern (see `supported_facets` for +the template): all constructors' asserts, copy ctors' init lists, the `-1`-id deserialization ctor, +`set_new_unique_id()`, cereal `save`/`load`, `is_texture_displacement_painted()`, and +`reset_extra_facets()` (called whenever a topology-changing op like Simplify or subdivision +replaces the mesh — this is what drops any unbaked texture-displacement paint, since there's no +remap-across-topology-change support for it yet, see Limitations). + +### Bake algorithm (`libslic3r/TextureDisplacement.cpp`) + +`build_texture_displacement(base_mesh, layers, facets_data)` is **accumulate-then-displace, and +topology-preserving**: the returned mesh has exactly the input's vertices and triangles, in the same +order — only the positions of displaced vertices differ. + +1. `its_compactify_vertices()` on a copy of the input. In practice a no-op (it only drops + *unreferenced* vertices, and preserves the order and indices of the rest). It is there to + guarantee the index alignment step 3 depends on. +2. Area-weighted vertex normals of the **undisplaced** mesh, computed once. Every layer both + projects and displaces along these, so a vertex covered by several layers moves along one single + well-defined direction. +3. For each layer in slot order: deserialize its stored paint mask into a `TriangleSelector` against + the **base mesh** (never against a previous layer's output), then + `selector.get_facets_strict(ENFORCER)` → the painted patch. Two facts are exploited: + - `get_facets_strict()` returns the mesh's **entire** referenced vertex array regardless of which + state was asked for — only `.indices` is filtered by state. So `get_facets_strict(ENFORCER)` + and `get_facets_strict(NONE)` share identical vertex indexing, which is what lets boundary + detection be a plain index check instead of a position-hash lookup. + - The selector's vertex array *starts with* the mesh's own vertices (extra ones created where a + brush stroke split a triangle are appended after them), and `get_facets_strict()` emits the + referenced ones in order. Combined with step 1, **selector vertex index `i` is our vertex `i`**. + Split vertices live past the end of our array and are simply skipped — they sit on the paint + boundary anyway (splitting only happens at partial coverage), so they would be pinned regardless. +4. A vertex used by at least one **unpainted** triangle is a boundary vertex — pinned, never + displaced (its final position is ambiguous, it belongs to both regions). Only vertices used + exclusively by painted triangles get displaced. This is what keeps bakes seamless with zero + remeshing/hole-filling at the seam. +5. Per interior vertex: sample the height texture (`sample_layer_height()`, see Projection methods) + and fold `height * depth_mm * (invert ? -1 : 1)` into that vertex's running total via the layer's + `TextureBlendMode` (see Blend modes). A `visited` set makes each layer fold in exactly **once** + per vertex, no matter how many of the patch's triangles share it — otherwise a Multiply/Subtract + layer would apply two or three times over depending on local triangle fan-out. +6. Finally, move each touched vertex along its (step 2) normal by its accumulated total. + +**This replaced a sequential design** that re-meshed after each layer and carried the next layer's +paint mask onto the result with `TriangleSelector::remap_painting()`. That was the root cause of the +reported "the second texture is never applied" bug: remapping a mask onto a mesh whose vertices had +just been displaced out from under it routinely produced an empty bitstream, and the layer was then +silently `continue`d past. It was also what forced the per-layer vertex duplication and the final +`its_compactify_vertices()` weld. The current formulation has neither problem, is substantially +faster (no remap, no welding, one pass), makes blend modes possible at all (they need a shared +per-vertex accumulator, which sequential re-meshing cannot provide), and — because the output keeps +the input's exact vertex indexing — lets GUI code overlay a preview on the base mesh with no index +translation. + +### Blend modes + +`TextureBlendMode` {Add, Subtract, Multiply, Divide}, per layer, applied per vertex against the +total accumulated by the layers **below** it (lower slots). The quantity blended is a signed +displacement in **mm**, not a pixel value. + +Add/Subtract are self-explanatory. Multiply/Divide are *scaling* operations and so need a unit +convention: they treat the layer's own value as a **factor relative to 1 mm**. That makes `depth_mm` +a gain, and — the property that makes a Multiply layer usable as a mask — a layer with depth 1 mm +sampling a white (1.0) texel multiplies by exactly 1, i.e. leaves the layers below unchanged. +Divide floors its divisor's magnitude at 0.05 — a black texel samples to *exactly* zero, so the +divisor really does hit zero in ordinary use, and an unbounded `1/0` would fling vertices thousands +of mm away and poison the mesh's bounding box (and every plate/print-volume check downstream). The +floor doubles as a cap on how far Divide can amplify the relief beneath it: at most 20×. + +The **lowest painted layer ignores its blend mode**: it has nothing beneath it, and Multiply/Divide +against an implicit zero base would annihilate (or blow up) it. Enforced in `build_texture_ +displacement()` (the first layer to reach a given vertex always folds in additively) and surfaced in +the UI, which labels that layer "Base layer" instead of offering a control that silently does nothing. + +### Projection methods + +Four choices per layer (`TextureProjectionMethod`), all funneling through `apply_uv_transform()` +(scale by `1/tiling_scale`, rotate by `rotation_deg`, add `offset`). They are dispatched by +`sample_layer_height()`, which returns a **height**, not a UV — because Triplanar takes three +texture samples per vertex and so has no single UV that represents it. + +- **Triplanar** (default) — samples the texture on all three world planes (`(y,z)`, `(x,z)`, `(x,y)`) + and blends the three by the vertex's own normal raised to `TRIPLANAR_BLEND_SHARPNESS` (4). + This is the fix for a real, user-reported bug. The previous version *hard-picked* the single axis + most aligned with the normal, which is discontinuous wherever that dominant axis flips: on a +X + face the planar coordinate is `(y, z)`, on a −Y face it is `(x, z)`, so at the shared edge `u` + jumps from `y_edge` to `x_edge`. On a box centred near the origin those two happen to **agree** at + the (+,+) and (−,−) corners and **differ by the full corner width** at the (+,−) and (−,+) corners + — which is exactly the "two bad corners, two good ones" symmetry that was observed. A weighted + blend is continuous across the transition by construction, since the weight of the axis being left + behind falls smoothly to zero. (Note this removes the hard *seam*; some cross-fade blurring in the + band right at a 90° edge is inherent to triplanar mapping. A genuinely seam-free wrap around a box + needs a real unwrap — that is what the LSCM mode is for.) +- **Cylindrical** — wraps around an axis through the patch centroid, axis auto-picked as the world + axis *least* aligned with the average normal (perpendicular to the outward radial normal, as a + cylinder's own axis would be). `u = angle * local_radius` (arc length in mm), `v = distance along + axis`. Approximation, not an exact fit for arbitrary geometry. +- **Spherical** — longitude/latitude around the centroid, scaled by local radius. Same caveat. +- **LSCM** — real UV unwrap via `MeshBoolean::cgal::parameterize_lscm()` (CGAL's + `Surface_mesh_parameterization` package, LSCM algorithm). Computed **once per patch** (not + per-vertex like the others — it's a single global least-squares solve), then each vertex looks up + its precomputed UV. Requires the patch to be a single topological disk (one connected component, + one boundary loop) — `compute_lscm_uvs()` returns empty and the layer silently falls back to + Triplanar if not (e.g. multiple disconnected painted islands, or a fully closed patch). + CGAL's parameterizer needs a mesh with no isolated/unreferenced vertices, but `get_facets_strict()` + returns the *whole* mesh's vertex array — so there's a compaction step + (`compact_patch_with_map()`) that builds a clean sub-mesh + an index map back to the original + (uncompacted) vertex numbering, purely local to this file. +- **ViewProjected** ("From view") — a flat projection along a fixed direction captured from the 3D + camera, like a slide projector. `capture_view_projection()` takes the camera's right/up axes, + transforms them into the volume's *local* frame (so the projection rides along if the part is later + moved), and stores them as `TextureDisplacementLayer::view_project_right/up` (unit vectors, so the + projected coordinate stays in mm and `tiling_scale` keeps meaning mm). `sample_layer_height()` + projects `Vec2f(dot(pos, right), dot(pos, up))`. Single-valued per point, so — like LSCM but unlike + blended Triplanar — the fast preview and UV-check overlay precompute it per vertex + (`compute_layer_vertex_uvs()`) and drive the shader's `use_vertex_uv` path. Faces angled away from + the projector smear; that is inherent to view projection, not a bug. + +### Manual seams and island cutting + +`TextureDisplacementLayer::lscm_seam_edges` — undirected mesh-vertex-index edge pairs the unwrap is +forced to cut along, on top of the dihedral-angle seams. `segment_into_charts()` takes a set of these +(translated from mesh → compacted-patch numbering inside `compute_patch_unwrap()`) and refuses to +union two triangles across a marked edge whatever their angle. Both the unwrap cache key and the +gizmo's `UVEditorState` include the seam list, so marking a seam (which leaves the paint mask +untouched) still forces a re-solve. Like the paint masks, seams are mesh-index-space and so dropped on +any topology change. + +Two ways to write to it: +- **Mark seam (manual, #9)** — a "Mark seams" click mode (`m_seam_edit_mode`) that suppresses + painting. A click raycasts the volume (`m_c->raycaster()->raycasters()[idx]->unproject_on_mesh()`, + `idx` = the volume's slot among model-part volumes), finds the facet's edge nearest the hit point, + and toggles it. Marked edges render as a red overlay (`render_seam_overlay()`), pulled toward the + camera so they read on top. This is the Blender mark-seam workflow. +- **Cut island (auto, #17)** — `cut_island()` takes the selected chart's triangles (back-mapped from + the unwrap via `source_vertex`), finds their 3D bounding box, and marks every edge that straddles + the mid-plane perpendicular to the longest axis. The re-unwrap then splits the chart across its + narrow waist — the "islands might be very long" case. Exposed as the UV pane's **Cut** button. + +### UV-check overlays (checker / distortion) + +`resources/shaders/{110,140}/texture_displacement_uvcheck.{vs,fs}`, one shader with a `mode` uniform, +drawn over the painted patch (`rebuild_uvcheck_mesh()`/`render_uvcheck_mesh()`, P3N3T2: `normal.x` = +distortion, `tex_coord` = uv), pulled forward with a polygon offset. **Checker** (#13) samples a +procedural checkerboard at the layer's uv (per-vertex for LSCM/ViewProjected, in-shader triplanar +otherwise) — squares that stay square mean low distortion. **Distortion** (#14) colours each triangle +blue→green→red by `log2(uv_area / surface_area)` centred on the patch's *median* stretch (so a +globally-scaled unwrap reads as uniformly ideal and only relative stretch shows), averaged to +vertices. A separate **Show mesh wireframe** toggle (#8) draws the whole volume's triangle edges, +rebuilt only when the vertex count changes (not per stroke). + +### Tiling + +`DecodedHeightTexture::sample(uv, tile_enabled, tile_method)`. Two tile methods when enabled +(Repeat, MirroredRepeat). **When `tile_enabled` is false, sampling outside `[0,1)` returns `0` +directly** — clamping the *coordinate* into range (what an earlier version did) instead smears the +border row/column of pixels outward to infinity in every direction, which is a real bug that was +reported and fixed (visually: streaky lines radiating out from the painted patch). + +### Subdivision (`subdivide_mesh_uniform()`) + +Deliberately **whole-mesh and uniform**, not limited to the painted patch. A patch-only / +adaptive subdivision would create a classic T-junction/cracking problem where the denser +(subdivided) and sparser (untouched) regions meet — the fine side has edge midpoints the coarse +side doesn't know about, producing a real (non-manifold-looking) crack in the baked geometry. This +was consciously scoped down from the original plan's "adaptive per-patch subdivider" idea to avoid +that correctness risk (a subtly-cracked mesh is a much worse outcome than "not implemented yet"). +Algorithm: recursive 1-to-4 triangle split via edge midpoints, with a shared per-pass midpoint cache +(keyed by sorted vertex-index pair) so triangles sharing an edge get the *same* new vertex — capped +at `max_iterations` (default 6) passes to bound worst-case triangle-count explosion. + +Wired as a "Subdivide model" button in the gizmo panel — a real, committed geometry change (like +Bake), using the same `save_painting()`/`set_mesh()`/`restore_painting()` dance `GLGizmoSimplify` +uses: supported/seam/mmu/fuzzy-skin masks get remapped onto the new triangles, texture-displacement +paint does not (no remap support yet) and is dropped rather than left pointing at now-meaningless +triangle indices. + +### Preview pipeline (perf) + +`rebuild_preview()` used to call `build_texture_displacement()` **synchronously on the UI thread** +on every stroke-end and every slider release. With multiple painted layers this got slow (each +layer's PNG sampling + vertex welding + `remap_painting()` stacks up). Fixed by moving the actual +computation into `TextureDisplacementPreviewJob` (mirrors `TextureDisplacementBakeJob`'s +process()/finalize() split), queued on the app's shared UI job worker via plain `queue_job()` (not +`replace_job()` — that worker is shared app-wide, including with Bake; `replace_job()` would cancel +an in-flight bake if one happened to be running). A `m_preview_generation` counter discards stale +results if a burst of edits queues several jobs in a row and an older one finishes after a newer one. + +Two other real perf fixes worth remembering: +- Sliders in ImGui report "changed" continuously on every drag frame, not just once on release — + gating the (then-synchronous) rebuild behind "mouse button not currently down" was necessary to + stop dozens of rebuilds per drag. +- `decode_height_texture()` used to re-decode the same PNG from scratch on every call. Now cached + in `TextureDisplacement.cpp`, keyed by a `weak_ptr` to the layer's `image_data` (not just the raw + pointer — a `weak_ptr` correctly detects a freed-then-reused address, where a raw-pointer key + would alias a stale cache entry onto an unrelated later texture). + +### Fast bump preview (GPU-only, no CPU meshing) + +`resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}`, registered as +`"texture_displacement_bump"`. Perturbs the *shading* normal from the height texture's local +gradient instead of moving geometry — active-layer-only, toggled via a "Fast preview (normal map)" +checkbox. Vertex format is `GLModel::Geometry::EVertexLayout::P3N3T2`: `normal.x` carries the +per-vertex paint weight (0/1) and `tex_coord` carries a precomputed texture UV, so it can use +`GLModel` normally instead of needing a hand-rolled VBO/VAO manager. Weight buffer is +rebuilt at the same cadence as the true-displacement preview (stroke-end/slider-release), using the +**live** `TriangleSelector` state (not the flushed model facets), so it doesn't lag by a full model +round-trip. + +The perturbed normal is the analytic one for a height field `H = ±depth_mm · h(uv)` displaced along +`N` over any orthonormal surface tangent pair `T`/`B`: + + N' = normalize(N − (dH/da)·T − (dH/db)·B), a = dot(p,T), b = dot(p,B) + +The two slopes have to be genuine **mm-per-mm** derivatives for the preview's apparent depth to +match the bake's — see bug #13. + +**Two projection paths (`use_vertex_uv` uniform):** +- **Triplanar (`use_vertex_uv = 0`)** — `uv` and the `T`/`B` axes are both derived in-shader from + the dominant normal component, mirroring `project_planar()`/`apply_uv_transform()`, and the slope is + formed analytically. `T`/`B` are the projection's axis-aligned pair, exact only when the face is + axis-aligned; the shader drops the along-normal component to keep the gradient in the surface. Here + one `uv` unit is exactly `tiling_scale` mm, so the `1/tiling_scale` gradient factor is right. +- **Precomputed UV (`use_vertex_uv = 1`, used for LSCM)** — `uv` comes per-vertex from the CPU + (`compute_lscm_uvs(patch, layer)`, so island placement + tiling/rotation/offset are already folded + in), and the perturbed normal is built with **Mikkelsen's method** ("Bump Mapping Unparametrized + Surfaces on the GPU"): the surface gradient taken directly from the screen-space derivatives of the + *sampled height* and position. **This makes no uv→mm scale assumption**, which is essential — + the first cut used the same global `1/tiling_scale` factor as triplanar and the depth came out + visibly wrong, because an LSCM map is **conformal, not isometric**: it is globally area-scaled but + the *local* mm-per-uv varies across the chart. `dFdx(h)` captures the true on-screen rate of change + however the chart is stretched. **This path is also what makes the fast preview follow the UV + editor: move an island and its uv — hence its bump — moves with it** (the bump mesh rebuilds on + drag-end, since `on_island_edited(finished)` → `rebuild_preview()` → `rebuild_bump_preview_mesh()`). + The branch is uniform (`use_vertex_uv` is a uniform) and the paint weight gates by multiply, so the + texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv → the + `det≈0` guard skips it (a localised preview-only artifact, never in the bake). + +Remaining deliberate approximation: the GPU sampler's wrap mode stands in for +`tile_enabled`/`tile_method`, so with tiling *off* the GPU repeats where the CPU returns 0 outside +`[0,1)`. + +> **Known divergence from the CPU path (not yet reconciled).** The shader's `project_uv()` mirrors +> the *hard-axis* `project_planar()`, but the CPU's Triplanar mode is now a **blended** three-plane +> sample (see Projection methods — that change is what fixed the 90°-corner seam). The two therefore +> still agree on any face that is roughly axis-aligned (one blend weight ≈ 1 there, so the blend +> degenerates to exactly the hard-axis pick), and disagree in the cross-fade band around a sharp +> edge — precisely where the fast preview will still show the old hard seam that the true preview and +> the bake no longer have. Reconciling it means sampling all three planes in the shader and blending +> the three gradients by `pow(abs(N), TRIPLANAR_BLEND_SHARPNESS)`, the same weights +> `sample_layer_height()` uses. Also note the shader is still **active-layer-only** and knows nothing +> about `TextureBlendMode`, so a multi-layer stack cannot match the true preview by construction. + +### On-canvas "Adjust Texture" gizmo + +A per-active-layer toggle ("Adjust placement (drag on model)") that disables painting and shows a +flat pan panel (free 2D drag on both axes) plus two arrows along the patch's own U/V axes +(constrained single-axis drag). Anchored to the painted patch's centroid/average-normal +(`compute_layer_paint_anchor()`). Hit-testing is screen-space distance/point-to-segment (not real +3D ray intersection against the handle geometry) — simple and good enough at this handle size. + +Known unverified detail: the **offset-drag direction/sign** is reasoning-based (increasing `offset` +shifts which texel is sampled at a fixed world position, which visually slides the pattern the +*opposite* way — so the code subtracts), not visually confirmed, since this environment can't +render pixels. May need a one-line sign flip once actually tested. The rotation-arrow-implied +direction should be reliable (it follows directly from a self-consistent 2D basis, no such +ambiguity). + +### UV Editor pane + +`UVEditorCanvas` (`src/slic3r/GUI/UVEditorCanvas.hpp/.cpp`) — a standalone `wxGLCanvas` rendering the +flattened LSCM islands (per-island wireframe + outline + fill) over the height texture (background +quad tiled across the whole unwrap), with mouse pan/zoom. It is wrapped in a **`UVEditorPanel`** +(same file) that adds a button row (Frame / Snap / Average scale) and a Blender-style status line +along the bottom naming the current gesture and the shortcuts in play. The *panel* is what is +registered as a `wxAuiPaneInfo` pane on `Plater`'s `m_aui_mgr`; `Plater::show_uv_editor(bool)` +shows/hides it (deferred via `CallAfter`, since the gizmo calls it mid-3D-frame), and +`get_uv_editor_canvas()` returns the inner canvas the gizmo talks to. + +Deliberately **shares the app's one real `wxGLContext`** (`wxGetApp().init_glcontext(*this)`, the +same call `View3D`/`Preview`/`AssembleView` make) rather than creating an independent context like +`SkipPartCanvas` does elsewhere in this codebase — this is what lets it reuse the already-registered +`"flat"`/`"flat_texture"` shaders and `GLModel` as-is, instead of needing its own shader +compilation/VBO management. + +**Geometry is uploaded once, in the unwrap's own (raw, mm) coordinates**, one `GLModel` set per +island; each island is then drawn through its own 2x3 affine (`island_transform_matrix()` composed +with the layer's tiling/rotation/offset) passed as the `flat` shader's `view_model_matrix`. This is +the fix for the ~200 ms-per-frame island-drag stall (#3): the old design pre-transformed every UV on +the CPU and re-uploaded the entire wireframe on every mouse-move event, which on a million-triangle +patch is exactly as slow as it sounds. Now a drag updates one matrix per island and touches no vertex +buffer — `on_island_edited(!finished)` calls only `set_island_transforms()`, and the full +`set_islands()` rebuild happens solely when the unwrap itself changes (`unwrap_changed` in +`update_uv_editor()`). + +**Gestures** (canvas-owned, reported to the gizmo as incremental deltas via `IslandEditFn`): left-drag += move, right-drag or **R** = rotate (hold **Shift** to snap to 15° steps — quantised on the +*cumulative* rotation, not each delta, so it doesn't judder, and accumulated incrementally so it +survives crossing ±180°), **S** = scale (R/S modal, click/Enter to confirm, Esc to cancel), wheel = +zoom about the cursor, middle-drag = pan, **Home**/**F** = frame all. Scale writes +`TextureIsland::scale`; "Average scale" (`average_island_scales()`) sets every island to the mean, so +one island scaled by hand can be matched back to its neighbours' texel density. **Snap** (canvas-owned +`m_snap_enabled`, toggled from the toolbar) sticks a dragged island's nearest boundary vertex onto a +neighbouring island's at drag-*end* only — a magnet that re-applies mid-drag is very hard to pull out +of. Toolbar commands the canvas can't service itself (Average scale) are forwarded to the gizmo via +`CommandFn`; view-only ones (Frame, Snap) it handles directly. + +## Bugs found and fixed this session (worth remembering) + +These were all real, confirmed root causes (found by reading the actual code path, not guessed): + +1. **Cross-face projection distortion** — see "Triplanar" above. Fixed by projecting each vertex + with its own normal instead of one shared patch-average normal. +2. **Disabled-tile smearing to infinity** — clamping the UV *coordinate* into `[0,1]` instead of + returning 0 outside it, when tiling is off. Fixed in `DecodedHeightTexture::sample()`. +3. **Invisible checkbox/radio "checked" state in light mode** — `ImGuiWrapper::push_toolbar_style()` + sets `ImGuiCol_CheckMark` to white while the checkbox/radio frame background is fully transparent + (alpha 0) over a light window background — a white checkmark on an effectively-white background + is invisible by construction. This is a **pre-existing, general app-wide bug**, not specific to + this feature (every panel using `push_toolbar_style()` in light mode has it) — fixed by changing + just the light-mode branch's `CheckMark` color to the app's teal accent. +4. **Distorted (non-aspect-correct) texture thumbnails** — was forcing a square `ImGui::Image` size + regardless of the source image's actual aspect ratio. +5. **`std::array` compile error** — see Data model above (MSVC C2280, + `std::array`'s implicit special members don't inherit element-type friendship). +6. **Eigen ternary expression-template type mismatch** (MSVC C2446) — `cond ? (n / len) : + Vec3f::UnitZ()` fails because the two branches are different unevaluated Eigen expression + *types* with no common type; fixed by wrapping the non-`UnitZ()` branch in an explicit + `Vec3f(...)` to force a concrete common type. +7. **Post-bake stale preview** — `GLGizmoPainterBase::data_changed()`'s change-detection only + checks object id / volume count, neither of which changes when Bake replaces a volume's mesh + (same object, same volume count, just a new mesh/id on the volume itself) — so the gizmo kept + rendering/painting against the pre-bake `TriangleSelectorPatch` until manually deselected and + reselected. Fixed by explicitly calling `update_from_model_object()` in the bake-completion + callback. +8. **Bake job / crash-report `resources` junction going stale** — unrelated to this feature's code, + but hit during testing: `build/src/Release/resources` was a leftover **empty plain directory** + instead of the junction CMake's post-build step creates (`if not exist` skipped it because the + empty folder already "existed"), so the built exe couldn't find `resources/data/hints.ini`, + leaving `HintDatabase`'s hint list empty → `rand() % 0` divide-by-zero crash before the UI ever + opened. Fixed by deleting the empty folder and manually recreating the `mklink /J` junction. +9. **Fast bump preview showing a solid black object** — `GLModel::render()` **unconditionally** + re-sets the shader's `"uniform_color"` uniform from its own internal `Geometry::color` field + (defaulting to `ColorRGBA::BLACK()`) right before every draw call — so a manual + `shader->set_uniform("uniform_color", ...)` call made just before `.render()` gets silently + clobbered. Any `GLModel` that needs a specific flat color **must** call `.set_color(...)` on the + model itself, not set the shader uniform directly. Found by reading `GLModel::render()`'s actual + source rather than guessing at shader/lighting math. +10. **UV editor canvas rendering nothing / showing stale content on resize** — the canvas requested + a generic `wxGLAttributes().Defaults()` pixel format while sharing the app's one real + `wxGLContext` (which was originally created against `View3D`'s canvas, itself requesting a + specific RGBA/24-bit-depth/8-bit-stencil format). `wxGLCanvas::SetCurrent()` on WGL/GLX + generally requires the target window's pixel format to be compatible with the one the context + was created against; a mismatch can make `SetCurrent()` silently fail, leaving the canvas + showing whatever was last in its backbuffer (looks exactly like "blank" or "stale image on + resize"). Fixed by requesting the same explicit attribute list + `OpenGLManager::create_wxglcanvas()` uses for the main view canvases — but see bug #14: the + first attempt at this copied only *part* of that list and the symptom therefore survived. +11. **`` / `` include-order conflict** — `wx/glcanvas.h` pulls in the + platform's real `GL/gl.h`; if that happens before `` is processed in the same + translation unit, glad's own header errors out (`OpenGL (gl.h) header already included`). + Fixed by including `` first in `UVEditorCanvas.hpp`, before `` — any + file that includes this header (including `Plater.cpp`, transitively) needs glad to win that + race. +12. **Fast preview hidden behind the paint-highlight overlay** — `render_painter_gizmo()` always + drew the selection-highlight overlay on top with a depth-bias trick (`glPolygonOffset`) that + only makes sense for the *true*-displacement preview: real geometry moves in the painted area, + so the depth-biased overlay only wins the depth test in the *unpainted* (coincident) region. + The bump preview never moves geometry — its depth is identical to the overlay's *everywhere* — + so the overlay was winning the depth test across the whole surface and hiding the bump shading + entirely. Fixed by skipping the overlay draw entirely when the bump-preview path is active. +13. **Fast preview's apparent depth not matching the true preview's** — the bump shader built its + perturbed normal as `normalize(N + depth_mm * vec3(hL-hR, hD-hU, 0))`. Two things wrong with + that. (a) `hL-hR` is a height difference across *one texel step*, i.e. `dh/du` already scaled by + `2·texel`, and `du` is in uv units, not mm — the real surface slope needs the full chain rule + back through `uv = R(rotation) · planar_mm / tiling_scale`, i.e. a further `1/tiling_scale` and + a rotation of the gradient by `−rotation`. The missing `1/(2·texel·tiling_scale)` factor is + ~26× at a 1024px texture and a 20mm tile size, all in the flattening direction — which is + exactly what "fast preview has a different height from the real preview" looks like. (b) the + gradient was added to model-space `xy`, but the two axes the planar projection actually runs + along are `yz`/`xz`/`xy` depending on the dominant normal component, so on any face not + dominated by `z` the perturbation was applied to the wrong axes. Fixed by computing the real + mm-per-mm slope and rotating it into the projection's own `T`/`B` axes (see "Fast bump preview" + above for the derivation). +14. **UV editor pane still blank after bug #10** — three separate causes, all of them live at once: + - The bug-#10 fix copied `OpenGLManager::create_wxglcanvas()`'s attribute list but **dropped its + multisampling attributes** (`WX_GL_SAMPLE_BUFFERS`/`WX_GL_SAMPLES`, 4 samples by default), on + the reasoning that a flat 2D wireframe view doesn't need AA. But a differing sample count *is* + a differing pixel format, so this left exactly the `wglMakeCurrent()` mismatch bug #10 set out + to fix. It now mirrors the full list, AA included, reading `OpenGLManager::can_multisample()` + (already resolved by then — `View3D` is constructed first). + - `set_mesh()`/`set_background_texture()` each called `render()` **inline**, and both are reached + from `update_uv_editor()` → `rebuild_preview()` → the gizmo's ImGui panel — i.e. from the + middle of the *3D* canvas's GL frame, and (since `show_uv_editor(true)` is the last line of + `update_uv_editor()`) while this pane was still **hidden**. `wxGLCanvas::SetCurrent()` returns + false outright on a canvas that isn't shown on screen, and the old code ignored the return + value — so every GL call in `render()`, `glViewport`/`glClear` included, silently landed on the + 3D canvas instead. These now only mark dirty + `Refresh()`; `render()` bails unless + `IsShownOnScreen()` *and* `SetCurrent()` succeeds; and `Plater::show_uv_editor()` defers its + AUI relayout via `CallAfter` so the pane's first size/paint can't be delivered mid-frame either. + - Even once drawing, nothing would have been *visible*: the background quad spanned `[-1,1]²` + while LSCM UVs land around `[0,1]²`, and the view was centered on the origin at a half-extent + of 0.6. The quad is now the unit square in the same UV space the wireframe uses (which is also + where `sample()` maps the texture, regardless of its pixel aspect), the projection's Y is + negated so `v` runs down-screen (putting the texture's first pixel row at the top rather than + upside down), and the view auto-fits to the unwrap ∪ unit square the first time a patch shows up. +15. **A second texture layer was silently never applied** — the reported "multiple textures don't + work reliably". Root cause was the old sequential bake: layer N's paint mask was stored against + the volume's original mesh, so before layer N+1 could be deserialized the mask had to be carried + onto the mesh layer N had *just displaced*, via `TriangleSelector::remap_painting()`. Remapping a + mask onto geometry that has moved out from under it routinely returned an empty bitstream, and + the code then did `if (data.bitstream.empty()) continue;` — i.e. dropped the layer **without any + diagnostic**. Fixed structurally rather than patched: every layer is now evaluated against the + base mesh and merged per vertex (see Bake algorithm), so no remap happens at all. Covered by a + regression test. +16. **Hard-axis triplanar seam at exactly two of a box's four vertical corners** — see "Triplanar" + under Projection methods. Worth recording the *diagnostic* here, because the asymmetry is what + pinned it down: the user reported the seam at the (X+,Y−) and (X−,Y+) corners with the other two + clean. That is precisely what a dominant-axis switch predicts (`u = y` on an X face, `u = x` on a + Y face; those agree where `x == y` and differ by the corner width where `x == −y`) and it ruled + out every "the texture is wrong" hypothesis, since the texture itself is fine — the *mapping* is + discontinuous. Fixed by blending the three axis projections instead of picking one. +17. **Use-after-free when removing a texture layer** (latent, pre-existing — found while touching the + panel, not caused by it). The layer list's "Remove" button called `remove_texture_layer()` *in + the middle of rendering that layer's row*. That erases the layer from + `mv->texture_displacement_layers`, shifting every later element down — after which the loop + happily carried on dereferencing `layer` for the rest of the row's widgets (depth/tiling sliders, + `PopID`) and kept iterating `ordered`, a vector of pointers into the storage that had just moved. + Never crashed loudly because `vector::erase` doesn't reallocate, so the reads landed on a *valid* + but *wrong* (shifted) layer. Fixed by recording the slot and doing the removal after the loop. +18. **Every LSCM island collapsed to a point** (the UV editor was empty; the Tile-size slider did + nothing; an LSCM bake came out as a flat "single-face extrude"). One line in + `compute_patch_unwrap()`: `chart.indices = std::move(chart_mesh.indices);` ran *before* + `area_3d(chart_mesh)` was taken. `area_3d()` iterates those indices, so on the moved-from + (emptied) mesh it returned `0`, giving `scale = sqrt(0 / uv_area) = 0` — **every chart's UVs + multiplied by zero**. That one zero explained all three symptoms at once (no island extent to + draw; a zero-size unwrap is still zero after any tiling divide; the bake sampled ~one constant + texel per chart). Fixed by measuring the 3D area before the move. Found only by instrumenting the + actual island bbox into the panel — three rounds of reasoning from screenshots had each guessed + wrong, because a collapsed-to-a-point unwrap and an off-screen-framed one look identical. + +## Known limitations / deferred work + +- **No `.3mf` serialization** for texture-displacement paint data or texture assets. A background + agent attempted this in an earlier session, hit its own usage limit mid-edit, and left + `bbs_3mf.cpp` with an undefined forward-declared function; that partial edit was reverted rather + than shipped broken. Practical impact: **baked** geometry round-trips fine (it's just an ordinary + part of the mesh via the existing mesh serialization path) — what does *not* survive a project + save/reload is any *unbaked* paint stroke and texture layer definition. +- **No remap-across-topology-change** for texture-displacement paint (`ModelObject::split()`, mesh + boolean ops, Simplify, and now `subdivide_mesh_uniform()` all drop it via `reset_extra_facets()`). + The other four paint channels (supported/seam/mmu/fuzzy) do get remapped in these cases. +- **Cylindrical/Spherical axis/center are auto-picked heuristically**, not user-controllable — no + UI to override the auto-detected wrap axis if it picks the "wrong" one for an odd shape. +- **Fast preview covers the active layer only**, while the true preview stacks every painted layer — + so with more than one layer painted the two will legitimately not agree, independently of bug #13. +- **Bump preview and true preview both only refresh at stroke-end**, not continuously during an + active drag (a deliberate scope cut for simplicity/consistency — the original plan's "instant + update mid-stroke" idea for the bump shader specifically was not carried through). +- **On-canvas Adjust-Texture gizmo's offset-drag direction is unverified** (see above). +- **The bump shader still uses hard-axis, single-layer projection** while the CPU path is now + blended-triplanar and blend-mode aware — see the callout under "Fast bump preview". +- **Displacement resolution is capped by the mesh's own vertex density.** Baking only ever *moves* + existing vertices (it never inserts any), so a coarse patch cannot show fine texture detail no + matter how high-resolution the height map is — that is what the "Subdivide model" button is for. + Since the rewrite the bake is topology-preserving, so this is now a hard, explicit property rather + than something partly papered over by the old per-layer re-meshing. +- **Placeholder toolbar icon** — reuses `toolbar_fuzzy_skin_paint.svg`, noted as a TODO in code. +- **Textures are matched to the picker by absolute path** (`TextureDisplacementLayer::path`), so the + picker's "which entry is selected" highlight goes blank if a project is moved between machines. + Harmless — the layer keeps its own embedded `image_data` and still bakes correctly. + +### Requested UV-editing features — status + +A user working through the feature end-to-end asked for a batch of UV-editing features. All of the +functional ones are now implemented (see the sections above): checker (#13), distortion heatmap +(#14), mesh wireframe overlay (#8), cut island (#17), project-from-view (#6), and Blender-style mark +seam (#9), plus per-island fills, the fast preview honouring the LSCM unwrap and island moves (#1), +the UV pane toolbar + status line (#18), Shift-snap rotation, midlevel/bidirectional displacement +(#19), and island scale/average/padding/snap (#15/#16/#2). + +Remaining cosmetic / known gaps: +- The UV pane toolbar has **text buttons, not icons** — no existing SVG reads cleanly as "average + island scale" / "snap islands", so real icons are deferred rather than mis-assigned. +- **Mark-seam edge picking** snaps to the nearest edge of the *hit facet* only; it does not + highlight the candidate edge on hover before you click (a hover-preview would be a nice refinement). +- **Cut island** always halves along the longest 3D axis; there is no UI to pick the cut line. + +## File map + +**libslic3r (core, no GUI dependency):** +- `src/libslic3r/TextureDisplacement.hpp/.cpp` — data model, bake algorithm, projection methods, + tiling, subdivision. See doc comments throughout, they're kept accurate and up to date. +- `src/libslic3r/MeshBoolean.hpp/.cpp` — added `parameterize_lscm()` in the `cgal` sub-namespace, + reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for + mesh boolean ops. New CGAL includes: `Polygon_mesh_processing/border.h`, + `Polygon_mesh_processing/connected_components.h`, `Surface_mesh_parameterization/{Error_code, + LSCM_parameterizer_3, parameterize}.h`. No new dependency — CGAL 5.6.3 is already vendored and + the `Surface_mesh_parameterization` package headers were already present, just unused before now. +- `src/libslic3r/Model.hpp/.cpp` — the 8 named `FacetsAnnotation` fields + accessor, + `texture_displacement_layers`, and all the mirrored touch points (see Data model above). + +**GUI:** +- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` — the gizmo. Panel controls: dock/ + undock toggle, brush/face/connected-area selection mode + "select whole model" button, per-layer + texture picker + depth/tiling/rotation/invert/tile-mode/projection-mode/blend-mode controls, + "Adjust placement" toggle (on-canvas gizmo), "Fast preview (normal map)" toggle, "Subdivide model" + button, Add layer/Erase all/Bake. +- `src/slic3r/GUI/TextureLibrary.hpp/.cpp` — scans the shipped + user texture folders, imports an + arbitrary image into the user folder (converting it to the 8-bit grayscale PNG libslic3r decodes), + and loads a library file's bytes for a layer. The image→grayscale-PNG conversion lives here, on the + GUI side, because libslic3r has no image toolkit; both the import path and the "pick a shipped + texture" path go through the same one function. +- `resources/textures/displacement/*.png` — the 10 shipped height maps (Bricks, Grid, Hexagons, + Knurl, Noise, Quilt, Studs, Waves, Weave, Wood Grain). All 512×512 8-bit grayscale and **seamless** + (each is periodic over the full image in both axes, so tiling shows no seam). Generated + procedurally; the whole `resources/` tree is installed recursively by CMake, so a new folder under + it ships with no build-system change. +- `src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp/.cpp` — background bake commit. +- `src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp/.cpp` — background preview compute + (mirrors the bake job's shape but commits nothing to the Model). +- `src/slic3r/GUI/UVEditorCanvas.hpp/.cpp` — the 2D UV unwrap viewer widget. +- `src/slic3r/GUI/Plater.hpp/.cpp` — `uv_editor_canvas` member, AUI pane registration, + `get_uv_editor_canvas()`/`show_uv_editor()`. +- `src/slic3r/GUI/GLShadersManager.cpp` — registers `"texture_displacement_bump"`. +- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` — the bump-preview shader. +- `src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp` — `PainterGizmoType::TEXTURE_DISPLACEMENT`. +- `src/slic3r/GUI/Gizmos/GLGizmosManager.hpp/.cpp` — `EType::TextureDisplacement` registration. +- `src/slic3r/GUI/ImGuiWrapper.cpp` — the light-mode checkmark-color fix (bug #3 above; a + pre-existing, general bug, not scoped to this feature). + +**CMake:** all new source files added to `src/libslic3r/CMakeLists.txt`, +`src/slic3r/CMakeLists.txt` (in roughly-alphabetical position matching each list's existing +convention), and `tests/libslic3r/CMakeLists.txt` for the unit test file. + +**Tests:** `tests/libslic3r/test_texture_displacement.cpp` — **run and passing** (7 cases, 116 +assertions). Covers `decode_height_texture` round-trip, empty-layer no-op, full-cube uniform +displacement, boundary-vertex pinning on a hand-built fan mesh, and — added with the bake rewrite — +a regression test that a **second layer over the same area actually contributes** (the bug that +rewrite fixed), a table-driven check of all four blend modes, and that the lowest layer ignores its +blend mode. `BUILD_TESTS` is `OFF` in the checked-in build cache; flip it on to run them: + + cmake -S . -B build -DBUILD_TESTS=ON + cmake --build build --config Release --target libslic3r_tests -- -m + ./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand + +## Build notes + +- Everything here lives in `libslic3r`/`libslic3r_gui`/`libslic3r_cgal` — no new external + dependency, no `deps/` rebuild needed. CGAL's parameterization package was already vendored. +- To build just this feature's code path fastest: `cmake --build build --config Release --target + libslic3r_gui -- -m` (pulls in `libslic3r` and `libslic3r_cgal` as needed). The full app target + is `OrcaSlicer_app_gui` (produces `build/src/Release/orca-slicer.exe`) — only needed to actually + run and visually test, not to verify compilation. +- `BUILD_TESTS` is `OFF` in the existing build cache; flip it on to actually run + `test_texture_displacement.cpp`. diff --git a/TEXTURE_DISPLACEMENT_GUIDE.md b/TEXTURE_DISPLACEMENT_GUIDE.md new file mode 100644 index 0000000000..ac16c97c9e --- /dev/null +++ b/TEXTURE_DISPLACEMENT_GUIDE.md @@ -0,0 +1,326 @@ +# Texture Displacement — Feature & Controls Guide + +Texture Displacement is a paint-style gizmo that stamps height-map textures onto a model's surface +and turns them into real relief — engraved or embossed detail — either as a live preview or baked +into actual mesh geometry. You paint where the texture applies, stack multiple textures as blended +layers, choose how each is projected onto the surface, and (for the unwrap projection) lay the result +out by hand in a dedicated 2D **UV Editor** pane. + +This document describes every feature and control. For the internal architecture and algorithms, see +`TEXTURE_DISPLACEMENT.md`. + +--- + +## Table of contents + +1. [Quick start](#quick-start) +2. [Entering the tool](#entering-the-tool) +3. [Selection modes](#selection-modes) +4. [View modes](#view-modes) +5. [Auto update](#auto-update) +6. [Texture layers](#texture-layers) +7. [Per-layer settings](#per-layer-settings) +8. [Projection methods](#projection-methods) +9. [The UV Editor](#the-uv-editor) +10. [Seams](#seams) +11. [Adjust placement (on-model)](#adjust-placement-on-model) +12. [Preparing the mesh: Subdivide & Remesh](#preparing-the-mesh-subdivide--remesh) +13. [Baking & resetting](#baking--resetting) +14. [Controls reference](#controls-reference) +15. [Tips & limitations](#tips--limitations) + +--- + +## Quick start + +1. Select an object and open the **Texture displacement** gizmo from the left toolbar. +2. A texture layer is added automatically. Pick a texture from the layer's picker, or import your own. +3. **Paint** the area you want the texture to affect (or press **Select whole model**). +4. The relief appears live on the model. Tune **Depth**, **Tile size**, **Rotation**, etc. +5. If the model is low-poly, use **Subdivide** or **Remesh** so there are enough vertices for detail. +6. Press **Bake** to convert the preview into real geometry, or leave it as a live preview. + +> The tool only ever affects the **painted** area. Everything you don't paint keeps its original +> surface, and bake blends the relief seamlessly into it. + +--- + +## Entering the tool + +The gizmo lives on the left gizmo toolbar (icon: `toolbar_texture_displacement.svg`). Its settings +panel opens beside the toolbar. You can **Dock panel / Undock panel** (top of the panel) to pin it or +float it freely over the 3D view, and **Close** at the bottom exits the gizmo. + +When you first open the tool on a never-textured object it starts with **one texture layer already +added**, so you can paint straight away. + +--- + +## Selection modes + +Choose *how* you paint. All three write into the **active layer's** mask. + +| Mode | What it does | +|------|--------------| +| **Brush** | Free-hand painting with a round brush. Shows a **Brush size** slider and a **Circle / Sphere** choice (circle = surface disc, sphere = 3D ball that also paints around curves). | +| **Face** | Click a single triangle to paint it. | +| **Connected area** | Click to flood-fill a region; the **Angle threshold** slider limits how far the fill spreads across changes in surface angle. | + +- **Select whole model** — marks the entire model as painted for the active layer, instead of + brushing it by hand. + +--- + +## View modes + +A row of icon buttons labelled **View** controls how the painted area is shown. The first four are a +radio group; **Wireframe** is an independent toggle. Hover any icon for its tooltip. + +| View | Meaning | +|------|---------| +| **Normal** | The true displaced geometry — exactly what **Bake** produces. Rebuilt in the background. | +| **Fast** | A GPU bump-shaded approximation of the *active layer only*. No real geometry movement — quick to update, not exact. Best while tuning or dragging islands. | +| **Checker** | A test grid painted over the unwrap so you can see stretching (squares stay square where the map isn't distorted). | +| **Distortion** | A blue→green→red heatmap of how much each area is compressed or stretched in UV space. Needs the **Unwrap (LSCM)** projection. | +| **Wireframe** | Overlays the mesh edges (white). Independent of the view above; in **Normal** view it sits on the displaced surface. | + +--- + +## Auto update + +**Auto update** (on by default) rebuilds the true displaced geometry as soon as *anything* changes — +painting, swapping textures, moving sliders. Turn it off on very heavy models to only rebuild when you +release a slider (painting still updates on stroke end). + +--- + +## Texture layers + +You can stack up to **8** texture layers. Each has its own independent paint mask, its own texture, +and its own parameters, and they combine in slot order like layers in an image editor. + +- **Add a layer** — the **+ icon** to the right of the *Texture layers* heading (reuses the tool icon + for now). +- **Remove** — the button on each layer's header row. +- **Active layer** — click a layer's header (or anywhere in its block) to make it active. The active + layer is the one you paint into and the one whose block is tinted. Only one layer is active at a time. +- **Erase all** — clears the active layer's paint. + +Each layer shows a texture **picker** (large preview + name). Open it to choose from the shipped +library or import your own image (any png/jpg/bmp; it's converted to an 8-bit grayscale height map and +copied into your user texture folder so app updates can't overwrite it). + +--- + +## Per-layer settings + +| Control | Range / options | What it does | +|---------|-----------------|--------------| +| **Depth (mm)** | 0.01–10 (log) | Maximum displacement along the surface normal. | +| **Tile size (mm)** | 0.2–200 (log) | Physical size of one texture tile on the surface. | +| **Rotation** | 0–360° | Rotates the texture on the surface. | +| **Midlevel** | 0–10 | The grey level that means "don't move". At 0 the texture only pushes outward; raise it and darker texels cut *inward* (one map both embosses and engraves). 0.5 makes mid-grey neutral. | +| **Smoothing** | 0–1 | Blurs the height texture before it displaces — rounds hard edges and removes speckle without needing a softer source image. | +| **Edge smoothing** | checkbox + **Edge amount** 0–1 | Fades the relief to flat toward the *edge of the painted area*, so it blends into the surrounding surface. A small amount softens only a thin band at the very edge; the maximum flattens the whole painted face. | +| **Invert** | checkbox | Flips the height map (peaks become valleys). | +| **Blend** | Add / Subtract / Multiply / Divide | How this layer combines with the layers **below** it where they overlap. Add/Subtract pile relief on or carve it away; Multiply/Divide scale the relief underneath (a mask). The lowest painted layer is the **Base** and always behaves additively. | +| **Tile** | checkbox + **Repeat / Mirrored repeat** | When off, the texture is placed once (a decal) instead of repeating. Mirrored repeat flips every other tile to hide seams. | +| **Projection** | see below | How the texture is mapped onto the painted surface. | + +> **Midlevel warning:** cutting inward can fold the surface through itself in sharp concave corners or +> thin walls. Keep Depth small relative to the feature you're cutting into; the panel warns when a deep +> inward setting is risky. + +--- + +## Projection methods + +How the 2D texture is wrapped onto the 3D painted area. + +| Method | Best for | Notes | +|--------|----------|-------| +| **Triplanar (blended)** | Patches wrapping around edges | Projects from all three axes at once and blends, so there's no seam across a sharp edge. | +| **Cylindrical** | Round, tube-like selections | Wraps the texture around the patch's own centre/axis. | +| **Spherical** | Ball-like selections | Longitude/latitude wrap around the patch centre. | +| **Unwrap (LSCM)** | Flat, controlled layout | A real conformal unwrap. Cuts the area into pieces at sharp edges (see **Seam angle**), flattens each, and lets you lay them out by hand in the **UV Editor**. Unlocks Checker/Distortion, seams, and island editing. | +| **From view** | Decals / slide-projector look | Projects straight onto the surface from the current camera direction. Use **Capture current view** to re-lay it from wherever you're looking. | + +### LSCM-only controls + +These appear when a layer uses **Unwrap (LSCM)**: + +- **Seam angle** (5–90°) — edges sharper than this are cut so each piece lies flat. Lower cuts more + (less stretching, more seams); raise to keep more in one piece. A box's 90° corners are cut by + default. *Ignored once you've marked any seam by hand* (your seams then define the pieces). +- **Connect islands** (on by default) — lays the unwrap out as a **connected net**: pieces that share + an edge are unfolded next to each other (a cube becomes a joined net instead of six loose squares). + They stay separate islands, so you can still move any of them by hand. Turn off for the classic + packed-grid layout. +- **Open UV editor** — shows the flattened unwrap in a side pane (see below). Opens *only* when you + turn this on — it never pops up on its own. +- **Mark seams** / **Path** / **Clear seams** — see [Seams](#seams). +- An **Unwrap: N islands, F faces, V verts** read-out tells you what the unwrap actually produced. + +--- + +## The UV Editor + +A dockable 2D pane (enable **Open UV editor** on an LSCM layer) showing the flattened unwrap over the +height texture. Islands are the flattened pieces; you can rearrange them freely — nothing re-packs them +behind your back. Moving an island updates the model **live** (in Fast view it tracks the cursor +smoothly, via a shader uniform — no rebuild until you release). + +### Navigation + +| Action | Control | +|--------|---------| +| Pan | Middle-drag | +| Zoom | Mouse wheel (zooms about the cursor) | +| Frame everything | **Home** or **F**, or the **Frame** toolbar button | + +### Editing an island + +| Action | Control | +|--------|---------| +| Select | Left-click an island | +| Move | Left-drag | +| Rotate | Right-drag, or press **R** then move the mouse (click/Enter to confirm, Esc to cancel) | +| Rotate snapped | Hold **Shift** while rotating — snaps to **global** 15° marks (0/15/30…). A protractor dial with tick marks and the current angle is shown. | +| Scale | Press **S** then move the mouse (click/Enter to confirm, Esc to cancel) | +| Undo / Redo | **Ctrl+Z** / **Ctrl+Shift+Z** or **Ctrl+Y** | + +The **selected** island gets a bold light-green outline and a brighter wireframe; unselected islands +are a translucent light-green wash. The texture underneath repeats exactly as it will when baked. +A **status line** along the bottom always names the current gesture and the shortcuts in play. + +### Toolbar + +| Button | Action | +|--------|--------| +| **Frame** | Frame all islands (same as Home). | +| **Snap** | Toggle magnetic snapping — a dragged island sticks its boundary to a neighbour's when they come close. | +| **Avg scale** | Give every island the same texel density (Blender's "Average Islands Scale"). | +| **Cut** | Split the selected island across its long axis (useful for very long islands). | +| **Join** | Unfold the selected island onto its nearest neighbour along their shared edge — keeps both as separate islands with their own borders. | +| **Unjoin** | Send the selected island back to its own packed position. | + +> **Checker / Distortion in the UV editor:** selecting those View modes also colours the UV pane — a +> checker background, or a per-island distortion heatmap — so you can judge stretch in 2D as well as +> on the model. + +--- + +## Seams + +Seams are edges the unwrap is forced to cut along, on top of whatever the Seam angle cuts — the +Blender "mark seam" workflow. They let you control exactly where the unwrap splits. + +Enable **Mark seams** on an active LSCM layer, then: + +- **Click an edge** on the model to mark it (it turns **red**); click a red edge again to unmark it. + The edge under the cursor is highlighted **yellow** so you can see what a click will toggle. +- **Path mode** (the **Path** checkbox) — for dense meshes where clicking each edge is tedious: click a + start point, then an end point, and the whole **shortest path** between them is seamed at once. It + chains (each click extends from the last point); the start vertex is shown in **green**. +- **Ctrl+drag** rotates/pans the camera while in seam mode. +- **Clear seams** removes them all. + +Once any seam is marked, the automatic Seam-angle cutting is disabled so *your* seams define the +islands — pieces you leave un-seamed merge together. + +--- + +## Adjust placement (on-model) + +**Adjust placement** (on an active layer) lets you position the texture by dragging a handle on the +model instead of nudging the Rotation/offset numbers. The handle is a flat panel in the patch's +tangent plane (drag anywhere on it to move freely) plus U/V arrows for single-axis nudges. It's +anchored to the painted patch, so paint something first. + +--- + +## Preparing the mesh: Subdivide & Remesh + +Displacement can only move vertices that exist, so a coarse model needs more of them first. + +### Subdivide + +Splits every triangle into four, **1–5 times** (each step roughly quadruples the triangle count). + +- **Subdivide steps** (1–5) — how many times to split. +- **Preview subdivision** — shows the result as a **cyan wireframe** without changing the model. +- **Apply** — commits the subdivision to the geometry. +- **Done** — ends the preview and leaves the model as it is. + +### Remesh + +Rebuilds the whole model with triangles close to a target edge length — evens out a mesh with wildly +varying triangle sizes (CGAL isotropic remeshing). + +- **Target edge (mm)** — desired triangle edge length (seeded to the model's current average). +- **Remesh** — splits the big triangles and merges the small ones to that size. + +> Both Subdivide-Apply and Remesh **replace the geometry** and clear any *not-yet-baked* paint on it +> (already-baked relief is kept). If you had the mesh **Wireframe** on before, it stays on afterward. + +--- + +## Baking & resetting + +- **Bake** — converts the current preview into real, permanent mesh geometry, restricted to the + painted area. Runs in the background; the button shows *Baking…* while it works. +- **Erase all** — clears the active layer's paint. + +Baking is the exact same algorithm as the **Normal** preview, so what you see is what you get. + +--- + +## Controls reference + +### Mouse — 3D view (while painting) + +| Input | Action | +|-------|--------| +| Left-drag | Paint the active layer | +| Ctrl + drag | Rotate / pan the camera (works in seam mode too) | +| Wheel | Zoom | + +### Mouse & keys — UV Editor + +| Input | Action | +|-------|--------| +| Left-click | Select island | +| Left-drag | Move island | +| Right-drag | Rotate island | +| **R** / **S** | Modal rotate / scale (mouse drives it, click or Enter confirms, Esc cancels) | +| **Shift** (while rotating) | Snap to global 15° marks | +| Middle-drag | Pan | +| Wheel | Zoom about cursor | +| **Home** / **F** | Frame all islands | +| **Ctrl+Z** / **Ctrl+Shift+Z** / **Ctrl+Y** | Undo / redo | + +### Seam mode + +| Input | Action | +|-------|--------| +| Click edge | Mark / unmark a seam (yellow = hover, red = marked) | +| Click (Path mode) | Set start, then seam the shortest path to the next click | +| Ctrl + drag | Rotate / pan camera | + +--- + +## Tips & limitations + +- **Paint first, then bake.** The preview is free to explore; only Bake changes the real mesh. +- **Not enough detail?** Subdivide or Remesh before painting fine textures. +- **Inward cuts** (high Midlevel + big Depth) can self-intersect on thin walls or sharp concave + corners — keep Depth modest there. +- **Fast vs Normal:** Fast preview shades a bump and shows only the active layer; use it for quick + tuning and smooth UV dragging, but trust **Normal**/**Bake** for the exact result. +- **Topology changes drop unbaked paint.** Subdivide-Apply, Remesh, and Simplify replace the mesh, and + texture-displacement paint isn't remapped across that change (already-baked relief is unaffected). +- **Island placements** are tied to the current unwrap. Re-painting or changing the Seam angle can + re-segment the charts and renumber them, so a re-unwrap re-lays the connected net and discards + hand placements made before it. +- **Connect islands** is on by default; turn it off (per layer) for the classic packed-grid layout, or + if an unfold looks wrong on an unusual mesh. From 00f55639d3588951dd73a31be63076ae876ea984 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 16 Jul 2026 08:52:00 +0200 Subject: [PATCH 08/89] Add new icons --- .../images/texture_displacement_checker.svg | 165 +++++++++++++++ .../texture_displacement_connected_area.svg | 115 ++++++++++ .../texture_displacement_distortion.svg | 197 ++++++++++++++++++ .../texture_displacement_fast_preview.svg | 132 ++++++++++++ .../texture_displacement_real_preview.svg | 150 +++++++++++++ .../images/texture_displacement_wireframe.svg | 163 +++++++++++++++ resources/images/toolbar_big_brush.svg | 60 ++++++ resources/images/toolbar_face.svg | 97 +++++++++ .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 93 +++++---- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 6 + 10 files changed, 1143 insertions(+), 35 deletions(-) create mode 100644 resources/images/texture_displacement_checker.svg create mode 100644 resources/images/texture_displacement_connected_area.svg create mode 100644 resources/images/texture_displacement_distortion.svg create mode 100644 resources/images/texture_displacement_fast_preview.svg create mode 100644 resources/images/texture_displacement_real_preview.svg create mode 100644 resources/images/texture_displacement_wireframe.svg create mode 100644 resources/images/toolbar_big_brush.svg create mode 100644 resources/images/toolbar_face.svg diff --git a/resources/images/texture_displacement_checker.svg b/resources/images/texture_displacement_checker.svg new file mode 100644 index 0000000000..4631116379 --- /dev/null +++ b/resources/images/texture_displacement_checker.svg @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_connected_area.svg b/resources/images/texture_displacement_connected_area.svg new file mode 100644 index 0000000000..b57e96d951 --- /dev/null +++ b/resources/images/texture_displacement_connected_area.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_distortion.svg b/resources/images/texture_displacement_distortion.svg new file mode 100644 index 0000000000..833a34f0cf --- /dev/null +++ b/resources/images/texture_displacement_distortion.svg @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_fast_preview.svg b/resources/images/texture_displacement_fast_preview.svg new file mode 100644 index 0000000000..9e13fb68de --- /dev/null +++ b/resources/images/texture_displacement_fast_preview.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_real_preview.svg b/resources/images/texture_displacement_real_preview.svg new file mode 100644 index 0000000000..f3ebdaa53d --- /dev/null +++ b/resources/images/texture_displacement_real_preview.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_wireframe.svg b/resources/images/texture_displacement_wireframe.svg new file mode 100644 index 0000000000..20ac8c1b03 --- /dev/null +++ b/resources/images/texture_displacement_wireframe.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/toolbar_big_brush.svg b/resources/images/toolbar_big_brush.svg new file mode 100644 index 0000000000..20be9ec53b --- /dev/null +++ b/resources/images/toolbar_big_brush.svg @@ -0,0 +1,60 @@ + + + + + + + + + + diff --git a/resources/images/toolbar_face.svg b/resources/images/toolbar_face.svg new file mode 100644 index 0000000000..6975e0f6e4 --- /dev/null +++ b/resources/images/toolbar_face.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index a8dcbaf7c4..426521f04d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -2093,6 +2093,19 @@ unsigned int GLGizmoTextureDisplacement::tool_icon_id() return m_tool_icon.get_id(); } +unsigned int GLGizmoTextureDisplacement::icon_id(const std::string &filename) +{ + auto it = m_icon_cache.find(filename); + if (it == m_icon_cache.end()) { + // First request for this icon: default-construct the texture in place and load it once (with a + // GL context current, since this runs from the panel render). A failed load leaves id 0 and is + // not retried. + it = m_icon_cache.try_emplace(filename).first; + it->second.load_from_svg_file(resources_dir() + "/images/" + filename, false, false, false, 32); + } + return it->second.get_id(); +} + void GLGizmoTextureDisplacement::add_texture_layer() { ModelVolume *mv = texture_volume(); @@ -2552,6 +2565,28 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float return changed; }; + // Icon toggle button shared by the selection-mode and view-mode rows: an SVG button with a teal + // backing and full-brightness icon when active, dimmed otherwise. Falls back to a text checkbox if + // its icon could not be loaded, so the control is never lost. + const float icon_btn_sz = m_imgui->scaled(1.5f); + const ImVec4 icon_active_bg(0.0f, 0.59f, 0.53f, 1.0f); + const auto icon_toggle = [&](int uid, unsigned int ic, bool active, const wxString &label, const wxString &tip) -> bool { + bool clicked; + ImGui::PushID(uid); + if (ic != 0) + clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic, ImVec2(icon_btn_sz, icon_btn_sz), ImVec2(0, 0), + ImVec2(1, 1), -1, active ? icon_active_bg : ImVec4(0, 0, 0, 0), + active ? ImVec4(1, 1, 1, 1) : ImVec4(0.65f, 0.65f, 0.65f, 1.f)); + else { + bool v = active; + clicked = ImGui::Checkbox(label.ToUTF8().data(), &v); + } + ImGui::PopID(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(tip, m_imgui->scaled(18.f)); + return clicked; + }; + if (m_imgui->button(m_undocked ? _L("Dock panel") : _L("Undock panel"))) m_undocked = !m_undocked; if (ImGui::IsItemHovered()) @@ -2568,17 +2603,22 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER; const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER; const bool is_area_mode = m_tool_type == ToolType::SMART_FILL; - if (ImGui::RadioButton(_u8L("Brush").c_str(), is_brush_mode)) { + ImGui::SameLine(); + if (icon_toggle(801, icon_id("toolbar_big_brush.svg"), is_brush_mode, _L("Brush"), + _L("Brush - paint over the surface by dragging"))) { m_tool_type = ToolType::BRUSH; m_cursor_type = TriangleSelector::CursorType::CIRCLE; } ImGui::SameLine(); - if (ImGui::RadioButton(_u8L("Face").c_str(), is_face_mode)) { + if (icon_toggle(802, icon_id("toolbar_face.svg"), is_face_mode, _L("Face"), + _L("Face - click individual triangles"))) { m_tool_type = ToolType::BRUSH; m_cursor_type = TriangleSelector::CursorType::POINTER; } ImGui::SameLine(); - if (ImGui::RadioButton(_u8L("Connected area").c_str(), is_area_mode)) { + if (icon_toggle(803, icon_id("texture_displacement_connected_area.svg"), is_area_mode, _L("Connected area"), + _L("Connected area - flood-fill the region reachable without crossing an edge sharper than the " + "angle threshold"))) { m_tool_type = ToolType::SMART_FILL; m_cursor_type = TriangleSelector::CursorType::POINTER; } @@ -2606,50 +2646,33 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float select_whole_model(); // View mode (#: "make View Mode with just icons"): Normal / Fast / Checker / Distortion behave as - // one radio group, Wireframe as an independent toggle. All reuse the one tool icon for now, so the - // tooltip carries the meaning. The underlying state stays m_use_bump_preview + m_uv_check_mode. + // one radio group, Wireframe as an independent toggle. Each has its own icon; the tooltip carries + // the meaning. The underlying state stays m_use_bump_preview + m_uv_check_mode. { - const unsigned int icon = tool_icon_id(); - const float vsz = m_imgui->scaled(1.5f); - const ImVec4 teal(0.0f, 0.59f, 0.53f, 1.0f); - const int cur_mode = m_use_bump_preview ? 1 : - (m_uv_check_mode == UVCheckMode::Checker ? 2 : - m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0); + const int cur_mode = m_use_bump_preview ? 1 : + (m_uv_check_mode == UVCheckMode::Checker ? 2 : + m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0); int new_mode = cur_mode; bool wf_toggle = false; - // One icon button; active state shown by a teal backing and a full-brightness (vs dimmed) icon. - // Falls back to a text toggle if the icon can't load. - const auto icon_toggle = [&](int uid, bool active, const wxString &label, const wxString &tip) -> bool { - bool clicked; - ImGui::PushID(uid); - if (icon != 0) - clicked = m_imgui->image_button((ImTextureID) (intptr_t) icon, ImVec2(vsz, vsz), ImVec2(0, 0), ImVec2(1, 1), - -1, active ? teal : ImVec4(0, 0, 0, 0), - active ? ImVec4(1, 1, 1, 1) : ImVec4(0.65f, 0.65f, 0.65f, 1.f)); - else { - bool v = active; - clicked = ImGui::Checkbox(label.ToUTF8().data(), &v); - } - ImGui::PopID(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(tip, m_imgui->scaled(18.f)); - return clicked; - }; - m_imgui->text(_L("View")); ImGui::SameLine(); - if (icon_toggle(701, cur_mode == 0, _L("Normal"), _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0; + if (icon_toggle(701, icon_id("texture_displacement_real_preview.svg"), cur_mode == 0, _L("Normal"), + _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0; ImGui::SameLine(); - if (icon_toggle(702, cur_mode == 1, _L("Fast"), _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1; + if (icon_toggle(702, icon_id("texture_displacement_fast_preview.svg"), cur_mode == 1, _L("Fast"), + _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1; ImGui::SameLine(); - if (icon_toggle(703, cur_mode == 2, _L("Checker"), _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2; + if (icon_toggle(703, icon_id("texture_displacement_checker.svg"), cur_mode == 2, _L("Checker"), + _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2; ImGui::SameLine(); - if (icon_toggle(704, cur_mode == 3, _L("Distortion"), _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3; + if (icon_toggle(704, icon_id("texture_displacement_distortion.svg"), cur_mode == 3, _L("Distortion"), + _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3; ImGui::SameLine(); ImGui::Dummy(ImVec2(m_imgui->scaled(0.6f), 0.f)); ImGui::SameLine(); - if (icon_toggle(705, m_wireframe_overlay, _L("Wireframe"), _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true; + if (icon_toggle(705, icon_id("texture_displacement_wireframe.svg"), m_wireframe_overlay, _L("Wireframe"), + _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true; if (new_mode != cur_mode) { m_use_bump_preview = (new_mode == 1); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index f81c6bc08a..bce96e89b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -449,6 +449,12 @@ private: GLTexture m_tool_icon; bool m_tool_icon_tried = false; unsigned int tool_icon_id(); // 0 if the icon could not be loaded + + // The panel's icon-button rows (selection mode, view mode) each need their own small SVG. Loaded and + // uploaded once on first use and cached by file name (under resources/images/). Returns 0 if an icon + // could not be loaded, in which case the button falls back to a text control. + std::map m_icon_cache; + unsigned int icon_id(const std::string &filename); }; } // namespace Slic3r::GUI From 8247514ae2211cdba854f3bf265712b591bcacda Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Fri, 17 Jul 2026 09:04:39 +0200 Subject: [PATCH 09/89] Fix cmake config --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5fbc8d4c1..2bd97d61cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -945,7 +945,7 @@ if (WIN32) endif () endif () set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Orca Slicer is an open source slicer for FDM printers") -set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer_3/OrcaSlicer") +set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer/OrcaSlicer") set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) set (CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/images\\\\OrcaSlicer.ico") set (CPACK_NSIS_MUI_ICON "${CPACK_PACKAGE_ICON}") From ab023f3f6d5c56d57567b800e05b29a25dbf1934 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 21 Jul 2026 09:44:25 +0200 Subject: [PATCH 10/89] Add texture projection frame overlay, fix remeshing and subdivision --- TEXTURE_DISPLACEMENT.md | 103 ++- resources/images/texture_displacement_add.svg | 120 ++++ .../texture_displacement_add_negative.svg | 109 ++++ .../images/texture_displacement_avg_scale.svg | 330 ++++++++++ .../images/texture_displacement_cross.svg | 121 ++++ resources/images/texture_displacement_cut.svg | 359 +++++++++++ .../images/texture_displacement_frame.svg | 242 +++++++ .../images/texture_displacement_join.svg | 387 +++++++++++ .../images/texture_displacement_snap.svg | 260 ++++++++ .../images/texture_displacement_unjoin.svg | 387 +++++++++++ .../texture_displacement_uv_select_edge.svg | 383 +++++++++++ .../texture_displacement_uv_select_island.svg | 413 ++++++++++++ .../texture_displacement_uv_select_vertex.svg | 378 +++++++++++ src/libslic3r/MeshBoolean.cpp | 48 +- src/libslic3r/MeshBoolean.hpp | 4 +- src/libslic3r/TextureDisplacement.cpp | 33 +- src/libslic3r/TextureDisplacement.hpp | 37 +- src/slic3r/CMakeLists.txt | 2 + .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 607 ++++++++++++++++-- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 66 +- src/slic3r/GUI/TextureProjectorFrame.cpp | 97 +++ src/slic3r/GUI/TextureProjectorFrame.hpp | 56 ++ src/slic3r/GUI/UVEditorCanvas.cpp | 210 +++++- src/slic3r/GUI/UVEditorCanvas.hpp | 26 +- 24 files changed, 4658 insertions(+), 120 deletions(-) create mode 100644 resources/images/texture_displacement_add.svg create mode 100644 resources/images/texture_displacement_add_negative.svg create mode 100644 resources/images/texture_displacement_avg_scale.svg create mode 100644 resources/images/texture_displacement_cross.svg create mode 100644 resources/images/texture_displacement_cut.svg create mode 100644 resources/images/texture_displacement_frame.svg create mode 100644 resources/images/texture_displacement_join.svg create mode 100644 resources/images/texture_displacement_snap.svg create mode 100644 resources/images/texture_displacement_unjoin.svg create mode 100644 resources/images/texture_displacement_uv_select_edge.svg create mode 100644 resources/images/texture_displacement_uv_select_island.svg create mode 100644 resources/images/texture_displacement_uv_select_vertex.svg create mode 100644 src/slic3r/GUI/TextureProjectorFrame.cpp create mode 100644 src/slic3r/GUI/TextureProjectorFrame.hpp diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index 02a0c357fa..6bbd5f085b 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -164,6 +164,21 @@ texture samples per vertex and so has no single UV that represents it. (`compute_layer_vertex_uvs()`) and drive the shader's `use_vertex_uv` path. Faces angled away from the projector smear; that is inherent to view projection, not a bug. + Two companions to this mode: + - **Projection frame overlay** (`TextureProjectorFrame`, see below) — a semi-transparent window + dragged over the 3D view whose border becomes the projection's edge. Applying it stores an exact + **projective** map in `view_project_matrix`, which supersedes the affine `right`/`up` axes above + for that layer (`view_project_projective`). + - **"Project only on visible"** (`select_visible_faces()`) — repaints the layer with exactly the + facets the camera can see, so the projected area matches the viewpoint the projector was captured + from. Two tests: a facing test (normal vs. view direction, per triangle — under perspective the + view direction varies across the model, so it is taken from the eye to each centroid), then + `MeshRaycaster::get_unobscured_idxs()` on the survivors to drop facets hidden behind other + geometry, so a concave part's far inner wall is correctly excluded. One ray query per front-facing + facet, hence click-driven (on the checkbox and on each "Capture current view"), never per frame. + It **replaces** the layer's paint rather than adding to it — "project onto what I can see" would + otherwise accumulate every angle the user had ever looked from. + ### Manual seams and island cutting `TextureDisplacementLayer::lscm_seam_edges` — undirected mesh-vertex-index edge pairs the unwrap is @@ -217,7 +232,10 @@ Algorithm: recursive 1-to-4 triangle split via edge midpoints, with a shared per (keyed by sorted vertex-index pair) so triangles sharing an edge get the *same* new vertex — capped at `max_iterations` (default 6) passes to bound worst-case triangle-count explosion. -Wired as a "Subdivide model" button in the gizmo panel — a real, committed geometry change (like +Wired as a "Subdivide steps" slider (**0–5**, where 0 means no subdivision and previews nothing) plus +Preview/Apply/Done in the gizmo panel. **Apply snaps the slider back to 0** — see bug #22: leaving it +at the count just committed made the panel immediately re-preview N more passes on top of a mesh 4^N +times denser, which is what the "subdivide lags" report was. A real, committed geometry change (like Bake), using the same `save_painting()`/`set_mesh()`/`restore_painting()` dance `GLGizmoSimplify` uses: supported/seam/mmu/fuzzy-skin masks get remapped onto the new triangles, texture-displacement paint does not (no remap support yet) and is dropped rather than left pointing at now-meaningless @@ -314,6 +332,53 @@ render pixels. May need a one-line sign flip once actually tested. The rotation- direction should be reliable (it follows directly from a self-consistent 2D basis, no such ambiguity). +### Projection frame overlay (ViewProjected) + +`src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` — a semi-transparent, resizable `wxFrame` the user +drags **over the 3D view**, like a slide projector's gate. Whatever the model shows through it is what +the texture is projected onto, and the window's border becomes the hard edge of the displacement. +Press **Apply projection frame** and the gizmo reads the window's rectangle and commits it. + +The window is deliberately **dumb**: it owns no placement state and reports nothing continuously. Its +position and size *are* the placement, read on demand at Apply — which is also when the expensive +visible-facet raycast runs. So dragging it is free and nothing recomputes until asked. + +Plain 2D (`wxPaintDC`), not a `wxGLCanvas`: a second GL canvas would have to share the app's one real +`wxGLContext`, the cause of bugs #10 and #14 below. It only ever draws a bitmap and a border. + +**The projective mapping (`apply_projection_frame()`)** is the substantive part. The frame defines a +**screen-space** rectangle, but the bake samples from a **local-space** position, so the two have to be +reconciled. `view_project_right/up` can only express an *affine* projection — exact under an +orthographic camera, but wrong under perspective, where the near end of a part projects larger than the +far end and no pair of axes reproduces that. So the layer instead stores a full projective map +(`view_project_matrix`, row-major 3×4, `uv = (row0·p̃/row2·p̃, row1·p̃/row2·p̃)`), built like this: + +- `K = projection · view · (instance · volume)`, i.e. local → clip, the same product the renderer uses. + Note `Camera::get_projection_matrix()` is typed `Transform3d` (nominally affine) but its perspective + form explicitly writes a `(0, 0, −1, 0)` bottom row into the underlying 4×4, so `clip.w = −z_eye` is + genuinely carried. The build therefore multiplies **`.matrix()` products** (plain `Matrix4d`), never + `Transform3d` products, which would not compose that row correctly. +- Window coordinates follow `igl::project`'s convention (as `CameraUtils::project` does), with y + measured downward. Writing `uv = (win − rect_origin) / rect_size` makes u and v affine in + `ndc = clip.xyz / clip.w`; multiplying through by `clip.w` leaves a plain linear combination of `K`'s + rows, which is exactly the 3×4 matrix — the perspective divide survives intact. +- `w > 0` is checked rather than divided blindly. A point behind the projector has `w < 0` and divides + to a plausible-looking but **mirrored** uv — the classic way a projected decal reappears on the back + of a model. `project_uv_projective()` returns false there and the caller treats it as no height. + +The map already includes placement, so `apply_uv_transform()` is **not** applied on top of it — the +window's own position and size are the placement, and the tiling/rotation/offset sliders would shove +the result off the frame the user just aligned. A "Clear" button drops back to the affine path where +those controls mean something again. + +Apply also sets `tile_enabled = false`, so `DecodedHeightTexture::sample()` returns 0 outside `[0,1)` +and the border is a hard edge rather than the first seam of an endless repeat, and repaints the layer +via `select_visible_faces(&matrix)` — the frame's uv square clips the selection, which both matches the +paint to the border and keeps the ray queries proportional to the framed area instead of the model. + +Owned by the gizmo and **destroyed** (not just hidden) in `on_shutdown()`. Closing it only hides it, so +reopening keeps it where it was left. + ### UV Editor pane `UVEditorCanvas` (`src/slic3r/GUI/UVEditorCanvas.hpp/.cpp`) — a standalone `wxGLCanvas` rendering the @@ -487,6 +552,37 @@ These were all real, confirmed root causes (found by reading the actual code pat actual island bbox into the panel — three rounds of reasoning from screenshots had each guessed wrong, because a collapsed-to-a-point unwrap and an off-screen-framed one look identical. +19. **Isotropic remeshing produced scrambled geometry with dropped triangles** — the "remeshing kinda + does not work properly" report, and a genuine one-line root cause. `isotropic_remeshing()` edits + the `Surface_mesh` **in place**, and its edge collapses only *mark* vertices and faces as removed; + the underlying arrays keep the holes until `collect_garbage()` is called. That matters because the + shared `cgal_to_indexed_triangle_set()` numbers its output vertices by **iteration order** (which + skips removed slots) while reading each face's corner as the **raw integer value of the vertex + descriptor** (which does not). The two agree only up to the first collapse; past that every + triangle indexes the wrong vertices, and any descriptor beyond the live vertex count hits the + converter's `iv >= vsize` guard and is silently dropped *together with its triangle*. Fixed by + compacting (`cgal_mesh.collect_garbage()`) before converting. Note the pre-existing callers were + unaffected: the boolean ops build their result into a fresh mesh, so only the in-place remesher + ever handed the converter a mesh with garbage in it. +20. **Remeshing rounded off every sharp edge** — the second half of the same report. CGAL's + `isotropic_remeshing` relaxes vertices tangentially along the surface, which erodes hard features + unless they are constrained: a cube came back with wobbly, eroded edges. Fixed by detecting sharp + edges (`PMP::detect_sharp_edges()` at a user-settable dihedral angle, default 40°) plus every open + border, constraining them, and passing `protect_constraints(true)`. That option additionally + requires each constrained edge to already be shorter than 4/3 · target, hence the + `PMP::split_long_edges()` pre-pass (given the same map, so the halves inherit the constraint) — + this mirrors CGAL's own isotropic_remeshing example. Also added a guard for the case where + `Surface_mesh::add_face()` refused faces on a non-manifold input: remeshing a mesh that silently + lost faces yields a **punctured** model, so it now bails out and reports instead. +21. **Remesh falsely reporting "did not change the model"** — the GUI decided success by comparing + *vertex counts*. A remesh that redistributes triangles at roughly the current density legitimately + lands on the same count, so a perfectly good result was discarded with an error. Now compared + structurally against the input (which is what `remesh_isotropic()` hands back on failure). +22. **Subdivide re-previewing at the same count after Apply** — Apply committed N passes and then + immediately rebuilt the *preview* at N passes again, now on top of a mesh up to 4^N times denser. + That is the single most expensive thing the panel can do, and it ran on every Apply. The slider + now starts at 0 (a real "no subdivision" value that previews nothing), and Apply snaps back to it. + ## Known limitations / deferred work - **No `.3mf` serialization** for texture-displacement paint data or texture assets. A background @@ -539,7 +635,8 @@ Remaining cosmetic / known gaps: **libslic3r (core, no GUI dependency):** - `src/libslic3r/TextureDisplacement.hpp/.cpp` — data model, bake algorithm, projection methods, tiling, subdivision. See doc comments throughout, they're kept accurate and up to date. -- `src/libslic3r/MeshBoolean.hpp/.cpp` — added `parameterize_lscm()` in the `cgal` sub-namespace, +- `src/libslic3r/MeshBoolean.hpp/.cpp` — added `parameterize_lscm()` and `remesh_isotropic()` + (sharp-feature-preserving isotropic remeshing, see bugs #19–#21) in the `cgal` sub-namespace, reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for mesh boolean ops. New CGAL includes: `Polygon_mesh_processing/border.h`, `Polygon_mesh_processing/connected_components.h`, `Surface_mesh_parameterization/{Error_code, @@ -567,6 +664,8 @@ Remaining cosmetic / known gaps: - `src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp/.cpp` — background bake commit. - `src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp/.cpp` — background preview compute (mirrors the bake job's shape but commits nothing to the Model). +- `src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` — the semi-transparent projection-frame overlay for + ViewProjected layers (plain 2D `wxPaintDC`, no GL context — see its section above). - `src/slic3r/GUI/UVEditorCanvas.hpp/.cpp` — the 2D UV unwrap viewer widget. - `src/slic3r/GUI/Plater.hpp/.cpp` — `uv_editor_canvas` member, AUI pane registration, `get_uv_editor_canvas()`/`show_uv_editor()`. diff --git a/resources/images/texture_displacement_add.svg b/resources/images/texture_displacement_add.svg new file mode 100644 index 0000000000..b4e5da179b --- /dev/null +++ b/resources/images/texture_displacement_add.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_add_negative.svg b/resources/images/texture_displacement_add_negative.svg new file mode 100644 index 0000000000..d7979dda4f --- /dev/null +++ b/resources/images/texture_displacement_add_negative.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_avg_scale.svg b/resources/images/texture_displacement_avg_scale.svg new file mode 100644 index 0000000000..c94a62709c --- /dev/null +++ b/resources/images/texture_displacement_avg_scale.svg @@ -0,0 +1,330 @@ + + diff --git a/resources/images/texture_displacement_cross.svg b/resources/images/texture_displacement_cross.svg new file mode 100644 index 0000000000..a2e62b9384 --- /dev/null +++ b/resources/images/texture_displacement_cross.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/images/texture_displacement_cut.svg b/resources/images/texture_displacement_cut.svg new file mode 100644 index 0000000000..532793eb25 --- /dev/null +++ b/resources/images/texture_displacement_cut.svg @@ -0,0 +1,359 @@ + + diff --git a/resources/images/texture_displacement_frame.svg b/resources/images/texture_displacement_frame.svg new file mode 100644 index 0000000000..3bf29bcc5a --- /dev/null +++ b/resources/images/texture_displacement_frame.svg @@ -0,0 +1,242 @@ + + diff --git a/resources/images/texture_displacement_join.svg b/resources/images/texture_displacement_join.svg new file mode 100644 index 0000000000..ff5e9a907f --- /dev/null +++ b/resources/images/texture_displacement_join.svg @@ -0,0 +1,387 @@ + + diff --git a/resources/images/texture_displacement_snap.svg b/resources/images/texture_displacement_snap.svg new file mode 100644 index 0000000000..f6abea8a3f --- /dev/null +++ b/resources/images/texture_displacement_snap.svg @@ -0,0 +1,260 @@ + + diff --git a/resources/images/texture_displacement_unjoin.svg b/resources/images/texture_displacement_unjoin.svg new file mode 100644 index 0000000000..2f50d35767 --- /dev/null +++ b/resources/images/texture_displacement_unjoin.svg @@ -0,0 +1,387 @@ + + diff --git a/resources/images/texture_displacement_uv_select_edge.svg b/resources/images/texture_displacement_uv_select_edge.svg new file mode 100644 index 0000000000..dbab9fdd93 --- /dev/null +++ b/resources/images/texture_displacement_uv_select_edge.svg @@ -0,0 +1,383 @@ + + diff --git a/resources/images/texture_displacement_uv_select_island.svg b/resources/images/texture_displacement_uv_select_island.svg new file mode 100644 index 0000000000..733f58053e --- /dev/null +++ b/resources/images/texture_displacement_uv_select_island.svg @@ -0,0 +1,413 @@ + + diff --git a/resources/images/texture_displacement_uv_select_vertex.svg b/resources/images/texture_displacement_uv_select_vertex.svg new file mode 100644 index 0000000000..f66446f5d0 --- /dev/null +++ b/resources/images/texture_displacement_uv_select_vertex.svg @@ -0,0 +1,378 @@ + + diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 478e0b950c..6a143b0a37 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -29,6 +29,7 @@ // For parameterize_lscm() #include #include +#include #include #include #include @@ -259,7 +260,8 @@ indexed_triangle_set cgal_to_indexed_triangle_set(const CGALMesh &cgalmesh) // Isotropic remeshing // ///////////////////////////////////////////////////////////////////////////// -indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, unsigned n_iterations) +indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, + unsigned n_iterations, double sharp_angle_deg) { if (mesh.indices.empty() || target_edge_length <= 0.0) return mesh; @@ -269,14 +271,56 @@ indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double t if (cgal_mesh.is_empty() || cgal_mesh.number_of_faces() == 0) return mesh; + // Surface_mesh::add_face() refuses any face that would make the mesh non-manifold and returns a + // null descriptor instead. Remeshing a mesh that silently lost faces that way produces holes in + // the output, so bail out and let the caller report it rather than hand back a punctured model. + if (cgal_mesh.number_of_faces() != mesh.indices.size()) + return mesh; + + using edge_descriptor = boost::graph_traits<_EpicMesh>::edge_descriptor; try { + // Sharp edges and open borders are pinned before remeshing. Without that, the tangential + // relaxation pass slides vertices along the surface and rounds every hard feature off - a + // cube comes back with wobbly, eroded edges, which is the most visible way "remeshing does + // not work properly". protect_constraints() forbids splitting or collapsing them, but it + // requires each constrained edge to already be shorter than 4/3 * target, hence the split + // first (passing the map so the halves inherit the constraint). This mirrors CGAL's own + // isotropic_remeshing example. + auto ecm = cgal_mesh.add_property_map("e:is_constrained", false).first; + if (sharp_angle_deg > 0.0) + CGALProc::detect_sharp_edges(cgal_mesh, sharp_angle_deg, ecm); + for (edge_descriptor e : edges(cgal_mesh)) { + const auto h = halfedge(e, cgal_mesh); + if (is_border(h, cgal_mesh) || is_border(opposite(h, cgal_mesh), cgal_mesh)) + put(ecm, e, true); + } + + std::vector constrained; + for (edge_descriptor e : edges(cgal_mesh)) + if (get(ecm, e)) + constrained.push_back(e); + if (!constrained.empty()) + CGALProc::split_long_edges(constrained, target_edge_length, cgal_mesh, + CGALParams::edge_is_constrained_map(ecm)); + CGALProc::isotropic_remeshing(faces(cgal_mesh), target_edge_length, cgal_mesh, - CGALParams::number_of_iterations(n_iterations)); + CGALParams::number_of_iterations(n_iterations) + .edge_is_constrained_map(ecm) + .protect_constraints(true)); } catch (const std::exception &) { return mesh; // CGAL throws on some non-manifold / degenerate inputs; leave the mesh untouched } if (cgal_mesh.number_of_faces() == 0) return mesh; + + // isotropic_remeshing edits in place, and its edge collapses only *mark* vertices and faces as + // removed - the underlying arrays keep the holes until the garbage is collected. That matters + // because cgal_to_indexed_triangle_set() numbers its output vertices by iteration order (which + // skips removed slots) while reading each face's corner as the raw integer value of the vertex + // descriptor (which does not). Past the first collapse the two disagree, so every triangle + // points at the wrong vertices, and any descriptor beyond the live vertex count is dropped + // together with its triangle. Compacting first makes descriptor == iteration order again. + cgal_mesh.collect_garbage(); return cgal_to_indexed_triangle_set(cgal_mesh); } diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 3fb49cb7c5..efaf553420 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -86,9 +86,11 @@ std::optional> parameterize_lscm(const indexed_triangle_set & // Isotropic remeshing (CGAL): rebuilds the mesh so its triangles are close to a uniform target edge // length, splitting oversized triangles and collapsing undersized ones. Used to even out a model with // wildly varying triangle sizes so texture displacement has a consistent vertex density to work with. +// Edges whose dihedral angle exceeds `sharp_angle_deg`, and any open border, are held fixed so hard +// features survive instead of being eroded by the relaxation pass; pass 0 to remesh everything. // Returns the input unchanged if remeshing fails (e.g. a non-manifold or self-intersecting input). indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, - unsigned n_iterations = 3); + unsigned n_iterations = 3, double sharp_angle_deg = 40.0); } namespace mcut { diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 50740bc532..5de11eafa6 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -934,6 +934,19 @@ float blend_displacement(float accumulated, float value, TextureBlendMode mode) } } +bool project_uv_projective(const std::array &m, const Vec3f &position, Vec2f &uv) +{ + const float x = position.x(), y = position.y(), z = position.z(); + const float w = m[8] * x + m[9] * y + m[10] * z + m[11]; + // Strictly greater than zero: at w == 0 the point sits on the projector's plane and maps to + // infinity, and at w < 0 it is behind the projector, where dividing yields a plausible-looking + // but mirrored uv - the classic way a projected decal reappears on the back of a model. + if (!(w > 1e-6f)) + return false; + uv = Vec2f((m[0] * x + m[1] * y + m[2] * z + m[3]) / w, (m[4] * x + m[5] * y + m[6] * z + m[7]) / w); + return true; +} + float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisplacementLayer &layer, const Vec3f &position, const Vec3f &normal, const Vec3f &patch_center, const Vec3f &patch_axis, const Vec2f *lscm_uv) @@ -957,6 +970,16 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp case TextureProjectionMethod::Spherical: return sample_at(project_spherical(position, patch_center)); case TextureProjectionMethod::ViewProjected: + if (layer.view_project_projective) { + // Exact projective placement written by the projection-frame overlay. Sampled directly, + // *without* apply_uv_transform(): the matrix already maps the window's border to the uv + // unit square, so the tiling/rotation/offset controls would displace it off the frame + // the user just aligned. A point behind the projector has no uv at all -> no height. + Vec2f uv; + if (!project_uv_projective(layer.view_project_matrix, position, uv)) + return 0.f; + return texture.sample(uv, layer.tile_enabled, layer.tile_method); + } // Flat projection onto the captured projector plane. Single-valued per point, so unlike // blended triplanar it is one sample, and it is what "project from view" places. return sample_at(Vec2f(position.dot(layer.view_project_right), position.dot(layer.view_project_up))); @@ -1163,9 +1186,15 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set int patch_vertex_count = 0; for (const stl_triangle_vertex_indices &tri : patch.indices) for (int i = 0; i < 3; ++i) { - average_normal += vertex_normals[tri[i]]; - patch_centroid += patch.vertices[tri[i]]; + const int vi = tri[i]; + patch_centroid += patch.vertices[size_t(vi)]; ++patch_vertex_count; + // A brush stroke that split a triangle appends new vertices past the base mesh's own + // (see the get_facets_strict() note above); vertex_normals is sized to the base mesh, + // so those split indices must be skipped here or this reads out of bounds. The main + // displacement loop below guards the same way. + if (vi < int(vertex_normals.size())) + average_normal += vertex_normals[size_t(vi)]; } average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); patch_centroid = (patch_vertex_count > 0) ? Vec3f(patch_centroid / float(patch_vertex_count)) : Vec3f::Zero(); diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index f6c34e93a2..dc78be701c 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -7,6 +7,7 @@ #include #include +#include // view_project_matrix is a std::array #include #include @@ -218,6 +219,24 @@ struct TextureDisplacementLayer // projects to Vec2f(dot(pos, right), dot(pos, up)) before the usual tiling/rotation/offset. Vec3f view_project_right = Vec3f::UnitX(); Vec3f view_project_up = Vec3f::UnitY(); + + // Also ViewProjected, and takes precedence over the two axes above when set: an exact *projective* + // map from a local-space position straight to a texture uv, written by the projection-frame overlay + // (the semi-transparent window dragged over the 3D view -- its border becomes the uv unit square). + // + // Row-major 3x4, applied to the homogeneous point p~ = (x, y, z, 1): + // uv = ( row0.p~ / row2.p~ , row1.p~ / row2.p~ ) + // The perspective divide is the whole point. view_project_right/up can only express an *affine* + // projection, which matches an orthographic camera exactly but not a perspective one -- under + // perspective the near end of a part projects larger than the far end, and no pair of axes + // reproduces that. Folding the camera's full projection*view*model product into one matrix does. + // Because a point behind the projector has row2.p~ <= 0 and no meaningful uv, sampling must check + // the sign rather than divide blindly; see project_uv_projective(). + // + // Note this map already includes placement, so the usual tiling/rotation/offset transform is NOT + // applied on top of it -- the window's own position and size are the placement. + bool view_project_projective = false; + std::array view_project_matrix{}; // Only used by TextureProjectionMethod::LSCM: hand placement of the unwrap's islands, indexed by // chart id (see TextureIsland). Shorter than the chart count simply means the missing ones are // still where the automatic packing put them. @@ -255,7 +274,7 @@ struct TextureDisplacementLayer static_cast(tile_method), static_cast(projection_method), lscm_seam_angle_deg, islands, static_cast(blend_mode), midlevel, island_padding_mm, lscm_seam_edges, view_project_right, view_project_up, smoothing, edge_smoothing, edge_smoothing_amount, auto_connect_islands, island_groups, - lscm_uv_overrides); + lscm_uv_overrides, view_project_projective, view_project_matrix); } template void load(Archive &ar) { @@ -266,7 +285,8 @@ struct TextureDisplacementLayer ar(slot, name, path, path_in_3mf, blob, depth_mm, tiling_scale, rotation_deg, offset, invert, tile_enabled, tile_method_int, projection_method_int, lscm_seam_angle_deg, islands, blend_mode_int, midlevel, island_padding_mm, lscm_seam_edges, view_project_right, view_project_up, smoothing, edge_smoothing, - edge_smoothing_amount, auto_connect_islands, island_groups, lscm_uv_overrides); + edge_smoothing_amount, auto_connect_islands, island_groups, lscm_uv_overrides, view_project_projective, + view_project_matrix); image_data = blob.empty() ? nullptr : std::make_shared>(blob.begin(), blob.end()); tile_method = static_cast(tile_method_int); projection_method = static_cast(projection_method_int); @@ -283,8 +303,11 @@ struct DecodedHeightTexture int height = 0; bool empty() const { return width <= 0 || height <= 0 || pixels.empty(); } - // Bilinearly sampled height in [0, 1] at a normalized uv coordinate. When tile_enabled is - // false, uv is clamped to the texture's edge instead of being wrapped/repeated. + // Bilinearly sampled height in [0, 1] at a normalized uv coordinate. When tile_enabled is false, + // a uv outside [0, 1) samples as 0 -- the texture simply is not there, rather than its border + // row/column being smeared outward forever (which is what clamping the coordinate would do, and + // was a real reported bug). Callers rely on this to get a hard edge: it is how the projection + // frame's border becomes the edge of the displacement. float sample(const Vec2f &uv, bool tile_enabled = true, TextureTileMethod tile_method = TextureTileMethod::Repeat) const; }; @@ -307,6 +330,12 @@ Vec2f project_planar(const Vec3f &position, const Vec3f &normal); // (which only knows how to compute the *analytic* methods from a single vertex + normal). Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer); +// Applies a row-major 3x4 projective matrix (see TextureDisplacementLayer::view_project_matrix) to a +// local-space point, writing the resulting texture uv. Returns false -- and leaves `uv` untouched -- +// when the point lies behind the projector or on its plane (w <= 0), where there is no meaningful uv +// and dividing would produce a mirrored or infinite coordinate. Callers treat that as "no height". +bool project_uv_projective(const std::array &m, const Vec3f &position, Vec2f &uv); + // Sample a layer's height texture at a mesh-local position, honouring the layer's projection // method, tiling scale, rotation, offset and tiling mode. Returns a height in [0, 1]. // diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 38bd235ff2..090ae9d609 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -474,6 +474,8 @@ set(SLIC3R_GUI_SOURCES GUI/TextLines.hpp GUI/TextureLibrary.cpp GUI/TextureLibrary.hpp + GUI/TextureProjectorFrame.cpp + GUI/TextureProjectorFrame.hpp GUI/TickCode.cpp GUI/TickCode.hpp GUI/TroubleshootDialog.cpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 426521f04d..4772c4e74d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -5,18 +5,22 @@ #include "libslic3r/MeshBoolean.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/format.hpp" #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/CameraUtils.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GLToolbar.hpp" // GLToolbar::Default_Icons_Size, to match the toolbar's icon size #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/MainFrame.hpp" // wxGetApp().mainframe, as the projector window's parent #include "slic3r/GUI/MsgDialog.hpp" #include "slic3r/GUI/OpenGLManager.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/TextureLibrary.hpp" +#include "slic3r/GUI/TextureProjectorFrame.hpp" #include "slic3r/GUI/UVEditorCanvas.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp" @@ -177,6 +181,16 @@ void GLGizmoTextureDisplacement::on_shutdown() // silently reopen the pane too. m_show_uv_editor = false; wxGetApp().plater()->show_uv_editor(false); + + // Destroyed, not just hidden: unlike the UV pane (owned by Plater), this frame is owned here, and + // it holds a callback capturing `this`. Leaving it alive past the gizmo would leave that callback + // pointing at a gizmo that is no longer driving anything. + if (m_projector_frame != nullptr) { + m_projector_frame->Destroy(); + m_projector_frame = nullptr; + } + m_projector_tex_source = nullptr; // a rebuilt frame starts with no texture in it + m_projector_tex_smoothing = -1.f; } PainterGizmoType GLGizmoTextureDisplacement::get_painter_type() const @@ -331,14 +345,13 @@ bool GLGizmoTextureDisplacement::on_mouse_seam(const wxMouseEvent &mouse_event) return false; } -std::pair GLGizmoTextureDisplacement::seam_edge_at(const Vec2d &mouse_pos) const +int GLGizmoTextureDisplacement::texture_volume_raycaster_index() const { const ModelVolume *mv = texture_volume(); const ModelObject *mo = m_c->selection_info()->model_object(); if (mv == nullptr || mo == nullptr) - return { -1, -1 }; + return -1; - // The mesh raycasters are built one per model-part volume, in that order; find this volume's slot. int idx = -1, count = 0; for (const ModelVolume *v : mo->volumes) { if (!v->is_model_part()) @@ -346,10 +359,21 @@ std::pair GLGizmoTextureDisplacement::seam_edge_at(const Vec2d &mouse_ if (v == mv) { idx = count; break; } ++count; } - const auto &raycasters = m_c->raycaster()->raycasters(); - if (idx < 0 || idx >= int(raycasters.size())) + return (idx >= 0 && idx < int(m_c->raycaster()->raycasters().size())) ? idx : -1; +} + +std::pair GLGizmoTextureDisplacement::seam_edge_at(const Vec2d &mouse_pos) const +{ + const ModelVolume *mv = texture_volume(); + const ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) return { -1, -1 }; + const int idx = texture_volume_raycaster_index(); + if (idx < 0) + return { -1, -1 }; + const auto &raycasters = m_c->raycaster()->raycasters(); + const Selection &selection = m_parent.get_selection(); const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); const Camera &camera = wxGetApp().plater()->get_camera(); @@ -406,16 +430,10 @@ int GLGizmoTextureDisplacement::seam_vertex_at(const Vec2d &mouse_pos) const const ModelObject *mo = m_c->selection_info()->model_object(); if (mv == nullptr || mo == nullptr) return -1; - int idx = -1, count = 0; - for (const ModelVolume *v : mo->volumes) { - if (!v->is_model_part()) - continue; - if (v == mv) { idx = count; break; } - ++count; - } - const auto &raycasters = m_c->raycaster()->raycasters(); - if (idx < 0 || idx >= int(raycasters.size())) + const int idx = texture_volume_raycaster_index(); + if (idx < 0) return -1; + const auto &raycasters = m_c->raycaster()->raycasters(); const Selection &selection = m_parent.get_selection(); const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); @@ -690,6 +708,14 @@ std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const in if (layer.projection_method == TextureProjectionMethod::ViewProjected) { std::vector uv(patch.vertices.size()); for (size_t vi = 0; vi < patch.vertices.size(); ++vi) { + if (layer.view_project_projective) { + // Matches sample_layer_height()'s projective branch, including skipping + // apply_uv_transform(). A vertex behind the projector gets a uv far outside [0,1] so + // it samples as nothing, rather than the mirrored coordinate a blind divide gives. + if (!project_uv_projective(layer.view_project_matrix, patch.vertices[vi], uv[vi])) + uv[vi] = Vec2f(-1e6f, -1e6f); + continue; + } const Vec2f planar(patch.vertices[vi].dot(layer.view_project_right), patch.vertices[vi].dot(layer.view_project_up)); uv[vi] = apply_uv_transform(planar, layer); @@ -1502,6 +1528,137 @@ void GLGizmoTextureDisplacement::capture_view_projection(TextureDisplacementLaye layer.view_project_up = local_up.norm() > 1e-9 ? Vec3f(local_up.normalized().cast()) : Vec3f::UnitY(); } +void GLGizmoTextureDisplacement::show_projector(bool show) +{ + if (!show) { + if (m_projector_frame != nullptr) + m_projector_frame->Hide(); + return; + } + + if (m_projector_frame == nullptr) { + // Parented to the main frame, not to Plater: Plater is a wxPanel, and wxFRAME_FLOAT_ON_PARENT + // wants a real top-level window to float above. + m_projector_frame = new TextureProjectorFrame(wxGetApp().mainframe); + m_projector_tex_source = nullptr; // fresh window, nothing uploaded into it yet + m_projector_tex_smoothing = -1.f; + m_projector_frame->set_opacity(m_projector_opacity); + + // Opened centred over the 3D canvas, at about half its size: the frame is meant to be + // dragged onto part of the model, so starting somewhere on top of it beats the OS's default + // cascade position, which is often off over the sidebar. + if (const wxGLCanvas *cnv = m_parent.get_wxglcanvas(); cnv != nullptr) { + const wxRect area = cnv->GetScreenRect(); + const wxSize size(std::max(160, area.width / 2), std::max(160, area.height / 2)); + m_projector_frame->SetSize(wxRect(area.GetTopLeft() + wxPoint((area.width - size.x) / 2, + (area.height - size.y) / 2), + size)); + } + } + + m_projector_frame->Show(); + m_projector_frame->Raise(); + update_projector(); +} + +void GLGizmoTextureDisplacement::update_projector() +{ + if (m_projector_frame == nullptr || !m_projector_frame->IsShown()) + return; + + const TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr || layer->projection_method != TextureProjectionMethod::ViewProjected) { + m_projector_frame->set_texture({}, 0, 0); + m_projector_tex_source = nullptr; + m_projector_tex_smoothing = -1.f; + return; + } + + // Only re-upload when the pixels actually changed - this is reached from the panel's per-edit + // flush, and rebuilding the bitmap from identical bytes every time would be pure waste. + if (m_projector_tex_source != layer->image_data.get() || m_projector_tex_smoothing != layer->smoothing) { + const DecodedHeightTexture height = decode_height_texture(*layer); + if (height.empty()) + m_projector_frame->set_texture({}, 0, 0); + else + m_projector_frame->set_texture(height.pixels, height.width, height.height); + m_projector_tex_source = layer->image_data.get(); + m_projector_tex_smoothing = layer->smoothing; + } +} + +int GLGizmoTextureDisplacement::apply_projection_frame() +{ + TextureDisplacementLayer *layer = active_layer(); + const ModelVolume *mv = texture_volume(); + const ModelObject *mo = m_c->selection_info()->model_object(); + wxGLCanvas *cnv = m_parent.get_wxglcanvas(); + if (layer == nullptr || mv == nullptr || mo == nullptr || cnv == nullptr || m_projector_frame == nullptr || + !m_projector_frame->IsShown()) + return -1; + + // The frame's gate, brought from screen coordinates into the GL viewport's pixel space. Two + // conversions, both necessary: ScreenToClient() because the viewport's origin is the canvas's + // top-left, not the desktop's, and the retina scale because the viewport is sized in physical + // pixels (see GLCanvas3D::get_canvas_size()) while wx hands out logical ones. + const wxRect gate = m_projector_frame->client_rect_on_screen(); + const wxPoint tl = cnv->ScreenToClient(gate.GetTopLeft()); + const double scale = double(m_parent.get_scale()); + const double rx = double(tl.x) * scale, ry = double(tl.y) * scale; + const double rw = double(gate.width) * scale, rh = double(gate.height) * scale; + if (rw < 1.0 || rh < 1.0) + return -1; + + const Camera &camera = wxGetApp().plater()->get_camera(); + const std::array &vp = camera.get_viewport(); + const Selection &sel = m_parent.get_selection(); + const Transform3d trafo = mo->instances[sel.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + + // Local space -> clip space, the same product the renderer uses, so the mapping agrees with what + // is actually on screen rather than with an idealisation of it. + const Eigen::Matrix4d K = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix() * trafo.matrix(); + + // Window coordinates follow igl::project's convention (as CameraUtils::project does): + // win_x = vp.x + vp.w * (ndc.x + 1) / 2, and y measured downward as vp.h - win_y_gl. + // Turning those into uv = (win - rect_origin) / rect_size gives u and v as affine functions of + // ndc.x and ndc.y, and since ndc = clip.xyz / clip.w, multiplying through by clip.w leaves a + // plain linear combination of K's rows - i.e. one 3x4 matrix carrying the perspective divide. + const double A = double(vp[2]) / (2.0 * rw); + const double B = (double(vp[0]) + double(vp[2]) * 0.5 - rx) / rw; + const double C = -double(vp[3]) / (2.0 * rh); + const double D = (double(vp[3]) * 0.5 - double(vp[1]) - ry) / rh; + + const Eigen::Vector4d row_u = A * K.row(0).transpose() + B * K.row(3).transpose(); + const Eigen::Vector4d row_v = C * K.row(1).transpose() + D * K.row(3).transpose(); + const Eigen::Vector4d row_w = K.row(3).transpose(); + + std::array m{}; + for (int i = 0; i < 4; ++i) { + m[size_t(i)] = float(row_u[i]); + m[size_t(4 + i)] = float(row_v[i]); + m[size_t(8 + i)] = float(row_w[i]); + } + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Apply texture projection frame"), + UndoRedo::SnapshotType::GizmoAction); + + layer->projection_method = TextureProjectionMethod::ViewProjected; + layer->view_project_projective = true; + layer->view_project_matrix = m; + // Off, so the height sampler returns 0 outside [0,1) and the frame's border becomes a hard edge + // of the displacement rather than the first seam of an endless repeat. + layer->tile_enabled = false; + // The affine axes are kept up to date too, so turning the projective mapping off later leaves a + // sane flat projection from this same viewpoint instead of whatever was captured long ago. + capture_view_projection(*layer); + + const int painted = select_visible_faces(&m); + m_preview_params_dirty = true; + rebuild_preview(); + m_parent.set_as_dirty(); + return painted; +} + void GLGizmoTextureDisplacement::cut_island(TextureDisplacementLayer &layer, int chart) { const ModelVolume *mv = texture_volume(); @@ -2088,22 +2245,45 @@ unsigned int GLGizmoTextureDisplacement::tool_icon_id() if (!m_tool_icon_tried) { m_tool_icon_tried = true; // Runs from the panel render, i.e. with a GL context current, so the upload is safe here. - m_tool_icon.load_from_svg_file(resources_dir() + "/images/toolbar_texture_displacement.svg", false, false, false, 32); + m_tool_icon.load_from_svg_file(resources_dir() + "/images/texture_displacement_add.svg", false, false, false, 32); } return m_tool_icon.get_id(); } -unsigned int GLGizmoTextureDisplacement::icon_id(const std::string &filename) +void GLGizmoTextureDisplacement::ensure_panel_icons() { - auto it = m_icon_cache.find(filename); - if (it == m_icon_cache.end()) { - // First request for this icon: default-construct the texture in place and load it once (with a - // GL context current, since this runs from the panel render). A failed load leaves id 0 and is - // not retried. - it = m_icon_cache.try_emplace(filename).first; - it->second.load_from_svg_file(resources_dir() + "/images/" + filename, false, false, false, 32); - } - return it->second.get_id(); + if (m_panel_icons_tried) + return; + m_panel_icons_tried = true; + + // Order is irrelevant; the map keys by file name. Loaded with color_wite_gray so each icon has both + // a monochrome ("normal", grey in the current theme) and an original-colour variant. + static const std::vector names = { + "toolbar_big_brush.svg", "toolbar_face.svg", "texture_displacement_connected_area.svg", + "texture_displacement_real_preview.svg", "texture_displacement_fast_preview.svg", + "texture_displacement_checker.svg", "texture_displacement_distortion.svg", + "texture_displacement_wireframe.svg", "texture_displacement_cross.svg", + "texture_displacement_uv_select_island.svg", "texture_displacement_uv_select_edge.svg", + "texture_displacement_uv_select_vertex.svg", + }; + std::vector paths; + paths.reserve(names.size()); + for (const std::string &n : names) + paths.push_back(resources_dir() + "/images/" + n); + + // Runs from the panel render, i.e. with a GL context current, so the upload is safe here. + // + // Rasterized at twice GLToolbar::Default_Icons_Size rather than at it: these are drawn at the + // toolbar's icon size, which is that constant scaled by DPI (toolbar_icon_scale() folds in + // em_unit), so on a 200% display the draw size reaches 80 px. Rasterizing at 40 would upscale a + // 40 px bitmap there, which is what actually reads as blurry - downscaling does not. The icon set + // is rasterized once for the gizmo's lifetime, so it cannot re-raster on a DPI change; sizing for + // the larger case and letting ImGui shrink it is the version that looks right on both. + const std::vector icons = + m_panel_icons.init(paths, ImVec2(2 * GLToolbar::Default_Icons_Size, 2 * GLToolbar::Default_Icons_Size), + IconManager::RasterType::color_wite_gray); + for (size_t i = 0; i < names.size() && i < icons.size(); ++i) + m_panel_icon_map[names[i]] = icons[i]; } void GLGizmoTextureDisplacement::add_texture_layer() @@ -2322,6 +2502,101 @@ void GLGizmoTextureDisplacement::remove_texture_layer(int slot) m_parent.set_as_dirty(); } +int GLGizmoTextureDisplacement::select_visible_faces(const std::array *uv_clip) +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + const int idx = texture_volume_raycaster_index(); + if (mv == nullptr || mo == nullptr || idx < 0 || idx >= int(m_triangle_selectors.size())) + return 0; + + const indexed_triangle_set &its = mv->mesh().its; + if (its.indices.empty()) + return 0; + + const Selection &selection = m_parent.get_selection(); + const Geometry::Transformation trafo(mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * + mv->get_matrix()); + const Transform3d &to_world = trafo.get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + // Normals transform by the inverse transpose, not by the matrix itself - with a non-uniform + // scale the two differ, and using the wrong one flips the facing test on the scaled axes. + const Matrix3d normal_matrix = to_world.matrix().block<3, 3>(0, 0).inverse().transpose(); + + // Pass 1 (cheap): drop back-facing triangles. Under perspective the view direction varies across + // the model, so it is taken per triangle from the eye to the centroid; under an orthographic + // camera get_position() is still a point on the view axis, so the same expression stays correct + // in direction terms for everything actually on screen. + const Vec3d eye = camera.get_position(); + const bool ortho = camera.get_type() == Camera::EType::Ortho; + const Vec3d fwd = camera.get_dir_forward(); + + std::vector centroids; // world coords, what get_unobscured_idxs() expects + std::vector front_facing; // parallel: which facet each centroid came from + centroids.reserve(its.indices.size() / 2); + front_facing.reserve(its.indices.size() / 2); + + for (size_t f = 0; f < its.indices.size(); ++f) { + const stl_triangle_vertex_indices &tri = its.indices[f]; + const Vec3d a = its.vertices[tri[0]].cast(); + const Vec3d b = its.vertices[tri[1]].cast(); + const Vec3d c = its.vertices[tri[2]].cast(); + const Vec3d n_world = normal_matrix * (b - a).cross(c - a); + if (n_world.squaredNorm() < 1e-20) + continue; // degenerate triangle: no meaningful normal, so no meaningful facing test + const Vec3d centroid_local = (a + b + c) / 3.0; + const Vec3d centroid_world = to_world * centroid_local; + const Vec3d view_dir = ortho ? fwd : Vec3d(centroid_world - eye); + if (n_world.dot(view_dir) >= 0.0) + continue; // facing away from the camera + + // Clip to the projection frame before the raycast, not after: outside the frame the texture + // samples to nothing anyway, so those facets would only be painted to no effect - and this + // is also what keeps the ray queries proportional to the framed area instead of the model. + if (uv_clip != nullptr) { + Vec2f uv; + if (!project_uv_projective(*uv_clip, centroid_local.cast(), uv)) + continue; // behind the projector + if (uv.x() < 0.f || uv.x() > 1.f || uv.y() < 0.f || uv.y() > 1.f) + continue; + } + centroids.emplace_back(centroid_world.cast()); + front_facing.push_back(unsigned(f)); + } + if (centroids.empty()) + return 0; + + // Pass 2 (the expensive one): a real ray query per surviving centroid, so geometry in front of a + // front-facing triangle correctly hides it - the far inner wall of a cup is front-facing but not + // visible. This is why the whole thing is click-driven rather than live. + std::vector unobscured; + { + wxBusyCursor wait; + unobscured = m_c->raycaster()->raycasters()[size_t(idx)]->get_unobscured_idxs( + trafo, camera, centroids, m_c->object_clipper()->get_clipping_plane()); + } + if (unobscured.empty()) + return 0; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Select visible faces for texture displacement"), + UndoRedo::SnapshotType::GizmoAction); + + // Replaces the layer's paint rather than adding to it: the checkbox means "project onto what I + // can see", so a second capture from a new angle should not leave the previous angle painted. + // TriangleSelectorGUI, not the TriangleSelector base: request_update_render_data() is declared on + // the GUI subclass, so binding to the base here would drop it. + TriangleSelectorGUI &selector = *m_triangle_selectors[size_t(idx)]; + selector.reset(); + for (unsigned i : unobscured) + selector.set_facet(int(front_facing[i]), EnforcerBlockerType::ENFORCER); + selector.request_update_render_data(); + + update_model_object(); + m_parent.set_as_dirty(); + return int(unobscured.size()); +} + void GLGizmoTextureDisplacement::select_whole_model() { ModelObject *mo = m_c->selection_info()->model_object(); @@ -2348,8 +2623,8 @@ void GLGizmoTextureDisplacement::subdivide_model() { ModelVolume *mv = texture_volume(); ModelObject *mo = m_c->selection_info()->model_object(); - if (mv == nullptr || mo == nullptr) - return; + if (mv == nullptr || mo == nullptr || m_subdivide_count < 1) + return; // 0 passes means "no subdivision" - don't take a snapshot for a no-op Plater *plater = wxGetApp().plater(); Plater::TakeSnapshot snapshot(plater, _u8L("Subdivide model for texture displacement"), UndoRedo::SnapshotType::GizmoAction); @@ -2391,14 +2666,24 @@ void GLGizmoTextureDisplacement::remesh_model() // CGAL isotropic remeshing can be slow on a big mesh; do it before taking the snapshot so a failure // (it returns the input unchanged) doesn't leave an empty undo step. - indexed_triangle_set remeshed; + const indexed_triangle_set &src = mv->mesh().its; + indexed_triangle_set remeshed; { wxBusyCursor wait; - remeshed = MeshBoolean::cgal::remesh_isotropic(mv->mesh().its, double(m_remesh_target_edge_mm), 3); + remeshed = MeshBoolean::cgal::remesh_isotropic(mv->mesh().its, double(m_remesh_target_edge_mm), 3, + m_remesh_keep_sharp_edges ? double(m_remesh_sharp_angle_deg) : 0.0); } - if (remeshed.indices.empty() || remeshed.vertices.size() == mv->mesh().its.vertices.size()) { - show_error(nullptr, _u8L("Remeshing did not change the model (it may be non-manifold or the target size " - "is already met).")); + // remesh_isotropic() signals failure by handing the input straight back, so compare against it + // structurally. Vertex count alone is not enough: a remesh that only redistributes triangles at + // roughly the current density legitimately lands on the same count, and treating that as failure + // meant a perfectly good result got thrown away with an error message. + const bool unchanged = remeshed.indices.empty() || + (remeshed.vertices.size() == src.vertices.size() && remeshed.indices.size() == src.indices.size() && + remeshed.indices == src.indices); + if (unchanged) { + show_error(nullptr, _u8L("Remeshing did not change the model. It may be non-manifold (open edges or " + "edges shared by more than two triangles), or the target edge length may " + "already be met.")); return; } @@ -2528,6 +2813,30 @@ void GLGizmoTextureDisplacement::bake() }); } +void GLGizmoTextureDisplacement::render_paint_cursor_hint() +{ + // Only in the plain paint/select modes; seam and adjust modes have their own click semantics where + // an add/remove sign would just be noise. + if (m_seam_edit_mode || m_adjust_texture_mode) + return; + const ImGuiIO &io = ImGui::GetIO(); + // The pointer must be over the 3D view, not over this panel (or any other ImGui window). + if (io.WantCaptureMouse || !ImGui::IsMousePosValid()) + return; + + // Shift erases (see handle_snapshot_action_name()); a plain stroke adds. + const bool removing = io.KeyShift; + const ImU32 color = removing ? IM_COL32(235, 70, 60, 255) : IM_COL32(90, 210, 110, 255); + const char *glyph = removing ? "-" : "+"; + + ImDrawList *dl = ImGui::GetForegroundDrawList(); + const float fs = ImGui::GetFontSize() * 1.5f; + const ImVec2 at(io.MousePos.x + 15.f, io.MousePos.y - fs - 6.f); + // A translucent dark disc behind the glyph so it reads on any material colour. + dl->AddCircleFilled(ImVec2(at.x + fs * 0.28f, at.y + fs * 0.5f), fs * 0.62f, IM_COL32(0, 0, 0, 150)); + dl->AddText(ImGui::GetFont(), fs, at, color, glyph); +} + void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float bottom_limit) { ModelObject *mo = m_c->selection_info()->model_object(); @@ -2565,19 +2874,42 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float return changed; }; - // Icon toggle button shared by the selection-mode and view-mode rows: an SVG button with a teal - // backing and full-brightness icon when active, dimmed otherwise. Falls back to a text checkbox if - // its icon could not be loaded, so the control is never lost. - const float icon_btn_sz = m_imgui->scaled(1.5f); - const ImVec4 icon_active_bg(0.0f, 0.59f, 0.53f, 1.0f); - const auto icon_toggle = [&](int uid, unsigned int ic, bool active, const wxString &label, const wxString &tip) -> bool { - bool clicked; + // Icon toggle button shared by the selection-mode and view-mode rows, styled like the main toolbar: + // an inactive button shows the icon in the theme's normal (grey) monochrome, an active one shows it + // in its original colours. All icons are the same square size. Falls back to a text checkbox if the + // icon set could not be loaded, so the control is never lost. + ensure_panel_icons(); + // Sized to match the 3D toolbar's icons exactly, rather than to the panel's font. It is the same + // expression GLCanvas3D::_update_toolbar_icons_scale() uses, and it is valid here because ImGui's + // DisplaySize is set from the canvas's pixel size (GLCanvas3D::_resize()) - so one ImGui unit is + // one canvas pixel, the very units the toolbar is drawn in. Deriving it rather than hard-coding a + // font multiple also keeps the two in step when the toolbar auto-fit shrinks its icons to make + // them fit a narrow window, which it does by lowering the same toolbar_icon_scale() read here. + const float icon_btn_sz = GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale() * m_parent.get_scale(); + // The icon SVGs carry their own border, so the ImGui button's own frame border and idle fill are + // suppressed here (FrameBorderSize 0 + transparent ImGuiCol_Button) to avoid a doubled border -- the + // hover/active fill is left in place for feedback. Applied only around these gizmo icon buttons. + const auto push_borderless_icon_style = []() { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f)); + }; + const auto pop_borderless_icon_style = []() { + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + }; + const auto icon_toggle = [&](int uid, const std::string &iconfile, bool active, const wxString &label, + const wxString &tip) -> bool { + bool clicked = false; + const auto it = m_panel_icon_map.find(iconfile); ImGui::PushID(uid); - if (ic != 0) - clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic, ImVec2(icon_btn_sz, icon_btn_sz), ImVec2(0, 0), - ImVec2(1, 1), -1, active ? icon_active_bg : ImVec4(0, 0, 0, 0), - active ? ImVec4(1, 1, 1, 1) : ImVec4(0.65f, 0.65f, 0.65f, 1.f)); - else { + // color_wite_gray variants: [0] normal/grey, [1] original colour, [2] disabled. + if (it != m_panel_icon_map.end() && it->second.size() >= 2 && it->second[active ? 1 : 0]->is_valid()) { + const IconManager::Icon &ic = *it->second[active ? 1 : 0]; + push_borderless_icon_style(); + clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(icon_btn_sz, icon_btn_sz), + ic.tl, ic.br, -1, ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)); + pop_borderless_icon_style(); + } else { bool v = active; clicked = ImGui::Checkbox(label.ToUTF8().data(), &v); } @@ -2586,6 +2918,27 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->tooltip(tip, m_imgui->scaled(18.f)); return clicked; }; + // Borderless icon button (non-toggle): always the grey monochrome variant. Falls back to a plain + // text button when the icon file is absent, so the control is never lost before the art lands. + const auto icon_button = [&](int uid, const std::string &iconfile, float sz, const wxString &label, + const wxString &tip) -> bool { + bool clicked = false; + const auto it = m_panel_icon_map.find(iconfile); + ImGui::PushID(uid); + if (it != m_panel_icon_map.end() && !it->second.empty() && it->second[0]->is_valid()) { + const IconManager::Icon &ic = *it->second[0]; + push_borderless_icon_style(); + clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz, sz), ic.tl, ic.br, -1, + ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)); + pop_borderless_icon_style(); + } else { + clicked = m_imgui->button(label); + } + ImGui::PopID(); + if (!tip.empty() && ImGui::IsItemHovered()) + m_imgui->tooltip(tip, m_imgui->scaled(18.f)); + return clicked; + }; if (m_imgui->button(m_undocked ? _L("Dock panel") : _L("Undock panel"))) m_undocked = !m_undocked; @@ -2604,19 +2957,19 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER; const bool is_area_mode = m_tool_type == ToolType::SMART_FILL; ImGui::SameLine(); - if (icon_toggle(801, icon_id("toolbar_big_brush.svg"), is_brush_mode, _L("Brush"), + if (icon_toggle(801, "toolbar_big_brush.svg", is_brush_mode, _L("Brush"), _L("Brush - paint over the surface by dragging"))) { m_tool_type = ToolType::BRUSH; m_cursor_type = TriangleSelector::CursorType::CIRCLE; } ImGui::SameLine(); - if (icon_toggle(802, icon_id("toolbar_face.svg"), is_face_mode, _L("Face"), + if (icon_toggle(802, "toolbar_face.svg", is_face_mode, _L("Face"), _L("Face - click individual triangles"))) { m_tool_type = ToolType::BRUSH; m_cursor_type = TriangleSelector::CursorType::POINTER; } ImGui::SameLine(); - if (icon_toggle(803, icon_id("texture_displacement_connected_area.svg"), is_area_mode, _L("Connected area"), + if (icon_toggle(803, "texture_displacement_connected_area.svg", is_area_mode, _L("Connected area"), _L("Connected area - flood-fill the region reachable without crossing an edge sharper than the " "angle threshold"))) { m_tool_type = ToolType::SMART_FILL; @@ -2657,21 +3010,21 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->text(_L("View")); ImGui::SameLine(); - if (icon_toggle(701, icon_id("texture_displacement_real_preview.svg"), cur_mode == 0, _L("Normal"), + if (icon_toggle(701, "texture_displacement_real_preview.svg", cur_mode == 0, _L("Normal"), _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0; ImGui::SameLine(); - if (icon_toggle(702, icon_id("texture_displacement_fast_preview.svg"), cur_mode == 1, _L("Fast"), + if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, _L("Fast"), _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1; ImGui::SameLine(); - if (icon_toggle(703, icon_id("texture_displacement_checker.svg"), cur_mode == 2, _L("Checker"), + if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, _L("Checker"), _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2; ImGui::SameLine(); - if (icon_toggle(704, icon_id("texture_displacement_distortion.svg"), cur_mode == 3, _L("Distortion"), + if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, _L("Distortion"), _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3; ImGui::SameLine(); ImGui::Dummy(ImVec2(m_imgui->scaled(0.6f), 0.f)); ImGui::SameLine(); - if (icon_toggle(705, icon_id("texture_displacement_wireframe.svg"), m_wireframe_overlay, _L("Wireframe"), + if (icon_toggle(705, "texture_displacement_wireframe.svg", m_wireframe_overlay, _L("Wireframe"), _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true; if (new_mode != cur_mode) { @@ -2711,8 +3064,15 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const unsigned int add_icon = tool_icon_id(); const float sz = m_imgui->scaled(1.3f); ImGui::SameLine(); - const bool add_clicked = (add_icon != 0) ? m_imgui->image_button((ImTextureID) (intptr_t) add_icon, ImVec2(sz, sz)) - : m_imgui->button(m_desc.at("add_texture")); + bool add_clicked; + if (add_icon != 0) { + // The add icon SVG carries its own border; suppress the ImGui frame border/idle fill. + push_borderless_icon_style(); + add_clicked = m_imgui->image_button((ImTextureID) (intptr_t) add_icon, ImVec2(sz, sz)); + pop_borderless_icon_style(); + } else { + add_clicked = m_imgui->button(m_desc.at("add_texture")); + } if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Add a texture layer"), m_imgui->scaled(20.f)); if (add_clicked) @@ -2765,7 +3125,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float set_active_layer(layer->slot); ImGui::PopStyleColor(2); ImGui::SameLine(); - if (m_imgui->button(m_desc.at("remove_layer"))) + if (icon_button(600 + layer->slot, "texture_displacement_cross.svg", m_imgui->scaled(1.3f), + m_desc.at("remove_layer"), _u8L("Remove this layer"))) slot_to_remove = layer->slot; // Everything below is this layer's own; the group lets a click anywhere inside it select @@ -2958,18 +3319,25 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // bake honours; Island is the move/rotate/scale-with-grouping behaviour. if (!m_uv_editor_unwrap.empty()) { m_imgui->text(_u8L("Select:")); + const auto set_uv_mode = [&](int mode) { + if (mode != m_uv_select_mode) { + m_uv_select_mode = mode; + if (UVEditorCanvas *c = wxGetApp().plater()->get_uv_editor_canvas()) + c->set_select_mode(static_cast(mode)); + } + }; ImGui::SameLine(); - int mode = m_uv_select_mode; - ImGui::RadioButton(_u8L("Island").c_str(), &mode, 0); + if (icon_toggle(810, "texture_displacement_uv_select_island.svg", m_uv_select_mode == 0, + _L("Island"), _L("Island - move, rotate and scale whole islands"))) + set_uv_mode(0); ImGui::SameLine(); - ImGui::RadioButton(_u8L("Vertex").c_str(), &mode, 1); + if (icon_toggle(811, "texture_displacement_uv_select_vertex.svg", m_uv_select_mode == 1, + _L("Vertex"), _L("Vertex - drag vertices to reshape; Shift/Ctrl to multi-select"))) + set_uv_mode(1); ImGui::SameLine(); - ImGui::RadioButton(_u8L("Edge").c_str(), &mode, 2); - if (mode != m_uv_select_mode) { - m_uv_select_mode = mode; - if (UVEditorCanvas *c = wxGetApp().plater()->get_uv_editor_canvas()) - c->set_select_mode(static_cast(mode)); - } + if (icon_toggle(812, "texture_displacement_uv_select_edge.svg", m_uv_select_mode == 2, + _L("Edge"), _L("Edge - drag edges to reshape; Shift/Ctrl to multi-select"))) + set_uv_mode(2); if (!layer->lscm_uv_overrides.empty()) { ImGui::SameLine(); if (m_imgui->button(_u8L("Clear UV edits"))) { @@ -3056,7 +3424,14 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // press this, and the texture is re-laid from the new angle. if (m_imgui->button(_u8L("Capture current view"))) { capture_view_projection(*layer); + // Selecting the visible faces *after* capturing means the projector axes are + // already the ones the selection was made against - the two describe the + // same viewpoint, which is the whole point of the option. + if (m_project_only_visible && select_visible_faces() == 0) + show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the " + "part you want to project onto.")); m_preview_params_dirty = true; + update_projector(); } if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Projects the texture straight onto the painted area from the direction " @@ -3064,6 +3439,66 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "that direction will stretch - turn the model to where you want the " "texture crisp, then capture."), m_imgui->scaled(20.f)); + + if (ImGui::Checkbox(_u8L("Project only on visible").c_str(), &m_project_only_visible)) { + if (m_project_only_visible && select_visible_faces() == 0) + show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the " + "part you want to project onto.")); + m_preview_params_dirty = true; + update_projector(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Paints exactly the faces you can currently see - facing the camera and " + "not hidden behind anything else - and projects onto those. This replaces " + "the layer's painted area, and is re-applied each time you capture the " + "view."), + m_imgui->scaled(20.f)); + + ImGui::Separator(); + bool projector_open = m_projector_frame != nullptr && m_projector_frame->IsShown(); + if (ImGui::Checkbox(_u8L("Projection frame").c_str(), &projector_open)) + show_projector(projector_open); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Opens a see-through window you drag over the model. Whatever shows " + "through it is what the texture is projected onto, and the window's " + "border becomes the edge of the projection. Move and resize it to frame " + "the area you want, then press Apply."), + m_imgui->scaled(20.f)); + + if (projector_open) { + ImGui::PushItemWidth(m_imgui->scaled(6.f)); + if (ImGui::SliderInt(_u8L("Opacity").c_str(), &m_projector_opacity, 20, 255)) + m_projector_frame->set_opacity(m_projector_opacity); + ImGui::PopItemWidth(); + + if (m_imgui->button(_u8L("Apply projection frame"))) { + const int painted = apply_projection_frame(); + if (painted == 0) + show_error(nullptr, _u8L("Nothing of the model is inside the frame - move it over the " + "part you want to project onto.")); + else if (painted < 0) + show_error(nullptr, _u8L("The frame could not be applied. Make sure it overlaps the " + "3D view.")); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Projects the texture through the frame from the direction you are " + "looking now, and paints the visible faces inside it. This replaces " + "the layer's painted area. The result is fixed to the model, so you " + "can orbit freely afterwards."), + m_imgui->scaled(20.f)); + } + + if (layer->view_project_projective) { + m_imgui->text(_u8L("Placed by projection frame.")); + ImGui::SameLine(); + if (m_imgui->button(_u8L("Clear"))) { + // Back to the plain axis projection, where tiling/rotation/offset mean + // something again - the matrix path deliberately ignores them. + layer->view_project_projective = false; + layer->tile_enabled = true; + m_preview_params_dirty = true; + } + } } } @@ -3114,6 +3549,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // that's driving the drag is released, i.e. once per edit instead of dozens of times per drag. if (m_preview_params_dirty && (m_auto_update || !ImGui::IsMouseDown(ImGuiMouseButton_Left))) { rebuild_preview(); + // Same edits (tile size, rotation, offset, a new texture) are what the projector window + // draws, so it refreshes on the same one-per-edit cadence. No-op while it is closed. + update_projector(); m_preview_params_dirty = false; } // (The "Add layer" button now lives next to the "Texture layers" heading, as an icon.) @@ -3121,16 +3559,17 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::Separator(); m_imgui->text(_u8L("Not enough vertices for fine detail?")); ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 1, 5)) { - m_subdivide_count = std::clamp(m_subdivide_count, 1, 5); + if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 0, 5)) { + m_subdivide_count = std::clamp(m_subdivide_count, 0, 5); if (m_subdivide_editing) - rebuild_subdivide_preview(); + rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one m_parent.set_as_dirty(); } ImGui::PopItemWidth(); if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " - "triangle count, so there are enough vertices for the height texture to displace."), + "triangle count, so there are enough vertices for the height texture to displace. " + "0 means no subdivision."), m_imgui->scaled(20.f)); if (!m_subdivide_editing) { @@ -3144,11 +3583,18 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "commit it, or Done to leave the model as it is."), m_imgui->scaled(20.f)); } else { + m_imgui->disabled_begin(m_subdivide_count < 1); if (m_imgui->button(_u8L("Apply"))) { - subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) - rebuild_subdivide_preview(); // re-preview against the now-denser mesh + subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) + // Back to 0 rather than staying at the count just applied: the mesh is now up to 4^N + // times denser, so re-previewing the same N passes on top of it is both pointless (the + // density asked for is already committed) and by far the slowest thing this panel does. + // rebuild_subdivide_preview() at 0 just drops the wireframe. + m_subdivide_count = 0; + rebuild_subdivide_preview(); m_parent.set_as_dirty(); } + m_imgui->disabled_end(); if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " "paint on it (already-baked bumps are unaffected)."), @@ -3180,6 +3626,23 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::PushItemWidth(m_imgui->scaled(8.4f)); m_imgui->slider_float(_u8L("Target edge (mm)"), &m_remesh_target_edge_mm, 0.1f, 20.f, "%.2f", ImGuiLogSlider); ImGui::PopItemWidth(); + + ImGui::Checkbox(_u8L("Keep sharp edges").c_str(), &m_remesh_keep_sharp_edges); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it " + "the remesher slides vertices along the surface and rounds every crisp edge off - " + "a cube comes back with wobbly edges."), + m_imgui->scaled(20.f)); + if (m_remesh_keep_sharp_edges) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + m_imgui->slider_float(_u8L("Sharp edge angle"), &m_remesh_sharp_angle_deg, 5.f, 90.f, "%.0f deg"); + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Edges bent by more than this count as hard features and are kept. Lower keeps " + "more detail but leaves more of the mesh untouched; higher remeshes more freely."), + m_imgui->scaled(20.f)); + } + m_imgui->disabled_begin(mv == nullptr); if (m_imgui->button(_u8L("Remesh"))) remesh_model(); @@ -3219,6 +3682,10 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float GizmoImguiEnd(); ImGuiWrapper::pop_toolbar_style(); + + // Drawn last, over everything, via the foreground draw list: the +/- add-remove sign next to the + // 3D cursor. Still inside the gizmo's ImGui frame here, which is what render_paint_cursor_hint() needs. + render_paint_cursor_hint(); } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index bce96e89b9..f3b98d89fd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -6,6 +6,7 @@ #include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GLTexture.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/IconManager.hpp" #include "slic3r/GUI/TextureLibrary.hpp" #include @@ -15,6 +16,8 @@ namespace Slic3r::GUI { +class TextureProjectorFrame; + // Paint-style gizmo that assigns one or more texture-displacement "layers" (see // libslic3r/TextureDisplacement.hpp) to painted areas of a model, and can bake the result into // real mesh geometry. See the project plan for the overall architecture; in short: @@ -70,6 +73,44 @@ private: // "whole model" as an alternative to brushing/clicking every triangle by hand. void select_whole_model(); + // The mesh raycasters are built one per model-part volume, in that order; this is the texture + // volume's slot among them, or -1 if it has none (no selection, or the lists disagree). + int texture_volume_raycaster_index() const; + + // Paints exactly the facets currently visible from the camera onto the active layer, replacing + // whatever that layer had painted. "Visible" is two tests: the facet faces the camera, and its + // centroid is not hidden behind other geometry (a real raycast, so a concave part's far inner + // wall is correctly excluded). When `uv_clip` is given (the projection frame's matrix), facets + // whose centroid falls outside the frame's uv unit square are skipped first - which both clips + // the selection to the frame and spares the raycast for everything outside it. Costs one ray + // query per surviving facet, so it is a one-shot action, never a per-frame one. Returns the + // number of facets selected. + int select_visible_faces(const std::array *uv_clip = nullptr); + + // When set, "Capture current view" also re-selects the visible faces, so the viewpoint the + // projector was captured from and the area it projects onto stay the same. Independent of the + // projection frame below: this takes every visible facet, the frame clips to its rectangle. + // Off by default, because turning it on replaces whatever the layer had painted. + bool m_project_only_visible = false; + + // The projection-frame overlay for a ViewProjected layer: a semi-transparent window dragged over + // the 3D view whose border becomes the projection's edge. Created lazily and owned here; hidden + // rather than destroyed when closed, so reopening keeps it where the user left it. + TextureProjectorFrame *m_projector_frame = nullptr; + int m_projector_opacity = 140; + // What the overlay's texture was last built from, so repeated updates don't rebuild the bitmap + // from unchanged pixels. Same shape as the m_thumbnail_source/m_thumbnail_smoothing pair above. + const void *m_projector_tex_source = nullptr; + float m_projector_tex_smoothing = -1.f; + void show_projector(bool show); + // Pushes the active layer's texture into the overlay. Cheap, and a no-op while it is hidden. + void update_projector(); + // Reads the overlay's rectangle and commits it as the layer's projection: builds the exact + // projective local->uv matrix from the camera and that rectangle, turns tiling off so the border + // is a hard edge, and repaints the layer with the visible facets inside the frame. Returns the + // number of facets selected, or -1 if the frame could not be used at all. + int apply_projection_frame(); + // Uniformly subdivides the volume's mesh (see libslic3r::subdivide_mesh_uniform()) so a // low-poly input model has enough vertices to actually show texture-displacement detail. // A real, committed geometry change (like Bake), so it needs its own snapshot; unlike Bake it @@ -120,6 +161,10 @@ private: bool update_adjust_anchor(); // recomputes m_adjust_anchor_pos/normal; false if nothing painted bool on_mouse_adjust_texture(const wxMouseEvent &mouse_event); void render_adjust_texture_gizmo(); + // Draws a small '+'/'-' next to the mouse over the 3D view while painting/selecting, so it is + // obvious whether the next stroke adds paint (default) or erases it (Shift). Uses ImGui's + // foreground draw list, so it must be called from inside the gizmo's ImGui frame. + void render_paint_cursor_hint(); // Mesh-local tangent-plane basis at m_adjust_anchor_normal, matching project_planar()'s // dominant-axis convention so dragging on-canvas maps consistently onto offset. void adjust_tangent_basis(Vec3f &u_axis, Vec3f &v_axis) const; @@ -251,6 +296,10 @@ private: // until "Apply". While previewing, the would-be subdivided mesh is drawn as a wireframe overlay so // the added density is visible; "Done" ends the preview without touching the model. The normal // "Show mesh wireframe" toggle is left alone, so a wireframe the user already had on stays on. + // 0 is a real value meaning "no subdivision": it previews nothing and Apply is a no-op. Apply + // snaps the slider back to it, because each pass quadruples the triangle count - leaving the + // count where it was would immediately re-preview N more passes on top of the mesh that was just + // committed, i.e. the most expensive thing the panel can do, on every Apply. int m_subdivide_count = 1; bool m_subdivide_editing = false; int m_subdivide_preview_count = -1; // the count m_subdivide_preview_glmodel was built for @@ -263,6 +312,10 @@ private: // with the mesh's mean edge length the first time the control is shown. Like subdivide, it replaces // the geometry and drops not-yet-baked paint (no remap across a topology change). float m_remesh_target_edge_mm = 0.f; + // Dihedral angle above which an edge counts as a hard feature and is held fixed by the remesher. + // Off by default would round every sharp edge off, so this is on; 0 disables the protection. + float m_remesh_sharp_angle_deg = 40.f; + bool m_remesh_keep_sharp_edges = true; void remesh_model(); // Live, pre-bake preview of the true displaced geometry (built by the same algorithm Bake @@ -450,11 +503,14 @@ private: bool m_tool_icon_tried = false; unsigned int tool_icon_id(); // 0 if the icon could not be loaded - // The panel's icon-button rows (selection mode, view mode) each need their own small SVG. Loaded and - // uploaded once on first use and cached by file name (under resources/images/). Returns 0 if an icon - // could not be loaded, in which case the button falls back to a text control. - std::map m_icon_cache; - unsigned int icon_id(const std::string &filename); + // Icons for the panel's selection-mode and view-mode button rows. Loaded through IconManager with + // the same colour/monochrome variants the main toolbar uses, so an inactive button shows the icon in + // the theme's normal (grey) foreground colour and an active one shows it in its original colours -- + // matching the toolbar's selected/unselected look. Uploaded once on first panel render. + IconManager m_panel_icons; + std::map m_panel_icon_map; // file name -> [normal, colour, disabled] + bool m_panel_icons_tried = false; + void ensure_panel_icons(); }; } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/TextureProjectorFrame.cpp b/src/slic3r/GUI/TextureProjectorFrame.cpp new file mode 100644 index 0000000000..cfa29967ad --- /dev/null +++ b/src/slic3r/GUI/TextureProjectorFrame.cpp @@ -0,0 +1,97 @@ +#include "TextureProjectorFrame.hpp" + +#include + +#include +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r { namespace GUI { + +TextureProjectorFrame::TextureProjectorFrame(wxWindow *parent) + : wxFrame(parent, wxID_ANY, _L("Projection frame - drag over the model, then Apply"), wxDefaultPosition, + wxSize(360, 360), + // Caption and resize border so moving and sizing are the native gestures the user + // already knows - "align it by moving the window" only works if the window moves the + // ordinary way. FLOAT_ON_PARENT keeps it above the 3D view without the antisocial + // always-on-top-of-everything behaviour of wxSTAY_ON_TOP. + wxCAPTION | wxRESIZE_BORDER | wxCLOSE_BOX | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT) +{ + SetBackgroundStyle(wxBG_STYLE_PAINT); + Bind(wxEVT_PAINT, &TextureProjectorFrame::on_paint, this); + // A resize changes the gate, so the texture has to be re-stretched under it. + Bind(wxEVT_SIZE, [this](wxSizeEvent &evt) { Refresh(); evt.Skip(); }); + SetTransparent(wxByte(m_alpha)); + + // Hide rather than destroy: the gizmo owns this window's lifetime, and reopening should keep the + // frame exactly where it was left - its position is the placement. + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) { + if (evt.CanVeto()) { + evt.Veto(); + Hide(); + } else + evt.Skip(); + }); +} + +void TextureProjectorFrame::set_texture(const std::vector &gray, int width, int height) +{ + if (width <= 0 || height <= 0 || gray.size() < size_t(width) * size_t(height)) { + m_bitmap = wxBitmap(); + Refresh(); + return; + } + + wxImage img(width, height); + unsigned char *dst = img.GetData(); + for (size_t i = 0, n = size_t(width) * size_t(height); i < n; ++i) { + const unsigned char v = gray[i]; + dst[i * 3 + 0] = v; + dst[i * 3 + 1] = v; + dst[i * 3 + 2] = v; + } + m_bitmap = wxBitmap(img); + Refresh(); +} + +void TextureProjectorFrame::set_opacity(int alpha) +{ + m_alpha = std::clamp(alpha, 20, 255); + SetTransparent(wxByte(m_alpha)); + Refresh(); +} + +wxRect TextureProjectorFrame::client_rect_on_screen() const +{ + const wxSize sz = GetClientSize(); + return wxRect(ClientToScreen(wxPoint(0, 0)), sz); +} + +void TextureProjectorFrame::on_paint(wxPaintEvent &) +{ + wxPaintDC dc(this); + const wxSize sz = GetClientSize(); + if (sz.x <= 0 || sz.y <= 0) + return; + + dc.SetBackground(wxBrush(wxColour(20, 20, 20))); + dc.Clear(); + + if (m_bitmap.IsOk()) { + // Stretched to fill the client area rather than kept at its own aspect: the gate maps to the + // uv unit square whatever its shape, so a non-square window genuinely does project a + // stretched texture. Showing it any other way would misrepresent the bake. + wxImage scaled = m_bitmap.ConvertToImage().Scale(sz.x, sz.y, wxIMAGE_QUALITY_NORMAL); + dc.DrawBitmap(wxBitmap(scaled), 0, 0, false); + } + + // The border is the projection's hard edge, so it is drawn explicitly - with the window + // translucent, the native frame alone reads poorly against a busy 3D scene. + dc.SetPen(wxPen(wxColour(0, 200, 180), 2)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(0, 0, sz.x, sz.y); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/TextureProjectorFrame.hpp b/src/slic3r/GUI/TextureProjectorFrame.hpp new file mode 100644 index 0000000000..784062680d --- /dev/null +++ b/src/slic3r/GUI/TextureProjectorFrame.hpp @@ -0,0 +1,56 @@ +#ifndef slic3r_TextureProjectorFrame_hpp_ +#define slic3r_TextureProjectorFrame_hpp_ + +// The projection-frame overlay for TextureProjectionMethod::ViewProjected. +// +// A semi-transparent, resizable window that the user drags over the 3D view like a slide projector's +// gate: whatever the model shows through this window is what the texture is projected onto, and the +// window's border is the hard edge of the projection. "Apply" then reads the window's client +// rectangle, converts it into the 3D canvas's own pixel space, and builds an exact projective map +// from it (see GLGizmoTextureDisplacement::apply_projection_frame()). +// +// The window itself is deliberately dumb - it owns no placement state and reports nothing +// continuously. Its position and size *are* the placement, and they are read on demand at Apply, +// which is also when the (expensive) visible-facet selection runs. Moving the window is therefore +// free, and nothing recomputes until the user asks for it. +// +// Plain 2D (wxGraphicsContext), not a wxGLCanvas: a second GL canvas would have to share the app's +// one real wxGLContext, the cause of bugs #10 and #14 in TEXTURE_DISPLACEMENT.md. A paint-DC window +// has no such failure mode, and this one only ever draws a bitmap and a border. + +#include + +#include +#include + +namespace Slic3r { namespace GUI { + +class TextureProjectorFrame : public wxFrame +{ +public: + explicit TextureProjectorFrame(wxWindow *parent); + + // The same 8-bit grayscale pixels build_texture_displacement() samples, so what is aligned here + // is what gets baked. Pass width/height <= 0 to clear it. + void set_texture(const std::vector &grayscale_pixels, int width, int height); + + // Whole-window opacity, 0..255. Low enough to see the model through it, high enough to judge + // where the texture lands - the useful range is roughly 60..200. + void set_opacity(int alpha); + int opacity() const { return m_alpha; } + + // The client area (the gate itself, excluding caption and borders) in screen coordinates. This + // is what the projection is built from, so it deliberately excludes the window decorations - + // the user aligns what they see, which is the client area. + wxRect client_rect_on_screen() const; + +private: + wxBitmap m_bitmap; + int m_alpha = 140; + + void on_paint(wxPaintEvent &); +}; + +}} // namespace Slic3r::GUI + +#endif // slic3r_TextureProjectorFrame_hpp_ diff --git a/src/slic3r/GUI/UVEditorCanvas.cpp b/src/slic3r/GUI/UVEditorCanvas.cpp index ab7d023f8b..95281acb8b 100644 --- a/src/slic3r/GUI/UVEditorCanvas.cpp +++ b/src/slic3r/GUI/UVEditorCanvas.cpp @@ -145,6 +145,7 @@ UVEditorCanvas::UVEditorCanvas(wxWindow *parent) Bind(wxEVT_MIDDLE_UP, &UVEditorCanvas::on_mouse, this); Bind(wxEVT_MOTION, &UVEditorCanvas::on_mouse, this); Bind(wxEVT_MOUSEWHEEL, &UVEditorCanvas::on_mouse, this); + Bind(wxEVT_LEAVE_WINDOW, &UVEditorCanvas::on_leave, this); Bind(wxEVT_KEY_DOWN, &UVEditorCanvas::on_key, this); Bind(wxEVT_ERASE_BACKGROUND, &UVEditorCanvas::on_erase_background, this); } @@ -177,6 +178,8 @@ void UVEditorCanvas::set_islands(Islands islands) // Vertex/edge picks index into the old unwrap; drop them too. m_active_vertex = -1; m_active_edge = { -1, -1 }; + m_sel_vertices.clear(); + m_sel_edges.clear(); } // Boundary vertices, bucketed per island, for snapping. @@ -283,9 +286,15 @@ void UVEditorCanvas::update_status() case Gesture::None: default: if (m_select_mode == SelectMode::Vertex) - msg = _L("Vertex mode: drag a vertex to reshape the unwrap | wheel = zoom, middle-drag = pan, Home = frame"); + msg = m_sel_vertices.size() > 1 ? + wxString::Format(_L("%d vertices selected | drag = move together, Shift/Ctrl click = add/remove"), + int(m_sel_vertices.size())) : + _L("Vertex mode: drag a vertex to reshape | Shift/Ctrl click = multi-select, wheel = zoom, Home = frame"); else if (m_select_mode == SelectMode::Edge) - msg = _L("Edge mode: drag an island edge to reshape the unwrap | wheel = zoom, middle-drag = pan, Home = frame"); + msg = m_sel_edges.size() > 1 ? + wxString::Format(_L("%d edges selected | drag = move together, Shift/Ctrl click = add/remove"), + int(m_sel_edges.size())) : + _L("Edge mode: drag an island edge to reshape | Shift/Ctrl click = multi-select, wheel = zoom, Home = frame"); else if (m_selection.size() > 1) msg = wxString::Format(_L("%d islands selected | drag = move together, Shift/Ctrl click = add/remove, R/S = rotate/scale primary"), int(m_selection.size())); @@ -391,6 +400,8 @@ void UVEditorCanvas::set_select_mode(SelectMode mode) m_select_mode = mode; m_active_vertex = -1; m_active_edge = { -1, -1 }; + m_sel_vertices.clear(); + m_sel_edges.clear(); m_gesture = Gesture::None; update_status(); Refresh(); @@ -520,6 +531,16 @@ Vec2f UVEditorCanvas::snap_correction(int island) const return best; } +std::vector UVEditorCanvas::selected_edge_endpoints() const +{ + std::vector verts; + for (const auto &[a, b] : m_sel_edges) + for (const int v : { a, b }) + if (v >= 0 && std::find(verts.begin(), verts.end(), v) == verts.end()) + verts.push_back(v); + return verts; +} + void UVEditorCanvas::end_gesture() { const bool was_editing = m_gesture == Gesture::MoveIsland || m_gesture == Gesture::RotateIsland || @@ -545,11 +566,22 @@ void UVEditorCanvas::end_gesture() if (v >= 0 && size_t(v) < m_islands.uvs.size()) edits.emplace_back(v, m_islands.uvs[size_t(v)]); }; - if (m_gesture == Gesture::MoveVertex) - add(m_active_vertex); - else { - add(m_active_edge.first); - add(m_active_edge.second); + // Commit every element of the multi-selection, not just the primary, so a group drag stores all + // the moved vertices' overrides. Falls back to the primary if the selection is somehow empty. + if (m_gesture == Gesture::MoveVertex) { + if (m_sel_vertices.empty()) + add(m_active_vertex); + else + for (const int v : m_sel_vertices) + add(v); + } else { + const std::vector endpoints = selected_edge_endpoints(); + if (endpoints.empty()) { + add(m_active_edge.first); + add(m_active_edge.second); + } else + for (const int v : endpoints) + add(v); } if (!edits.empty()) m_on_vertex_edit(edits); @@ -638,6 +670,12 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) const wxEventType type = evt.GetEventType(); const wxPoint pos = evt.GetPosition(); + // Track the pointer so the +/- add-remove hint can be drawn next to it in Vertex/Edge mode. + m_cursor_px = pos; + m_cursor_inside = true; + if (type == wxEVT_MOTION && m_select_mode != SelectMode::Island && m_gesture == Gesture::None) + Refresh(); // animate the hint (and its +/- flip) as the pointer/modifiers move + // Key events (R/S/Home) only arrive if this canvas has focus, and clicking it is the natural way // to ask for it - the pane is not in the tab order. if (type == wxEVT_LEFT_DOWN || type == wxEVT_RIGHT_DOWN || type == wxEVT_MIDDLE_DOWN) @@ -656,15 +694,50 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) m_drag_last_px = pos; m_gesture_last_uv = uv; if (m_select_mode == SelectMode::Vertex) { - m_active_vertex = vertex_at(uv); + const int hit = vertex_at(uv); m_active_edge = { -1, -1 }; m_vertex_edit_moved = false; - m_gesture = (m_active_vertex >= 0) ? Gesture::MoveVertex : Gesture::Pan; + // Same Shift-adds / Ctrl-toggles / plain-replaces rules as island selection, so a group of + // vertices can be picked and dragged together. + if (hit >= 0) { + if (evt.ShiftDown()) { + if (!is_vertex_selected(hit)) + m_sel_vertices.push_back(hit); + } else if (evt.ControlDown()) { + if (auto it = std::find(m_sel_vertices.begin(), m_sel_vertices.end(), hit); it != m_sel_vertices.end()) + m_sel_vertices.erase(it); + else + m_sel_vertices.push_back(hit); + } else if (!is_vertex_selected(hit)) { + m_sel_vertices.assign(1, hit); + } + } else if (!evt.ShiftDown() && !evt.ControlDown()) { + m_sel_vertices.clear(); + } + // Primary = the clicked vertex only if it is (still) selected; a Ctrl-deselect just toggles. + m_active_vertex = (hit >= 0 && is_vertex_selected(hit)) ? hit : -1; + m_gesture = (m_active_vertex >= 0) ? Gesture::MoveVertex : Gesture::Pan; } else if (m_select_mode == SelectMode::Edge) { - m_active_edge = edge_at(uv); + const std::pair hit = edge_at(uv); m_active_vertex = -1; m_vertex_edit_moved = false; - m_gesture = (m_active_edge.first >= 0) ? Gesture::MoveEdge : Gesture::Pan; + if (hit.first >= 0) { + if (evt.ShiftDown()) { + if (!is_edge_selected(hit)) + m_sel_edges.push_back(hit); + } else if (evt.ControlDown()) { + if (auto it = std::find(m_sel_edges.begin(), m_sel_edges.end(), hit); it != m_sel_edges.end()) + m_sel_edges.erase(it); + else + m_sel_edges.push_back(hit); + } else if (!is_edge_selected(hit)) { + m_sel_edges.assign(1, hit); + } + } else if (!evt.ShiftDown() && !evt.ControlDown()) { + m_sel_edges.clear(); + } + m_active_edge = (hit.first >= 0 && is_edge_selected(hit)) ? hit : std::pair{ -1, -1 }; + m_gesture = (m_active_edge.first >= 0) ? Gesture::MoveEdge : Gesture::Pan; } else { const int hit = island_at(uv); if (hit >= 0) { @@ -739,8 +812,15 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) break; } case Gesture::MoveVertex: { - const Vec2f uv = screen_to_uv(pos); - move_vertex_raw(m_active_vertex, uv - m_gesture_last_uv); + const Vec2f uv = screen_to_uv(pos); + const Vec2f delta = uv - m_gesture_last_uv; + // Move the whole selection by the same uv-space delta (each vertex converts it through its + // own island transform in move_vertex_raw). Falls back to the primary if none is selected. + if (m_sel_vertices.empty()) + move_vertex_raw(m_active_vertex, delta); + else + for (const int v : m_sel_vertices) + move_vertex_raw(v, delta); m_gesture_last_uv = uv; m_vertex_edit_moved = true; Refresh(); @@ -749,8 +829,14 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) case Gesture::MoveEdge: { const Vec2f uv = screen_to_uv(pos); const Vec2f delta = uv - m_gesture_last_uv; - move_vertex_raw(m_active_edge.first, delta); - move_vertex_raw(m_active_edge.second, delta); + // Move every unique endpoint of every selected edge once. Falls back to the primary edge. + const std::vector endpoints = selected_edge_endpoints(); + if (endpoints.empty()) { + move_vertex_raw(m_active_edge.first, delta); + move_vertex_raw(m_active_edge.second, delta); + } else + for (const int v : endpoints) + move_vertex_raw(v, delta); m_gesture_last_uv = uv; m_vertex_edit_moved = true; Refresh(); @@ -1046,6 +1132,16 @@ void UVEditorCanvas::on_size(wxSizeEvent &evt) Update(); } +void UVEditorCanvas::on_leave(wxMouseEvent &evt) +{ + evt.Skip(); + if (m_cursor_inside) { + m_cursor_inside = false; + if (m_select_mode != SelectMode::Island) + Refresh(); // the +/- hint was following the cursor; drop it now the pointer is gone + } +} + void UVEditorCanvas::render() { if (m_context == nullptr || !IsShownOnScreen()) @@ -1190,7 +1286,8 @@ void UVEditorCanvas::render() // Vertex/Edge mode handles: a small square drawn over the picked vertex (or each endpoint of the // picked edge), so it is obvious which sub-element a drag will move. The already-drawn boundary // sits between an edge's two markers, so the pair reads as "this edge". - if (m_select_mode != SelectMode::Island && (m_active_vertex >= 0 || m_active_edge.first >= 0)) { + if (m_select_mode != SelectMode::Island && + (!m_sel_vertices.empty() || !m_sel_edges.empty() || m_active_vertex >= 0 || m_active_edge.first >= 0)) { if (!m_vertex_marker_glmodel.is_initialized()) { GLModel::Geometry q; q.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; @@ -1215,14 +1312,57 @@ void UVEditorCanvas::render() m.scale(double(marker)); draw(m_vertex_marker_glmodel, UV_COLOR_SEL_BOUNDARY, m); }; - if (m_active_vertex >= 0) - draw_marker(m_active_vertex); - if (m_active_edge.first >= 0) { - draw_marker(m_active_edge.first); - draw_marker(m_active_edge.second); + // Mark every element of the multi-selection (falling back to the primary when nothing is in the + // set yet), so a Shift/Ctrl group is all visibly highlighted. + if (m_select_mode == SelectMode::Vertex) { + if (m_sel_vertices.empty()) + draw_marker(m_active_vertex); + else + for (const int v : m_sel_vertices) + draw_marker(v); + } else { + if (m_sel_edges.empty()) { + draw_marker(m_active_edge.first); + draw_marker(m_active_edge.second); + } else + for (const auto &[a, b] : m_sel_edges) { + draw_marker(a); + draw_marker(b); + } } } + // Add/remove hint next to the cursor in Vertex/Edge mode: a green '+' when a click will add to the + // selection (plain or Shift), a red '-' when Ctrl is held and a click will remove one -- the UV-side + // twin of the 3D paint cursor's own sign. Rebuilt each frame at the pointer, sized in pixels. + if (m_select_mode != SelectMode::Island && m_cursor_inside) { + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + const Vec2f centre = screen_to_uv(m_cursor_px) + Vec2f(14.f, -14.f) * uv_per_px; // up-right of the pointer + const float r = 6.f * uv_per_px; + const bool removing = wxGetKeyState(WXK_CONTROL); + + GLModel::Geometry sign; + sign.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + unsigned idx = 0; + const auto seg = [&](const Vec2f &a, const Vec2f &b) { + sign.add_vertex(Vec3f(a.x(), a.y(), 0.f)); + sign.add_vertex(Vec3f(b.x(), b.y(), 0.f)); + sign.add_line(idx, idx + 1); + idx += 2; + }; + seg(centre - Vec2f(r, 0.f), centre + Vec2f(r, 0.f)); // the '-' bar, shared by both signs + if (!removing) + seg(centre - Vec2f(0.f, r), centre + Vec2f(0.f, r)); // the extra stroke that makes it a '+' + + m_cursor_sign_glmodel.reset(); + if (!sign.is_empty()) + m_cursor_sign_glmodel.init_from(std::move(sign)); + set_line_width(3.f); + draw(m_cursor_sign_glmodel, removing ? ColorRGBA(1.f, 0.30f, 0.25f, 0.95f) : ColorRGBA(0.35f, 0.90f, 0.45f, 0.95f), + identity); + set_line_width(1.f); + } + // The GL context is shared with the 3D view; leave the bits we touched as we found them. glsafe(::glDisable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1253,29 +1393,33 @@ enum : int { UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { - // Icon + label buttons: the tool icon (toolbar_texture_displacement.svg) is reused on all four as a - // placeholder until dedicated per-tool art exists, and the label is kept alongside it so the - // buttons stay tellable apart while they share one picture. - const wxBitmap icon = create_scaled_bitmap("toolbar_texture_displacement", this, 16); + // Icon + label buttons: each has its own dedicated SVG (see the map below), with the label kept + // alongside it. A missing SVG simply leaves the button showing only its label -- never bitmap-less + // garbage -- so the bar stays usable before the art lands. auto *bar = new wxBoxSizer(wxHORIZONTAL); - const auto add_button = [&](int id, const wxString &label, const wxString &tip) { + const auto set_icon = [this](wxAnyButton *b, const std::string &iconname) { + const wxBitmap bmp = create_scaled_bitmap(iconname, this, 16); + if (bmp.IsOk()) + b->SetBitmap(bmp); + }; + const auto add_button = [&](int id, const std::string &iconname, const wxString &label, const wxString &tip) { auto *b = new wxButton(this, id, label, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - b->SetBitmap(icon); + set_icon(b, iconname); b->SetToolTip(tip); bar->Add(b, 0, wxALL, 2); b->Bind(wxEVT_BUTTON, &UVEditorPanel::on_tool, this); return b; }; - add_button(ID_UV_FRAME, _L("Frame"), _L("Frame all islands (Home)")); + add_button(ID_UV_FRAME, "texture_displacement_frame", _L("Frame"), _L("Frame all islands (Home)")); m_snap_button = new wxToggleButton(this, ID_UV_SNAP, _L("Snap"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - m_snap_button->SetBitmap(icon); + set_icon(m_snap_button, "texture_displacement_snap"); m_snap_button->SetToolTip(_L("Snap islands together when dragging")); bar->Add(m_snap_button, 0, wxALL, 2); m_snap_button->Bind(wxEVT_TOGGLEBUTTON, &UVEditorPanel::on_tool, this); - add_button(ID_UV_AVG_SCALE, _L("Avg scale"), _L("Give every island the same texel density")); - add_button(ID_UV_CUT, _L("Cut"), _L("Split the selected island across its long axis")); - add_button(ID_UV_JOIN, _L("Join"), _L("Unfold the selected island onto its nearest neighbour along their shared edge")); - add_button(ID_UV_UNJOIN, _L("Unjoin"), _L("Send the selected island back to its own packed position")); + add_button(ID_UV_AVG_SCALE, "texture_displacement_avg_scale", _L("Avg scale"), _L("Give every island the same texel density")); + add_button(ID_UV_CUT, "texture_displacement_cut", _L("Cut"), _L("Split the selected island across its long axis")); + add_button(ID_UV_JOIN, "texture_displacement_join", _L("Join"), _L("Unfold the selected island onto its nearest neighbour along their shared edge")); + add_button(ID_UV_UNJOIN, "texture_displacement_join", _L("Unjoin"), _L("Send the selected island back to its own packed position")); m_canvas = new UVEditorCanvas(this); m_canvas->set_status_callback([this](const wxString &text) { diff --git a/src/slic3r/GUI/UVEditorCanvas.hpp b/src/slic3r/GUI/UVEditorCanvas.hpp index 87d85d6e2c..3e1047eeb9 100644 --- a/src/slic3r/GUI/UVEditorCanvas.hpp +++ b/src/slic3r/GUI/UVEditorCanvas.hpp @@ -144,6 +144,7 @@ private: void on_size(wxSizeEvent &evt); void on_mouse(wxMouseEvent &evt); void on_key(wxKeyEvent &evt); + void on_leave(wxMouseEvent &evt); // drops the +/- cursor hint when the pointer leaves the canvas void on_erase_background(wxEraseEvent &evt) {} // required to avoid flicker on MSW, deliberately a no-op void render(); @@ -244,14 +245,37 @@ private: Gesture m_gesture = Gesture::None; SelectMode m_select_mode = SelectMode::Island; - // The sub-element being edited in Vertex/Edge mode (unwrapped-vertex indices), or -1/{-1,-1}. + // The sub-element being edited in Vertex/Edge mode (unwrapped-vertex indices), or -1/{-1,-1}. This + // is the *primary* (last-picked) element of the multi-selection below. int m_active_vertex = -1; std::pair m_active_edge{ -1, -1 }; + // Multi-selection for Vertex/Edge modes, mirroring the island selection: plain click replaces, Shift + // adds, Ctrl toggles, and a drag moves the whole set together. m_active_vertex/m_active_edge stay the + // primary. Kept as small vectors (tiny, and order doesn't matter here). + std::vector m_sel_vertices; + std::vector> m_sel_edges; + bool is_vertex_selected(int v) const + { + return std::find(m_sel_vertices.begin(), m_sel_vertices.end(), v) != m_sel_vertices.end(); + } + bool is_edge_selected(const std::pair &e) const + { + return std::find(m_sel_edges.begin(), m_sel_edges.end(), e) != m_sel_edges.end(); + } + // Unique unwrapped-vertex endpoints of every selected edge (an endpoint shared by two selected edges + // is returned once, so a drag doesn't move it twice). + std::vector selected_edge_endpoints() const; + // Last known mouse position over the canvas, and whether the pointer is currently inside it. Used to + // draw the +/- add/remove sign next to the cursor in Vertex/Edge mode. + wxPoint m_cursor_px{ 0, 0 }; + bool m_cursor_inside = false; // Set once a Vertex/Edge drag actually moves, so a bare click (select without drag) doesn't commit a // no-op edit and take an undo snapshot for nothing. bool m_vertex_edit_moved = false; // Lazily-built small filled square, drawn at an edited/hovered vertex as a handle. GLModel m_vertex_marker_glmodel; + // Rebuilt each frame at the cursor while a +/- add-remove hint is shown (Vertex/Edge mode). + GLModel m_cursor_sign_glmodel; int m_selected_island = -1; // The full multi-selection; m_selected_island is its primary (last-clicked) member. Kept as a small // vector rather than a set because it is tiny and iteration order (primary last) is convenient. From 61d2d4355a4609836de4d6e5ab76b05e40f23624 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 21 Jul 2026 10:12:09 +0200 Subject: [PATCH 11/89] Cleanup --- TEXTURE_DISPLACEMENT.md | 454 ++++-------------- .../shaders/140/texture_displacement_bump.fs | 8 +- .../140/texture_displacement_uvcheck.fs | 4 +- .../140/texture_displacement_uvcheck.vs | 4 +- src/libslic3r/TextureDisplacement.cpp | 52 +- src/libslic3r/TextureDisplacement.hpp | 76 +-- src/slic3r/GUI/GLTexture.cpp | 8 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 38 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 48 +- src/slic3r/GUI/ImGuiWrapper.cpp | 6 +- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 4 +- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 2 +- .../Jobs/TextureDisplacementPreviewJob.cpp | 4 +- .../Jobs/TextureDisplacementPreviewJob.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 24 +- src/slic3r/GUI/TextureLibrary.cpp | 2 +- src/slic3r/GUI/TextureLibrary.hpp | 4 +- src/slic3r/GUI/UVEditorCanvas.cpp | 12 +- src/slic3r/GUI/UVEditorCanvas.hpp | 30 +- 19 files changed, 251 insertions(+), 533 deletions(-) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index 6bbd5f085b..d1109644e2 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -1,4 +1,4 @@ -# Texture Displacement — Technical Notes +# Texture Displacement - Technical Notes Branch: `feature/texture_displacement`. This document is a knowledge dump of the whole feature as it stands: architecture, file map, algorithms, known bugs found and fixed (with root causes worth @@ -26,39 +26,18 @@ A paint-style gizmo (`GLGizmoTextureDisplacement`) that lets you: ### Data model (per `ModelVolume`) Each of up to `TEXTURE_DISPLACEMENT_MAX_LAYERS` (8) layers gets its **own independent -`FacetsAnnotation`** paint mask — the exact same `TriangleSelector`/`FacetsAnnotation` machinery +`FacetsAnnotation`** paint mask - the exact same `TriangleSelector`/`FacetsAnnotation` machinery every other paint gizmo (FdmSupports, Seam, MMU, FuzzySkin) already uses, just one full instance per layer slot instead of one per volume. This is what makes "layered/blended" painting work for free: the same triangle can be `ENFORCER` in layer 2's mask and layer 5's mask simultaneously, and at bake/preview time each layer displaces the surface left by the previous one (image-editor-layer semantics). -**Important gotcha**: `ModelVolume` does **not** hold `std::array`. It holds -8 individually-named fields (`texture_displacement_facets_0` .. `_7`) plus a -`texture_displacement_facet(int slot)` accessor. Reason: `FacetsAnnotation`'s ctor is private, -friended only to `ModelVolume`; `std::array`'s own implicitly-generated special member functions -are generated with **`std::array`'s** access rights, not the enclosing class's, so friendship does -not propagate through the array wrapper. This is a real MSVC C2280 if you try it — confirmed by -attempting it. `TextureDisplacementFacetsData` (a `std::array`, -used to carry paint-mask *data* around, e.g. into the bake job) is fine as a real `std::array` -since `TriangleSplittingData` has an ordinary public ctor — only the `FacetsAnnotation` object -itself has the friend-ctor problem. - -Plus `std::vector texture_displacement_layers;` — the plain-data layer -definitions (texture bytes + params), ordinary public ctor, safe in a vector. - -Touch points that had to mirror the existing `FacetsAnnotation` pattern (see `supported_facets` for -the template): all constructors' asserts, copy ctors' init lists, the `-1`-id deserialization ctor, -`set_new_unique_id()`, cereal `save`/`load`, `is_texture_displacement_painted()`, and -`reset_extra_facets()` (called whenever a topology-changing op like Simplify or subdivision -replaces the mesh — this is what drops any unbaked texture-displacement paint, since there's no -remap-across-topology-change support for it yet, see Limitations). - ### Bake algorithm (`libslic3r/TextureDisplacement.cpp`) `build_texture_displacement(base_mesh, layers, facets_data)` is **accumulate-then-displace, and topology-preserving**: the returned mesh has exactly the input's vertices and triangles, in the same -order — only the positions of displaced vertices differ. +order - only the positions of displaced vertices differ. 1. `its_compactify_vertices()` on a copy of the input. In practice a no-op (it only drops *unreferenced* vertices, and preserves the order and indices of the rest). It is there to @@ -70,36 +49,25 @@ order — only the positions of displaced vertices differ. the **base mesh** (never against a previous layer's output), then `selector.get_facets_strict(ENFORCER)` → the painted patch. Two facts are exploited: - `get_facets_strict()` returns the mesh's **entire** referenced vertex array regardless of which - state was asked for — only `.indices` is filtered by state. So `get_facets_strict(ENFORCER)` + state was asked for - only `.indices` is filtered by state. So `get_facets_strict(ENFORCER)` and `get_facets_strict(NONE)` share identical vertex indexing, which is what lets boundary detection be a plain index check instead of a position-hash lookup. - The selector's vertex array *starts with* the mesh's own vertices (extra ones created where a brush stroke split a triangle are appended after them), and `get_facets_strict()` emits the referenced ones in order. Combined with step 1, **selector vertex index `i` is our vertex `i`**. - Split vertices live past the end of our array and are simply skipped — they sit on the paint + Split vertices live past the end of our array and are simply skipped - they sit on the paint boundary anyway (splitting only happens at partial coverage), so they would be pinned regardless. -4. A vertex used by at least one **unpainted** triangle is a boundary vertex — pinned, never +4. A vertex used by at least one **unpainted** triangle is a boundary vertex - pinned, never displaced (its final position is ambiguous, it belongs to both regions). Only vertices used exclusively by painted triangles get displaced. This is what keeps bakes seamless with zero remeshing/hole-filling at the seam. 5. Per interior vertex: sample the height texture (`sample_layer_height()`, see Projection methods) and fold `height * depth_mm * (invert ? -1 : 1)` into that vertex's running total via the layer's `TextureBlendMode` (see Blend modes). A `visited` set makes each layer fold in exactly **once** - per vertex, no matter how many of the patch's triangles share it — otherwise a Multiply/Subtract + per vertex, no matter how many of the patch's triangles share it - otherwise a Multiply/Subtract layer would apply two or three times over depending on local triangle fan-out. 6. Finally, move each touched vertex along its (step 2) normal by its accumulated total. -**This replaced a sequential design** that re-meshed after each layer and carried the next layer's -paint mask onto the result with `TriangleSelector::remap_painting()`. That was the root cause of the -reported "the second texture is never applied" bug: remapping a mask onto a mesh whose vertices had -just been displaced out from under it routinely produced an empty bitstream, and the layer was then -silently `continue`d past. It was also what forced the per-layer vertex duplication and the final -`its_compactify_vertices()` weld. The current formulation has neither problem, is substantially -faster (no remap, no welding, one pass), makes blend modes possible at all (they need a shared -per-vertex accumulator, which sequential re-meshing cannot provide), and — because the output keeps -the input's exact vertex indexing — lets GUI code overlay a preview on the base mesh with no index -translation. - ### Blend modes `TextureBlendMode` {Add, Subtract, Multiply, Divide}, per layer, applied per vertex against the @@ -108,9 +76,9 @@ displacement in **mm**, not a pixel value. Add/Subtract are self-explanatory. Multiply/Divide are *scaling* operations and so need a unit convention: they treat the layer's own value as a **factor relative to 1 mm**. That makes `depth_mm` -a gain, and — the property that makes a Multiply layer usable as a mask — a layer with depth 1 mm +a gain, and - the property that makes a Multiply layer usable as a mask - a layer with depth 1 mm sampling a white (1.0) texel multiplies by exactly 1, i.e. leaves the layers below unchanged. -Divide floors its divisor's magnitude at 0.05 — a black texel samples to *exactly* zero, so the +Divide floors its divisor's magnitude at 0.05 - a black texel samples to *exactly* zero, so the divisor really does hit zero in ordinary use, and an unbounded `1/0` would fling vertices thousands of mm away and poison the mesh's bounding box (and every plate/print-volume check downstream). The floor doubles as a cap on how far Divide can amplify the relief beneath it: at most 20×. @@ -124,64 +92,64 @@ the UI, which labels that layer "Base layer" instead of offering a control that Four choices per layer (`TextureProjectionMethod`), all funneling through `apply_uv_transform()` (scale by `1/tiling_scale`, rotate by `rotation_deg`, add `offset`). They are dispatched by -`sample_layer_height()`, which returns a **height**, not a UV — because Triplanar takes three +`sample_layer_height()`, which returns a **height**, not a UV - because Triplanar takes three texture samples per vertex and so has no single UV that represents it. -- **Triplanar** (default) — samples the texture on all three world planes (`(y,z)`, `(x,z)`, `(x,y)`) +- **Triplanar** (default) - samples the texture on all three world planes (`(y,z)`, `(x,z)`, `(x,y)`) and blends the three by the vertex's own normal raised to `TRIPLANAR_BLEND_SHARPNESS` (4). This is the fix for a real, user-reported bug. The previous version *hard-picked* the single axis most aligned with the normal, which is discontinuous wherever that dominant axis flips: on a +X face the planar coordinate is `(y, z)`, on a −Y face it is `(x, z)`, so at the shared edge `u` jumps from `y_edge` to `x_edge`. On a box centred near the origin those two happen to **agree** at the (+,+) and (−,−) corners and **differ by the full corner width** at the (+,−) and (−,+) corners - — which is exactly the "two bad corners, two good ones" symmetry that was observed. A weighted + - which is exactly the "two bad corners, two good ones" symmetry that was observed. A weighted blend is continuous across the transition by construction, since the weight of the axis being left behind falls smoothly to zero. (Note this removes the hard *seam*; some cross-fade blurring in the band right at a 90° edge is inherent to triplanar mapping. A genuinely seam-free wrap around a box - needs a real unwrap — that is what the LSCM mode is for.) -- **Cylindrical** — wraps around an axis through the patch centroid, axis auto-picked as the world + needs a real unwrap - that is what the LSCM mode is for.) +- **Cylindrical** - wraps around an axis through the patch centroid, axis auto-picked as the world axis *least* aligned with the average normal (perpendicular to the outward radial normal, as a cylinder's own axis would be). `u = angle * local_radius` (arc length in mm), `v = distance along axis`. Approximation, not an exact fit for arbitrary geometry. -- **Spherical** — longitude/latitude around the centroid, scaled by local radius. Same caveat. -- **LSCM** — real UV unwrap via `MeshBoolean::cgal::parameterize_lscm()` (CGAL's +- **Spherical** - longitude/latitude around the centroid, scaled by local radius. Same caveat. +- **LSCM** - real UV unwrap via `MeshBoolean::cgal::parameterize_lscm()` (CGAL's `Surface_mesh_parameterization` package, LSCM algorithm). Computed **once per patch** (not - per-vertex like the others — it's a single global least-squares solve), then each vertex looks up + per-vertex like the others - it's a single global least-squares solve), then each vertex looks up its precomputed UV. Requires the patch to be a single topological disk (one connected component, - one boundary loop) — `compute_lscm_uvs()` returns empty and the layer silently falls back to + one boundary loop) - `compute_lscm_uvs()` returns empty and the layer silently falls back to Triplanar if not (e.g. multiple disconnected painted islands, or a fully closed patch). CGAL's parameterizer needs a mesh with no isolated/unreferenced vertices, but `get_facets_strict()` - returns the *whole* mesh's vertex array — so there's a compaction step + returns the *whole* mesh's vertex array - so there's a compaction step (`compact_patch_with_map()`) that builds a clean sub-mesh + an index map back to the original (uncompacted) vertex numbering, purely local to this file. -- **ViewProjected** ("From view") — a flat projection along a fixed direction captured from the 3D +- **ViewProjected** ("From view") - a flat projection along a fixed direction captured from the 3D camera, like a slide projector. `capture_view_projection()` takes the camera's right/up axes, transforms them into the volume's *local* frame (so the projection rides along if the part is later moved), and stores them as `TextureDisplacementLayer::view_project_right/up` (unit vectors, so the projected coordinate stays in mm and `tiling_scale` keeps meaning mm). `sample_layer_height()` - projects `Vec2f(dot(pos, right), dot(pos, up))`. Single-valued per point, so — like LSCM but unlike - blended Triplanar — the fast preview and UV-check overlay precompute it per vertex + projects `Vec2f(dot(pos, right), dot(pos, up))`. Single-valued per point, so - like LSCM but unlike + blended Triplanar - the fast preview and UV-check overlay precompute it per vertex (`compute_layer_vertex_uvs()`) and drive the shader's `use_vertex_uv` path. Faces angled away from the projector smear; that is inherent to view projection, not a bug. Two companions to this mode: - - **Projection frame overlay** (`TextureProjectorFrame`, see below) — a semi-transparent window + - **Projection frame overlay** (`TextureProjectorFrame`, see below) - a semi-transparent window dragged over the 3D view whose border becomes the projection's edge. Applying it stores an exact **projective** map in `view_project_matrix`, which supersedes the affine `right`/`up` axes above for that layer (`view_project_projective`). - - **"Project only on visible"** (`select_visible_faces()`) — repaints the layer with exactly the + - **"Project only on visible"** (`select_visible_faces()`) - repaints the layer with exactly the facets the camera can see, so the projected area matches the viewpoint the projector was captured - from. Two tests: a facing test (normal vs. view direction, per triangle — under perspective the + from. Two tests: a facing test (normal vs. view direction, per triangle - under perspective the view direction varies across the model, so it is taken from the eye to each centroid), then `MeshRaycaster::get_unobscured_idxs()` on the survivors to drop facets hidden behind other geometry, so a concave part's far inner wall is correctly excluded. One ray query per front-facing facet, hence click-driven (on the checkbox and on each "Capture current view"), never per frame. - It **replaces** the layer's paint rather than adding to it — "project onto what I can see" would + It **replaces** the layer's paint rather than adding to it - "project onto what I can see" would otherwise accumulate every angle the user had ever looked from. ### Manual seams and island cutting -`TextureDisplacementLayer::lscm_seam_edges` — undirected mesh-vertex-index edge pairs the unwrap is +`TextureDisplacementLayer::lscm_seam_edges` - undirected mesh-vertex-index edge pairs the unwrap is forced to cut along, on top of the dihedral-angle seams. `segment_into_charts()` takes a set of these (translated from mesh → compacted-patch numbering inside `compute_patch_unwrap()`) and refuses to union two triangles across a marked edge whatever their angle. Both the unwrap cache key and the @@ -190,15 +158,15 @@ untouched) still forces a re-solve. Like the paint masks, seams are mesh-index-s any topology change. Two ways to write to it: -- **Mark seam (manual, #9)** — a "Mark seams" click mode (`m_seam_edit_mode`) that suppresses +- **Mark seam (manual, #9)** - a "Mark seams" click mode (`m_seam_edit_mode`) that suppresses painting. A click raycasts the volume (`m_c->raycaster()->raycasters()[idx]->unproject_on_mesh()`, `idx` = the volume's slot among model-part volumes), finds the facet's edge nearest the hit point, and toggles it. Marked edges render as a red overlay (`render_seam_overlay()`), pulled toward the camera so they read on top. This is the Blender mark-seam workflow. -- **Cut island (auto, #17)** — `cut_island()` takes the selected chart's triangles (back-mapped from +- **Cut island (auto, #17)** - `cut_island()` takes the selected chart's triangles (back-mapped from the unwrap via `source_vertex`), finds their 3D bounding box, and marks every edge that straddles the mid-plane perpendicular to the longest axis. The re-unwrap then splits the chart across its - narrow waist — the "islands might be very long" case. Exposed as the UV pane's **Cut** button. + narrow waist - the "islands might be very long" case. Exposed as the UV pane's **Cut** button. ### UV-check overlays (checker / distortion) @@ -206,7 +174,7 @@ Two ways to write to it: drawn over the painted patch (`rebuild_uvcheck_mesh()`/`render_uvcheck_mesh()`, P3N3T2: `normal.x` = distortion, `tex_coord` = uv), pulled forward with a polygon offset. **Checker** (#13) samples a procedural checkerboard at the layer's uv (per-vertex for LSCM/ViewProjected, in-shader triplanar -otherwise) — squares that stay square mean low distortion. **Distortion** (#14) colours each triangle +otherwise) - squares that stay square mean low distortion. **Distortion** (#14) colours each triangle blue→green→red by `log2(uv_area / surface_area)` centred on the patch's *median* stretch (so a globally-scaled unwrap reads as uniformly ideal and only relative stretch shows), averaged to vertices. A separate **Show mesh wireframe** toggle (#8) draws the whole volume's triangle edges, @@ -216,7 +184,7 @@ rebuilt only when the vertex count changes (not per stroke). `DecodedHeightTexture::sample(uv, tile_enabled, tile_method)`. Two tile methods when enabled (Repeat, MirroredRepeat). **When `tile_enabled` is false, sampling outside `[0,1)` returns `0` -directly** — clamping the *coordinate* into range (what an earlier version did) instead smears the +directly** - clamping the *coordinate* into range (what an earlier version did) instead smears the border row/column of pixels outward to infinity in every direction, which is a real bug that was reported and fixed (visually: streaky lines radiating out from the painted patch). @@ -224,48 +192,26 @@ reported and fixed (visually: streaky lines radiating out from the painted patch Deliberately **whole-mesh and uniform**, not limited to the painted patch. A patch-only / adaptive subdivision would create a classic T-junction/cracking problem where the denser -(subdivided) and sparser (untouched) regions meet — the fine side has edge midpoints the coarse +(subdivided) and sparser (untouched) regions meet - the fine side has edge midpoints the coarse side doesn't know about, producing a real (non-manifold-looking) crack in the baked geometry. This was consciously scoped down from the original plan's "adaptive per-patch subdivider" idea to avoid that correctness risk (a subtly-cracked mesh is a much worse outcome than "not implemented yet"). Algorithm: recursive 1-to-4 triangle split via edge midpoints, with a shared per-pass midpoint cache -(keyed by sorted vertex-index pair) so triangles sharing an edge get the *same* new vertex — capped +(keyed by sorted vertex-index pair) so triangles sharing an edge get the *same* new vertex - capped at `max_iterations` (default 6) passes to bound worst-case triangle-count explosion. Wired as a "Subdivide steps" slider (**0–5**, where 0 means no subdivision and previews nothing) plus -Preview/Apply/Done in the gizmo panel. **Apply snaps the slider back to 0** — see bug #22: leaving it -at the count just committed made the panel immediately re-preview N more passes on top of a mesh 4^N -times denser, which is what the "subdivide lags" report was. A real, committed geometry change (like +Preview/Apply/Done in the gizmo panel. **Apply snaps the slider back to 0**. A real, committed geometry change (like Bake), using the same `save_painting()`/`set_mesh()`/`restore_painting()` dance `GLGizmoSimplify` uses: supported/seam/mmu/fuzzy-skin masks get remapped onto the new triangles, texture-displacement paint does not (no remap support yet) and is dropped rather than left pointing at now-meaningless triangle indices. -### Preview pipeline (perf) - -`rebuild_preview()` used to call `build_texture_displacement()` **synchronously on the UI thread** -on every stroke-end and every slider release. With multiple painted layers this got slow (each -layer's PNG sampling + vertex welding + `remap_painting()` stacks up). Fixed by moving the actual -computation into `TextureDisplacementPreviewJob` (mirrors `TextureDisplacementBakeJob`'s -process()/finalize() split), queued on the app's shared UI job worker via plain `queue_job()` (not -`replace_job()` — that worker is shared app-wide, including with Bake; `replace_job()` would cancel -an in-flight bake if one happened to be running). A `m_preview_generation` counter discards stale -results if a burst of edits queues several jobs in a row and an older one finishes after a newer one. - -Two other real perf fixes worth remembering: -- Sliders in ImGui report "changed" continuously on every drag frame, not just once on release — - gating the (then-synchronous) rebuild behind "mouse button not currently down" was necessary to - stop dozens of rebuilds per drag. -- `decode_height_texture()` used to re-decode the same PNG from scratch on every call. Now cached - in `TextureDisplacement.cpp`, keyed by a `weak_ptr` to the layer's `image_data` (not just the raw - pointer — a `weak_ptr` correctly detects a freed-then-reused address, where a raw-pointer key - would alias a stale cache entry onto an unrelated later texture). - ### Fast bump preview (GPU-only, no CPU meshing) `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}`, registered as `"texture_displacement_bump"`. Perturbs the *shading* normal from the height texture's local -gradient instead of moving geometry — active-layer-only, toggled via a "Fast preview (normal map)" +gradient instead of moving geometry - active-layer-only, toggled via a "Fast preview (normal map)" checkbox. Vertex format is `GLModel::Geometry::EVertexLayout::P3N3T2`: `normal.x` carries the per-vertex paint weight (0/1) and `tex_coord` carries a precomputed texture UV, so it can use `GLModel` normally instead of needing a hand-rolled VBO/VAO manager. Weight buffer is @@ -279,24 +225,24 @@ The perturbed normal is the analytic one for a height field `H = ±depth_mm · h N' = normalize(N − (dH/da)·T − (dH/db)·B), a = dot(p,T), b = dot(p,B) The two slopes have to be genuine **mm-per-mm** derivatives for the preview's apparent depth to -match the bake's — see bug #13. +match the bake's - see bug #13. **Two projection paths (`use_vertex_uv` uniform):** -- **Triplanar (`use_vertex_uv = 0`)** — `uv` and the `T`/`B` axes are both derived in-shader from +- **Triplanar (`use_vertex_uv = 0`)** - `uv` and the `T`/`B` axes are both derived in-shader from the dominant normal component, mirroring `project_planar()`/`apply_uv_transform()`, and the slope is formed analytically. `T`/`B` are the projection's axis-aligned pair, exact only when the face is axis-aligned; the shader drops the along-normal component to keep the gradient in the surface. Here one `uv` unit is exactly `tiling_scale` mm, so the `1/tiling_scale` gradient factor is right. -- **Precomputed UV (`use_vertex_uv = 1`, used for LSCM)** — `uv` comes per-vertex from the CPU +- **Precomputed UV (`use_vertex_uv = 1`, used for LSCM)** - `uv` comes per-vertex from the CPU (`compute_lscm_uvs(patch, layer)`, so island placement + tiling/rotation/offset are already folded in), and the perturbed normal is built with **Mikkelsen's method** ("Bump Mapping Unparametrized Surfaces on the GPU"): the surface gradient taken directly from the screen-space derivatives of the - *sampled height* and position. **This makes no uv→mm scale assumption**, which is essential — + *sampled height* and position. **This makes no uv→mm scale assumption**, which is essential - the first cut used the same global `1/tiling_scale` factor as triplanar and the depth came out visibly wrong, because an LSCM map is **conformal, not isometric**: it is globally area-scaled but the *local* mm-per-uv varies across the chart. `dFdx(h)` captures the true on-screen rate of change however the chart is stretched. **This path is also what makes the fast preview follow the UV - editor: move an island and its uv — hence its bump — moves with it** (the bump mesh rebuilds on + editor: move an island and its uv - hence its bump - moves with it** (the bump mesh rebuilds on drag-end, since `on_island_edited(finished)` → `rebuild_preview()` → `rebuild_bump_preview_mesh()`). The branch is uniform (`use_vertex_uv` is a uniform) and the paint weight gates by multiply, so the texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv → the @@ -306,49 +252,31 @@ Remaining deliberate approximation: the GPU sampler's wrap mode stands in for `tile_enabled`/`tile_method`, so with tiling *off* the GPU repeats where the CPU returns 0 outside `[0,1)`. -> **Known divergence from the CPU path (not yet reconciled).** The shader's `project_uv()` mirrors -> the *hard-axis* `project_planar()`, but the CPU's Triplanar mode is now a **blended** three-plane -> sample (see Projection methods — that change is what fixed the 90°-corner seam). The two therefore -> still agree on any face that is roughly axis-aligned (one blend weight ≈ 1 there, so the blend -> degenerates to exactly the hard-axis pick), and disagree in the cross-fade band around a sharp -> edge — precisely where the fast preview will still show the old hard seam that the true preview and -> the bake no longer have. Reconciling it means sampling all three planes in the shader and blending -> the three gradients by `pow(abs(N), TRIPLANAR_BLEND_SHARPNESS)`, the same weights -> `sample_layer_height()` uses. Also note the shader is still **active-layer-only** and knows nothing -> about `TextureBlendMode`, so a multi-layer stack cannot match the true preview by construction. - ### On-canvas "Adjust Texture" gizmo A per-active-layer toggle ("Adjust placement (drag on model)") that disables painting and shows a flat pan panel (free 2D drag on both axes) plus two arrows along the patch's own U/V axes (constrained single-axis drag). Anchored to the painted patch's centroid/average-normal (`compute_layer_paint_anchor()`). Hit-testing is screen-space distance/point-to-segment (not real -3D ray intersection against the handle geometry) — simple and good enough at this handle size. - -Known unverified detail: the **offset-drag direction/sign** is reasoning-based (increasing `offset` -shifts which texel is sampled at a fixed world position, which visually slides the pattern the -*opposite* way — so the code subtracts), not visually confirmed, since this environment can't -render pixels. May need a one-line sign flip once actually tested. The rotation-arrow-implied -direction should be reliable (it follows directly from a self-consistent 2D basis, no such -ambiguity). +3D ray intersection against the handle geometry) - simple and good enough at this handle size. ### Projection frame overlay (ViewProjected) -`src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` — a semi-transparent, resizable `wxFrame` the user +`src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` - a semi-transparent, resizable `wxFrame` the user drags **over the 3D view**, like a slide projector's gate. Whatever the model shows through it is what the texture is projected onto, and the window's border becomes the hard edge of the displacement. Press **Apply projection frame** and the gizmo reads the window's rectangle and commits it. The window is deliberately **dumb**: it owns no placement state and reports nothing continuously. Its -position and size *are* the placement, read on demand at Apply — which is also when the expensive +position and size *are* the placement, read on demand at Apply - which is also when the expensive visible-facet raycast runs. So dragging it is free and nothing recomputes until asked. Plain 2D (`wxPaintDC`), not a `wxGLCanvas`: a second GL canvas would have to share the app's one real `wxGLContext`, the cause of bugs #10 and #14 below. It only ever draws a bitmap and a border. -**The projective mapping (`apply_projection_frame()`)** is the substantive part. The frame defines a +**The projective mapping (`apply_projection_frame()`)**. The frame defines a **screen-space** rectangle, but the bake samples from a **local-space** position, so the two have to be -reconciled. `view_project_right/up` can only express an *affine* projection — exact under an +reconciled. `view_project_right/up` can only express an *affine* projection - exact under an orthographic camera, but wrong under perspective, where the near end of a part projects larger than the far end and no pair of axes reproduces that. So the layer instead stores a full projective map (`view_project_matrix`, row-major 3×4, `uv = (row0·p̃/row2·p̃, row1·p̃/row2·p̃)`), built like this: @@ -361,19 +289,19 @@ far end and no pair of axes reproduces that. So the layer instead stores a full - Window coordinates follow `igl::project`'s convention (as `CameraUtils::project` does), with y measured downward. Writing `uv = (win − rect_origin) / rect_size` makes u and v affine in `ndc = clip.xyz / clip.w`; multiplying through by `clip.w` leaves a plain linear combination of `K`'s - rows, which is exactly the 3×4 matrix — the perspective divide survives intact. + rows, which is exactly the 3×4 matrix - the perspective divide survives intact. - `w > 0` is checked rather than divided blindly. A point behind the projector has `w < 0` and divides - to a plausible-looking but **mirrored** uv — the classic way a projected decal reappears on the back + to a plausible-looking but **mirrored** uv - the classic way a projected decal reappears on the back of a model. `project_uv_projective()` returns false there and the caller treats it as no height. -The map already includes placement, so `apply_uv_transform()` is **not** applied on top of it — the +The map already includes placement, so `apply_uv_transform()` is **not** applied on top of it - the window's own position and size are the placement, and the tiling/rotation/offset sliders would shove the result off the frame the user just aligned. A "Clear" button drops back to the affine path where those controls mean something again. Apply also sets `tile_enabled = false`, so `DecodedHeightTexture::sample()` returns 0 outside `[0,1)` and the border is a hard edge rather than the first seam of an endless repeat, and repaints the layer -via `select_visible_faces(&matrix)` — the frame's uv square clips the selection, which both matches the +via `select_visible_faces(&matrix)` - the frame's uv square clips the selection, which both matches the paint to the border and keeps the ray queries proportional to the framed area instead of the model. Owned by the gizmo and **destroyed** (not just hidden) in `on_shutdown()`. Closing it only hides it, so @@ -381,10 +309,10 @@ reopening keeps it where it was left. ### UV Editor pane -`UVEditorCanvas` (`src/slic3r/GUI/UVEditorCanvas.hpp/.cpp`) — a standalone `wxGLCanvas` rendering the +`UVEditorCanvas` (`src/slic3r/GUI/UVEditorCanvas.hpp/.cpp`) - a standalone `wxGLCanvas` rendering the flattened LSCM islands (per-island wireframe + outline + fill) over the height texture (background quad tiled across the whole unwrap), with mouse pan/zoom. It is wrapped in a **`UVEditorPanel`** -(same file) that adds a button row (Frame / Snap / Average scale) and a Blender-style status line +(same file) that adds a button row (Frame / Snap / Average scale) and a status line along the bottom naming the current gesture and the shortcuts in play. The *panel* is what is registered as a `wxAuiPaneInfo` pane on `Plater`'s `m_aui_mgr`; `Plater::show_uv_editor(bool)` shows/hides it (deferred via `CallAfter`, since the gizmo calls it mid-3D-frame), and @@ -392,312 +320,102 @@ shows/hides it (deferred via `CallAfter`, since the gizmo calls it mid-3D-frame) Deliberately **shares the app's one real `wxGLContext`** (`wxGetApp().init_glcontext(*this)`, the same call `View3D`/`Preview`/`AssembleView` make) rather than creating an independent context like -`SkipPartCanvas` does elsewhere in this codebase — this is what lets it reuse the already-registered +`SkipPartCanvas` does elsewhere in this codebase - this is what lets it reuse the already-registered `"flat"`/`"flat_texture"` shaders and `GLModel` as-is, instead of needing its own shader compilation/VBO management. **Geometry is uploaded once, in the unwrap's own (raw, mm) coordinates**, one `GLModel` set per island; each island is then drawn through its own 2x3 affine (`island_transform_matrix()` composed -with the layer's tiling/rotation/offset) passed as the `flat` shader's `view_model_matrix`. This is -the fix for the ~200 ms-per-frame island-drag stall (#3): the old design pre-transformed every UV on -the CPU and re-uploaded the entire wireframe on every mouse-move event, which on a million-triangle -patch is exactly as slow as it sounds. Now a drag updates one matrix per island and touches no vertex -buffer — `on_island_edited(!finished)` calls only `set_island_transforms()`, and the full +with the layer's tiling/rotation/offset) passed as the `flat` shader's `view_model_matrix`. A +drag updates one matrix per island and touches no vertex +buffer - `on_island_edited(!finished)` calls only `set_island_transforms()`, and the full `set_islands()` rebuild happens solely when the unwrap itself changes (`unwrap_changed` in `update_uv_editor()`). **Gestures** (canvas-owned, reported to the gizmo as incremental deltas via `IslandEditFn`): left-drag -= move, right-drag or **R** = rotate (hold **Shift** to snap to 15° steps — quantised on the += move, right-drag or **R** = rotate (hold **Shift** to snap to 15° steps - quantised on the *cumulative* rotation, not each delta, so it doesn't judder, and accumulated incrementally so it survives crossing ±180°), **S** = scale (R/S modal, click/Enter to confirm, Esc to cancel), wheel = zoom about the cursor, middle-drag = pan, **Home**/**F** = frame all. Scale writes `TextureIsland::scale`; "Average scale" (`average_island_scales()`) sets every island to the mean, so one island scaled by hand can be matched back to its neighbours' texel density. **Snap** (canvas-owned `m_snap_enabled`, toggled from the toolbar) sticks a dragged island's nearest boundary vertex onto a -neighbouring island's at drag-*end* only — a magnet that re-applies mid-drag is very hard to pull out +neighbouring island's at drag-*end* only - a magnet that re-applies mid-drag is very hard to pull out of. Toolbar commands the canvas can't service itself (Average scale) are forwarded to the gizmo via `CommandFn`; view-only ones (Frame, Snap) it handles directly. -## Bugs found and fixed this session (worth remembering) - -These were all real, confirmed root causes (found by reading the actual code path, not guessed): - -1. **Cross-face projection distortion** — see "Triplanar" above. Fixed by projecting each vertex - with its own normal instead of one shared patch-average normal. -2. **Disabled-tile smearing to infinity** — clamping the UV *coordinate* into `[0,1]` instead of - returning 0 outside it, when tiling is off. Fixed in `DecodedHeightTexture::sample()`. -3. **Invisible checkbox/radio "checked" state in light mode** — `ImGuiWrapper::push_toolbar_style()` - sets `ImGuiCol_CheckMark` to white while the checkbox/radio frame background is fully transparent - (alpha 0) over a light window background — a white checkmark on an effectively-white background - is invisible by construction. This is a **pre-existing, general app-wide bug**, not specific to - this feature (every panel using `push_toolbar_style()` in light mode has it) — fixed by changing - just the light-mode branch's `CheckMark` color to the app's teal accent. -4. **Distorted (non-aspect-correct) texture thumbnails** — was forcing a square `ImGui::Image` size - regardless of the source image's actual aspect ratio. -5. **`std::array` compile error** — see Data model above (MSVC C2280, - `std::array`'s implicit special members don't inherit element-type friendship). -6. **Eigen ternary expression-template type mismatch** (MSVC C2446) — `cond ? (n / len) : - Vec3f::UnitZ()` fails because the two branches are different unevaluated Eigen expression - *types* with no common type; fixed by wrapping the non-`UnitZ()` branch in an explicit - `Vec3f(...)` to force a concrete common type. -7. **Post-bake stale preview** — `GLGizmoPainterBase::data_changed()`'s change-detection only - checks object id / volume count, neither of which changes when Bake replaces a volume's mesh - (same object, same volume count, just a new mesh/id on the volume itself) — so the gizmo kept - rendering/painting against the pre-bake `TriangleSelectorPatch` until manually deselected and - reselected. Fixed by explicitly calling `update_from_model_object()` in the bake-completion - callback. -8. **Bake job / crash-report `resources` junction going stale** — unrelated to this feature's code, - but hit during testing: `build/src/Release/resources` was a leftover **empty plain directory** - instead of the junction CMake's post-build step creates (`if not exist` skipped it because the - empty folder already "existed"), so the built exe couldn't find `resources/data/hints.ini`, - leaving `HintDatabase`'s hint list empty → `rand() % 0` divide-by-zero crash before the UI ever - opened. Fixed by deleting the empty folder and manually recreating the `mklink /J` junction. -9. **Fast bump preview showing a solid black object** — `GLModel::render()` **unconditionally** - re-sets the shader's `"uniform_color"` uniform from its own internal `Geometry::color` field - (defaulting to `ColorRGBA::BLACK()`) right before every draw call — so a manual - `shader->set_uniform("uniform_color", ...)` call made just before `.render()` gets silently - clobbered. Any `GLModel` that needs a specific flat color **must** call `.set_color(...)` on the - model itself, not set the shader uniform directly. Found by reading `GLModel::render()`'s actual - source rather than guessing at shader/lighting math. -10. **UV editor canvas rendering nothing / showing stale content on resize** — the canvas requested - a generic `wxGLAttributes().Defaults()` pixel format while sharing the app's one real - `wxGLContext` (which was originally created against `View3D`'s canvas, itself requesting a - specific RGBA/24-bit-depth/8-bit-stencil format). `wxGLCanvas::SetCurrent()` on WGL/GLX - generally requires the target window's pixel format to be compatible with the one the context - was created against; a mismatch can make `SetCurrent()` silently fail, leaving the canvas - showing whatever was last in its backbuffer (looks exactly like "blank" or "stale image on - resize"). Fixed by requesting the same explicit attribute list - `OpenGLManager::create_wxglcanvas()` uses for the main view canvases — but see bug #14: the - first attempt at this copied only *part* of that list and the symptom therefore survived. -11. **`` / `` include-order conflict** — `wx/glcanvas.h` pulls in the - platform's real `GL/gl.h`; if that happens before `` is processed in the same - translation unit, glad's own header errors out (`OpenGL (gl.h) header already included`). - Fixed by including `` first in `UVEditorCanvas.hpp`, before `` — any - file that includes this header (including `Plater.cpp`, transitively) needs glad to win that - race. -12. **Fast preview hidden behind the paint-highlight overlay** — `render_painter_gizmo()` always - drew the selection-highlight overlay on top with a depth-bias trick (`glPolygonOffset`) that - only makes sense for the *true*-displacement preview: real geometry moves in the painted area, - so the depth-biased overlay only wins the depth test in the *unpainted* (coincident) region. - The bump preview never moves geometry — its depth is identical to the overlay's *everywhere* — - so the overlay was winning the depth test across the whole surface and hiding the bump shading - entirely. Fixed by skipping the overlay draw entirely when the bump-preview path is active. -13. **Fast preview's apparent depth not matching the true preview's** — the bump shader built its - perturbed normal as `normalize(N + depth_mm * vec3(hL-hR, hD-hU, 0))`. Two things wrong with - that. (a) `hL-hR` is a height difference across *one texel step*, i.e. `dh/du` already scaled by - `2·texel`, and `du` is in uv units, not mm — the real surface slope needs the full chain rule - back through `uv = R(rotation) · planar_mm / tiling_scale`, i.e. a further `1/tiling_scale` and - a rotation of the gradient by `−rotation`. The missing `1/(2·texel·tiling_scale)` factor is - ~26× at a 1024px texture and a 20mm tile size, all in the flattening direction — which is - exactly what "fast preview has a different height from the real preview" looks like. (b) the - gradient was added to model-space `xy`, but the two axes the planar projection actually runs - along are `yz`/`xz`/`xy` depending on the dominant normal component, so on any face not - dominated by `z` the perturbation was applied to the wrong axes. Fixed by computing the real - mm-per-mm slope and rotating it into the projection's own `T`/`B` axes (see "Fast bump preview" - above for the derivation). -14. **UV editor pane still blank after bug #10** — three separate causes, all of them live at once: - - The bug-#10 fix copied `OpenGLManager::create_wxglcanvas()`'s attribute list but **dropped its - multisampling attributes** (`WX_GL_SAMPLE_BUFFERS`/`WX_GL_SAMPLES`, 4 samples by default), on - the reasoning that a flat 2D wireframe view doesn't need AA. But a differing sample count *is* - a differing pixel format, so this left exactly the `wglMakeCurrent()` mismatch bug #10 set out - to fix. It now mirrors the full list, AA included, reading `OpenGLManager::can_multisample()` - (already resolved by then — `View3D` is constructed first). - - `set_mesh()`/`set_background_texture()` each called `render()` **inline**, and both are reached - from `update_uv_editor()` → `rebuild_preview()` → the gizmo's ImGui panel — i.e. from the - middle of the *3D* canvas's GL frame, and (since `show_uv_editor(true)` is the last line of - `update_uv_editor()`) while this pane was still **hidden**. `wxGLCanvas::SetCurrent()` returns - false outright on a canvas that isn't shown on screen, and the old code ignored the return - value — so every GL call in `render()`, `glViewport`/`glClear` included, silently landed on the - 3D canvas instead. These now only mark dirty + `Refresh()`; `render()` bails unless - `IsShownOnScreen()` *and* `SetCurrent()` succeeds; and `Plater::show_uv_editor()` defers its - AUI relayout via `CallAfter` so the pane's first size/paint can't be delivered mid-frame either. - - Even once drawing, nothing would have been *visible*: the background quad spanned `[-1,1]²` - while LSCM UVs land around `[0,1]²`, and the view was centered on the origin at a half-extent - of 0.6. The quad is now the unit square in the same UV space the wireframe uses (which is also - where `sample()` maps the texture, regardless of its pixel aspect), the projection's Y is - negated so `v` runs down-screen (putting the texture's first pixel row at the top rather than - upside down), and the view auto-fits to the unwrap ∪ unit square the first time a patch shows up. -15. **A second texture layer was silently never applied** — the reported "multiple textures don't - work reliably". Root cause was the old sequential bake: layer N's paint mask was stored against - the volume's original mesh, so before layer N+1 could be deserialized the mask had to be carried - onto the mesh layer N had *just displaced*, via `TriangleSelector::remap_painting()`. Remapping a - mask onto geometry that has moved out from under it routinely returned an empty bitstream, and - the code then did `if (data.bitstream.empty()) continue;` — i.e. dropped the layer **without any - diagnostic**. Fixed structurally rather than patched: every layer is now evaluated against the - base mesh and merged per vertex (see Bake algorithm), so no remap happens at all. Covered by a - regression test. -16. **Hard-axis triplanar seam at exactly two of a box's four vertical corners** — see "Triplanar" - under Projection methods. Worth recording the *diagnostic* here, because the asymmetry is what - pinned it down: the user reported the seam at the (X+,Y−) and (X−,Y+) corners with the other two - clean. That is precisely what a dominant-axis switch predicts (`u = y` on an X face, `u = x` on a - Y face; those agree where `x == y` and differ by the corner width where `x == −y`) and it ruled - out every "the texture is wrong" hypothesis, since the texture itself is fine — the *mapping* is - discontinuous. Fixed by blending the three axis projections instead of picking one. -17. **Use-after-free when removing a texture layer** (latent, pre-existing — found while touching the - panel, not caused by it). The layer list's "Remove" button called `remove_texture_layer()` *in - the middle of rendering that layer's row*. That erases the layer from - `mv->texture_displacement_layers`, shifting every later element down — after which the loop - happily carried on dereferencing `layer` for the rest of the row's widgets (depth/tiling sliders, - `PopID`) and kept iterating `ordered`, a vector of pointers into the storage that had just moved. - Never crashed loudly because `vector::erase` doesn't reallocate, so the reads landed on a *valid* - but *wrong* (shifted) layer. Fixed by recording the slot and doing the removal after the loop. -18. **Every LSCM island collapsed to a point** (the UV editor was empty; the Tile-size slider did - nothing; an LSCM bake came out as a flat "single-face extrude"). One line in - `compute_patch_unwrap()`: `chart.indices = std::move(chart_mesh.indices);` ran *before* - `area_3d(chart_mesh)` was taken. `area_3d()` iterates those indices, so on the moved-from - (emptied) mesh it returned `0`, giving `scale = sqrt(0 / uv_area) = 0` — **every chart's UVs - multiplied by zero**. That one zero explained all three symptoms at once (no island extent to - draw; a zero-size unwrap is still zero after any tiling divide; the bake sampled ~one constant - texel per chart). Fixed by measuring the 3D area before the move. Found only by instrumenting the - actual island bbox into the panel — three rounds of reasoning from screenshots had each guessed - wrong, because a collapsed-to-a-point unwrap and an off-screen-framed one look identical. - -19. **Isotropic remeshing produced scrambled geometry with dropped triangles** — the "remeshing kinda - does not work properly" report, and a genuine one-line root cause. `isotropic_remeshing()` edits - the `Surface_mesh` **in place**, and its edge collapses only *mark* vertices and faces as removed; - the underlying arrays keep the holes until `collect_garbage()` is called. That matters because the - shared `cgal_to_indexed_triangle_set()` numbers its output vertices by **iteration order** (which - skips removed slots) while reading each face's corner as the **raw integer value of the vertex - descriptor** (which does not). The two agree only up to the first collapse; past that every - triangle indexes the wrong vertices, and any descriptor beyond the live vertex count hits the - converter's `iv >= vsize` guard and is silently dropped *together with its triangle*. Fixed by - compacting (`cgal_mesh.collect_garbage()`) before converting. Note the pre-existing callers were - unaffected: the boolean ops build their result into a fresh mesh, so only the in-place remesher - ever handed the converter a mesh with garbage in it. -20. **Remeshing rounded off every sharp edge** — the second half of the same report. CGAL's - `isotropic_remeshing` relaxes vertices tangentially along the surface, which erodes hard features - unless they are constrained: a cube came back with wobbly, eroded edges. Fixed by detecting sharp - edges (`PMP::detect_sharp_edges()` at a user-settable dihedral angle, default 40°) plus every open - border, constraining them, and passing `protect_constraints(true)`. That option additionally - requires each constrained edge to already be shorter than 4/3 · target, hence the - `PMP::split_long_edges()` pre-pass (given the same map, so the halves inherit the constraint) — - this mirrors CGAL's own isotropic_remeshing example. Also added a guard for the case where - `Surface_mesh::add_face()` refused faces on a non-manifold input: remeshing a mesh that silently - lost faces yields a **punctured** model, so it now bails out and reports instead. -21. **Remesh falsely reporting "did not change the model"** — the GUI decided success by comparing - *vertex counts*. A remesh that redistributes triangles at roughly the current density legitimately - lands on the same count, so a perfectly good result was discarded with an error. Now compared - structurally against the input (which is what `remesh_isotropic()` hands back on failure). -22. **Subdivide re-previewing at the same count after Apply** — Apply committed N passes and then - immediately rebuilt the *preview* at N passes again, now on top of a mesh up to 4^N times denser. - That is the single most expensive thing the panel can do, and it ran on every Apply. The slider - now starts at 0 (a real "no subdivision" value that previews nothing), and Apply snaps back to it. - ## Known limitations / deferred work - **No `.3mf` serialization** for texture-displacement paint data or texture assets. A background agent attempted this in an earlier session, hit its own usage limit mid-edit, and left `bbs_3mf.cpp` with an undefined forward-declared function; that partial edit was reverted rather than shipped broken. Practical impact: **baked** geometry round-trips fine (it's just an ordinary - part of the mesh via the existing mesh serialization path) — what does *not* survive a project + part of the mesh via the existing mesh serialization path) - what does *not* survive a project save/reload is any *unbaked* paint stroke and texture layer definition. - **No remap-across-topology-change** for texture-displacement paint (`ModelObject::split()`, mesh boolean ops, Simplify, and now `subdivide_mesh_uniform()` all drop it via `reset_extra_facets()`). The other four paint channels (supported/seam/mmu/fuzzy) do get remapped in these cases. -- **Cylindrical/Spherical axis/center are auto-picked heuristically**, not user-controllable — no +- **Cylindrical/Spherical axis/center are auto-picked heuristically**, not user-controllable - no UI to override the auto-detected wrap axis if it picks the "wrong" one for an odd shape. -- **Fast preview covers the active layer only**, while the true preview stacks every painted layer — - so with more than one layer painted the two will legitimately not agree, independently of bug #13. -- **Bump preview and true preview both only refresh at stroke-end**, not continuously during an - active drag (a deliberate scope cut for simplicity/consistency — the original plan's "instant - update mid-stroke" idea for the bump shader specifically was not carried through). -- **On-canvas Adjust-Texture gizmo's offset-drag direction is unverified** (see above). -- **The bump shader still uses hard-axis, single-layer projection** while the CPU path is now - blended-triplanar and blend-mode aware — see the callout under "Fast bump preview". +- **Fast preview covers the active layer only** - **Displacement resolution is capped by the mesh's own vertex density.** Baking only ever *moves* existing vertices (it never inserts any), so a coarse patch cannot show fine texture detail no - matter how high-resolution the height map is — that is what the "Subdivide model" button is for. + matter how high-resolution the height map is - that is what the "Subdivide model" button is for. Since the rewrite the bake is topology-preserving, so this is now a hard, explicit property rather than something partly papered over by the old per-layer re-meshing. -- **Placeholder toolbar icon** — reuses `toolbar_fuzzy_skin_paint.svg`, noted as a TODO in code. -- **Textures are matched to the picker by absolute path** (`TextureDisplacementLayer::path`), so the - picker's "which entry is selected" highlight goes blank if a project is moved between machines. - Harmless — the layer keeps its own embedded `image_data` and still bakes correctly. - -### Requested UV-editing features — status - -A user working through the feature end-to-end asked for a batch of UV-editing features. All of the -functional ones are now implemented (see the sections above): checker (#13), distortion heatmap -(#14), mesh wireframe overlay (#8), cut island (#17), project-from-view (#6), and Blender-style mark -seam (#9), plus per-island fills, the fast preview honouring the LSCM unwrap and island moves (#1), -the UV pane toolbar + status line (#18), Shift-snap rotation, midlevel/bidirectional displacement -(#19), and island scale/average/padding/snap (#15/#16/#2). - -Remaining cosmetic / known gaps: -- The UV pane toolbar has **text buttons, not icons** — no existing SVG reads cleanly as "average - island scale" / "snap islands", so real icons are deferred rather than mis-assigned. -- **Mark-seam edge picking** snaps to the nearest edge of the *hit facet* only; it does not - highlight the candidate edge on hover before you click (a hover-preview would be a nice refinement). -- **Cut island** always halves along the longest 3D axis; there is no UI to pick the cut line. ## File map **libslic3r (core, no GUI dependency):** -- `src/libslic3r/TextureDisplacement.hpp/.cpp` — data model, bake algorithm, projection methods, +- `src/libslic3r/TextureDisplacement.hpp/.cpp` - data model, bake algorithm, projection methods, tiling, subdivision. See doc comments throughout, they're kept accurate and up to date. -- `src/libslic3r/MeshBoolean.hpp/.cpp` — added `parameterize_lscm()` and `remesh_isotropic()` - (sharp-feature-preserving isotropic remeshing, see bugs #19–#21) in the `cgal` sub-namespace, +- `src/libslic3r/MeshBoolean.hpp/.cpp` - added `parameterize_lscm()` and `remesh_isotropic()` + in the `cgal` sub-namespace, reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for mesh boolean ops. New CGAL includes: `Polygon_mesh_processing/border.h`, `Polygon_mesh_processing/connected_components.h`, `Surface_mesh_parameterization/{Error_code, - LSCM_parameterizer_3, parameterize}.h`. No new dependency — CGAL 5.6.3 is already vendored and + LSCM_parameterizer_3, parameterize}.h`. No new dependency - CGAL 5.6.3 is already vendored and the `Surface_mesh_parameterization` package headers were already present, just unused before now. -- `src/libslic3r/Model.hpp/.cpp` — the 8 named `FacetsAnnotation` fields + accessor, +- `src/libslic3r/Model.hpp/.cpp` - the 8 named `FacetsAnnotation` fields + accessor, `texture_displacement_layers`, and all the mirrored touch points (see Data model above). **GUI:** -- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` — the gizmo. Panel controls: dock/ +- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` - the gizmo. Panel controls: dock/ undock toggle, brush/face/connected-area selection mode + "select whole model" button, per-layer texture picker + depth/tiling/rotation/invert/tile-mode/projection-mode/blend-mode controls, "Adjust placement" toggle (on-canvas gizmo), "Fast preview (normal map)" toggle, "Subdivide model" button, Add layer/Erase all/Bake. -- `src/slic3r/GUI/TextureLibrary.hpp/.cpp` — scans the shipped + user texture folders, imports an +- `src/slic3r/GUI/TextureLibrary.hpp/.cpp` - scans the shipped + user texture folders, imports an arbitrary image into the user folder (converting it to the 8-bit grayscale PNG libslic3r decodes), and loads a library file's bytes for a layer. The image→grayscale-PNG conversion lives here, on the GUI side, because libslic3r has no image toolkit; both the import path and the "pick a shipped texture" path go through the same one function. -- `resources/textures/displacement/*.png` — the 10 shipped height maps (Bricks, Grid, Hexagons, +- `resources/textures/displacement/*.png` - the 10 shipped height maps (Bricks, Grid, Hexagons, Knurl, Noise, Quilt, Studs, Waves, Weave, Wood Grain). All 512×512 8-bit grayscale and **seamless** (each is periodic over the full image in both axes, so tiling shows no seam). Generated procedurally; the whole `resources/` tree is installed recursively by CMake, so a new folder under it ships with no build-system change. -- `src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp/.cpp` — background bake commit. -- `src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp/.cpp` — background preview compute +- `src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp/.cpp` - background bake commit. +- `src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp/.cpp` - background preview compute (mirrors the bake job's shape but commits nothing to the Model). -- `src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` — the semi-transparent projection-frame overlay for - ViewProjected layers (plain 2D `wxPaintDC`, no GL context — see its section above). -- `src/slic3r/GUI/UVEditorCanvas.hpp/.cpp` — the 2D UV unwrap viewer widget. -- `src/slic3r/GUI/Plater.hpp/.cpp` — `uv_editor_canvas` member, AUI pane registration, +- `src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` - the semi-transparent projection-frame overlay for + ViewProjected layers (plain 2D `wxPaintDC`, no GL context - see its section above). +- `src/slic3r/GUI/UVEditorCanvas.hpp/.cpp` - the 2D UV unwrap viewer widget. +- `src/slic3r/GUI/Plater.hpp/.cpp` - `uv_editor_canvas` member, AUI pane registration, `get_uv_editor_canvas()`/`show_uv_editor()`. -- `src/slic3r/GUI/GLShadersManager.cpp` — registers `"texture_displacement_bump"`. -- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` — the bump-preview shader. -- `src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp` — `PainterGizmoType::TEXTURE_DISPLACEMENT`. -- `src/slic3r/GUI/Gizmos/GLGizmosManager.hpp/.cpp` — `EType::TextureDisplacement` registration. -- `src/slic3r/GUI/ImGuiWrapper.cpp` — the light-mode checkmark-color fix (bug #3 above; a - pre-existing, general bug, not scoped to this feature). +- `src/slic3r/GUI/GLShadersManager.cpp` - registers `"texture_displacement_bump"`. +- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` - the bump-preview shader. +- `src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp` - `PainterGizmoType::TEXTURE_DISPLACEMENT`. +- `src/slic3r/GUI/Gizmos/GLGizmosManager.hpp/.cpp` - `EType::TextureDisplacement` registration. +- `src/slic3r/GUI/ImGuiWrapper.cpp` - the light-mode checkmark-color fix -**CMake:** all new source files added to `src/libslic3r/CMakeLists.txt`, -`src/slic3r/CMakeLists.txt` (in roughly-alphabetical position matching each list's existing -convention), and `tests/libslic3r/CMakeLists.txt` for the unit test file. - -**Tests:** `tests/libslic3r/test_texture_displacement.cpp` — **run and passing** (7 cases, 116 +**Tests:** `tests/libslic3r/test_texture_displacement.cpp` - **run and passing** (7 cases, 116 assertions). Covers `decode_height_texture` round-trip, empty-layer no-op, full-cube uniform -displacement, boundary-vertex pinning on a hand-built fan mesh, and — added with the bake rewrite — -a regression test that a **second layer over the same area actually contributes** (the bug that -rewrite fixed), a table-driven check of all four blend modes, and that the lowest layer ignores its +displacement, boundary-vertex pinning on a hand-built fan mesh, and - added with the bake rewrite - +a regression test that a **second layer over the same area actually contributes**, +a table-driven check of all four blend modes, and that the lowest layer ignores its blend mode. `BUILD_TESTS` is `OFF` in the checked-in build cache; flip it on to run them: cmake -S . -B build -DBUILD_TESTS=ON cmake --build build --config Release --target libslic3r_tests -- -m - ./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand - -## Build notes - -- Everything here lives in `libslic3r`/`libslic3r_gui`/`libslic3r_cgal` — no new external - dependency, no `deps/` rebuild needed. CGAL's parameterization package was already vendored. -- To build just this feature's code path fastest: `cmake --build build --config Release --target - libslic3r_gui -- -m` (pulls in `libslic3r` and `libslic3r_cgal` as needed). The full app target - is `OrcaSlicer_app_gui` (produces `build/src/Release/orca-slicer.exe`) — only needed to actually - run and visually test, not to verify compilation. -- `BUILD_TESTS` is `OFF` in the existing build cache; flip it on to actually run - `test_texture_displacement.cpp`. + ./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand \ No newline at end of file diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 5f762c4f32..3844e709f8 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -62,7 +62,7 @@ uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived ta // A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of // the island currently being dragged in the UV editor (active > 0.5). Identity when nothing is // dragged, so this whole path is a no-op then. Lets a UV island drag move the bump on the model with -// only a uniform update -- no mesh rebuild -- exactly the way Adjust placement moves the whole texture. +// only a uniform update uniform vec4 island_delta_lin; uniform vec2 island_delta_tr; @@ -76,7 +76,7 @@ in vec2 vertex_uv; out vec4 out_color; // The two model-space axes the triplanar planar coordinate is read off, per dominant normal -// component -- same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y +// component - same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y // along b. void projection_axes(vec3 n, out vec3 t, out vec3 b) { @@ -113,10 +113,10 @@ void main() triangle_normal = -triangle_normal; if (use_vertex_uv) { - // Precomputed-uv (LSCM) path -- Mikkelsen's surface-gradient bump ("Bump Mapping + // Precomputed-uv (LSCM) path - Mikkelsen's surface-gradient bump ("Bump Mapping // Unparametrized Surfaces on the GPU"). The perturbed normal is derived straight from the // screen-space derivatives of the *sampled height* and the position, so it is scale-exact - // with no uv->mm assumption at all -- which is the whole point here: an LSCM map is conformal, + // with no uv->mm assumption at all - which is the whole point here: an LSCM map is conformal, // not isometric, so the local mm-per-uv varies across the chart and the earlier "one global // 1/tiling factor" got the depth visibly wrong. dFdx(h) captures the true on-screen rate of // change however the chart is stretched or however fine the tiling is. diff --git a/resources/shaders/140/texture_displacement_uvcheck.fs b/resources/shaders/140/texture_displacement_uvcheck.fs index 323a49a4ba..8b430db52d 100644 --- a/resources/shaders/140/texture_displacement_uvcheck.fs +++ b/resources/shaders/140/texture_displacement_uvcheck.fs @@ -2,11 +2,11 @@ // UV-check overlay for the texture-displacement gizmo, drawn over the painted patch so the LSCM // unwrap can be sanity-checked on the real 3D surface (mode set by the `mode` uniform): -// mode 0 -- Checker: a procedural checkerboard sampled at the layer's uv. Even squares that stay +// mode 0 - Checker: a procedural checkerboard sampled at the layer's uv. Even squares that stay // square everywhere on the model mean the unwrap is low-distortion; squares that smear or // shear reveal exactly where it stretches. Same uv the bake samples, so what you see is // where the texture actually lands. -// mode 1 -- Distortion heatmap: the per-vertex area-distortion carried in `distortion`, blue +// mode 1 - Distortion heatmap: the per-vertex area-distortion carried in `distortion`, blue // (compressed) -> green (ideal) -> red (stretched). // Both are lit with the same cheap two-light diffuse the bump preview uses, so the surface still // reads as 3D. diff --git a/resources/shaders/140/texture_displacement_uvcheck.vs b/resources/shaders/140/texture_displacement_uvcheck.vs index 86a7dd13e9..79e858e0d7 100644 --- a/resources/shaders/140/texture_displacement_uvcheck.vs +++ b/resources/shaders/140/texture_displacement_uvcheck.vs @@ -2,9 +2,9 @@ // Vertex stage for the UV-check overlay (checker / distortion heatmap) drawn over the painted patch // by GLGizmoTextureDisplacement. Reuses GLModel's P3N3T2 layout so it needs no bespoke buffer: -// v_normal.x -- per-vertex UV distortion (uv-area / surface-area ratio, remapped so 0.5 = ideal); +// v_normal.x - per-vertex UV distortion (uv-area / surface-area ratio, remapped so 0.5 = ideal); // only the distortion mode reads it. -// v_tex_coord -- precomputed texture uv, valid only when use_vertex_uv is set (LSCM); the checker +// v_tex_coord - precomputed texture uv, valid only when use_vertex_uv is set (LSCM); the checker // mode reconstructs uv in the fragment shader otherwise. uniform mat4 view_model_matrix; diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 5de11eafa6..0a6325cd92 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -38,7 +38,7 @@ float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTi if (!tile_enabled && (uv.x() < 0.f || uv.x() >= 1.f || uv.y() < 0.f || uv.y() >= 1.f)) // Outside the single, non-repeating placement entirely: no texture there, not "smeared - // edge pixel" -- clamping the *coordinate* to [0, 1] would otherwise keep returning the + // edge pixel" - clamping the *coordinate* to [0, 1] would otherwise keep returning the // border row/column's height forever in every direction, stretching it out to infinity. return 0.f; @@ -136,7 +136,7 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer if (layer.empty()) return result; - // The raw (unsmoothed) decode is what gets cached, keyed by the image_data allocation -- decoding + // The raw (unsmoothed) decode is what gets cached, keyed by the image_data allocation - decoding // a PNG is the expensive part and never changes for a given image. Smoothing is applied afterwards // to a throwaway copy, so moving the smoothing slider never invalidates the decode cache. const void *key = layer.image_data.get(); @@ -192,7 +192,7 @@ Vec2f project_planar(const Vec3f &position, const Vec3f &normal) // tri-planar/cube projection; a patch spanning several differently-oriented faces gets each // face projected along its own best-fit axis instead of all faces sharing one axis picked // from a single averaged normal (which looks correct on one face but visibly distorts on any - // other face in the same patch -- exactly the bug an earlier version of this feature had). + // other face in the same patch - exactly the bug an earlier version of this feature had). const Vec3f n = normal.cwiseAbs(); if (n.x() >= n.y() && n.x() >= n.z()) return Vec2f(position.y(), position.z()); @@ -203,7 +203,7 @@ Vec2f project_planar(const Vec3f &position, const Vec3f &normal) namespace { // Wrapped around patch_axis, centered at patch_center. u is the arc length (mm) around the axis at -// this point's own radius, v is the signed distance along the axis -- a reasonable approximation +// this point's own radius, v is the signed distance along the axis - a reasonable approximation // for roughly cylindrical selections, not an exact fit for arbitrary geometry. Vec2f project_cylindrical(const Vec3f &position, const Vec3f &patch_center, const Vec3f &patch_axis) { @@ -225,7 +225,7 @@ Vec2f project_cylindrical(const Vec3f &position, const Vec3f &patch_center, cons // Longitude/latitude around patch_center. u/v are scaled by this point's own distance from the // center so the result is in roughly the same mm-ish units tiling_scale expects, rather than bare -// radians -- again an approximation, not an exact geodesic parametrization. +// radians - again an approximation, not an exact geodesic parametrization. Vec2f project_spherical(const Vec3f &position, const Vec3f &patch_center) { const Vec3f rel = position - patch_center; @@ -239,7 +239,7 @@ Vec2f project_spherical(const Vec3f &position, const Vec3f &patch_center) return Vec2f(longitude, latitude) * radius; } -// CGAL's LSCM parameterizer expects a clean mesh with no isolated (unreferenced) vertices -- but +// CGAL's LSCM parameterizer expects a clean mesh with no isolated (unreferenced) vertices - but // `patch` here (from TriangleSelector::get_facets_strict()) carries the *entire* mesh's vertex // array, only its `indices` filtered to the painted triangles. Build a compacted copy referencing // only the vertices `patch.indices` actually uses, plus a map back to the original vertex index so @@ -328,8 +328,8 @@ std::vector segment_into_charts(const indexed_triangle_set &mesh, const std } } - // The chart-join test compares a *neighbourhood-averaged* normal per face -- the face plus its - // edge-adjacent neighbours (~5 samples) -- rather than the single face normal. On a finely + // The chart-join test compares a *neighbourhood-averaged* normal per face - the face plus its + // edge-adjacent neighbours (~5 samples) - rather than the single face normal. On a finely // tessellated curved surface this stops one noisy triangle from spuriously cutting (or a lone // near-flat sliver from wrongly merging) a chart, while a genuine sharp crease, where the whole // neighbourhood on each side agrees, still cuts. This is the "use 5 points, not one" refinement. @@ -352,7 +352,7 @@ std::vector segment_into_charts(const indexed_triangle_set &mesh, const std for (const auto &[key, fp] : edge_faces) { if (fp.second < 0) continue; // a boundary edge of the patch, nothing on the far side to join - // A manually/auto marked seam always cuts, whatever the dihedral angle -- that is exactly + // A manually/auto marked seam always cuts, whatever the dihedral angle - that is exactly // what lets "mark seam" / "cut island" split a chart that is otherwise flat enough to merge. if (!seam_keys.empty() && seam_keys.count(key)) continue; @@ -373,7 +373,7 @@ std::vector segment_into_charts(const indexed_triangle_set &mesh, const std } // Projects a chart onto an orthonormal basis of its own average normal. This is *isometric* for a -// flat chart -- lengths and angles come out exactly right -- which is why a flat chart never needs a +// flat chart - lengths and angles come out exactly right - which is why a flat chart never needs a // solve at all, and why this also serves as the fallback for a chart LSCM cannot handle. std::vector project_to_tangent_plane(const indexed_triangle_set &chart, const Vec3f &normal) { @@ -407,7 +407,7 @@ float area_2d(const std::vector &uvs, const std::vector> &seam_edges) @@ -532,7 +532,7 @@ PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_a const Vec3f chart_normal = (normal_sum.norm() > 1e-12f) ? Vec3f(normal_sum.normalized()) : Vec3f::UnitZ(); // Is the chart flat? Charts are grown by a *pairwise* angle threshold, so a chart can still - // curve gradually across many triangles -- being merged is not the same as being planar. But + // curve gradually across many triangles - being merged is not the same as being planar. But // when it is planar (a cube face, and after seam-cutting that is the common case), the // tangent-plane projection is already the exact answer, and skipping the solve is the single // biggest speed-up here. @@ -541,14 +541,14 @@ PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_a if (chart_of[f] == c) planar = normals[f].dot(chart_normal) >= 0.9998f; // ~1 degree - // Measured before chart_mesh.indices is moved out from under it, below -- area_3d() iterates + // Measured before chart_mesh.indices is moved out from under it, below - area_3d() iterates // those indices, so taking it afterwards silently measures an empty mesh and returns 0. const float mesh_area_3d = area_3d(chart_mesh); std::optional> uvs; if (!planar) uvs = MeshBoolean::cgal::parameterize_lscm(chart_mesh); - // Flat chart, or one LSCM refused (not a topological disk -- closed, or holed). + // Flat chart, or one LSCM refused (not a topological disk - closed, or holed). chart.uvs = uvs ? std::move(*uvs) : project_to_tangent_plane(chart_mesh, chart_normal); chart.indices = std::move(chart_mesh.indices); @@ -871,7 +871,7 @@ std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const Tex return {}; // Manual per-vertex UV edits (UV editor Vertex/Edge modes) override the automatic raw unwrap - // coordinate for a mesh vertex, before the island transform -- so the edit rides along with any + // coordinate for a mesh vertex, before the island transform - so the edit rides along with any // island move/rotate exactly like the rest of the island. See TextureDisplacementLayer:: // lscm_uv_overrides. Small (hand edits), so a plain map is ample. std::map overrides; @@ -880,7 +880,7 @@ std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const Tex // One UV per patch vertex: a seam vertex has several (one per chart it touches) and has to // settle on one, since it can only be displaced to a single position. See compute_lscm_uvs()'s - // header comment -- the surface stays watertight regardless. + // header comment - the surface stays watertight regardless. std::vector per_vertex(patch.vertices.size(), Vec2f::Zero()); std::vector assigned(patch.vertices.size(), false); for (size_t i = 0; i < unwrap.uvs.size(); ++i) { @@ -918,12 +918,12 @@ float blend_displacement(float accumulated, float value, TextureBlendMode mode) // (see TextureBlendMode): a 1 mm-deep layer sampling a white texel is then exactly neutral. case TextureBlendMode::Multiply: return accumulated * value; case TextureBlendMode::Divide: { - // Every height map has black regions, and a black texel samples to *exactly* zero -- so this + // Every height map has black regions, and a black texel samples to *exactly* zero - so this // divisor really does hit zero in ordinary use, not just in some contrived edge case. Floor // its magnitude: an unbounded 1/0 would not merely look wrong, it would fling vertices // thousands of mm away and poison the mesh's bounding box (and with it every plate/print // volume check downstream). The floor doubles as a cap on how far Divide can ever amplify - // the relief beneath it -- at most 1/0.05 = 20x. + // the relief beneath it - at most 1/0.05 = 20x. constexpr float min_divisor = 0.05f; const float divisor = (std::abs(value) < min_divisor) ? std::copysign(min_divisor, value < 0.f ? -1.f : 1.f) : value; @@ -983,7 +983,7 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp // Flat projection onto the captured projector plane. Single-valued per point, so unlike // blended triplanar it is one sample, and it is what "project from view" places. return sample_at(Vec2f(position.dot(layer.view_project_right), position.dot(layer.view_project_up))); - case TextureProjectionMethod::LSCM: // no usable unwrap for this patch -- fall back to Triplanar + case TextureProjectionMethod::LSCM: // no usable unwrap for this patch - fall back to Triplanar case TextureProjectionMethod::Triplanar: default: break; } @@ -1048,7 +1048,7 @@ bool compute_layer_paint_anchor(const indexed_triangle_set &b } // Area-weighted vertex normals of the undisplaced mesh. build_texture_displacement() computes -// these once, up front, and every layer both projects and displaces along them -- so a vertex +// these once, up front, and every layer both projects and displaces along them - so a vertex // covered by several layers is pushed along one single, well-defined direction rather than along // whatever direction the surface happened to be pointing partway through the stack. static std::vector texture_displacement_vertex_normals(const indexed_triangle_set &its) @@ -1122,7 +1122,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set indexed_triangle_set mesh = base_mesh; // TriangleSelector's vertex array starts with the mesh's own vertices (any extra ones, created // where a brush stroke split a triangle, are appended after them), and get_facets_strict() - // emits exactly the *referenced* ones, in order. So selector vertex index i is our vertex i -- + // emits exactly the *referenced* ones, in order. So selector vertex index i is our vertex i - // but only if every vertex of `mesh` is referenced by some triangle, which is precisely what // this call establishes. It is a no-op (indices untouched) for any mesh that already is, which // in practice is all of them; it exists so an input carrying stray unreferenced vertices can't @@ -1140,7 +1140,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set std::sort(ordered_layers.begin(), ordered_layers.end(), [](const TextureDisplacementLayer *a, const TextureDisplacementLayer *b) { return a->slot < b->slot; }); - // Every layer measures its displacement against the *original* surface -- normals included -- + // Every layer measures its displacement against the *original* surface - normals included - // rather than against whatever the previous layer left behind. That is what lets all the layers // be evaluated independently and merged per vertex, instead of having to re-mesh and remap the // paint masks between them (see the header for why that earlier design was dropped). @@ -1167,7 +1167,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (patch.indices.empty()) continue; // get_facets_strict() returns the same vertex array whichever state is asked for (only the - // triangles are filtered), so `patch` and `rest` share one indexing -- and, per the + // triangles are filtered), so `patch` and `rest` share one indexing - and, per the // compactify above, it is our own. const indexed_triangle_set rest = selector.get_facets_strict(EnforcerBlockerType::NONE); @@ -1208,7 +1208,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set else if (an.y() <= an.x() && an.y() <= an.z()) patch_axis = Vec3f::UnitY(); - // A real unwrap of the whole patch, computed once here rather than per vertex -- it is a + // A real unwrap of the whole patch, computed once here rather than per vertex - it is a // per-chart solve over the whole patch, not a per-point formula. Cached, so repeating this // for every slider tweak costs a hash rather than a re-solve (see compute_patch_unwrap()). const std::vector lscm_uvs = (layer->projection_method == TextureProjectionMethod::LSCM) ? @@ -1255,14 +1255,14 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set patch_centroid, patch_axis, lscm_uv); // midlevel is the height that means "stay put", so anything below it displaces - // *inwards* -- see TextureDisplacementLayer::midlevel. At the default of 0 this is + // *inwards* - see TextureDisplacementLayer::midlevel. At the default of 0 this is // exactly the old outward-only behaviour. const float edge_w = edge_weight.empty() ? 1.f : edge_weight[size_t(vi)]; const float signed_height = (h - layer->midlevel) * layer->depth_mm * sign * edge_w; displacement[size_t(vi)] = blend_displacement(displacement[size_t(vi)], signed_height, displaced[size_t(vi)] ? layer->blend_mode : TextureBlendMode::Add); // The first layer to reach a vertex has nothing underneath it to blend with, so it - // always starts the total off additively -- a Multiply/Divide against an implicit + // always starts the total off additively - a Multiply/Divide against an implicit // zero base would otherwise annihilate (or blow up) it, which is never what the // user means by putting a mask on the bottom of the stack. displaced[size_t(vi)] = true; diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index dc78be701c..d3698eaec1 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -51,13 +51,13 @@ enum class TextureProjectionMethod : int Triplanar = 0, // Wrapped around an axis running through the patch's centroid. The axis itself is picked // automatically as the world axis *least* aligned with the patch's average normal (since a - // cylinder's own axis is perpendicular to its outward radial normal) -- a reasonable default + // cylinder's own axis is perpendicular to its outward radial normal) - a reasonable default // for roughly cylindrical selections, not a precise fit for arbitrary geometry. Cylindrical = 1, - // Wrapped around the patch's centroid using longitude/latitude -- reasonable for roughly + // Wrapped around the patch's centroid using longitude/latitude - reasonable for roughly // spherical/rounded selections, again an approximation rather than an exact geodesic map. Spherical = 2, - // Real UV unwrap of the painted patch -- a proper low-distortion flattening rather than a + // Real UV unwrap of the painted patch - a proper low-distortion flattening rather than a // planar/cylindrical/spherical approximation. The patch is first cut into charts along its // sharp edges and each chart is flattened on its own (see compute_patch_unwrap()), so a patch // that is not a single developable surface still unwraps sensibly. Falls back to Triplanar for @@ -65,20 +65,20 @@ enum class TextureProjectionMethod : int LSCM = 3, // Flat projection along a fixed direction captured from the 3D camera ("project from view"): the // texture is laid onto the painted area as seen from that angle, like a decal projector. Single - // planar map (no per-face axis switch), so it can smear on faces turned away from the projector -- + // planar map (no per-face axis switch), so it can smear on faces turned away from the projector - // that is inherent to view projection and is the user's call, not a bug. The projector's two // in-plane axes live in TextureDisplacementLayer::view_project_right/up. ViewProjected = 4, }; // Dihedral angle (degrees) above which an edge between two painted triangles becomes a chart seam -// -- i.e. the unwrap is cut there rather than being forced to flatten across it. +// - i.e. the unwrap is cut there rather than being forced to flatten across it. // // The whole point of this being a threshold rather than "flatten everything as one piece": three // faces meeting at a cube corner are not developable, so a single-chart solve has to distort them // badly to lie flat (they splay out into a fan, which is what "it merges all the edges into a // triangle" describes). Cutting at the 90-degree edges instead lets each face flatten exactly. -// Meanwhile a smoothly curved surface -- a subdivided sphere, say -- has only small angles between +// Meanwhile a smoothly curved surface - a subdivided sphere, say - has only small angles between // neighbouring triangles, stays a single chart, and unwraps as one piece the way it should. static constexpr float LSCM_DEFAULT_SEAM_ANGLE_DEG = 30.f; @@ -95,8 +95,8 @@ static constexpr float TRIPLANAR_BLEND_SHARPNESS = 4.f; // // Add/Subtract are in mm and need no further explanation. Multiply/Divide are *scaling* operations // and therefore need a unit convention: they treat the layer's own value as a unitless factor -// relative to 1 mm. That makes `depth_mm` act as a gain -- a layer with depth 1 mm and a white -// (1.0) texel multiplies the accumulated relief by exactly 1, i.e. leaves it unchanged -- which is +// relative to 1 mm. That makes `depth_mm` act as a gain - a layer with depth 1 mm and a white +// (1.0) texel multiplies the accumulated relief by exactly 1, i.e. leaves it unchanged - which is // the behaviour that makes a Multiply layer usable as a mask over the layers beneath it. enum class TextureBlendMode : int { @@ -112,20 +112,20 @@ float blend_displacement(float accumulated, float value, TextureBlendMode mode); // Where one unwrap island (chart) sits in UV space, on top of wherever compute_patch_unwrap() first // packed it. This is what the UV editor's drag/rotate gestures write to, so a user can lay the -// islands out by hand -- move them, rotate them, overlap them -- rather than being stuck with the +// islands out by hand - move them, rotate them, overlap them - rather than being stuck with the // automatic packing. // // Indexed by chart id, which compute_patch_unwrap() assigns in first-encountered-triangle order. That // is stable for a given patch and seam angle, but *not* across a change to either: repainting the // patch, or moving the seam-angle slider, can renumber the charts and so leave a hand-placed island -// applied to a different one. Accepted deliberately -- the alternative is a persistent chart identity +// applied to a different one. Accepted deliberately - the alternative is a persistent chart identity // that survives arbitrary re-segmentation, which is a much larger problem than this feature warrants. struct TextureIsland { Vec2f offset = Vec2f::Zero(); // in the unwrap's own mm space float rotation_deg = 0.f; // about the island's own centroid // About the island's own centroid too. 1 = the size compute_patch_unwrap() gave it, which is - // already its true surface area in mm -- so scaling an island away from 1 deliberately makes its + // already its true surface area in mm - so scaling an island away from 1 deliberately makes its // texel density differ from its neighbours'. See average_island_scales(). float scale = 1.f; @@ -168,7 +168,7 @@ struct TextureDisplacementLayer // The height value that means "don't move this vertex". The sampled height (0..1) has this // subtracted before being scaled by depth_mm, so with the default of 0 the surface only ever // moves *outwards* (the classic height-map convention), while 0.5 makes mid-grey neutral and - // lets darker texels cut *into* the surface -- an engraved-and-embossed result from one map. + // lets darker texels cut *into* the surface - an engraved-and-embossed result from one map. // // Cutting inward is not free: vertices move along their own normals, which converge inside a // concave corner and inside a thin wall, so a large depth_mm against a small feature really can @@ -197,7 +197,7 @@ struct TextureDisplacementLayer bool auto_connect_islands = true; // When false, the texture is sampled once (clamped to its edge pixels outside [0, 1)) instead - // of being repeated -- useful for a single decal-like placement rather than a repeating tile. + // of being repeated - useful for a single decal-like placement rather than a repeating tile. bool tile_enabled = true; TextureTileMethod tile_method = TextureTileMethod::Repeat; @@ -222,19 +222,19 @@ struct TextureDisplacementLayer // Also ViewProjected, and takes precedence over the two axes above when set: an exact *projective* // map from a local-space position straight to a texture uv, written by the projection-frame overlay - // (the semi-transparent window dragged over the 3D view -- its border becomes the uv unit square). + // (the semi-transparent window dragged over the 3D view - its border becomes the uv unit square). // // Row-major 3x4, applied to the homogeneous point p~ = (x, y, z, 1): // uv = ( row0.p~ / row2.p~ , row1.p~ / row2.p~ ) // The perspective divide is the whole point. view_project_right/up can only express an *affine* - // projection, which matches an orthographic camera exactly but not a perspective one -- under + // projection, which matches an orthographic camera exactly but not a perspective one - under // perspective the near end of a part projects larger than the far end, and no pair of axes // reproduces that. Folding the camera's full projection*view*model product into one matrix does. // Because a point behind the projector has row2.p~ <= 0 and no meaningful uv, sampling must check // the sign rather than divide blindly; see project_uv_projective(). // // Note this map already includes placement, so the usual tiling/rotation/offset transform is NOT - // applied on top of it -- the window's own position and size are the placement. + // applied on top of it - the window's own position and size are the placement. bool view_project_projective = false; std::array view_project_matrix{}; // Only used by TextureProjectionMethod::LSCM: hand placement of the unwrap's islands, indexed by @@ -243,7 +243,7 @@ struct TextureDisplacementLayer std::vector islands; // Only used by TextureProjectionMethod::LSCM: persistent "join" groups, indexed by chart id. Charts - // that share a group id move together as one in the UV editor -- this is what the explicit "Join" + // that share a group id move together as one in the UV editor - this is what the explicit "Join" // command records (over and above placing the child next to its parent). An entry of -1, or an index // past the end of the vector, means the chart is its own singleton group (moves alone). Empty means // every chart is a singleton. Same chart-renumbering caveat as `islands`: a re-unwrap can reshuffle @@ -252,7 +252,7 @@ struct TextureDisplacementLayer // Only used by TextureProjectionMethod::LSCM: manual per-vertex UV edits made in the UV editor's // Vertex/Edge select modes. Each pair is (mesh vertex index, its overriding raw-unwrap coordinate in - // mm) -- the *raw* unwrap position, i.e. before the island transform, so the edited vertex still + // mm) - the *raw* unwrap position, i.e. before the island transform, so the edited vertex still // moves and rotates with its island. In compute_lscm_uvs() this replaces the automatic unwrap // coordinate for that vertex; in the editor it edits the displayed geometry directly. Keyed in mesh- // vertex space like lscm_seam_edges (dropped on a topology change). The raw coordinate is only @@ -304,7 +304,7 @@ struct DecodedHeightTexture bool empty() const { return width <= 0 || height <= 0 || pixels.empty(); } // Bilinearly sampled height in [0, 1] at a normalized uv coordinate. When tile_enabled is false, - // a uv outside [0, 1) samples as 0 -- the texture simply is not there, rather than its border + // a uv outside [0, 1) samples as 0 - the texture simply is not there, rather than its border // row/column being smeared outward forever (which is what clamping the coordinate would do, and // was a real reported bug). Callers rely on this to get a hard edge: it is how the projection // frame's border becomes the edge of the displacement. @@ -317,13 +317,13 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer // Raw dominant-axis planar projection of `position` (in mm, not yet scaled/rotated/offset by any // layer), dropping the axis position that best aligns with `normal`. Exposed on its own (rather -// than only inline inside project_texture_displacement_uv()) so GUI code -- the on-canvas -// "adjust texture placement" gizmo -- can map a dragged 3D point into the exact same 2D space +// than only inline inside project_texture_displacement_uv()) so GUI code - the on-canvas +// "adjust texture placement" gizmo - can map a dragged 3D point into the exact same 2D space // tiling_scale/rotation_deg/offset operate in, without duplicating the axis-selection logic. Vec2f project_planar(const Vec3f &position, const Vec3f &normal); // Applies a layer's tiling_scale/rotation_deg/offset to an already-projected planar coordinate -// (in mm, dominant-axis planar, cylindrical, spherical, or CGAL LSCM output -- any of them, all +// (in mm, dominant-axis planar, cylindrical, spherical, or CGAL LSCM output - any of them, all // share this same final step). Exposed separately so build_texture_displacement() can route CGAL // LSCM's per-patch UV solve through the same scale/rotate/offset controls as every other // projection method, without going through project_texture_displacement_uv()'s own dispatch @@ -331,7 +331,7 @@ Vec2f project_planar(const Vec3f &position, const Vec3f &normal); Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer); // Applies a row-major 3x4 projective matrix (see TextureDisplacementLayer::view_project_matrix) to a -// local-space point, writing the resulting texture uv. Returns false -- and leaves `uv` untouched -- +// local-space point, writing the resulting texture uv. Returns false - and leaves `uv` untouched - // when the point lies behind the projector or on its plane (w <= 0), where there is no meaningful uv // and dividing would produce a mirrored or infinite coordinate. Callers treat that as "no height". bool project_uv_projective(const std::array &m, const Vec3f &position, Vec2f &uv); @@ -340,11 +340,11 @@ bool project_uv_projective(const std::array &m, const Vec3f &position // method, tiling scale, rotation, offset and tiling mode. Returns a height in [0, 1]. // // This returns a *height* rather than a UV because TextureProjectionMethod::Triplanar is a blend -// of three separate axis projections and therefore takes three texture samples per vertex -- there +// of three separate axis projections and therefore takes three texture samples per vertex - there // is no single UV that represents it. The other methods do map to one UV internally. // - `normal` is this specific vertex's own normal; used only by Triplanar (for its blend weights). -// - `patch_center`/`patch_axis` describe the painted patch as a whole (its centroid, and -- for -// Cylindrical only -- the wrap axis); used only by the Cylindrical/Spherical methods. +// - `patch_center`/`patch_axis` describe the painted patch as a whole (its centroid, and - for +// Cylindrical only - the wrap axis); used only by the Cylindrical/Spherical methods. // - `lscm_uv`, when non-null, is this vertex's precomputed LSCM coordinate and takes precedence // over `layer.projection_method` (LSCM is a single per-patch solve, not a per-vertex formula, // so build_texture_displacement() computes it once up front and passes it in here). @@ -356,7 +356,7 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp const Vec2f *lscm_uv = nullptr); // Area-weighted centroid and average normal of a layer's currently painted patch, in mesh-local -// coordinates -- the same measurements build_texture_displacement() uses to pick its dominant +// coordinates - the same measurements build_texture_displacement() uses to pick its dominant // projection axis. Used by the GUI to anchor the on-canvas "adjust texture placement" gizmo to // wherever the layer is actually painted. Returns false (leaving the outputs untouched) if the // layer has nothing painted yet. @@ -365,7 +365,7 @@ bool compute_layer_paint_anchor(const indexed_triangle_set &b Vec3f &anchor_pos, Vec3f &anchor_normal); -// Extracts the currently painted patch from a volume's base mesh + stored facet data -- the same +// Extracts the currently painted patch from a volume's base mesh + stored facet data - the same // extraction build_texture_displacement() and compute_layer_paint_anchor() each do internally via // TriangleSelector::get_facets_strict(ENFORCER). Returns an empty mesh if nothing is painted. // Exposed so GUI code (the LSCM "UV editor" preview pane) can get the same patch build_texture_ @@ -388,7 +388,7 @@ struct PatchUnwrap std::vector source_vertex; // unwrapped vertex -> index into patch.vertices std::vector vertex_chart; // unwrapped vertex -> chart (island) id std::vector indices; // patch triangles, re-indexed into `uvs` - // Per chart, the centroid of its uvs -- the point a TextureIsland's rotation turns about. + // Per chart, the centroid of its uvs - the point a TextureIsland's rotation turns about. std::vector chart_centroid; // Edges belonging to exactly one triangle: the outline of each island. Indices into `uvs`. This // is what the UV editor draws highlighted, so the boundaries the seam angle cut are visible. @@ -424,13 +424,13 @@ bool join_chart_placement(const PatchUnwrap &unwrap, const std::vector> &seam_edges = {}); -// One UV per patch vertex, for displacement. Displacement is inherently per-vertex -- a vertex has -// exactly one position, so it can only be pushed out by one height -- which means a seam vertex has +// One UV per patch vertex, for displacement. Displacement is inherently per-vertex - a vertex has +// exactly one position, so it can only be pushed out by one height - which means a seam vertex has // to settle on a single one of its charts' UVs (the first, arbitrarily). That is not a compromise // in the result: the surface stays watertight either way, since neighbouring vertices each move // along their own normals and nothing depends on the UVs agreeing across the seam. It is only the @@ -457,7 +457,7 @@ using TextureDisplacementFacetsData = std::array data, unsigned int // codebase has historically considered unreliable on some graphics cards. // // Each level is a 2x2 box filter of the level above it. Note this used to re-upload the - // *level-0* buffer at every level instead, which does not downscale anything -- it just + // *level-0* buffer at every level instead, which does not downscale anything - it just // reinterprets the image's first lod_w * lod_h texels as the whole smaller level, i.e. every // level below 0 held a crop of the top-left corner. It went unnoticed for as long as every // caller drew these textures at roughly their native size (where only level 0 is ever @@ -660,8 +660,8 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont& found = false; src -= 3; - for (int h = m_height; h > 0; --h) { - for (int w = m_width; w > 0; --w) { + for (int h = m_height; h > 0; -h) { + for (int w = m_width; w > 0; -w) { if ((*src) != background.Red() && !found) { found = true; if (h < font.GetPointSize()) @@ -742,7 +742,7 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h) { auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; }; - auto upper_power_of_two = [](int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }; + auto upper_power_of_two = [](int v) { v-; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }; int new_w = std::max(w, h); if (!is_power_of_two(new_w)) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 4772c4e74d..d1bce40b6b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -637,10 +637,10 @@ void GLGizmoTextureDisplacement::render_seam_overlay() glsafe(::glEnable(GL_POLYGON_OFFSET_LINE)); glsafe(::glPolygonOffset(-2.0f, -2.0f)); // pull further forward than the wireframe so seams read on top // A seam edge is geometrically the same line as a wireframe edge, so a mere polygon offset is a - // fragile way to make the red seam beat the white wireframe -- drivers apply GL_POLYGON_OFFSET_LINE + // fragile way to make the red seam beat the white wireframe - drivers apply GL_POLYGON_OFFSET_LINE // inconsistently, and the two lines then z-fight and the wireframe wins. When the wireframe is on, // or while actively marking, just draw the seams with depth testing off so they are unconditionally - // on top -- being visible is the one thing this overlay has to guarantee. + // on top - being visible is the one thing this overlay has to guarantee. const bool seams_on_top = m_wireframe_overlay || m_seam_edit_mode; if (seams_on_top) glsafe(::glDisable(GL_DEPTH_TEST)); @@ -812,7 +812,7 @@ void GLGizmoTextureDisplacement::compute_bump_active_vertices(const std::vector< m_bump_active_vertex.assign(mv->mesh().its.vertices.size(), 0); // Flag the base vertices of every chart being moved. For a group/multi move that is more than one // chart, but since such a move is a pure translation the shader applies the same delta to them all - // (see on_island_edited) -- exactly the "joined islands move together" behaviour. + // (see on_island_edited) - exactly the "joined islands move together" behaviour. for (size_t i = 0; i < u.uvs.size(); ++i) { if (i >= u.vertex_chart.size() || std::find(charts.begin(), charts.end(), u.vertex_chart[i]) == charts.end()) @@ -893,7 +893,7 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() // Reuses the layer-list panel's already-decoded, already-uploaded GPU thumbnail (smoothing-aware), // whose grayscale value lives in the R channel exactly as the shader samples it. Its width/height - // are read straight off the texture -- decoding the PNG here every frame would re-run the smoothing + // are read straight off the texture - decoding the PNG here every frame would re-run the smoothing // blur on every camera move, which is what tanked the frame rate at high smoothing. GLTexture *tex = get_layer_thumbnail(*layer); if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0) @@ -1211,7 +1211,7 @@ void GLGizmoTextureDisplacement::update_uv_editor() // A vertex/edge edit committing (or an undo reverting one) changes the per-vertex UV overrides // without going through the Unwrap button. Detect that and force a re-solve, so the pane's geometry - // stays in step with what will bake -- the one exception to "only re-solve on Unwrap". + // stays in step with what will bake - the one exception to "only re-solve on Unwrap". { size_t sig = 1469598103934665603ull; // FNV-1a seed const auto mix = [&sig](uint64_t x) { sig = (sig ^ x) * 1099511628211ull; }; @@ -1236,7 +1236,7 @@ void GLGizmoTextureDisplacement::update_uv_editor() state.seam_edges = layer->lscm_seam_edges; // The re-solve happens only when the user pressed "Unwrap" (m_uv_unwrap_pending). Every other call - // into here -- a paint stroke ending, a slider release, the check mode changing -- must not pay for + // into here - a paint stroke ending, a slider release, the check mode changing - must not pay for // a fresh LSCM solve; it just re-applies the cheap affine transforms over whatever unwrap already // exists. If the paint changed underneath but the user hasn't asked to re-unwrap, the pane keeps // showing the last unwrap on purpose (that is the whole point of making it an explicit action). @@ -1255,7 +1255,7 @@ void GLGizmoTextureDisplacement::update_uv_editor() m_uv_editor_unwrap = compute_patch_unwrap(patch, layer->lscm_seam_angle_deg, 0.f, layer->lscm_seam_edges); // Re-apply any stored per-vertex UV edits onto the fresh unwrap, so the pane shows exactly what // compute_lscm_uvs() will bake (which applies the same overrides). Keyed by mesh vertex, so every - // unwrapped copy of that vertex gets it -- matching the bake's single-UV-per-vertex settle. + // unwrapped copy of that vertex gets it - matching the bake's single-UV-per-vertex settle. if (!layer->lscm_uv_overrides.empty()) { std::map ov; for (const auto &[mv2, uv] : layer->lscm_uv_overrides) @@ -1316,7 +1316,7 @@ void GLGizmoTextureDisplacement::update_uv_editor() // Connected-net layout (on by default): a *fresh* unwrap is unfolded so adjacent charts sit // edge-to-edge (cube -> a net), rather than as separately packed squares. Only when the user pressed - // Unwrap (m_uv_apply_connected_net) -- a re-segmentation renumbers charts anyway, so any hand + // Unwrap (m_uv_apply_connected_net) - a re-segmentation renumbers charts anyway, so any hand // placement from before is already meaningless. A *refresh* re-solve (a committed vertex edit, or an // undo) must NOT relayout, or it would throw away every island placement on every vertex edit. if (unwrap_changed && m_uv_apply_connected_net && layer->auto_connect_islands) { @@ -1459,7 +1459,7 @@ void GLGizmoTextureDisplacement::on_uv_command(int cmd) return; } // Join to whichever neighbouring island (one it shares an edge with) is currently placed - // nearest -- i.e. the one it was dragged up against. + // nearest - i.e. the one it was dragged up against. const Eigen::Matrix sel_m = island_transform_matrix(chart, m_uv_editor_unwrap, layer->islands); const Vec2f sel_c = sel_m.block<2, 2>(0, 0) * m_uv_editor_unwrap.chart_centroid[size_t(chart)] + sel_m.col(2); int best_parent = -1; @@ -1770,7 +1770,7 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse // just the primary for a rotate/scale. m_island_move_set = is_move ? build_island_move_set(*layer, island) : std::vector{ island }; // Set up the GPU drag: flag the moved islands' vertices and bake the mesh once (via the dirty - // flag). From then on the drag is a uniform update, no rebuild -- see render_bump_preview_mesh(). + // flag). From then on the drag is a uniform update, no rebuild - see render_bump_preview_mesh(). m_bump_active_chart = island; compute_bump_active_vertices(m_island_move_set); m_bump_island_delta = Eigen::Matrix::Identity(); @@ -1791,7 +1791,7 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse if (c == island) { target.rotation_deg += rotation_delta; // Guarded: a scale that reaches zero is unrecoverable (every subsequent factor multiplies it) - // and would collapse the island to a point -- exactly the failure this feature hit once already. + // and would collapse the island to a point - exactly the failure this feature hit once already. target.scale = std::clamp(target.scale * scale_factor, 0.001f, 1000.f); } } @@ -1811,7 +1811,7 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse // interactive on a patch with a million triangles. uv_canvas->set_island_transforms(xf); } - // Move the island on the model live through the shader's island_delta uniform -- no mesh + // Move the island on the model live through the shader's island_delta uniform - no mesh // rebuild. delta = F_current * F_baked^-1 in final-uv space (the bump mesh bakes F_baked; the // shader applies delta to the flagged island's uv). The one rebuild that bakes the flags is // scheduled at drag start above and consumed once per frame by render_painter_gizmo(). @@ -1893,7 +1893,7 @@ Vec3f GLGizmoTextureDisplacement::adjust_handle_center(const TextureDisplacement { // apply_uv_transform() maps a planar mm coordinate p to uv = R(p / tiling_scale) + offset, and // on_mouse_adjust_texture() drives offset by offset = offset_start - R(delta / tiling_scale). - // Inverting that, the handle's displacement from the anchor is - R^-1(offset) * tiling_scale -- + // Inverting that, the handle's displacement from the anchor is - R^-1(offset) * tiling_scale - // which, substituted into the drag equation, moves the handle by exactly `delta`. So the handle // follows the cursor precisely, and is back on the anchor exactly when offset is zero. const float rad = layer.rotation_deg * float(M_PI) / 180.f; @@ -2887,7 +2887,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // them fit a narrow window, which it does by lowering the same toolbar_icon_scale() read here. const float icon_btn_sz = GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale() * m_parent.get_scale(); // The icon SVGs carry their own border, so the ImGui button's own frame border and idle fill are - // suppressed here (FrameBorderSize 0 + transparent ImGuiCol_Button) to avoid a doubled border -- the + // suppressed here (FrameBorderSize 0 + transparent ImGuiCol_Button) to avoid a doubled border - the // hover/active fill is left in place for feedback. Applied only around these gizmo icon buttons. const auto push_borderless_icon_style = []() { ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); @@ -2950,7 +2950,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // Selection mode: which of TriangleSelector's existing click/brush mechanisms drives painting. // "Face" and "Connected area" reuse the exact same underlying selection machinery every other - // paint gizmo already has (single-facet click, and angle-limited flood fill respectively) -- + // paint gizmo already has (single-facet click, and angle-limited flood fill respectively) - // just exposed here as an alternative to brushing, one triangle/region at a time. m_imgui->text(_L("Selection mode")); const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER; @@ -3059,7 +3059,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // Add-layer affordance as an icon beside the heading. It is deliberately *not* right-aligned // against the window edge: this panel uses ImGuiWindowFlags_AlwaysAutoResize, and positioning // an item at GetWindowContentRegionMax().x - w feeds the window's own width back into its - // auto-fit, growing it by one item-spacing every frame -- which, with the panel docked and + // auto-fit, growing it by one item-spacing every frame - which, with the panel docked and // anchored by its right edge, walked it left off-screen on hover. A plain SameLine can't do that. const unsigned int add_icon = tool_icon_id(); const float sz = m_imgui->scaled(1.3f); @@ -3298,8 +3298,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (is_active) { // Explicit unwrap (#: "Add unwrap button so it does not recompute on every - // change"). The LSCM solve runs only when this is pressed -- painting, the seam - // angle slider and seam marking no longer trigger it -- and the pane opens right + // change"). The LSCM solve runs only when this is pressed - painting, the seam + // angle slider and seam marking no longer trigger it - and the pane opens right // afterwards. Re-press it to fold in any edits made since. if (m_imgui->button(_u8L("Unwrap"))) { m_uv_unwrap_pending = true; @@ -3309,7 +3309,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Flatten the painted area into UV islands and open the UV editor. The " - "unwrap is computed only when you press this, not on every edit -- so " + "unwrap is computed only when you press this, not on every edit - so " "paint, change the seam angle or mark seams first, then press Unwrap to " "see the result. Islands can then be moved, rotated and scaled."), m_imgui->scaled(20.f)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index f3b98d89fd..bbcd355ebe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -22,7 +22,7 @@ class TextureProjectorFrame; // libslic3r/TextureDisplacement.hpp) to painted areas of a model, and can bake the result into // real mesh geometry. See the project plan for the overall architecture; in short: // - each layer owns its own independent paint mask (ModelVolume::texture_displacement_facets), -// reusing the same TriangleSelector/FacetsAnnotation machinery as every other paint gizmo -- +// reusing the same TriangleSelector/FacetsAnnotation machinery as every other paint gizmo - // only one layer is "active" (paintable) at a time, selected in the panel below; // - "Bake" runs build_texture_displacement() in a background job and commits the result exactly // like the Emboss/SVG "project on surface" gizmo does. @@ -69,7 +69,7 @@ private: void set_active_layer(int slot); // flushes the previous layer's edits, then reloads selectors void bake(); - // Marks every facet of every model-part volume as painted for the currently active layer -- + // Marks every facet of every model-part volume as painted for the currently active layer - // "whole model" as an alternative to brushing/clicking every triangle by hand. void select_whole_model(); @@ -124,7 +124,7 @@ private: // A texture from the picker's library (see slic3r/GUI/TextureLibrary.hpp), read and uploaded // once and then kept for the gizmo's lifetime. The decoded bytes are held alongside the GPU - // thumbnail so that picking the texture can hand the layer this very same image_data buffer -- + // thumbnail so that picking the texture can hand the layer this very same image_data buffer - // which both avoids re-reading the file and lets decode_height_texture()'s own cache (keyed by // exactly this pointer) hit immediately on the first bake/preview. struct LibraryTexture @@ -149,13 +149,13 @@ private: // "Adjust Texture" mode: instead of painting, dragging an on-canvas handle changes the active // layer's offset. The handle is a flat panel lying in the paint patch's own tangent plane - // (a "pan" -- drag anywhere on it for free 2D movement), plus two arrows along the patch's + // (a "pan" - drag anywhere on it for free 2D movement), plus two arrows along the patch's // own U/V axes that constrain the drag to just that one axis for precise nudging. Anchored to // the centroid/average-normal of the active layer's current paint patch (see // libslic3r::compute_layer_paint_anchor()), so nothing is drawn if it has nothing painted yet. // // NOTE: the drag direction/sign below is this session's best-effort reasoning about which way - // the texture should appear to move as the handle is dragged -- it could not be visually + // the texture should appear to move as the handle is dragged - it could not be visually // confirmed while writing it (no way to render/see pixels in this environment), so it may // need a one-line sign flip once actually tested. bool update_adjust_anchor(); // recomputes m_adjust_anchor_pos/normal; false if nothing painted @@ -170,13 +170,13 @@ private: void adjust_tangent_basis(Vec3f &u_axis, Vec3f &v_axis) const; // The plane a drag is measured against: the paint patch's anchor, lifted clear of the surface. - // Deliberately *fixed* -- independent of the layer's offset -- so that moving the handle cannot + // Deliberately *fixed* - independent of the layer's offset - so that moving the handle cannot // move the plane the handle's own motion is derived from, which would be a feedback loop. Vec3f adjust_plane_point() const; // Where the handle is actually drawn, in mesh-local coordinates. This is NOT just the patch's // centroid: the handle *represents the texture's placement*, so it has to travel as `offset` - // changes. Pinning it to the centroid is why dragging it looked broken -- the texture slid but + // changes. Pinning it to the centroid is why dragging it looked broken - the texture slid but // the handle stayed put. Undoing apply_uv_transform()'s scale and rotation turns the layer's // offset back into a displacement in mm within the patch's tangent plane, which is what gets // added to the anchor here. That is exactly consistent with the drag arithmetic in @@ -190,7 +190,7 @@ private: // Recomputes m_preview_glmodel from the volume's current (unbaked) paint state, using the same // build_texture_displacement() algorithm as Bake. Called whenever the paint mask changes - // (stroke end, layer switch, undo/redo reload, post-bake refresh) rather than every frame -- + // (stroke end, layer switch, undo/redo reload, post-bake refresh) rather than every frame - // this is real mesh work (PNG sampling, vertex welding), not something to redo per paint stroke // drag sample or idle repaint. With several painted layers this can be slow, so the actual // computation runs in a background TextureDisplacementPreviewJob; this function only queues @@ -201,9 +201,9 @@ private: // Alternate, GPU-only preview: perturbs shading normals from the active layer's height texture // (a classic bump map) instead of actually moving vertices, using the // resources/shaders/*/texture_displacement_bump.* shader. Faster than the true-displacement - // preview (no CPU meshing at all -- just a per-vertex paint-weight buffer built at the same + // preview (no CPU meshing at all - just a per-vertex paint-weight buffer built at the same // cadence as rebuild_preview()) but only shows the *active* layer, and any bump is a shading - // illusion, not real geometry -- "Bake" always produces the true, exact result either way. + // illusion, not real geometry - "Bake" always produces the true, exact result either way. void rebuild_bump_preview_mesh(); void render_bump_preview_mesh(); @@ -216,7 +216,7 @@ private: // Applies one island edit reported by the UV editor's drag/rotate gestures to the active layer. // Deltas are incremental (see UVEditorCanvas::IslandEditFn); `finished` ends the gesture, which - // is when -- and only when -- the 3D preview is rebuilt, since doing that per mouse-move would + // is when - and only when - the 3D preview is rebuilt, since doing that per mouse-move would // queue a mesh recompute for every pixel of a drag. void on_island_edited(int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished); // Applies a committed vertex/edge edit from the UV editor's Vertex/Edge modes: each entry is an @@ -227,7 +227,7 @@ private: // UV-editor sub-element select mode, mirrored into the canvas: 0 = Island, 1 = Vertex, 2 = Edge. int m_uv_select_mode = 0; // One affine per island (columns: x basis, y basis, translation), mapping the unwrap's raw mm - // coordinates to texture UVs -- the same type as UVEditorCanvas::IslandTransform, spelled out + // coordinates to texture UVs - the same type as UVEditorCanvas::IslandTransform, spelled out // here so this header needn't drag in wxGLCanvas/glad. Cheap to recompute (it is per *island*, // not per vertex), which is what lets an island drag update the pane without re-uploading a // single vertex. @@ -245,7 +245,7 @@ private: void capture_view_projection(TextureDisplacementLayer &layer); // Manual seam marking (#9): a mode where clicking the model toggles the nearest mesh edge in the - // active layer's lscm_seam_edges, so the unwrap can be cut exactly where the user wants -- the + // active layer's lscm_seam_edges, so the unwrap can be cut exactly where the user wants - the // Blender "mark seam" workflow. Painting is suppressed while it is on. bool m_seam_edit_mode = false; GLModel m_seam_glmodel; // the current seam edges, highlighted on the mesh @@ -255,7 +255,7 @@ private: void render_seam_overlay(); // The mesh edge nearest the mouse, in the volume's own vertex indices, or {-1,-1} if the ray misses. // Factored out of toggle_seam_at() so the same pick can drive a live hover highlight (below) that - // shows which edge a click would toggle -- the "I don't know how it works" feedback the user hit. + // shows which edge a click would toggle - the "I don't know how it works" feedback the user hit. std::pair seam_edge_at(const Vec2d &mouse_pos) const; std::pair m_seam_hover_edge{ -1, -1 }; // The vertex a click would pick in shortest-path mode, so the target is visible on hover the same @@ -280,7 +280,7 @@ private: // Which of the up to TEXTURE_DISPLACEMENT_MAX_LAYERS paint masks the brush currently writes // into. Always a valid slot index (0 by default) so the base class's per-volume selector - // machinery always has something to work with, even before any texture has been added -- + // machinery always has something to work with, even before any texture has been added - // painting into a slot with no texture assigned is harmless, it just has no visible/bake // effect until a texture is added to that slot. int m_active_layer_slot = 0; @@ -323,13 +323,13 @@ private: // back to the standard paint-mask overlay like every other painting gizmo. GLModel m_preview_glmodel; // Set while a layer parameter slider has changed since the last rebuild_preview() call but the - // mouse button driving the drag hasn't been released yet -- see on_render_input_window(). + // mouse button driving the drag hasn't been released yet - see on_render_input_window(). bool m_preview_params_dirty = false; // See rebuild_bump_preview_mesh()/render_bump_preview_mesh(). bool m_use_bump_preview = false; // Set from the UV editor's per-move island edits instead of rebuilding the (potentially large) bump - // mesh synchronously inside that mouse handler -- doing the rebuild there stalled both the UV pane + // mesh synchronously inside that mouse handler - doing the rebuild there stalled both the UV pane // and the 3D view. The rebuild is instead coalesced to once per 3D frame (render_painter_gizmo). bool m_bump_preview_dirty = false; GLModel m_bump_preview_glmodel; @@ -339,7 +339,7 @@ private: // GPU island drag: while an island is dragged in the UV editor, the bump mesh is baked once (with // the dragged island's vertices flagged, v_normal.y = 1) and then moved purely through the shader's - // island_delta uniform -- one uniform update per mouse move, no rebuild -- so it tracks the cursor + // island_delta uniform - one uniform update per mouse move, no rebuild - so it tracks the cursor // as smoothly as Adjust placement. m_bump_active_chart is the dragged island (or -1); // m_bump_active_vertex flags its base vertices; m_bump_baked_active_xf is that island's placement // baked into the current mesh, against which the live delta is measured; m_bump_island_delta is the @@ -363,7 +363,7 @@ private: static int island_group_of(const std::vector &groups, int c); // Merges chart `b`'s join group into chart `a`'s (materialising `groups` to `chart_count` first). static void join_island_groups(std::vector &groups, int a, int b, int chart_count); - // Final per-vertex texture uv for the projections the shader can't reconstruct itself -- LSCM (an + // Final per-vertex texture uv for the projections the shader can't reconstruct itself - LSCM (an // unwrap) and ViewProjected (a projector plane the shader doesn't know). One entry per patch/base // vertex, already through apply_uv_transform(). Empty for Triplanar/Cylindrical/Spherical, which // the shader projects on its own. Shared by the bump preview and the UV-check overlay. @@ -381,7 +381,7 @@ private: void render_uvcheck_mesh(); // The UV editor pane is opened only on the user's explicit request (this toggle in the panel), - // never automatically just because a patch exists -- auto-popping it whenever there was "a + // never automatically just because a patch exists - auto-popping it whenever there was "a // selection to process" is exactly what the user asked to stop. update_uv_editor() keeps the pane // hidden unless this is set. Reset on gizmo shutdown so reopening the gizmo doesn't reopen the pane. bool m_show_uv_editor = false; @@ -395,7 +395,7 @@ private: // leave island placements untouched. bool m_uv_apply_connected_net = false; // Signature of the per-vertex UV overrides last reflected in the pane. When it changes without the - // user pressing Unwrap -- a vertex/edge edit committing, or an undo/redo reverting one -- the pane + // user pressing Unwrap - a vertex/edge edit committing, or an undo/redo reverting one - the pane // is re-solved so its geometry follows, even though a plain edit otherwise never re-solves (#Feat2). size_t m_uv_overrides_sig = 0; // What the UV pane's background currently holds, so update_uv_editor() only re-uploads it when the @@ -437,7 +437,7 @@ private: std::map m_library_textures; // Everything the *unwrap* depends on. update_uv_editor() runs from rebuild_preview(), i.e. on - // every stroke end and every slider release -- but depth/tiling/rotation/offset/blend change + // every stroke end and every slider release - but depth/tiling/rotation/offset/blend change // none of this, so re-extracting the patch and re-solving on those edits would be pure waste. // Held as the real values rather than a hash: TriangleSplittingData has an exact operator==, so // there is no reason to accept a hash's (however unlikely) chance of showing a stale unwrap. @@ -460,7 +460,7 @@ private: }; UVEditorState m_uv_editor_state; // Bounds of the UVs last handed to the pane, purely so the panel can show where the unwrap - // actually landed -- it is packed in mm and then divided by the tile size, so it is easy for it + // actually landed - it is packed in mm and then divided by the tile size, so it is easy for it // to end up far outside the texture's first tile without any of that being visible. Vec2f m_uv_editor_bbox_min = Vec2f::Zero(); Vec2f m_uv_editor_bbox_max = Vec2f::Zero(); @@ -505,7 +505,7 @@ private: // Icons for the panel's selection-mode and view-mode button rows. Loaded through IconManager with // the same colour/monochrome variants the main toolbar uses, so an inactive button shows the icon in - // the theme's normal (grey) foreground colour and an active one shows it in its original colours -- + // the theme's normal (grey) foreground colour and an active one shows it in its original colours - // matching the toolbar's selected/unselected look. Uploaded once on first panel render. IconManager m_panel_icons; std::map m_panel_icon_map; // file name -> [normal, colour, disabled] diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 94083dacee..6fd025f6ed 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1902,7 +1902,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co scroll_up(); else { if (hovered_id > 0) - --hovered_id; + -hovered_id; scroll_y(hovered_id); } }); @@ -2293,7 +2293,7 @@ std::string ImGuiWrapper::trunc(const std::string &text, } else { // decrease letter count while (count_letter > 1) { - --count_letter; + -count_letter; result_text = text_.substr(0, count_letter); text_width = calc_text_size(result_text).x; if (text_width < allowed_width) break; @@ -2555,7 +2555,7 @@ void ImGuiWrapper::push_toolbar_style(const float scale) ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11 ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, COL_GREEN_LIGHT); // 12 // The checkbox/radio frame behind this is drawn fully transparent (see FrameBg above, - // alpha 0), showing the light window background through it -- a white check mark there is + // alpha 0), showing the light window background through it - a white check mark there is // invisible. Dark mode doesn't have this problem (its window background is dark), so only // this branch needs a check mark color with real contrast against a light background. ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 156 / 255.f, 136 / 255.f, 1.00f));//13 diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 9e76c55666..155760709d 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -22,8 +22,8 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) { ctl.update_status(0, _u8L("Baking texture displacement")); - // Only ever touches m_input (captured by value before this job was queued) and local state -- - // never the live Model -- so this is safe to run concurrently with the UI thread. + // Only ever touches m_input (captured by value before this job was queued) and local state - + // never the live Model - so this is safe to run concurrently with the UI thread. m_result = TriangleMesh(build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data)); } diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp index 2d8a22c759..082fcfa976 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -24,7 +24,7 @@ struct TextureDisplacementBakeInput }; // Bakes a volume's painted texture-displacement layers into real mesh geometry in the background, -// then commits the result on the main thread -- mirrors EmbossJob's UpdateJob/update_volume() +// then commits the result on the main thread - mirrors EmbossJob's UpdateJob/update_volume() // bake-and-commit pattern (see EmbossJob.cpp). class TextureDisplacementBakeJob : public Job { diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index e4a7ca616c..48b031dcc3 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -14,8 +14,8 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) { ctl.update_status(0, _u8L("Computing texture displacement preview")); - // Only ever touches m_input (captured by value before this job was queued) and local state -- - // never the live Model -- so this is safe to run concurrently with the UI thread. + // Only ever touches m_input (captured by value before this job was queued) and local state - + // never the live Model - so this is safe to run concurrently with the UI thread. m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data); } diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp index d024fc2548..93d66bcfb2 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -12,7 +12,7 @@ namespace Slic3r::GUI { -// Everything process() needs, captured by value on the main thread when the job is queued -- +// Everything process() needs, captured by value on the main thread when the job is queued - // mirrors TextureDisplacementBakeInput, but a preview never writes back to the Model. struct TextureDisplacementPreviewInput { @@ -24,7 +24,7 @@ struct TextureDisplacementPreviewInput // Computes the true (unbaked) displaced-mesh preview in the background. With several painted // layers this is real, non-trivial CPU work (PNG sampling, per-layer vertex welding), which used // to run synchronously on every paint stroke and parameter tweak and made editing feel slow with -// more than one or two layers. Unlike Bake, this never touches the live Model -- a preview is +// more than one or two layers. Unlike Bake, this never touches the live Model - a preview is // purely informational, there is nothing to commit. class TextureDisplacementPreviewJob : public Job { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8deede98ac..74d362e94d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -695,7 +695,7 @@ void Sidebar::priv::flush_printer_sync(bool restart) } //btn_sync_printer->SetBackgroundColorNormal((*counter_sync_printer & 1) ? "#F8F8F8" :"#009688"); m_printer_bbl_sync->SetBitmap_((*counter_sync_printer & 1) ? "printer_sync_not" : "printer_sync_ok"); - if (--*counter_sync_printer <= 0) + if (-*counter_sync_printer <= 0) timer_sync_printer->Stop(); } @@ -1124,7 +1124,7 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title btn_up->SetBackgroundColour(*wxWHITE); btn_up->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this, index](auto &evt) { if (page_cur > 0) - --page_cur; + -page_cur; update_ams(); }); btn_up->Hide(); @@ -1189,7 +1189,7 @@ void ExtruderGroup::update_ams() ams[index]->Refresh(); ams[index]->Open(); } - for (size_t i = i1; i < ams_n1 && left > 0; ++i, ++index, --left) { + for (size_t i = i1; i < ams_n1 && left > 0; ++i, ++index, -left) { ams[index]->Update(i < ams_1.size() ? ams_1[i] : info1); ams[index]->Refresh(); ams[index]->Open(); @@ -3781,7 +3781,7 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) for (size_t i = 0; i < merge_info.merges.size(); i++) { auto& cur = merge_info.merges[i]; - for (int j = cur.size() -1; j >= 1 ; j--) { + for (int j = cur.size() -1; j >= 1 ; j-) { auto last_index = cur[j]; change_filament(last_index, cur[0]); cur.erase(cur.begin() + j); @@ -4471,7 +4471,7 @@ struct Plater::priv AssembleView* assemble_view { nullptr }; // Docked/resizable 2D pane showing GLGizmoTextureDisplacement's LSCM unwrap of a painted // patch; a sibling AUI pane alongside "sidebar"/"main", not part of the view3D/preview/ - // assemble_view sizer -- see its registration below and Plater::get_uv_editor_canvas(). The + // assemble_view sizer - see its registration below and Plater::get_uv_editor_canvas(). The // pane hosts the panel (toolbar + canvas + status line); uv_editor_canvas is its inner canvas, // cached so the gizmo can reach it directly. UVEditorPanel* uv_editor_panel { nullptr }; @@ -4697,7 +4697,7 @@ struct Plater::priv bool up_to_date(bool saved, bool backup); void suppress_snapshots() { m_prevent_snapshots++; } - void allow_snapshots() { m_prevent_snapshots--; } + void allow_snapshots() { m_prevent_snapshots-; } // BBS: single snapshot void single_snapshots_enter(SingleSnapshot *single) { @@ -5141,7 +5141,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) .BottomDockable(false) .BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit()))); - // UV editor pane for GLGizmoTextureDisplacement's LSCM unwrap preview -- a resizable/dockable + // UV editor pane for GLGizmoTextureDisplacement's LSCM unwrap preview - a resizable/dockable // sibling of "sidebar"/"main" like everything else registered on this same AUI manager, not a // change to the view3D/preview/assemble_view sizer above. Hidden by default: only relevant // while that gizmo is active with a layer using the "Unwrap (LSCM)" projection method (see @@ -11808,7 +11808,7 @@ void Plater::priv::undo() const std::vector &snapshots = this->undo_redo_stack().snapshots(); auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time())); // BBS: undo-redo until modify record - while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); + while (-it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); if (it_current == snapshots.begin()) return; if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction && @@ -11828,7 +11828,7 @@ void Plater::priv::redo() while (it_current != snapshots.end() && !snapshot_modifies_project(*it_current++)); if (it_current != snapshots.end()) { while (it_current != snapshots.end() && !snapshot_modifies_project(*it_current++)); - this->undo_redo_to(--it_current); + this->undo_redo_to(-it_current); } } @@ -13085,7 +13085,7 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; - /// --- scale --- + /// -- scale -- // model is created for a 0.4 nozzle, scale z with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printer_config->option("nozzle_diameter"); std::vector extruder_types = printer_config->option("extruder_type")->values; @@ -16706,7 +16706,7 @@ void Plater::on_filaments_delete(size_t num_filaments, size_t filament_id, int r for (auto& item : item->second.gcodes) { if (item.type == CustomGCode::Type::ToolChange && item.extruder > filament_id) - item.extruder--; + item.extruder-; } } } @@ -17191,7 +17191,7 @@ void Plater::show_uv_editor(bool show) if (!pane.IsOk() || pane.IsShown() == show) return; - // Deferred, because GLGizmoTextureDisplacement calls this from its ImGui panel -- that is, from + // Deferred, because GLGizmoTextureDisplacement calls this from its ImGui panel - that is, from // the middle of the 3D canvas's GL frame. Showing an AUI pane re-lays out the window and // delivers the resulting size/paint events synchronously, and the UV canvas painting itself // makes its own surface current in the app's *shared* GL context, which mid-frame is the one diff --git a/src/slic3r/GUI/TextureLibrary.cpp b/src/slic3r/GUI/TextureLibrary.cpp index 349d82a050..67df12c32b 100644 --- a/src/slic3r/GUI/TextureLibrary.cpp +++ b/src/slic3r/GUI/TextureLibrary.cpp @@ -157,7 +157,7 @@ std::optional import_texture_to_library(const std::string & return std::nullopt; // Never overwrite an existing texture (the user's or, if they picked the same name twice, their - // own earlier import) -- uniquify instead. + // own earlier import) - uniquify instead. const std::string stem = boost::filesystem::path(source_path).stem().string(); boost::filesystem::path dest = boost::filesystem::path(user_dir) / (stem + ".png"); for (int i = 2; boost::filesystem::exists(dest); ++i) diff --git a/src/slic3r/GUI/TextureLibrary.hpp b/src/slic3r/GUI/TextureLibrary.hpp index acef78d4d9..4140acaadc 100644 --- a/src/slic3r/GUI/TextureLibrary.hpp +++ b/src/slic3r/GUI/TextureLibrary.hpp @@ -40,8 +40,8 @@ std::optional import_texture_to_library(const std::string & // Encoded bytes of `path`, ready to hand to TextureDisplacementLayer::image_data. Files already in // the supported 8-bit grayscale PNG form (everything in the two library folders, by construction) -// are passed through verbatim; anything else -- e.g. a colour PNG the user copied into the folder -// by hand -- is converted on the fly, so a valid image never silently produces a blank layer. +// are passed through verbatim; anything else - e.g. a colour PNG the user copied into the folder +// by hand - is converted on the fly, so a valid image never silently produces a blank layer. // Returns nullptr with `error` set if the file cannot be read or decoded at all. std::shared_ptr> load_texture_image_data(const std::string &path, std::string &error); diff --git a/src/slic3r/GUI/UVEditorCanvas.cpp b/src/slic3r/GUI/UVEditorCanvas.cpp index 95281acb8b..25d737f2bb 100644 --- a/src/slic3r/GUI/UVEditorCanvas.cpp +++ b/src/slic3r/GUI/UVEditorCanvas.cpp @@ -851,7 +851,7 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) m_gesture_last_angle = angle; // With Shift, quantise to *global* 15-degree marks (0/15/30...), i.e. snap the island's - // absolute on-screen orientation, not 15 degrees relative to wherever it started (#10) -- + // absolute on-screen orientation, not 15 degrees relative to wherever it started (#10) - // snapping the target rather than each delta is what keeps it from juddering on a step. constexpr float STEP = 15.f; const float absolute = m_rot_base_deg + m_rot_raw_deg; @@ -1333,7 +1333,7 @@ void UVEditorCanvas::render() } // Add/remove hint next to the cursor in Vertex/Edge mode: a green '+' when a click will add to the - // selection (plain or Shift), a red '-' when Ctrl is held and a click will remove one -- the UV-side + // selection (plain or Shift), a red '-' when Ctrl is held and a click will remove one - the UV-side // twin of the 3D paint cursor's own sign. Rebuilt each frame at the pointer, sized in pixels. if (m_select_mode != SelectMode::Island && m_cursor_inside) { const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); @@ -1375,9 +1375,9 @@ void UVEditorCanvas::render() update_status(); } -// ---------------------------------------------------------------------------------------------- +// ----------------------------------------------- // UVEditorPanel -// ---------------------------------------------------------------------------------------------- +// ----------------------------------------------- namespace { enum : int { @@ -1394,8 +1394,8 @@ enum : int { UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { // Icon + label buttons: each has its own dedicated SVG (see the map below), with the label kept - // alongside it. A missing SVG simply leaves the button showing only its label -- never bitmap-less - // garbage -- so the bar stays usable before the art lands. + // alongside it. A missing SVG simply leaves the button showing only its label - never bitmap-less + // garbage - so the bar stays usable before the art lands. auto *bar = new wxBoxSizer(wxHORIZONTAL); const auto set_icon = [this](wxAnyButton *b, const std::string &iconname) { const wxBitmap bmp = create_scaled_bitmap(iconname, this, 16); diff --git a/src/slic3r/GUI/UVEditorCanvas.hpp b/src/slic3r/GUI/UVEditorCanvas.hpp index 3e1047eeb9..40397470fd 100644 --- a/src/slic3r/GUI/UVEditorCanvas.hpp +++ b/src/slic3r/GUI/UVEditorCanvas.hpp @@ -23,26 +23,26 @@ namespace Slic3r::GUI { -// Standalone 2D viewer/editor for a flattened (UV-unwrapped) mesh patch -- shows the result of +// Standalone 2D viewer/editor for a flattened (UV-unwrapped) mesh patch - shows the result of // GLGizmoTextureDisplacement's LSCM projection method as its own resizable pane (see Plater's // "uv_editor" AUI pane) rather than folding 2D UV-space rendering into the main 3D viewport. // // Islands can be laid out by hand, roughly the way Blender's UV editor works: click one to select // it, drag to move it, right-drag or press R to rotate it, S to scale it, both about its own centre. -// Islands are free to overlap -- nothing re-packs them behind the user's back. The texture underneath +// Islands are free to overlap - nothing re-packs them behind the user's back. The texture underneath // is always drawn upright and axis-aligned, and it is the islands that move over it, which is what // makes "rotate this island" a meaningful gesture rather than just spinning the whole texture. // // **Geometry is uploaded in the unwrap's own (raw, mm) coordinates, once**, and each island is drawn // through its own affine matrix passed as a shader uniform. That matters: a patch can easily run to -// a million triangles, and the earlier design -- which pre-transformed every UV on the CPU and -// re-uploaded the whole wireframe on every mouse-move event -- made a drag cost a couple of hundred +// a million triangles, and the earlier design - which pre-transformed every UV on the CPU and +// re-uploaded the whole wireframe on every mouse-move event - made a drag cost a couple of hundred // milliseconds per frame. Moving an island now touches a 2x3 matrix and nothing else. // // Uses the app's single shared wxGLContext (via wxGetApp().init_glcontext(), the same call // View3D/Preview/AssembleView each make in GUI_Preview.cpp) rather than an independent context of // its own, specifically so it can reuse the app's already-registered "flat"/"flat_texture" -// shaders and GLModel as-is -- GLModel::render() looks up its shader via a GUI_App-wide "current +// shaders and GLModel as-is - GLModel::render() looks up its shader via a GUI_App-wide "current // shader", which only means anything for canvases sharing the app's one real GL context. class UVEditorCanvas : public wxGLCanvas { @@ -54,7 +54,7 @@ public: // y basis, translation). using IslandTransform = Eigen::Matrix; - // The unwrap to display, in the unwrap's own mm coordinates -- *not* texture UVs. Changing this + // The unwrap to display, in the unwrap's own mm coordinates - *not* texture UVs. Changing this // is the expensive path (it rebuilds every vertex buffer), so it must only be called when the // unwrap itself changes, never merely because an island moved. Pass an empty `indices` to show // nothing. @@ -71,13 +71,13 @@ public: // The cheap path: one transform per island. Safe to call on every mouse-move of a drag. void set_island_transforms(std::vector transforms); - // One fill colour per island, overriding the default light-green wash -- used to paint the UV + // One fill colour per island, overriding the default light-green wash - used to paint the UV // distortion heatmap over the islands when the gizmo's "Distortion" check mode is on (#7/#14). // Pass empty to go back to the default wash. Cheap: it never touches a vertex buffer. void set_island_fill_colors(std::vector colors); // The layer's own tiling scale and rotation. Needed to map a gesture, which happens in texture-UV - // space, back into the unwrap's mm space -- which is where a TextureIsland's offset actually + // space, back into the unwrap's mm space - which is where a TextureIsland's offset actually // lives (see apply_uv_transform()). The tile settings come along because the background has to // repeat exactly the way the height sampler does, or the pane would stop showing what gets baked. void set_uv_transform(float tiling_scale, float rotation_deg, bool tile_enabled, bool tile_mirrored); @@ -94,7 +94,7 @@ public: // High-level actions the pane toolbar triggers. The canvas handles the view-only ones (framing, // the snap toggle) itself and forwards the rest to whoever owns the island data (the gizmo), via - // the command callback -- the canvas has the selection and the view, the gizmo has the layer. + // the command callback - the canvas has the selection and the view, the gizmo has the layer. enum class Command { FrameAll, ToggleSnap, AverageScale, CutSelectedIsland, ProjectFromView, JoinSelected, UnjoinSelected }; void run_command(Command cmd); using CommandFn = std::function; @@ -106,7 +106,7 @@ public: void set_status_callback(StatusFn fn) { m_on_status = std::move(fn); } // Reports an island edit as it happens. The deltas are *incremental* (one mouse event's worth) - // and already converted into the units a TextureIsland stores -- unwrap mm, degrees, and a scale + // and already converted into the units a TextureIsland stores - unwrap mm, degrees, and a scale // *factor* to multiply the island's existing scale by. They are incremental on purpose: the owner // applies them and hands back fresh transforms, and if the gesture tracked geometry rather than // raw mouse motion that round trip would feed back into itself. `finished` marks the end of a @@ -116,7 +116,7 @@ public: void set_island_edit_callback(IslandEditFn fn) { m_on_island_edit = std::move(fn); } // What a click grabs: a whole island (move/rotate/scale, groups move together), a single vertex, or - // a single edge (both its endpoints). Vertex/Edge are free-form UV editing -- they move the actual + // a single edge (both its endpoints). Vertex/Edge are free-form UV editing - they move the actual // unwrap coordinates, which the owner then folds into the layer's per-vertex UV overrides so the // change is baked, not just shown (see set_vertex_edit_callback). enum class SelectMode { Island, Vertex, Edge }; @@ -177,7 +177,7 @@ private: void move_vertex_raw(int unwrapped_vertex, const Vec2f &delta_uv); Vec2f island_centroid(int island) const; // Converts a delta in texture-UV space into the unwrap's mm space, undoing the layer's scale and - // rotation -- the inverse of what apply_uv_transform() did on the way in. + // rotation - the inverse of what apply_uv_transform() did on the way in. Vec2f uv_delta_to_unwrap(const Vec2f &delta_uv) const; // The correction that would bring the selected island's nearest boundary vertex onto a boundary // vertex of some *other* island, in texture-UV space. Zero if nothing is within reach (#2). @@ -192,7 +192,7 @@ private: std::vector m_transforms; // Per-island fill colour override (distortion heatmap); empty means use the default wash (#7). std::vector m_island_fill_colors; - // Boundary vertices per island, for snapping -- a patch's boundary is a tiny fraction of it, and + // Boundary vertices per island, for snapping - a patch's boundary is a tiny fraction of it, and // rescanning the whole uv array on every snap test would not be. std::vector> m_island_boundary_verts; @@ -203,7 +203,7 @@ private: std::vector m_island_boundary; // outline std::vector m_island_fill; // filled, for the selected island's wash - GLModel m_tile_outline_glmodel; // the texture's first tile, [0,1]^2 -- the "you are here" + GLModel m_tile_outline_glmodel; // the texture's first tile, [0,1]^2 - the "you are here" GLModel m_grid_glmodel; float m_grid_step = 0.f; // the UV step m_grid_glmodel was built for; 0 = not built @@ -290,7 +290,7 @@ private: float m_gesture_last_dist = 0.f; // Rotation is tracked as two running totals over the gesture: the raw mouse rotation, and how much // has actually been applied. With Shift held the applied total is quantised to 15-degree steps - // (Blender-style angle snapping), so the two diverge -- and driving the applied total off the raw + // (Blender-style angle snapping), so the two diverge - and driving the applied total off the raw // one, rather than snapping each incremental delta, is what makes the snap stable instead of // juddering. The raw/applied split also survives crossing +/-180 degrees, which a single wrapped // angle would not. m_rot_applied doubles as the modal-rotate undo amount for Esc. From 219cddf40e9e9d697204aa4febfca47700961a36 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 21 Jul 2026 11:28:51 +0200 Subject: [PATCH 12/89] Fix typo after cleanup --- src/slic3r/GUI/GLTexture.cpp | 6 +++--- src/slic3r/GUI/Plater.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index fb230c0405..ab27c81bf7 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -660,8 +660,8 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont& found = false; src -= 3; - for (int h = m_height; h > 0; -h) { - for (int w = m_width; w > 0; -w) { + for (int h = m_height; h > 0; --h) { + for (int w = m_width; w > 0; --w) { if ((*src) != background.Red() && !found) { found = true; if (h < font.GetPointSize()) @@ -742,7 +742,7 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h) { auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; }; - auto upper_power_of_two = [](int v) { v-; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }; + auto upper_power_of_two = [](int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }; int new_w = std::max(w, h); if (!is_power_of_two(new_w)) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d8a978a835..7efa17b470 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5467,7 +5467,7 @@ struct Plater::priv bool up_to_date(bool saved, bool backup); void suppress_snapshots() { m_prevent_snapshots++; } - void allow_snapshots() { m_prevent_snapshots-; } + void allow_snapshots() { m_prevent_snapshots--; } // BBS: single snapshot void single_snapshots_enter(SingleSnapshot *single) { @@ -17513,7 +17513,7 @@ void Plater::on_filaments_delete(size_t num_filaments, size_t filament_id, int r for (auto& item : item->second.gcodes) { if (item.type == CustomGCode::Type::ToolChange && item.extruder > filament_id) - item.extruder-; + item.extruder--; } } } From 39bfceca6dd9feb30fa2871784aa9f283d0c982c Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 21 Jul 2026 12:53:00 +0200 Subject: [PATCH 13/89] Fix typo again --- src/slic3r/GUI/ImGuiWrapper.cpp | 4 ++-- src/slic3r/GUI/Plater.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index cd8f0aa2e5..2519595be9 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1902,7 +1902,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co scroll_up(); else { if (hovered_id > 0) - -hovered_id; + --hovered_id; scroll_y(hovered_id); } }); @@ -2293,7 +2293,7 @@ std::string ImGuiWrapper::trunc(const std::string &text, } else { // decrease letter count while (count_letter > 1) { - -count_letter; + --count_letter; result_text = text_.substr(0, count_letter); text_width = calc_text_size(result_text).x; if (text_width < allowed_width) break; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7efa17b470..cc6e6fedd6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -866,7 +866,7 @@ void Sidebar::priv::flush_printer_sync(bool restart) } //btn_sync_printer->SetBackgroundColorNormal((*counter_sync_printer & 1) ? "#F8F8F8" :"#009688"); m_printer_bbl_sync->SetBitmap_((*counter_sync_printer & 1) ? "printer_sync_not" : "printer_sync_ok"); - if (-*counter_sync_printer <= 0) + if (--*counter_sync_printer <= 0) timer_sync_printer->Stop(); } @@ -1307,7 +1307,7 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title btn_up->SetBackgroundColour(*wxWHITE); btn_up->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this, index](auto &evt) { if (page_cur > 0) - -page_cur; + --page_cur; update_ams(); }); btn_up->Hide(); @@ -1376,7 +1376,7 @@ void ExtruderGroup::update_ams() ams[index]->Refresh(); ams[index]->Open(); } - for (size_t i = i1; i < ams_n1 && left > 0; ++i, ++index, -left) { + for (size_t i = i1; i < ams_n1 && left > 0; ++i, ++index, --left) { ams[index]->Update(i < ams_1.size() ? ams_1[i] : info1); ams[index]->Refresh(); ams[index]->Open(); @@ -4519,7 +4519,7 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) for (size_t i = 0; i < merge_info.merges.size(); i++) { auto& cur = merge_info.merges[i]; - for (int j = cur.size() -1; j >= 1 ; j-) { + for (int j = cur.size() -1; j >= 1 ; j--) { auto last_index = cur[j]; change_filament(last_index, cur[0]); cur.erase(cur.begin() + j); @@ -12615,7 +12615,7 @@ void Plater::priv::undo() const std::vector &snapshots = this->undo_redo_stack().snapshots(); auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time())); // BBS: undo-redo until modify record - while (-it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); + while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); if (it_current == snapshots.begin()) return; if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction && @@ -12635,7 +12635,7 @@ void Plater::priv::redo() while (it_current != snapshots.end() && !snapshot_modifies_project(*it_current++)); if (it_current != snapshots.end()) { while (it_current != snapshots.end() && !snapshot_modifies_project(*it_current++)); - this->undo_redo_to(-it_current); + this->undo_redo_to(--it_current); } } From d9f679d154387755b0a497a66adf37c6fe688864 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 21 Jul 2026 14:07:51 +0200 Subject: [PATCH 14/89] Rename reserved GLSL word on AMD GPU --- resources/shaders/110/texture_displacement_bump.fs | 4 ++-- resources/shaders/110/texture_displacement_bump.vs | 8 ++++---- resources/shaders/140/texture_displacement_bump.fs | 8 ++++---- resources/shaders/140/texture_displacement_bump.vs | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index a3c44413b5..75a2baa290 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -40,7 +40,7 @@ varying vec3 clipping_planes_dots; varying vec4 model_pos; varying vec4 world_pos; varying float weight; -varying float active; +varying float island_active; varying vec2 vertex_uv; void projection_axes(vec3 n, out vec3 t, out vec3 b) @@ -81,7 +81,7 @@ void main() // Mikkelsen surface-gradient bump; see the 140 variant for the full rationale. Scale-exact // for a conformal LSCM map (no global 1/tiling assumption), and gated by the paint weight // via a multiply so the branch stays uniform (use_vertex_uv is a uniform). - vec2 uv = (active > 0.5) + vec2 uv = (island_active > 0.5) ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr : vertex_uv; float h = texture2D(height_tex, uv).r; diff --git a/resources/shaders/110/texture_displacement_bump.vs b/resources/shaders/110/texture_displacement_bump.vs index 293129cbb8..5e268ed346 100644 --- a/resources/shaders/110/texture_displacement_bump.vs +++ b/resources/shaders/110/texture_displacement_bump.vs @@ -17,7 +17,7 @@ varying vec3 clipping_planes_dots; varying vec4 model_pos; varying vec4 world_pos; varying float weight; -varying float active; +varying float island_active; varying vec2 vertex_uv; void main() @@ -28,7 +28,7 @@ void main() gl_Position = projection_matrix * view_model_matrix * model_pos; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); - weight = v_normal.x; - active = v_normal.y; - vertex_uv = v_tex_coord; + weight = v_normal.x; + island_active = v_normal.y; + vertex_uv = v_tex_coord; } diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 3844e709f8..58f79722aa 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -60,7 +60,7 @@ uniform vec2 uv_offset; uniform bool invert; uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM) // A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of -// the island currently being dragged in the UV editor (active > 0.5). Identity when nothing is +// the island currently being dragged in the UV editor (island_active > 0.5). Identity when nothing is // dragged, so this whole path is a no-op then. Lets a UV island drag move the bump on the model with // only a uniform update uniform vec4 island_delta_lin; @@ -70,7 +70,7 @@ in vec3 clipping_planes_dots; in vec4 model_pos; in vec4 world_pos; in float weight; -in float active; +in float island_active; in vec2 vertex_uv; out vec4 out_color; @@ -125,8 +125,8 @@ void main() // derivatives are well defined; the paint weight gates the result by a plain multiply (k) // rather than a per-fragment branch, keeping it that way. // The dragged island's uv rides a uniform affine so its bump moves without a rebuild; every - // other vertex (active == 0) samples its baked uv unchanged. - vec2 uv = (active > 0.5) + // other vertex (island_active == 0) samples its baked uv unchanged. + vec2 uv = (island_active > 0.5) ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr : vertex_uv; float h = texture(height_tex, uv).r; diff --git a/resources/shaders/140/texture_displacement_bump.vs b/resources/shaders/140/texture_displacement_bump.vs index 4518fb6687..3cb82284a7 100644 --- a/resources/shaders/140/texture_displacement_bump.vs +++ b/resources/shaders/140/texture_displacement_bump.vs @@ -27,7 +27,7 @@ out vec3 clipping_planes_dots; out vec4 model_pos; out vec4 world_pos; out float weight; -out float active; +out float island_active; out vec2 vertex_uv; void main() @@ -38,7 +38,7 @@ void main() gl_Position = projection_matrix * view_model_matrix * model_pos; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); - weight = v_normal.x; - active = v_normal.y; - vertex_uv = v_tex_coord; + weight = v_normal.x; + island_active = v_normal.y; + vertex_uv = v_tex_coord; } From 0df1424101b50e0661ab4edda3b7907f693f6f23 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Fri, 24 Jul 2026 09:03:06 +0200 Subject: [PATCH 15/89] Add adaptive subdivision --- TEXTURE_DISPLACEMENT.md | 65 +++-- src/libslic3r/TextureDisplacement.cpp | 144 ++++++++++ src/libslic3r/TextureDisplacement.hpp | 29 ++ .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 248 ++++++++++++++++-- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 21 +- tests/libslic3r/test_texture_displacement.cpp | 88 +++++++ 6 files changed, 552 insertions(+), 43 deletions(-) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index d1109644e2..b4b904fea7 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -188,24 +188,43 @@ directly** - clamping the *coordinate* into range (what an earlier version did) border row/column of pixels outward to infinity in every direction, which is a real bug that was reported and fixed (visually: streaky lines radiating out from the painted patch). -### Subdivision (`subdivide_mesh_uniform()`) +### Subdivision — two modes -Deliberately **whole-mesh and uniform**, not limited to the painted patch. A patch-only / -adaptive subdivision would create a classic T-junction/cracking problem where the denser -(subdivided) and sparser (untouched) regions meet - the fine side has edge midpoints the coarse -side doesn't know about, producing a real (non-manifold-looking) crack in the baked geometry. This -was consciously scoped down from the original plan's "adaptive per-patch subdivider" idea to avoid -that correctness risk (a subtly-cracked mesh is a much worse outcome than "not implemented yet"). -Algorithm: recursive 1-to-4 triangle split via edge midpoints, with a shared per-pass midpoint cache -(keyed by sorted vertex-index pair) so triangles sharing an edge get the *same* new vertex - capped -at `max_iterations` (default 6) passes to bound worst-case triangle-count explosion. +**Uniform (`subdivide_mesh_uniform()`)** — whole-mesh, 1-to-4 split. Recursive edge-midpoint split with +a shared per-pass midpoint cache (keyed by sorted vertex-index pair) so triangles sharing an edge get +the *same* new vertex - capped at `max_iterations` (default 6). Whole-mesh so it never leaves a +T-junction, at the cost of densifying everywhere. Wired as a "Subdivide steps" slider (**0–5**, 0 = +no subdivision), Apply snaps back to 0. Drops texture-displacement paint (no remap) via the standard +`save_painting()`/`set_mesh()`/`restore_painting()` dance; the other four channels are remapped. -Wired as a "Subdivide steps" slider (**0–5**, where 0 means no subdivision and previews nothing) plus -Preview/Apply/Done in the gizmo panel. **Apply snaps the slider back to 0**. A real, committed geometry change (like -Bake), using the same `save_painting()`/`set_mesh()`/`restore_painting()` dance `GLGizmoSimplify` -uses: supported/seam/mmu/fuzzy-skin masks get remapped onto the new triangles, texture-displacement -paint does not (no remap support yet) and is dropped rather than left pointing at now-meaningless -triangle indices. +**Adaptive (`subdivide_mesh_adaptive()`)** — refine **only the painted area**, down to a target edge +length, by **Rivara longest-edge bisection**. This is the algorithm that was "scoped out" originally +for fear of the T-junction/crack problem; it is safe because it is *conformal by construction*. Each +pass bisects only **terminal** edges - an edge that is the longest edge of *every* triangle sharing it +- which splits both those triangles along one shared midpoint at once, so a hanging node is never +created. Only a triangle's own longest edge can be terminal, so each triangle is split by at most one +bisection per pass. When a triangle that still needs refining has a longest edge that is not yet +terminal, the neighbour across it has a strictly longer edge and is refined first; that propagation +grades the mesh down into the region and closes what would be cracks (pulling a thin, bounded band of +transition triangles just outside the painted patch). Tie-broken by mesh-vertex key so both sides of +an edge always agree on "the" longest. + +The win: a small decal on a big model no longer quadruples the *whole* model's triangle count. + +**It carries the paint forward**, which is what makes it usable (uniform/remesh both drop paint). Because +the refinement is *driven by* the paint, the remap is trivial: `subdivide_mesh_adaptive()` fills an +`out_source[new_tri] = input_tri` map (children inherit their parent), and the gizmo rebuilds each +layer's mask on the new mesh - a new triangle is painted iff its source was fully painted in that +layer. `collect_paint_region()` derives both the union refine-region (any vertex of a painted patch, +i.e. patch + a one-ring, so the boundary itself refines) and the per-layer fully-painted-triangle sets +(a `get_facets_strict(ENFORCER)` sub-triangle with all three *original* vertex indices == a whole, +fully-painted original triangle; a partial stroke's sub-triangles always carry a split vertex). The +other four channels still ride the normal `restore_painting()` remap. Covered by a conformality unit +test (`every_edge_used_twice` on a partially-refined cube - an exact crack detector for a closed mesh). + +Both share the gizmo's Preview/Apply/Done flow; the **"Only painted area (adaptive)"** checkbox picks +the mode, and the adaptive preview follows the paint live (`rebuild_preview()` refreshes the wireframe +while the subdivide preview is open in adaptive mode). ### Fast bump preview (GPU-only, no CPU meshing) @@ -353,11 +372,16 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward part of the mesh via the existing mesh serialization path) - what does *not* survive a project save/reload is any *unbaked* paint stroke and texture layer definition. - **No remap-across-topology-change** for texture-displacement paint (`ModelObject::split()`, mesh - boolean ops, Simplify, and now `subdivide_mesh_uniform()` all drop it via `reset_extra_facets()`). - The other four paint channels (supported/seam/mmu/fuzzy) do get remapped in these cases. + boolean ops, Simplify, uniform subdivide, and remesh all drop it via `reset_extra_facets()`). The + other four paint channels (supported/seam/mmu/fuzzy) do get remapped in these cases. The lone + exception is **adaptive subdivide**, which carries texture-displacement paint forward itself via its + source map (see the Subdivision section) - a targeted remap that only works because the operation is + driven by the paint. - **Cylindrical/Spherical axis/center are auto-picked heuristically**, not user-controllable - no UI to override the auto-detected wrap axis if it picks the "wrong" one for an odd shape. -- **Fast preview covers the active layer only** +- **Fast preview covers the active layer only** — and is now the **default** view when the gizmo + opens (`m_use_bump_preview = true`): it is the instant, no-CPU-meshing preview, so it is the better + first impression while painting. The exact true-displacement view is one click away in the View row. - **Displacement resolution is capped by the mesh's own vertex density.** Baking only ever *moves* existing vertices (it never inserts any), so a coarse patch cannot show fine texture detail no matter how high-resolution the height map is - that is what the "Subdivide model" button is for. @@ -368,7 +392,8 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward **libslic3r (core, no GUI dependency):** - `src/libslic3r/TextureDisplacement.hpp/.cpp` - data model, bake algorithm, projection methods, - tiling, subdivision. See doc comments throughout, they're kept accurate and up to date. + tiling, subdivision (uniform + adaptive longest-edge bisection). See doc comments throughout, + they're kept accurate and up to date. - `src/libslic3r/MeshBoolean.hpp/.cpp` - added `parameterize_lscm()` and `remesh_isotropic()` in the `cgal` sub-namespace, reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 0a6325cd92..7bc855cf15 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -1346,4 +1346,148 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl return current; } +indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, + const std::vector &refine_region, + float target_edge_length_mm, int max_iterations, + std::vector *out_source) +{ + // Each triangle carries the input-triangle index it descends from, so children inherit it and + // the caller can remap per-triangle data (paint masks) for free. + struct Tri { int v[3]; int src; }; + + std::vector verts = mesh.vertices; + std::vector tris; + tris.reserve(mesh.indices.size()); + for (size_t i = 0; i < mesh.indices.size(); ++i) + tris.push_back({ { mesh.indices[i][0], mesh.indices[i][1], mesh.indices[i][2] }, int(i) }); + + auto emit = [&](std::vector &t) -> indexed_triangle_set { + indexed_triangle_set out; + out.vertices = verts; + out.indices.reserve(t.size()); + if (out_source) { + out_source->clear(); + out_source->reserve(t.size()); + } + for (const Tri &tr : t) { + out.indices.emplace_back(tr.v[0], tr.v[1], tr.v[2]); + if (out_source) + out_source->push_back(tr.src); + } + return out; + }; + + // refine_region is indexed by input-triangle index, and every triangle's src stays in that range + // (children inherit their parent's src), so a wrong size would be an out-of-bounds read. Guard it. + if (target_edge_length_mm <= 0.f || refine_region.size() != mesh.indices.size()) + return emit(tris); + if (std::none_of(refine_region.begin(), refine_region.end(), [](uint8_t v) { return v != 0; })) + return emit(tris); // nothing flagged: no-op + const float target_sq = target_edge_length_mm * target_edge_length_mm; + + auto edge_key = [](int a, int b) -> uint64_t { + if (a > b) + std::swap(a, b); + return (uint64_t(uint32_t(a)) << 32) | uint32_t(b); + }; + auto edge_len_sq = [&](uint64_t k) -> float { + return (verts[int(k >> 32)] - verts[int(uint32_t(k))]).squaredNorm(); + }; + // The one edge of a triangle chosen as its "longest": greatest squared length, ties broken by the + // smaller edge key. The tie-break is by mesh-vertex indices, which both triangles sharing an edge + // compute identically - so they never disagree about whether that shared edge is "the" longest, + // which is what the conformality argument rests on. + auto longest_key = [&](const Tri &t) -> uint64_t { + uint64_t best_k = edge_key(t.v[0], t.v[1]); + float best_len = edge_len_sq(best_k); + for (int e = 1; e < 3; ++e) { + const uint64_t k = edge_key(t.v[e], t.v[(e + 1) % 3]); + const float l = edge_len_sq(k); + if (l > best_len || (l == best_len && k < best_k)) { + best_len = l; + best_k = k; + } + } + return best_k; + }; + + for (int iter = 0; iter < max_iterations; ++iter) { + // edge -> the (up to two) triangles sharing it. A third triangle on an edge means a + // non-manifold input; it is left in the second slot's place and simply not treated as + // terminal, so such an edge is never bisected (better a missed refinement than a torn mesh). + std::unordered_map> edge_tris; + edge_tris.reserve(tris.size() * 3); + for (int ti = 0; ti < int(tris.size()); ++ti) + for (int e = 0; e < 3; ++e) { + const uint64_t k = edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]); + auto it = edge_tris.find(k); + if (it == edge_tris.end()) + edge_tris.emplace(k, std::array{ ti, -1 }); + else if (it->second[1] == -1) + it->second[1] = ti; + else + it->second[0] = -2; // >2 triangles: poison this edge (never terminal) + } + + std::vector tri_longest(tris.size()); + for (int ti = 0; ti < int(tris.size()); ++ti) + tri_longest[ti] = longest_key(tris[ti]); + + // Which edges to bisect this pass: terminal (the longest edge of every triangle on it) AND + // long enough AND wanted by the region (either side in it). Terminal-ness is exactly what + // guarantees both sides split together, so no hanging node is ever produced. + std::unordered_set to_bisect; + for (const auto &[k, slot] : edge_tris) { + const int t0 = slot[0], t1 = slot[1]; + if (t0 < 0) + continue; // poisoned (non-manifold) + if (tri_longest[t0] != k) + continue; + if (t1 >= 0 && tri_longest[t1] != k) + continue; + if (edge_len_sq(k) <= target_sq) + continue; + const bool in_region = (refine_region[tris[t0].src] != 0) || + (t1 >= 0 && refine_region[tris[t1].src] != 0); + if (in_region) + to_bisect.insert(k); + } + if (to_bisect.empty()) + break; + + // One midpoint per bisected edge, shared by both sides. + std::unordered_map mid; + mid.reserve(to_bisect.size()); + for (const uint64_t k : to_bisect) { + const int a = int(k >> 32), b = int(uint32_t(k)); + mid.emplace(k, int(verts.size())); + verts.push_back((verts[a] + verts[b]) * 0.5f); + } + + std::vector next; + next.reserve(tris.size() + to_bisect.size()); + for (const Tri &t : tris) { + // At most one of a triangle's edges can be terminal (only its own longest can be), so at + // most one is in to_bisect - find that one. + int be = -1; + for (int e = 0; e < 3; ++e) + if (to_bisect.count(edge_key(t.v[e], t.v[(e + 1) % 3])) != 0) { + be = e; + break; + } + if (be == -1) { + next.push_back(t); + continue; + } + const int a = t.v[be], b = t.v[(be + 1) % 3], c = t.v[(be + 2) % 3]; + const int m = mid.at(edge_key(a, b)); + next.push_back({ { a, m, c }, t.src }); // both children keep the original winding a->b->c + next.push_back({ { m, b, c }, t.src }); + } + tris.swap(next); + } + + return emit(tris); +} + } // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index d3698eaec1..5bae458e43 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -511,6 +511,35 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume); // during baking. indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, float max_edge_length_mm, int max_iterations = 6); +// Adaptive subdivision by Rivara longest-edge bisection, restricted to a region. +// +// Unlike subdivide_mesh_uniform() this only densifies where asked - a triangle is refined when its +// source is flagged in `refine_region` and its longest edge exceeds target_edge_length_mm - so a +// small painted patch on a large model does not quadruple the whole model's triangle count. It is +// nonetheless *conformal*: it never leaves a T-junction/crack at the boundary between the refined +// and coarse regions (the trap that made subdivide_mesh_uniform() deliberately whole-mesh). +// +// How it stays crack-free: each pass bisects only "terminal" edges - an edge that is the longest +// edge of *every* triangle sharing it. Bisecting such an edge splits both its triangles 1->2 along +// the same new midpoint at once, so no hanging node is ever created. Because only a triangle's own +// longest edge can be terminal, each triangle is split by at most one terminal edge per pass. When +// a triangle that needs refining has a longest edge that is *not* yet terminal, the neighbour across +// it has a strictly longer edge and is refined first; that propagation is what grades the mesh down +// into the region and closes what would otherwise be cracks (Rivara, "New longest-edge algorithms"). +// The transition triangles it pulls in just outside the region are a thin, bounded band. +// +// `refine_region` is indexed by input-triangle index (empty or all-false => no-op). If `out_source` +// is non-null it is resized to the output triangle count and out_source[i] receives the input +// triangle that output triangle i descends from (children inherit their parent's index), so a caller +// can carry per-triangle data - e.g. a paint mask - across the topology change without a geometric +// remap. `max_iterations` bounds the worst case; ties in "longest edge" are broken by a mesh-vertex +// key so both triangles on an edge always agree, at the cost of occasionally stopping one pass early +// on a pathologically tie-heavy mesh (a quality shortfall, never a crack). +indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, + const std::vector &refine_region, + float target_edge_length_mm, int max_iterations = 12, + std::vector *out_source = nullptr); + } // namespace Slic3r #endif // slic3r_TextureDisplacement_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index d1bce40b6b..0fd02a3ba6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -1153,6 +1153,11 @@ void GLGizmoTextureDisplacement::rebuild_preview() rebuild_bump_preview_mesh(); rebuild_uvcheck_mesh(); rebuild_seam_overlay(); + // The adaptive subdivision preview is driven by the painted area, so it has to follow the paint + // while it is open - a plain stroke changes which triangles would be refined. The uniform preview + // depends only on the mesh, so it is left to its own controls. + if (m_subdivide_editing && m_subdivide_adaptive) + rebuild_subdivide_preview(); const ModelVolume *mv = texture_volume(); if (mv == nullptr || !mv->is_texture_displacement_painted()) { @@ -2655,6 +2660,143 @@ void GLGizmoTextureDisplacement::subdivide_model() m_parent.set_as_dirty(); } +bool GLGizmoTextureDisplacement::collect_paint_region( + std::vector ®ion, + std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> *painted_tri) const +{ + const ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return false; + const indexed_triangle_set &its = mv->mesh().its; + const size_t ntri = its.indices.size(); + const size_t nvert = its.vertices.size(); + + region.assign(ntri, 0); + if (painted_tri) + for (auto &pt : *painted_tri) + pt.clear(); + + // Sorted-vertex-triple -> triangle index, so a fully-painted patch sub-triangle (which comes + // back with the original mesh's own three vertex indices) can be mapped to its source triangle. + // A sub-triangle produced by a *partial* brush stroke has at least one appended (split) vertex, + // so "all three indices are original" is exactly the test for a whole, fully-painted triangle. + std::map, int> tri_by_verts; + for (size_t i = 0; i < ntri; ++i) { + std::array k{ its.indices[i][0], its.indices[i][1], its.indices[i][2] }; + std::sort(k.begin(), k.end()); + tri_by_verts.emplace(k, int(i)); + } + + bool any_paint = false; + for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { + const TriangleSelector::TriangleSplittingData &data = mv->texture_displacement_facet(slot).get_data(); + if (!TriangleSelector::has_facets(data, EnforcerBlockerType::ENFORCER)) + continue; + + TriangleSelector sel(mv->mesh()); + sel.deserialize(data, false); + const indexed_triangle_set patch = sel.get_facets_strict(EnforcerBlockerType::ENFORCER); + + std::vector painted_vertex(nvert, 0); + if (painted_tri) + (*painted_tri)[slot].assign(ntri, 0); + + for (const stl_triangle_vertex_indices &t : patch.indices) { + bool all_original = true; + for (int k = 0; k < 3; ++k) { + if (size_t(t[k]) < nvert) + painted_vertex[t[k]] = 1; // marks the refine region (any coverage, plus a ring) + else + all_original = false; // a split vertex -> this is a partial sub-triangle + } + if (all_original && painted_tri) { + std::array k{ t[0], t[1], t[2] }; + std::sort(k.begin(), k.end()); + if (auto it = tri_by_verts.find(k); it != tri_by_verts.end()) + (*painted_tri)[slot][it->second] = 1; + } + } + + // A triangle is in the refine region if any of its vertices is painted. That deliberately + // over-includes a one-triangle ring just outside the strict patch, which is exactly the + // transition band the conformal bisection would pull in anyway - and it makes sure the patch + // boundary itself gets refined rather than staying coarse right where the relief ends. + for (size_t i = 0; i < ntri; ++i) + for (int k = 0; k < 3; ++k) + if (painted_vertex[its.indices[i][k]]) { + region[i] = 1; + break; + } + any_paint = true; + } + return any_paint; +} + +void GLGizmoTextureDisplacement::subdivide_model_adaptive() +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr || m_subdivide_target_mm <= 0.f) + return; + + update_model_object(); // flush any in-progress stroke into the committed masks first + + std::vector region; + std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> painted_tri; + if (!collect_paint_region(region, &painted_tri)) { + show_error(nullptr, _u8L("Paint the area you want to subdivide first - adaptive subdivision only " + "refines where you have painted.")); + return; + } + + // Do the (potentially slow) refinement before taking the snapshot, so a no-op leaves no empty + // undo step - mirrors remesh_model(). + std::vector source; + indexed_triangle_set refined; + { + wxBusyCursor wait; + refined = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, 12, &source); + } + if (refined.indices.size() == mv->mesh().its.indices.size()) { + show_error(nullptr, _u8L("Nothing to subdivide - the painted area is already at or below the target " + "edge length.")); + return; + } + + Plater *plater = wxGetApp().plater(); + Plater::TakeSnapshot snapshot(plater, _u8L("Adaptive subdivide for texture displacement"), UndoRedo::SnapshotType::GizmoAction); + + // Other paint channels ride across via the standard remap; texture-displacement paint is rebuilt + // by hand below from the source map, which is the whole point of driving this by the paint. + std::optional saved_painting = mv->save_painting(); + mv->set_mesh(TriangleMesh(std::move(refined))); // refined is not needed past here; source carries the paint map + mv->set_new_unique_id(); + mv->calculate_convex_hull(); + mv->restore_painting(saved_painting); // resets extra facets (incl. texture-displacement) + remaps the rest + + // Carry each layer's paint onto the new mesh: a new triangle is painted iff its source triangle + // was fully painted in that layer. Children inherit their parent's source, so this is exact. + for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { + if (painted_tri[slot].empty()) + continue; + TriangleSelector sel(mv->mesh()); + for (size_t i = 0; i < source.size(); ++i) + if (painted_tri[slot][source[i]]) + sel.set_facet(int(i), EnforcerBlockerType::ENFORCER); + mv->texture_displacement_facet(slot).set(sel); + } + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), mo); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + plater->changed_object(*mo); + update_from_model_object(false); // reload selectors/preview against the new mesh + carried paint + m_parent.set_as_dirty(); +} + void GLGizmoTextureDisplacement::remesh_model() { ModelVolume *mv = texture_volume(); @@ -2712,12 +2854,24 @@ void GLGizmoTextureDisplacement::rebuild_subdivide_preview() m_subdivide_preview_glmodel.reset(); m_subdivide_preview_count = -1; const ModelVolume *mv = texture_volume(); - if (mv == nullptr || m_subdivide_count < 1) + if (mv == nullptr) return; - // The same subdivision Apply would commit, but kept in a throwaway mesh and shown only as a + // The same subdivision Apply would commit, kept in a throwaway mesh and shown only as a // wireframe - the model itself is not touched until Apply. - const indexed_triangle_set its = subdivide_mesh_uniform(mv->mesh().its, 0.f, m_subdivide_count); + indexed_triangle_set its; + if (m_subdivide_adaptive) { + if (m_subdivide_target_mm <= 0.f) + return; + std::vector region; + if (!collect_paint_region(region, nullptr)) + return; // nothing painted yet: nothing to preview + its = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, 12, nullptr); + } else { + if (m_subdivide_count < 1) + return; + its = subdivide_mesh_uniform(mv->mesh().its, 0.f, m_subdivide_count); + } if (its.indices.empty()) return; m_subdivide_preview_count = m_subdivide_count; @@ -3558,20 +3712,66 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::Separator(); m_imgui->text(_u8L("Not enough vertices for fine detail?")); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 0, 5)) { - m_subdivide_count = std::clamp(m_subdivide_count, 0, 5); + + if (ImGui::Checkbox(_u8L("Only painted area (adaptive)").c_str(), &m_subdivide_adaptive)) { if (m_subdivide_editing) - rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one + rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result m_parent.set_as_dirty(); } - ImGui::PopItemWidth(); if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " - "triangle count, so there are enough vertices for the height texture to displace. " - "0 means no subdivision."), + m_imgui->tooltip(_u8L("Refine only where you have painted, down to a target edge length, instead of splitting " + "the whole model. Keeps the triangle count down on a big part with a small decal, and - " + "unlike whole-model subdivision - your paint is carried onto the finer mesh instead of " + "being cleared."), m_imgui->scaled(20.f)); + if (m_subdivide_adaptive) { + if (m_subdivide_target_mm <= 0.f && mv != nullptr) { + // Seed the target at about half the mesh's mean edge length, so the default already adds + // a useful amount of detail rather than landing on "no change". + const indexed_triangle_set &its = mv->mesh().its; + double sum = 0.0; size_t cnt = 0; + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) { + sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm(); + ++cnt; + } + m_subdivide_target_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)) * 0.5f, 0.1f, 20.f) : 1.f; + } + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + // "##subdiv" keeps the visible label "Target edge (mm)" but gives it an ImGui ID distinct + // from the remesh slider below, which shows the same text - same label == same widget to + // ImGui, so without this the two would collide. + if (m_imgui->slider_float(std::string(_u8L("Target edge (mm)")) + "##subdiv", &m_subdivide_target_mm, + 0.1f, 20.f, "%.2f", ImGuiLogSlider)) { + if (m_subdivide_editing && !ImGui::IsMouseDown(ImGuiMouseButton_Left)) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangles in the painted area are split until every edge is at or below this " + "length. Smaller means finer detail and more triangles."), + m_imgui->scaled(20.f)); + } else { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 0, 5)) { + m_subdivide_count = std::clamp(m_subdivide_count, 0, 5); + if (m_subdivide_editing) + rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one + m_parent.set_as_dirty(); + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " + "triangle count, so there are enough vertices for the height texture to displace. " + "0 means no subdivision."), + m_imgui->scaled(20.f)); + } + + // Apply is a no-op when there is nothing to commit: 0 uniform passes, or an adaptive target that + // is not set (the preview covers the painted-area check itself). + const bool subdivide_ready = m_subdivide_adaptive ? (m_subdivide_target_mm > 0.f) : (m_subdivide_count >= 1); if (!m_subdivide_editing) { if (m_imgui->button(_u8L("Preview subdivision"))) { m_subdivide_editing = true; @@ -3583,22 +3783,28 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "commit it, or Done to leave the model as it is."), m_imgui->scaled(20.f)); } else { - m_imgui->disabled_begin(m_subdivide_count < 1); + m_imgui->disabled_begin(!subdivide_ready); if (m_imgui->button(_u8L("Apply"))) { - subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) - // Back to 0 rather than staying at the count just applied: the mesh is now up to 4^N - // times denser, so re-previewing the same N passes on top of it is both pointless (the - // density asked for is already committed) and by far the slowest thing this panel does. - // rebuild_subdivide_preview() at 0 just drops the wireframe. - m_subdivide_count = 0; + if (m_subdivide_adaptive) { + subdivide_model_adaptive(); // refines only the painted area, carrying the paint forward + } else { + subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) + // Back to 0 rather than staying at the count just applied: the mesh is now up to 4^N + // times denser, so re-previewing the same N passes on top of it is both pointless (the + // density asked for is already committed) and by far the slowest thing this panel does. + m_subdivide_count = 0; + } rebuild_subdivide_preview(); m_parent.set_as_dirty(); } m_imgui->disabled_end(); if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " - "paint on it (already-baked bumps are unaffected)."), - m_imgui->scaled(20.f)); + m_imgui->tooltip(m_subdivide_adaptive ? + _u8L("Refines the painted area to the target edge length and carries your paint onto " + "the finer mesh. The rest of the model is left as it is.") : + _u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " + "paint on it (already-baked bumps are unaffected)."), + m_imgui->scaled(20.f)); ImGui::SameLine(); if (m_imgui->button(_u8L("Done"))) { m_subdivide_editing = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index bbcd355ebe..018e1f00c8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -307,6 +307,20 @@ private: void rebuild_subdivide_preview(); void render_subdivide_preview(); + // Adaptive subdivision: refine only the painted area, down to a target edge length, via + // conformal longest-edge bisection (subdivide_mesh_adaptive()). Unlike the count-based uniform + // path it does not touch the unpainted rest of the model, and - because it is driven by the paint + // - it can carry that paint forward across the topology change (children of a painted triangle + // are painted), so the region survives the subdivision instead of being dropped. + bool m_subdivide_adaptive = false; + float m_subdivide_target_mm = 0.f; // 0 = not yet seeded; filled from the mesh on first show + void subdivide_model_adaptive(); + // Fills `region` (per current-mesh triangle, 1 = refine) from the union of every layer's painted + // area. If `painted_tri` is non-null, also fills, per layer, the fully-painted triangles to carry + // forward. Returns false when nothing is painted at all. Shared by the preview and the commit. + bool collect_paint_region(std::vector ®ion, + std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> *painted_tri) const; + // Isotropic remeshing (CGAL) to even out wildly varying triangle sizes so displacement has a // consistent density to work with. Target edge length in mm; 0 means "not yet initialised", filled // with the mesh's mean edge length the first time the control is shown. Like subdivide, it replaces @@ -326,8 +340,11 @@ private: // mouse button driving the drag hasn't been released yet - see on_render_input_window(). bool m_preview_params_dirty = false; - // See rebuild_bump_preview_mesh()/render_bump_preview_mesh(). - bool m_use_bump_preview = false; + // See rebuild_bump_preview_mesh()/render_bump_preview_mesh(). On by default: it is the cheap, + // instant-updating preview, so it is the better first impression while painting. The true- + // displacement view (a background CPU remesh) is one click away in the View row when the user + // wants an exact look at what Bake will produce. + bool m_use_bump_preview = true; // Set from the UV editor's per-move island edits instead of rebuilding the (potentially large) bump // mesh synchronously inside that mouse handler - doing the rebuild there stalled both the UV pane // and the 3D view. The rebuild is instead coalesced to once per 3D frame (render_painter_gizmo). diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index b7a9566e3f..ed45ec66e6 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -233,3 +233,91 @@ TEST_CASE("TextureDisplacement: boundary vertices shared with unpainted triangle CHECK_FALSE(still_at_original_position(fan.vertices[2])); // B: interior, must have moved CHECK_FALSE(still_at_original_position(fan.vertices[3])); // C: interior, must have moved } + +// Every undirected edge of a closed manifold mesh is shared by exactly two triangles. A T-junction +// (a hanging node where a refined region meets a coarse one) breaks that: the coarse side spans an +// edge that the fine side has replaced with two half-edges, so those three edges each show up an +// odd number of times. Counting edge uses is therefore an exact crack detector for a closed mesh. +static bool every_edge_used_twice(const indexed_triangle_set &its) +{ + std::map, int> uses; + for (const auto &t : its.indices) + for (int e = 0; e < 3; ++e) { + int a = t[e], b = t[(e + 1) % 3]; + if (a > b) + std::swap(a, b); + ++uses[{ a, b }]; + } + for (const auto &[edge, n] : uses) + if (n != 2) + return false; + return true; +} + +TEST_CASE("TextureDisplacement: adaptive subdivision is conformal and region-restricted", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + REQUIRE(every_edge_used_twice(cube)); // sanity: the input really is a closed manifold + + auto longest_edge = [](const indexed_triangle_set &its, const stl_triangle_vertex_indices &t) { + float m = 0.f; + for (int e = 0; e < 3; ++e) + m = std::max(m, (its.vertices[t[e]] - its.vertices[t[(e + 1) % 3]]).norm()); + return m; + }; + + SECTION("whole-mesh region refines everywhere and stays conformal") + { + std::vector region(cube.indices.size(), 1); + std::vector source; + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 3.f, 12, &source); + + CHECK(out.indices.size() > cube.indices.size()); // it actually refined + CHECK(every_edge_used_twice(out)); // ... without opening a single crack + + REQUIRE(source.size() == out.indices.size()); + for (int s : source) + CHECK((s >= 0 && s < int(cube.indices.size()))); // every child names a real parent + } + + SECTION("a partial region refines only there, and the boundary is still crack-free") + { + // Refine only the triangles whose centroid is in the upper (z > 5) half of the cube. + std::vector region(cube.indices.size(), 0); + size_t region_count = 0; + for (size_t i = 0; i < cube.indices.size(); ++i) { + const auto &t = cube.indices[i]; + const float cz = (cube.vertices[t[0]].z() + cube.vertices[t[1]].z() + cube.vertices[t[2]].z()) / 3.f; + if (cz > 5.f) { + region[i] = 1; + ++region_count; + } + } + REQUIRE(region_count > 0); + + std::vector source; + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 2.f, 12, &source); + + CHECK(out.indices.size() > cube.indices.size()); + CHECK(every_edge_used_twice(out)); // the refined/coarse seam has no T-junction + + // The region's own triangles came down in size; count how big the largest region-sourced + // output triangle is versus the largest region-sourced input triangle. + float max_in = 0.f, max_out = 0.f; + for (size_t i = 0; i < cube.indices.size(); ++i) + if (region[i]) + max_in = std::max(max_in, longest_edge(cube, cube.indices[i])); + for (size_t i = 0; i < out.indices.size(); ++i) + if (region[source[i]]) + max_out = std::max(max_out, longest_edge(out, out.indices[i])); + CHECK(max_out < max_in); // refinement genuinely happened inside the region + } + + SECTION("an empty region is a no-op") + { + std::vector region(cube.indices.size(), 0); + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 1.f, 12, nullptr); + CHECK(out.indices.size() == cube.indices.size()); + CHECK(out.vertices.size() == cube.vertices.size()); + } +} From 7b98433dd7bba6ff4a202627b1f93ba21dfddfa3 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 28 Jul 2026 11:42:40 +0200 Subject: [PATCH 16/89] Rewrite adaptive subdivision to refine worst-first against a triangle budget --- TEXTURE_DISPLACEMENT.md | 101 +++- src/libslic3r/TextureDisplacement.cpp | 498 ++++++++++++++---- src/libslic3r/TextureDisplacement.hpp | 89 +++- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 280 +++++++--- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 16 +- tests/libslic3r/test_texture_displacement.cpp | 139 ++++- 6 files changed, 877 insertions(+), 246 deletions(-) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index b4b904fea7..680c984998 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -197,34 +197,92 @@ T-junction, at the cost of densifying everywhere. Wired as a "Subdivide steps" s no subdivision), Apply snaps back to 0. Drops texture-displacement paint (no remap) via the standard `save_painting()`/`set_mesh()`/`restore_painting()` dance; the other four channels are remapped. -**Adaptive (`subdivide_mesh_adaptive()`)** — refine **only the painted area**, down to a target edge -length, by **Rivara longest-edge bisection**. This is the algorithm that was "scoped out" originally -for fear of the T-junction/crack problem; it is safe because it is *conformal by construction*. Each -pass bisects only **terminal** edges - an edge that is the longest edge of *every* triangle sharing it -- which splits both those triangles along one shared midpoint at once, so a hanging node is never -created. Only a triangle's own longest edge can be terminal, so each triangle is split by at most one -bisection per pass. When a triangle that still needs refining has a longest edge that is not yet -terminal, the neighbour across it has a strictly longer edge and is refined first; that propagation -grades the mesh down into the region and closes what would be cracks (pulling a thin, bounded band of -transition triangles just outside the painted patch). Tie-broken by mesh-vertex key so both sides of -an edge always agree on "the" longest. +**Adaptive (`subdivide_mesh_adaptive()`)** — refine **only the painted area**, by **Rivara longest-edge +bisection**. This is the algorithm that was "scoped out" originally for fear of the T-junction/crack +problem; it is safe because it is *conformal by construction*. Only **terminal** edges are ever bisected +- an edge that is the longest edge of *every* triangle sharing it - which splits both those triangles +along one shared midpoint at once, so a hanging node is never created. The edge to split for a triangle +that wants refining is found by **longest-edge propagation (LEPP)**: walk to the longest edge of +ever-longer-edged neighbours until a terminal one is reached, and bisect that. Edge length strictly +increases along the path (ties broken by mesh-vertex key, which both sides of an edge compute +identically), so the walk cannot cycle, and Rivara's result is that repeating it refines the original +triangle in a bounded number of bisections. The transition triangles it pulls in just outside the +painted patch are the graded band that makes the size change conformal. The win: a small decal on a big model no longer quadruples the *whole* model's triangle count. +**Run to completion, worst-first, against a triangle budget.** The refinement loop is not a fixed number +of sweeps: it holds every triangle that is over its criteria in a max-heap keyed by *how many times over* +it is, pops the worst, walks its LEPP, bisects, and re-scores. Edge adjacency (`nb[e]`, the triangle +across each edge) is built **once** and maintained incrementally through each bisection, so the cost +scales with the refined region rather than with the whole model. `max_triangles` is the only bound; +stopping on it leaves a perfectly valid, still-conformal mesh that spent its budget on the largest errors. + +This shape replaced a first version that ran a fixed 12 sweeps, each rebuilding a whole-mesh edge map and +bisecting one terminal edge per active triangle. Two failure modes came out of that, and they are worth +remembering because they look like separate bugs and are not: the sweeps were consumed grading the +*coarse surroundings* (whose edges are the longest, so they win every terminal-edge contest), which both +**stopped refinement of the painted patch far short** of the requested detail and left the band outside +it looking wildly over-refined relative to the patch itself. + **It carries the paint forward**, which is what makes it usable (uniform/remesh both drop paint). Because the refinement is *driven by* the paint, the remap is trivial: `subdivide_mesh_adaptive()` fills an `out_source[new_tri] = input_tri` map (children inherit their parent), and the gizmo rebuilds each layer's mask on the new mesh - a new triangle is painted iff its source was fully painted in that -layer. `collect_paint_region()` derives both the union refine-region (any vertex of a painted patch, -i.e. patch + a one-ring, so the boundary itself refines) and the per-layer fully-painted-triangle sets -(a `get_facets_strict(ENFORCER)` sub-triangle with all three *original* vertex indices == a whole, -fully-painted original triangle; a partial stroke's sub-triangles always carry a split vertex). The -other four channels still ride the normal `restore_painting()` remap. Covered by a conformality unit -test (`every_edge_used_twice` on a partially-refined cube - an exact crack detector for a closed mesh). +layer. `collect_paint_region()` derives both: +- the union refine-region: **exactly** the original triangles the brush touched, read straight off + `TriangleSplittingData::triangles_to_split` (`serialize()` records an entry per original triangle that + is either split - i.e. partially painted, the patch boundary - or carries a non-default state). No + dilation. An earlier version marked every triangle sharing a *vertex* with the patch, which drags in a + whole fan of huge unpainted neighbours and then refines *those* down to the resolution floor, since the + height field the detail test samples is not restricted to the painted area. The conformal closure + already grades the size change outward on its own; it does not need help. +- the per-layer fully-painted-triangle sets (a `get_facets_strict(ENFORCER)` sub-triangle with all three + *original* vertex indices == a whole, fully-painted original triangle; a partial stroke's sub-triangles + always carry a split vertex). + +The other four channels still ride the normal `restore_painting()` remap. Covered by a conformality unit +test (`every_edge_used_twice` on a partially-refined cube - an exact crack detector for a closed mesh), +plus tests that the target edge length is actually *reached* and that the budget caps the result without +opening a crack. Both share the gizmo's Preview/Apply/Done flow; the **"Only painted area (adaptive)"** checkbox picks the mode, and the adaptive preview follows the paint live (`rebuild_preview()` refreshes the wireframe -while the subdivide preview is open in adaptive mode). +while the subdivide preview is open in adaptive mode). The panel shows the previewed triangle count. + +**Feature-adaptive (follow texture detail).** A sub-mode of adaptive (the **"Follow texture detail"** +checkbox) that puts triangles where the *displaced surface actually bends*, not evenly. The insight: +a flat region or a linear **ramp** needs no extra vertices (linear interpolation is exact for a ramp); +what needs them is **curvature** - the *second* derivative, not the gradient. So the extra predicate is a +**chord-error** test: sample the combined displacement at the triangle's three edge midpoints *and its +centroid* (sampling the interior is what catches a bump sitting inside a triangle, the blind spot of an +edge-only test) and take the largest departure from the flat triangle's barycentric interpolation. Refine +while that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp ⇒ untouched; high on +a bump/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The +per-triangle error is cached and recomputed only for the children of a split. + +Four knobs bracket it, and all four matter: +- **"Max edge (mm)"** (`target_edge_length_mm`) is a **baseline that applies in feature mode too**. + Without it the chord test aliases: a big triangle over a fine pattern can sample four points that all + land at similar heights, report no error, and stall before refinement ever starts. The baseline + guarantees a sampling density fine enough for the curvature test to see the texture at all. +- **"Detail (mm)"** is the chord tolerance above. +- **"Min edge (mm)"** is a hard floor under both, and is what guarantees termination across a sharp + texture *step*, where the error never falls however fine the mesh gets. +- **"Added triangles (k)"** is the budget, passed as `max_triangles` (the model's own triangle count plus + the slider, so the control still means something on an already-dense model). + +The height field is `make_combined_displacement_sampler()` - it mirrors `build_texture_displacement()`'s +per-layer setup (decode, patch centroid, cylinder axis, blend order, "lowest layer folds additively") +but evaluated per point. Two deliberate simplifications, both erring toward *more* detail (safe - +over-refinement is never a crack): every sampleable layer is sampled at every point (no per-point paint +test), and edge-smoothing falloff is ignored. Note the first one is *why* the refine region must not be +dilated - outside the paint the sampler still reports full relief. **LSCM layers are skipped** (no +per-point UV); a purely LSCM stack yields a null sampler and the code falls back to the length baseline +alone. Per-vertex heights are sampled lazily, so a small patch on a huge model never pays for the rest of +it. Covered by unit tests: a Gaussian bump refines densely at its center and leaves flat corners coarse, +a linear ramp produces *zero* extra triangles (the case a gradient criterion would over-refine), and a +flat field still honours the max-edge baseline. ### Fast bump preview (GPU-only, no CPU meshing) @@ -384,9 +442,10 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward first impression while painting. The exact true-displacement view is one click away in the View row. - **Displacement resolution is capped by the mesh's own vertex density.** Baking only ever *moves* existing vertices (it never inserts any), so a coarse patch cannot show fine texture detail no - matter how high-resolution the height map is - that is what the "Subdivide model" button is for. - Since the rewrite the bake is topology-preserving, so this is now a hard, explicit property rather - than something partly papered over by the old per-layer re-meshing. + matter how high-resolution the height map is - that is what the subdivision controls are for + (uniform, adaptive, or feature-adaptive; see the Subdivision section). Since the rewrite the bake is + topology-preserving, so this is now a hard, explicit property rather than something partly papered + over by the old per-layer re-meshing. ## File map diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 7bc855cf15..1d8df784e7 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -1289,6 +1289,94 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume) return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data); } +HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data) +{ + // One decoded texture + placement per sampleable layer, in blend (slot) order. Held by shared_ptr + // so the returned closure owns it for as long as the subdivider keeps calling back. + struct Prepared { + DecodedHeightTexture tex; + TextureDisplacementLayer layer; // a copy of the params (depth/tiling/rotation/offset/blend/...) + Vec3f center; // patch centroid, for Cylindrical/Spherical + Vec3f axis; // cylinder axis, for Cylindrical + }; + auto prepared = std::make_shared>(); + + if (base_mesh.indices.empty()) + return nullptr; + + std::vector ordered; + for (const TextureDisplacementLayer &l : layers) + if (l.slot >= 0 && l.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) + ordered.push_back(&l); + std::sort(ordered.begin(), ordered.end(), + [](const TextureDisplacementLayer *a, const TextureDisplacementLayer *b) { return a->slot < b->slot; }); + + const std::vector vertex_normals = texture_displacement_vertex_normals(base_mesh); + const TriangleMesh selector_mesh(base_mesh); + + for (const TextureDisplacementLayer *layer : ordered) { + if (layer->projection_method == TextureProjectionMethod::LSCM) + continue; // no per-point UV -> not sampleable here (caller falls back to uniform for these) + const TriangleSelector::TriangleSplittingData &data = facets_data[size_t(layer->slot)]; + if (data.triangles_to_split.empty()) + continue; + const DecodedHeightTexture tex = decode_height_texture(*layer); + if (tex.empty()) + continue; + + TriangleSelector selector(selector_mesh); + selector.deserialize(data, false); + const indexed_triangle_set patch = selector.get_facets_strict(EnforcerBlockerType::ENFORCER); + if (patch.indices.empty()) + continue; + + // Patch centroid + cylinder axis, computed exactly as build_texture_displacement() does, so a + // Cylindrical/Spherical layer's detach criterion matches the geometry the bake will produce. + Vec3f average_normal = Vec3f::Zero(); + Vec3f centroid = Vec3f::Zero(); + int count = 0; + for (const stl_triangle_vertex_indices &tri : patch.indices) + for (int i = 0; i < 3; ++i) { + const int vi = tri[i]; + centroid += patch.vertices[size_t(vi)]; + ++count; + if (vi < int(vertex_normals.size())) + average_normal += vertex_normals[size_t(vi)]; + } + average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); + centroid = (count > 0) ? Vec3f(centroid / float(count)) : Vec3f::Zero(); + + Vec3f axis = Vec3f::UnitZ(); + const Vec3f an = average_normal.cwiseAbs(); + if (an.x() <= an.y() && an.x() <= an.z()) + axis = Vec3f::UnitX(); + else if (an.y() <= an.x() && an.y() <= an.z()) + axis = Vec3f::UnitY(); + + prepared->push_back({ tex, *layer, centroid, axis }); + } + + if (prepared->empty()) + return nullptr; + + return [prepared](const Vec3f &pos, const Vec3f &normal) -> float { + float total = 0.f; + bool any = false; + for (const Prepared &p : *prepared) { + const float h = sample_layer_height(p.tex, p.layer, pos, normal, p.center, p.axis, nullptr); + const float sign = p.layer.invert ? -1.f : 1.f; + const float signed_h = (h - p.layer.midlevel) * p.layer.depth_mm * sign; + // The first (lowest) sampleable layer folds additively; the rest use their own blend mode - + // same rule build_texture_displacement() applies per vertex. + total = blend_displacement(total, signed_h, any ? p.layer.blend_mode : TextureBlendMode::Add); + any = true; + } + return total; + }; +} + indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, float max_edge_length_mm, int max_iterations) { indexed_triangle_set current = mesh; @@ -1348,146 +1436,340 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const std::vector &refine_region, - float target_edge_length_mm, int max_iterations, - std::vector *out_source) + float target_edge_length_mm, int max_triangles, + std::vector *out_source, const HeightFieldSampler &sampler, + float chord_tolerance_mm, float min_edge_length_mm) { - // Each triangle carries the input-triangle index it descends from, so children inherit it and - // the caller can remap per-triangle data (paint masks) for free. - struct Tri { int v[3]; int src; }; + // Neighbour slots that are not a triangle index. + constexpr int NB_BOUNDARY = -1; // open edge: terminal on its own, bisected from this side alone + constexpr int NB_NONMANIFOLD = -2; // >2 triangles on the edge: never bisected, that would tear it + + // v[] and nb[] are parallel: nb[e] is the triangle across edge (v[e], v[(e+1)%3]). `src` is the + // input triangle this one descends from - children inherit it, so a caller can carry per-triangle + // data (a paint mask) across the topology change with no geometric remap. + struct Tri { int v[3]; int nb[3]; int src; }; std::vector verts = mesh.vertices; - std::vector tris; - tris.reserve(mesh.indices.size()); + std::vector tris(mesh.indices.size()); for (size_t i = 0; i < mesh.indices.size(); ++i) - tris.push_back({ { mesh.indices[i][0], mesh.indices[i][1], mesh.indices[i][2] }, int(i) }); + tris[i] = { { mesh.indices[i][0], mesh.indices[i][1], mesh.indices[i][2] }, + { NB_BOUNDARY, NB_BOUNDARY, NB_BOUNDARY }, int(i) }; - auto emit = [&](std::vector &t) -> indexed_triangle_set { + auto emit = [&]() -> indexed_triangle_set { indexed_triangle_set out; out.vertices = verts; - out.indices.reserve(t.size()); + out.indices.reserve(tris.size()); if (out_source) { out_source->clear(); - out_source->reserve(t.size()); + out_source->reserve(tris.size()); } - for (const Tri &tr : t) { - out.indices.emplace_back(tr.v[0], tr.v[1], tr.v[2]); + for (const Tri &t : tris) { + out.indices.emplace_back(t.v[0], t.v[1], t.v[2]); if (out_source) - out_source->push_back(tr.src); + out_source->push_back(t.src); } return out; }; + // Feature-adaptive when a sampler and a positive tolerance are supplied; otherwise refinement is + // driven by the length baseline alone. + const bool feature_mode = bool(sampler) && chord_tolerance_mm > 0.f; + const float min_floor_sq = min_edge_length_mm > 0.f ? min_edge_length_mm * min_edge_length_mm : 0.f; + const float target_sq = target_edge_length_mm > 0.f ? target_edge_length_mm * target_edge_length_mm : 0.f; + // refine_region is indexed by input-triangle index, and every triangle's src stays in that range // (children inherit their parent's src), so a wrong size would be an out-of-bounds read. Guard it. - if (target_edge_length_mm <= 0.f || refine_region.size() != mesh.indices.size()) - return emit(tris); + if (refine_region.size() != mesh.indices.size() || int(tris.size()) + 2 > max_triangles) + return emit(); + if (!feature_mode && target_sq <= 0.f) + return emit(); // no criterion at all if (std::none_of(refine_region.begin(), refine_region.end(), [](uint8_t v) { return v != 0; })) - return emit(tris); // nothing flagged: no-op - const float target_sq = target_edge_length_mm * target_edge_length_mm; + return emit(); // nothing flagged: no-op auto edge_key = [](int a, int b) -> uint64_t { if (a > b) std::swap(a, b); return (uint64_t(uint32_t(a)) << 32) | uint32_t(b); }; - auto edge_len_sq = [&](uint64_t k) -> float { - return (verts[int(k >> 32)] - verts[int(uint32_t(k))]).squaredNorm(); - }; - // The one edge of a triangle chosen as its "longest": greatest squared length, ties broken by the - // smaller edge key. The tie-break is by mesh-vertex indices, which both triangles sharing an edge - // compute identically - so they never disagree about whether that shared edge is "the" longest, - // which is what the conformality argument rests on. - auto longest_key = [&](const Tri &t) -> uint64_t { - uint64_t best_k = edge_key(t.v[0], t.v[1]); - float best_len = edge_len_sq(best_k); - for (int e = 1; e < 3; ++e) { - const uint64_t k = edge_key(t.v[e], t.v[(e + 1) % 3]); - const float l = edge_len_sq(k); - if (l > best_len || (l == best_len && k < best_k)) { - best_len = l; - best_k = k; - } - } - return best_k; - }; - for (int iter = 0; iter < max_iterations; ++iter) { - // edge -> the (up to two) triangles sharing it. A third triangle on an edge means a - // non-manifold input; it is left in the second slot's place and simply not treated as - // terminal, so such an edge is never bisected (better a missed refinement than a torn mesh). - std::unordered_map> edge_tris; - edge_tris.reserve(tris.size() * 3); + // Edge adjacency, built once here and then maintained incrementally by bisect() below. Rebuilding + // it per refinement pass is what made the previous version's cost scale with the whole model + // instead of with the refined region, and what forced the tiny pass budget that stopped + // refinement short. + { + struct EdgeRec { int he[2]; int count; }; // he = encoded half-edge (triangle * 3 + local edge) + std::unordered_map edges; + edges.reserve(tris.size() * 2); for (int ti = 0; ti < int(tris.size()); ++ti) for (int e = 0; e < 3; ++e) { - const uint64_t k = edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]); - auto it = edge_tris.find(k); - if (it == edge_tris.end()) - edge_tris.emplace(k, std::array{ ti, -1 }); - else if (it->second[1] == -1) - it->second[1] = ti; - else - it->second[0] = -2; // >2 triangles: poison this edge (never terminal) + EdgeRec &r = edges.try_emplace(edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]), + EdgeRec{ { -1, -1 }, 0 }) + .first->second; + if (r.count < 2) + r.he[r.count] = ti * 3 + e; + ++r.count; } - - std::vector tri_longest(tris.size()); for (int ti = 0; ti < int(tris.size()); ++ti) - tri_longest[ti] = longest_key(tris[ti]); - - // Which edges to bisect this pass: terminal (the longest edge of every triangle on it) AND - // long enough AND wanted by the region (either side in it). Terminal-ness is exactly what - // guarantees both sides split together, so no hanging node is ever produced. - std::unordered_set to_bisect; - for (const auto &[k, slot] : edge_tris) { - const int t0 = slot[0], t1 = slot[1]; - if (t0 < 0) - continue; // poisoned (non-manifold) - if (tri_longest[t0] != k) - continue; - if (t1 >= 0 && tri_longest[t1] != k) - continue; - if (edge_len_sq(k) <= target_sq) - continue; - const bool in_region = (refine_region[tris[t0].src] != 0) || - (t1 >= 0 && refine_region[tris[t1].src] != 0); - if (in_region) - to_bisect.insert(k); - } - if (to_bisect.empty()) - break; - - // One midpoint per bisected edge, shared by both sides. - std::unordered_map mid; - mid.reserve(to_bisect.size()); - for (const uint64_t k : to_bisect) { - const int a = int(k >> 32), b = int(uint32_t(k)); - mid.emplace(k, int(verts.size())); - verts.push_back((verts[a] + verts[b]) * 0.5f); - } - - std::vector next; - next.reserve(tris.size() + to_bisect.size()); - for (const Tri &t : tris) { - // At most one of a triangle's edges can be terminal (only its own longest can be), so at - // most one is in to_bisect - find that one. - int be = -1; - for (int e = 0; e < 3; ++e) - if (to_bisect.count(edge_key(t.v[e], t.v[(e + 1) % 3])) != 0) { - be = e; - break; - } - if (be == -1) { - next.push_back(t); - continue; + for (int e = 0; e < 3; ++e) { + const EdgeRec &r = edges.at(edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3])); + if (r.count > 2) + tris[ti].nb[e] = NB_NONMANIFOLD; + else if (r.count == 2) + tris[ti].nb[e] = (r.he[0] == ti * 3 + e ? r.he[1] : r.he[0]) / 3; } - const int a = t.v[be], b = t.v[(be + 1) % 3], c = t.v[(be + 2) % 3]; - const int m = mid.at(edge_key(a, b)); - next.push_back({ { a, m, c }, t.src }); // both children keep the original winding a->b->c - next.push_back({ { m, b, c }, t.src }); - } - tris.swap(next); } - return emit(tris); + // Feature mode: per-vertex surface normal, and the sampled displacement height at each vertex. + // Heights are filled in lazily - on a big model only a small painted region is ever looked at, and + // a sampler call is a texture fetch (plus trig) per layer, so sampling every vertex of the whole + // mesh up front was pure waste. Both arrays grow in lockstep with `verts`. + std::vector vnormal; + std::vector vheight; + std::vector vheight_valid; + if (feature_mode) { + vnormal.assign(verts.size(), Vec3f::Zero()); + for (const Tri &t : tris) { + const Vec3f fn = (verts[t.v[1]] - verts[t.v[0]]).cross(verts[t.v[2]] - verts[t.v[0]]); // area-weighted + for (int i = 0; i < 3; ++i) + vnormal[t.v[i]] += fn; + } + for (Vec3f &n : vnormal) { + const float l = n.norm(); + n = (l > 1e-12f) ? Vec3f(n / l) : Vec3f(Vec3f::UnitZ()); + } + vheight.assign(verts.size(), 0.f); + vheight_valid.assign(verts.size(), 0); + } + auto height_of = [&](int v) -> float { + if (!vheight_valid[v]) { + vheight[v] = sampler(verts[v], vnormal[v]); + vheight_valid[v] = 1; + } + return vheight[v]; + }; + + auto elen_sq = [&](int a, int b) -> float { return (verts[a] - verts[b]).squaredNorm(); }; + + // The one edge of a triangle taken as its "longest": greatest squared length, exact ties broken by + // the smaller (sorted) vertex-index key. Both triangles sharing an edge compute the same key for + // it, so they can never disagree about which of them is longest - the property the conformality + // argument and the LEPP walk's termination both rest on. + auto longest_local = [&](int ti) -> int { + const Tri &t = tris[ti]; + int best = 0; + float bl = elen_sq(t.v[0], t.v[1]); + uint64_t bk = edge_key(t.v[0], t.v[1]); + for (int e = 1; e < 3; ++e) { + const float l = elen_sq(t.v[e], t.v[(e + 1) % 3]); + const uint64_t k = edge_key(t.v[e], t.v[(e + 1) % 3]); + if (l > bl || (l == bl && k < bk)) { + bl = l; + bk = k; + best = e; + } + } + return best; + }; + + // How far the *displaced* surface departs from the flat triangle, sampled across the WHOLE + // triangle - the three edge midpoints and the centroid - not just one edge midpoint. Sampling the + // interior is what catches a bump that sits inside a triangle (the blind spot of an edge-only + // test). Cached per triangle: it can only change when the triangle is split, and then both + // children are fresh entries. + std::vector tri_err; + if (feature_mode) + tri_err.assign(tris.size(), -1.f); + auto detail_error = [&](int ti) -> float { + if (tri_err[ti] >= 0.f) + return tri_err[ti]; + const Tri &t = tris[ti]; + const Vec3f pa = verts[t.v[0]], pb = verts[t.v[1]], pc = verts[t.v[2]]; + const Vec3f na = vnormal[t.v[0]], nb = vnormal[t.v[1]], nc = vnormal[t.v[2]]; + const float ha = height_of(t.v[0]), hb = height_of(t.v[1]), hc = height_of(t.v[2]); + static const float BARY[4][3] = { { 0.5f, 0.5f, 0.f }, { 0.f, 0.5f, 0.5f }, + { 0.5f, 0.f, 0.5f }, { 1.f / 3, 1.f / 3, 1.f / 3 } }; + float maxerr = 0.f; + for (const auto &w : BARY) { + Vec3f n = w[0] * na + w[1] * nb + w[2] * nc; + const float nl = n.norm(); + n = (nl > 1e-12f) ? Vec3f(n / nl) : na; + const float actual = sampler(w[0] * pa + w[1] * pb + w[2] * pc, n); + maxerr = std::max(maxerr, std::abs(actual - (w[0] * ha + w[1] * hb + w[2] * hc))); + } + tri_err[ti] = maxerr; + return maxerr; + }; + + // How many times over its criteria a triangle is: <= 1 means "good enough, leave it alone", and + // the larger the value the more a split buys. Driving the heap with this is what makes a run that + // runs out of budget spend it on the worst offenders instead of wherever a sweep happened to + // reach. Triangles outside the region always score 0 - they are only ever touched by the conformal + // closure below, never refined on their own account. + auto priority = [&](int ti) -> float { + const Tri &t = tris[ti]; + if (refine_region[t.src] == 0) + return 0.f; + const int le = longest_local(ti); + const float ll = elen_sq(t.v[le], t.v[(le + 1) % 3]); + if (ll <= min_floor_sq) + return 0.f; // at the resolution floor - also what stops a sharp texture step going forever + float p = (target_sq > 0.f) ? ll / target_sq : 0.f; + if (feature_mode) + p = std::max(p, detail_error(ti) / chord_tolerance_mm); + return p; + }; + + auto set_nb = [&](int ti, int u, int v, int val) { + if (ti < 0) + return; + Tri &t = tris[ti]; + for (int e = 0; e < 3; ++e) + if ((t.v[e] == u && t.v[(e + 1) % 3] == v) || (t.v[e] == v && t.v[(e + 1) % 3] == u)) { + t.nb[e] = val; + return; + } + }; + + // Bisects triangle `ti` across its local edge `e`, which the caller has established is terminal. + // Both triangles on that edge are split in the one operation, around a single shared midpoint - + // which is exactly why a hanging node (and so a crack) can never appear. `ti` and the opposite + // triangle are each reused as one of their own children, so only two back-pointers in the + // surrounding mesh need repointing. Triangles whose geometry changed are left in `touched`. + std::vector touched; + auto bisect = [&](int ti, int e) -> bool { + const int a = tris[ti].v[e], b = tris[ti].v[(e + 1) % 3], c = tris[ti].v[(e + 2) % 3]; + const int n = tris[ti].nb[e]; + // Locate the shared edge from the far side before touching anything: bailing out half way + // through would be the one way this could leave a crack. + int f = -1; + if (n >= 0) { + for (int k = 0; k < 3; ++k) { + const int u = tris[n].v[k], w = tris[n].v[(k + 1) % 3]; + if ((u == a && w == b) || (u == b && w == a)) { + f = k; + break; + } + } + if (f < 0) { + tris[ti].nb[e] = NB_NONMANIFOLD; // inconsistent adjacency: refuse to split across it + return false; + } + } + + const int m = int(verts.size()); + verts.push_back(0.5f * (verts[a] + verts[b])); + if (feature_mode) { + const Vec3f mn = vnormal[a] + vnormal[b]; + const float ml = mn.norm(); + vnormal.push_back(ml > 1e-12f ? Vec3f(mn / ml) : vnormal[a]); + vheight.push_back(0.f); + vheight_valid.push_back(0); + } + + // Near side: ti becomes (a, m, c), the new triangle is (m, b, c). Both keep the original + // a->b->c winding. + const int nb_bc = tris[ti].nb[(e + 1) % 3]; + const int nb_ca = tris[ti].nb[(e + 2) % 3]; + const int src = tris[ti].src; + const int t2 = int(tris.size()); + tris.push_back(Tri{ { m, b, c }, { NB_BOUNDARY, nb_bc, ti }, src }); + { + Tri &t1 = tris[ti]; + t1.v[0] = a; t1.v[1] = m; t1.v[2] = c; + t1.nb[0] = NB_BOUNDARY; t1.nb[1] = t2; t1.nb[2] = nb_ca; + } + set_nb(nb_bc, b, c, t2); // that outer neighbour borders the second child now, not ti + if (feature_mode) { + tri_err.push_back(-1.f); + tri_err[ti] = -1.f; + } + touched.assign({ ti, t2 }); + + if (n < 0) { + return true; // boundary edge: nothing on the far side to split + } + + // Far side, same shape: n becomes (p, m, d), the new triangle is (m, q, d). + const int p = tris[n].v[f], q = tris[n].v[(f + 1) % 3], d = tris[n].v[(f + 2) % 3]; + const int nb_qd = tris[n].nb[(f + 1) % 3]; + const int nb_dp = tris[n].nb[(f + 2) % 3]; + const int nsrc = tris[n].src; + const int n2 = int(tris.size()); + tris.push_back(Tri{ { m, q, d }, { NB_BOUNDARY, nb_qd, n }, nsrc }); + { + Tri &n1 = tris[n]; + n1.v[0] = p; n1.v[1] = m; n1.v[2] = d; + n1.nb[0] = NB_BOUNDARY; n1.nb[1] = n2; n1.nb[2] = nb_dp; + } + set_nb(nb_qd, q, d, n2); + if (feature_mode) { + tri_err.push_back(-1.f); + tri_err[n] = -1.f; + } + + // Stitch the two sides back together: whichever far child holds `a` borders the near child + // that holds `a`. (Which one that is depends on how n happens to be wound.) + const int side_a = (p == a) ? n : n2; + const int side_b = (p == a) ? n2 : n; + tris[ti].nb[0] = side_a; // near child (a, m, c), edge (a, m) + tris[t2].nb[0] = side_b; // near child (m, b, c), edge (m, b) + set_nb(side_a, a, m, ti); + set_nb(side_b, b, m, t2); + touched.assign({ ti, t2, n, n2 }); + return true; + }; + + // Worst-first. Entries go stale as their triangle is split; a stale entry is harmless - it is + // re-scored on pop and dropped or re-pushed. The ordering is a budget-allocation heuristic only: + // neither correctness nor conformality depends on it. + std::priority_queue> queue; + for (int ti = 0; ti < int(tris.size()); ++ti) + if (const float p = priority(ti); p > 1.f) + queue.emplace(p, ti); + + // Every iteration either drops one satisfied triangle from the queue or performs exactly one + // bisection, and bisections are capped by the triangle budget, so this always terminates. + while (!queue.empty() && int(tris.size()) + 2 <= max_triangles) { + const int ti = queue.top().second; + queue.pop(); + if (priority(ti) <= 1.f) + continue; // stale: already refined past its criteria + + // Longest-Edge Propagation Path: step to the neighbour across the current longest edge for as + // long as that neighbour has a strictly longer one, then bisect the terminal edge we land on. + // Length strictly increases along the path, so it cannot cycle. + int cur = ti, split_edge = -1; + for (size_t guard = 0; guard <= tris.size(); ++guard) { + const int le = longest_local(cur); + const int nbr = tris[cur].nb[le]; + if (nbr == NB_NONMANIFOLD) + break; // cannot split across it without tearing the mesh: give up on this path + if (nbr == NB_BOUNDARY) { + split_edge = le; // boundary longest edge -> terminal + break; + } + const int nle = longest_local(nbr); + if (edge_key(tris[nbr].v[nle], tris[nbr].v[(nle + 1) % 3]) == + edge_key(tris[cur].v[le], tris[cur].v[(le + 1) % 3])) { + split_edge = le; // mutual longest edge -> terminal + break; + } + cur = nbr; + } + if (split_edge < 0 || !bisect(cur, split_edge)) + continue; // ti sits in a non-manifold neighbourhood and cannot be refined safely + + for (const int t : touched) + if (const float p = priority(t); p > 1.f) + queue.emplace(p, t); + // The bisection may have been a step on the way to ti rather than ti itself, in which case ti + // is not in `touched` and has to go back on the heap to be walked again. + if (cur != ti) + if (const float p = priority(ti); p > 1.f) + queue.emplace(p, ti); + } + + return emit(); } } // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 5bae458e43..39e3ce634f 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -2,6 +2,7 @@ #define slic3r_TextureDisplacement_hpp_ #include +#include #include #include #include @@ -493,6 +494,25 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set // and forwards to the overload above. indexed_triangle_set build_texture_displacement(const ModelVolume &volume); +// Returns a scalar height (in mm - a displacement magnitude) at a surface point, given that point's +// position and interpolated normal. This is what feature-adaptive subdivision samples to decide +// where the displaced surface has *curvature* worth spending triangles on. Called serially from the +// subdivider, so it only needs to be safe on the calling thread. +using HeightFieldSampler = std::function; + +// Builds a sampler of the *combined* (all-layers) displacement height in mm at an arbitrary surface +// point, for feature-adaptive subdivision. It mirrors build_texture_displacement()'s per-layer setup +// (decode, patch centroid, cylinder axis, blend order, "lowest layer folds additively") but evaluates +// per point instead of per vertex. Two deliberate simplifications, both erring toward *more* detail +// (safe - over-refinement is never a crack): every sampleable layer is evaluated at every point (no +// per-point paint-mask test, so a point sees all layers' textures, not only the ones painted there), +// and edge-smoothing's boundary falloff is ignored. LSCM layers have no per-point UV and are skipped. +// Returns a null sampler (bool false) when no layer can be sampled - the caller then falls back to +// uniform adaptive subdivision. +HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data); + // Uniformly subdivides `mesh` (every triangle recursively split into 4 via edge midpoints, using a // shared cache so a midpoint is computed once and reused by both triangles on either side of that // edge) until every edge is at or below max_edge_length_mm, or max_iterations passes have run, @@ -513,32 +533,57 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl // Adaptive subdivision by Rivara longest-edge bisection, restricted to a region. // -// Unlike subdivide_mesh_uniform() this only densifies where asked - a triangle is refined when its -// source is flagged in `refine_region` and its longest edge exceeds target_edge_length_mm - so a -// small painted patch on a large model does not quadruple the whole model's triangle count. It is -// nonetheless *conformal*: it never leaves a T-junction/crack at the boundary between the refined -// and coarse regions (the trap that made subdivide_mesh_uniform() deliberately whole-mesh). +// Unlike subdivide_mesh_uniform() this only densifies where asked - `refine_region` (indexed by +// input-triangle index; empty or all-false => no-op) flags the triangles allowed to drive refinement +// - so a small painted patch on a large model does not quadruple the whole model's triangle count. +// It is nonetheless *conformal*: it never leaves a T-junction/crack at the boundary between the +// refined and coarse regions (the trap that made subdivide_mesh_uniform() deliberately whole-mesh). // -// How it stays crack-free: each pass bisects only "terminal" edges - an edge that is the longest -// edge of *every* triangle sharing it. Bisecting such an edge splits both its triangles 1->2 along -// the same new midpoint at once, so no hanging node is ever created. Because only a triangle's own -// longest edge can be terminal, each triangle is split by at most one terminal edge per pass. When -// a triangle that needs refining has a longest edge that is *not* yet terminal, the neighbour across -// it has a strictly longer edge and is refined first; that propagation is what grades the mesh down -// into the region and closes what would otherwise be cracks (Rivara, "New longest-edge algorithms"). -// The transition triangles it pulls in just outside the region are a thin, bounded band. +// A triangle wants refining while it is over at least one of these, whichever applies: +// - length: its longest edge exceeds `target_edge_length_mm` (a *baseline* - it applies in feature +// mode too, and is what stops a coarse triangle from being declared flat merely because +// the four points the chord test samples happened to land at similar heights on a +// high-frequency texture: the classic aliasing stall); +// - feature: (only when `sampler` is set and `chord_tolerance_mm > 0`) the *displaced* surface +// departs from the flat triangle by more than `chord_tolerance_mm`, measured as the max +// over the three edge midpoints AND the centroid of |sampled displacement - the flat +// triangle's barycentric interpolation|. Sampling the interior, not just edge midpoints, +// is what catches a bump that sits inside a triangle. This is a *curvature* test: it is +// exactly zero on a plane or a linear ramp (barycentric interpolation is exact there, so +// those stay coarse - the case a gradient criterion would over-refine) and large on a +// bump/ridge/noise. +// `min_edge_length_mm` is a hard floor under both: no triangle whose longest edge is already at or +// below it is ever refined, which is also what guarantees termination across a sharp texture step +// (where the chord error never falls below the tolerance no matter how fine the mesh gets). // -// `refine_region` is indexed by input-triangle index (empty or all-false => no-op). If `out_source` -// is non-null it is resized to the output triangle count and out_source[i] receives the input -// triangle that output triangle i descends from (children inherit their parent's index), so a caller -// can carry per-triangle data - e.g. a paint mask - across the topology change without a geometric -// remap. `max_iterations` bounds the worst case; ties in "longest edge" are broken by a mesh-vertex -// key so both triangles on an edge always agree, at the cost of occasionally stopping one pass early -// on a pathologically tie-heavy mesh (a quality shortfall, never a crack). +// Refinement runs to completion, not for a fixed number of passes: triangles are taken worst-first +// from a max-heap keyed by how many times over its criteria each one is, so a run that hits the +// `max_triangles` budget has spent it on the largest errors rather than wherever a sweep happened to +// reach. The budget is the only bound on a pathological height field; stopping on it leaves a +// perfectly valid, still-conformal mesh. +// +// How it stays crack-free: only "terminal" edges are ever bisected - an edge that is the longest edge +// of *both* triangles sharing it (or a boundary edge that is the longest of its one triangle). +// Bisecting such an edge splits both its triangles 1->2 around the same new midpoint, so a hanging +// node is never created. The edge to split for a triangle that wants refining is found by Rivara +// longest-edge propagation (LEPP): walk to the longest edge of ever-longer-edged neighbours until a +// terminal edge is reached, and bisect that. Edge length strictly increases along the path (ties +// broken by a mesh-vertex key, which both sides of an edge compute identically), so the walk cannot +// cycle, and Rivara's result is that repeating it refines the original triangle in a bounded number +// of bisections - closing the propagation gap a plain per-edge test leaves behind. The transition +// triangles this pulls in just outside the region are the graded band that makes the size change +// conformal; they are a bounded cost paid once, not a per-pass tax. +// +// If `out_source` is non-null it is resized to the output triangle count and out_source[i] receives +// the input triangle that output triangle i descends from (children inherit their parent's index), so +// a caller can carry per-triangle data - e.g. a paint mask - across the topology change without a +// geometric remap. indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const std::vector &refine_region, - float target_edge_length_mm, int max_iterations = 12, - std::vector *out_source = nullptr); + float target_edge_length_mm, int max_triangles = 1000000, + std::vector *out_source = nullptr, + const HeightFieldSampler &sampler = nullptr, + float chord_tolerance_mm = 0.f, float min_edge_length_mm = 0.f); } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 0fd02a3ba6..ea489364aa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -166,7 +166,7 @@ void GLGizmoTextureDisplacement::on_shutdown() m_seam_path_mode = false; m_seam_path_anchor = -1; m_subdivide_editing = false; - m_subdivide_preview_count = -1; + m_subdivide_preview_tris = -1; m_subdivide_preview_glmodel.reset(); m_bump_active_chart = -1; m_bump_active_vertex.clear(); @@ -231,6 +231,11 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() m_bump_preview_dirty = false; } const bool use_bump = m_use_bump_preview && m_bump_preview_glmodel.is_initialized(); + // In Checker/Distortion mode the UV-check overlay *is* the surface visualization the user is + // looking at, so the opaque paint-selection highlight must not be drawn on top of it - same + // reasoning as skipping it for the bump preview (see bug #12). Without this the painted area + // covers the checker/heatmap and it can't be seen. + const bool show_paint_overlay = m_uv_check_mode == UVCheckMode::None; if (use_bump) { m_parent.toggle_model_objects_visibility(true); if (ModelVolume *mv = texture_volume()) @@ -244,11 +249,13 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() m_c->selection_info()->get_active_instance(), mv); render_preview_mesh(); - glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); - glsafe(::glPolygonOffset(-1.0f, -1.0f)); - render_triangles(selection); - glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); - } else { + if (show_paint_overlay) { + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(-1.0f, -1.0f)); + render_triangles(selection); + glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); + } + } else if (show_paint_overlay) { render_triangles(selection); } @@ -744,10 +751,9 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() if (base.vertices.size() != patch.vertices.size()) return; // shouldn't happen: get_facets_strict() always returns the full vertex array - std::vector is_painted(patch.vertices.size(), false); - for (const stl_triangle_vertex_indices &tri : patch.indices) - for (int i = 0; i < 3; ++i) - is_painted[tri[i]] = true; + // Unpainted triangles, so the surrounding surface still renders (the render path hides the real + // model in bump mode). get_facets_strict() returns the same vertex array whatever state is asked. + const indexed_triangle_set rest = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::NONE); // For LSCM we hand the shader the finished per-vertex texture uv (island placement + tiling/ // rotation/offset already folded in, exactly what the bake samples), because it cannot be @@ -761,26 +767,38 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() vertex_uv.clear(); GLModel::Geometry init_data; - // P3N3T2: normal.x carries the paint weight, tex_coord carries the precomputed uv (see the vertex - // shader). Reuses a standard GLModel layout rather than a bespoke vertex buffer. + // P3N3T2: normal.x carries the paint weight, tex_coord the precomputed uv (see the vertex shader). init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; - init_data.reserve_vertices(base.vertices.size()); - init_data.reserve_indices(base.indices.size() * 3); - // Every triangle of the whole mesh is included (not just the painted ones) so the surrounding, - // un-bumped surface still renders - the per-vertex weight (0 outside the patch) is what fades - // the shader's bump effect to nothing there, exactly like the true-displacement preview fades - // to the untouched surface at its boundary. - // normal.y flags the island currently dragged in the UV editor, so the shader can move just that - // island through the island_delta uniform (see the bump shaders / on_island_edited). - const bool have_active = m_bump_active_chart >= 0 && !m_bump_active_vertex.empty(); - for (size_t vi = 0; vi < base.vertices.size(); ++vi) { - const float active = (have_active && vi < m_bump_active_vertex.size() && m_bump_active_vertex[vi]) ? 1.f : 0.f; - const Vec3f weight_normal(is_painted[vi] ? 1.f : 0.f, active, 0.f); - const Vec2f uv = m_bump_preview_uses_vertex_uv ? vertex_uv[vi] : Vec2f::Zero(); - init_data.add_vertex(base.vertices[vi], weight_normal, uv); - } - for (const stl_triangle_vertex_indices &tri : base.indices) - init_data.add_triangle(unsigned(tri[0]), unsigned(tri[1]), unsigned(tri[2])); + + // Per-triangle weighting via a *flat* (unshared-vertex) mesh: every corner of a painted triangle + // gets weight 1, every corner of an unpainted one weight 0. This is what a coarse mesh needs - one + // painted face of a raw cube has no strictly-interior vertex (all 8 are shared), so per-vertex + // weighting would either bleed onto the neighbours (boundary weight 1) or vanish outright (boundary + // weight 0, which is what made a single face show nothing). Duplicating vertices costs no shading + // quality here because the bump shader takes its surface normal from screen-space derivatives of + // position (dFdx/dFdy), not from a per-vertex normal. normal.y flags the UV-editor island being + // dragged so the shader can move just that island via the island_delta uniform. + const bool have_active = m_bump_active_chart >= 0 && !m_bump_active_vertex.empty(); + const size_t tri_total = patch.indices.size() + rest.indices.size(); + init_data.reserve_vertices(tri_total * 3); + init_data.reserve_indices(tri_total * 3); + unsigned vcount = 0; + const auto emit_triangles = [&](const indexed_triangle_set &its, float weight) { + for (const stl_triangle_vertex_indices &tri : its.indices) { + for (int i = 0; i < 3; ++i) { + const int idx = tri[i]; + const float act = (have_active && idx >= 0 && size_t(idx) < m_bump_active_vertex.size() && + m_bump_active_vertex[size_t(idx)]) ? 1.f : 0.f; + const Vec2f uv = (weight > 0.5f && m_bump_preview_uses_vertex_uv && size_t(idx) < vertex_uv.size()) ? + vertex_uv[size_t(idx)] : Vec2f::Zero(); + init_data.add_vertex(its.vertices[size_t(idx)], Vec3f(weight, act, 0.f), uv); + } + init_data.add_triangle(vcount, vcount + 1, vcount + 2); + vcount += 3; + } + }; + emit_triangles(patch, 1.f); // painted -> bumped + emit_triangles(rest, 0.f); // untouched surface -> flat, so it still shows but isn't bumped m_bump_preview_glmodel.init_from(std::move(init_data)); // GLModel::render() unconditionally re-sets the shader's "uniform_color" from this internal @@ -2186,6 +2204,13 @@ void GLGizmoTextureDisplacement::update_model_object() updated |= mv->texture_displacement_facet(m_active_layer_slot).set(*m_triangle_selectors[idx]); } + // The fast (bump) preview reads the live selector, so it has to be rebuilt after any stroke that + // flushes here - not only when set() reports a change. Rebuilding it via rebuild_preview() below + // is gated on `updated`, which misses e.g. the first paint into a slot; marking it dirty makes the + // render loop (render_painter_gizmo) rebuild it next frame regardless. Without this, fast preview - + // now the default view - stayed blank until a full reload (select-whole-model / reopen). + m_bump_preview_dirty = true; + if (updated) { const ModelObjectPtrs &mos = wxGetApp().model().objects; wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); @@ -2676,16 +2701,17 @@ bool GLGizmoTextureDisplacement::collect_paint_region( for (auto &pt : *painted_tri) pt.clear(); - // Sorted-vertex-triple -> triangle index, so a fully-painted patch sub-triangle (which comes - // back with the original mesh's own three vertex indices) can be mapped to its source triangle. - // A sub-triangle produced by a *partial* brush stroke has at least one appended (split) vertex, - // so "all three indices are original" is exactly the test for a whole, fully-painted triangle. + // Sorted-vertex-triple -> triangle index, so a fully-painted patch sub-triangle (which comes back + // with the original mesh's own three vertex indices) can be mapped to its source triangle. Only + // the paint carry-forward needs it, and the live subdivide preview calls this on every slider + // frame, so it is not built for the region-only path. std::map, int> tri_by_verts; - for (size_t i = 0; i < ntri; ++i) { - std::array k{ its.indices[i][0], its.indices[i][1], its.indices[i][2] }; - std::sort(k.begin(), k.end()); - tri_by_verts.emplace(k, int(i)); - } + if (painted_tri) + for (size_t i = 0; i < ntri; ++i) { + std::array k{ its.indices[i][0], its.indices[i][1], its.indices[i][2] }; + std::sort(k.begin(), k.end()); + tri_by_verts.emplace(k, int(i)); + } bool any_paint = false; for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { @@ -2693,40 +2719,34 @@ bool GLGizmoTextureDisplacement::collect_paint_region( if (!TriangleSelector::has_facets(data, EnforcerBlockerType::ENFORCER)) continue; - TriangleSelector sel(mv->mesh()); - sel.deserialize(data, false); - const indexed_triangle_set patch = sel.get_facets_strict(EnforcerBlockerType::ENFORCER); + // The refine region is exactly the original triangles the brush touched. `triangles_to_split` + // lists precisely those: serialize() records an entry for every original triangle that is + // either split (i.e. partially painted, which is the patch boundary) or carries a non-default + // state (fully painted). No dilation - an earlier version marked every triangle sharing a + // *vertex* with the patch, which on a coarse model pulls in a whole fan of huge unpainted + // neighbours and then refines them to the resolution floor, since the height field the detail + // test samples is not restricted to the painted area. The conformal closure inside + // subdivide_mesh_adaptive() already grades the size change outward on its own. + for (const TriangleSelector::TriangleBitStreamMapping &m : data.triangles_to_split) + if (size_t(m.triangle_idx) < ntri) + region[m.triangle_idx] = 1; - std::vector painted_vertex(nvert, 0); - if (painted_tri) + if (painted_tri) { + TriangleSelector sel(mv->mesh()); + sel.deserialize(data, false); (*painted_tri)[slot].assign(ntri, 0); - - for (const stl_triangle_vertex_indices &t : patch.indices) { - bool all_original = true; - for (int k = 0; k < 3; ++k) { - if (size_t(t[k]) < nvert) - painted_vertex[t[k]] = 1; // marks the refine region (any coverage, plus a ring) - else - all_original = false; // a split vertex -> this is a partial sub-triangle - } - if (all_original && painted_tri) { + for (const stl_triangle_vertex_indices &t : sel.get_facets_strict(EnforcerBlockerType::ENFORCER).indices) { + // A sub-triangle produced by a *partial* stroke always carries at least one appended + // (split) vertex, so "all three indices are original" is exactly the test for a whole, + // fully-painted triangle - the only kind whose paint can be inherited wholesale. + if (size_t(t[0]) >= nvert || size_t(t[1]) >= nvert || size_t(t[2]) >= nvert) + continue; std::array k{ t[0], t[1], t[2] }; std::sort(k.begin(), k.end()); if (auto it = tri_by_verts.find(k); it != tri_by_verts.end()) (*painted_tri)[slot][it->second] = 1; } } - - // A triangle is in the refine region if any of its vertices is painted. That deliberately - // over-includes a one-triangle ring just outside the strict patch, which is exactly the - // transition band the conformal bisection would pull in anyway - and it makes sure the patch - // boundary itself gets refined rather than staying coarse right where the relief ends. - for (size_t i = 0; i < ntri; ++i) - for (int k = 0; k < 3; ++k) - if (painted_vertex[its.indices[i][k]]) { - region[i] = 1; - break; - } any_paint = true; } return any_paint; @@ -2749,17 +2769,36 @@ void GLGizmoTextureDisplacement::subdivide_model_adaptive() return; } + // Feature-adaptive: sample the combined displacement so refinement follows texture curvature. A + // null sampler (only LSCM layers, or nothing decodable) falls back to the uniform target below. + HeightFieldSampler sampler; + if (m_subdivide_feature) { + TextureDisplacementFacetsData facets{}; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + facets[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + sampler = make_combined_displacement_sampler(mv->mesh().its, mv->texture_displacement_layers, facets); + } + // Do the (potentially slow) refinement before taking the snapshot, so a no-op leaves no empty // undo step - mirrors remesh_model(). + // "Min edge" is a feature-mode control (it is the floor the curvature test refines down to); in + // plain adaptive mode the target edge length is the only criterion, so the floor must not be + // allowed to silently override a target the user set below it. + const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f; + const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f; std::vector source; indexed_triangle_set refined; { wxBusyCursor wait; - refined = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, 12, &source); + // The budget slider is "triangles the refinement may *add*", so the model's own count is the + // baseline - otherwise the control would be meaningless (or a dead end) on a dense model. + refined = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, + int(mv->mesh().its.indices.size()) + m_subdivide_budget_k * 1000, + &source, sampler, tol, floor); } if (refined.indices.size() == mv->mesh().its.indices.size()) { - show_error(nullptr, _u8L("Nothing to subdivide - the painted area is already at or below the target " - "edge length.")); + show_error(nullptr, _u8L("Nothing to subdivide - the painted area already meets the target edge " + "length and detail tolerance, or the triangle budget is already used up.")); return; } @@ -2852,7 +2891,7 @@ void GLGizmoTextureDisplacement::remesh_model() void GLGizmoTextureDisplacement::rebuild_subdivide_preview() { m_subdivide_preview_glmodel.reset(); - m_subdivide_preview_count = -1; + m_subdivide_preview_tris = -1; const ModelVolume *mv = texture_volume(); if (mv == nullptr) return; @@ -2866,7 +2905,20 @@ void GLGizmoTextureDisplacement::rebuild_subdivide_preview() std::vector region; if (!collect_paint_region(region, nullptr)) return; // nothing painted yet: nothing to preview - its = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, 12, nullptr); + HeightFieldSampler sampler; + if (m_subdivide_feature) { + TextureDisplacementFacetsData facets{}; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + facets[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + sampler = make_combined_displacement_sampler(mv->mesh().its, mv->texture_displacement_layers, facets); + } + // Feature mode: curvature (Detail tolerance) on top of the Max-edge baseline, down to the + // Min-edge floor. Plain adaptive: tol 0, so only the target-edge-length criterion applies. + const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f; + const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f; + its = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, + int(mv->mesh().its.indices.size()) + m_subdivide_budget_k * 1000, + nullptr, sampler, tol, floor); } else { if (m_subdivide_count < 1) return; @@ -2874,7 +2926,7 @@ void GLGizmoTextureDisplacement::rebuild_subdivide_preview() } if (its.indices.empty()) return; - m_subdivide_preview_count = m_subdivide_count; + m_subdivide_preview_tris = int(its.indices.size()); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; @@ -3701,6 +3753,13 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // once on release - rebuilding the preview (a real CPU mesh recompute) on every one of those // frames is what made dragging these sliders feel slow. Only rebuild once the mouse button // that's driving the drag is released, i.e. once per edit instead of dozens of times per drag. + // The feature-adaptive subdivision follows the *displaced* surface (it samples depth * texture), + // so depth / tile-size / rotation / invert all change where it puts triangles. The heavy + // displacement preview below only rebuilds on release, which made the subdivide wireframe look + // like it ignored those edits - so rebuild it live here (during the drag), the same cadence its + // own sliders use. Cheap: it is bounded by the painted region. + if (m_preview_params_dirty && m_subdivide_editing && m_subdivide_adaptive && m_subdivide_feature) + rebuild_subdivide_preview(); if (m_preview_params_dirty && (m_auto_update || !ImGui::IsMouseDown(ImGuiMouseButton_Left))) { rebuild_preview(); // Same edits (tile size, rotation, offset, a new texture) are what the projector window @@ -3736,23 +3795,80 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm(); ++cnt; } - m_subdivide_target_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)) * 0.5f, 0.1f, 20.f) : 1.f; + m_subdivide_target_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)) * 0.5f, 0.001f, 20.f) : 1.f; } - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - // "##subdiv" keeps the visible label "Target edge (mm)" but gives it an ImGui ID distinct - // from the remesh slider below, which shows the same text - same label == same widget to - // ImGui, so without this the two would collide. - if (m_imgui->slider_float(std::string(_u8L("Target edge (mm)")) + "##subdiv", &m_subdivide_target_mm, - 0.1f, 20.f, "%.2f", ImGuiLogSlider)) { - if (m_subdivide_editing && !ImGui::IsMouseDown(ImGuiMouseButton_Left)) + // Live preview: rebuild the wireframe as the slider moves, not only on release, so it tracks + // the value. The rebuild is bounded by the painted region, so it stays responsive. + const auto preview_live = [this]() { + if (m_subdivide_editing) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + }; + + if (ImGui::Checkbox(_u8L("Follow texture detail").c_str(), &m_subdivide_feature)) { + if (m_subdivide_editing) rebuild_subdivide_preview(); m_parent.set_as_dirty(); } - ImGui::PopItemWidth(); if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Triangles in the painted area are split until every edge is at or below this " - "length. Smaller means finer detail and more triangles."), + m_imgui->tooltip(_u8L("Put triangles only where the texture actually bends - dense over hills, ridges and " + "noise, sparse over flat areas and smooth slopes - instead of an even density " + "everywhere. Uses the combined displacement of all painted layers."), m_imgui->scaled(20.f)); + + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + // The edge-length target is a baseline in both modes. In feature mode it is what guarantees + // the curvature test can actually see the texture: left too coarse, a big triangle over a + // fine pattern can sample four points that all happen to land at similar heights, report no + // error, and stall before refinement ever starts. "##subdiv" avoids an ID clash with the + // remesh "Target edge (mm)" slider below (same label == same widget to ImGui). + if (m_imgui->slider_float(std::string(m_subdivide_feature ? _u8L("Max edge (mm)") : _u8L("Target edge (mm)")) + + "##subdiv", + &m_subdivide_target_mm, 0.001f, 20.f, "%.3f", ImGuiLogSlider)) + preview_live(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(m_subdivide_feature ? + _u8L("Nothing in the painted area stays coarser than this, even where the texture is " + "flat. Keep it near the size of the features you want picked up - too coarse and " + "fine detail can be missed entirely.") : + _u8L("Triangles in the painted area are split until every edge is at or below this " + "length. Smaller means finer detail and more triangles."), + m_imgui->scaled(20.f)); + + if (m_subdivide_feature) { + // On top of the baseline: the chord-error tolerance, and the resolution floor. + if (m_imgui->slider_float(std::string(_u8L("Detail (mm)")) + "##subdivdetail", &m_subdivide_detail_mm, + 0.001f, 1.f, "%.3f", ImGuiLogSlider)) + preview_live(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How closely the mesh follows the texture's relief. Smaller captures finer bumps; " + "larger only chases the big features."), + m_imgui->scaled(20.f)); + if (m_imgui->slider_float(std::string(_u8L("Min edge (mm)")) + "##subdivmin", &m_subdivide_min_edge_mm, + 0.001f, 20.f, "%.3f", ImGuiLogSlider)) + preview_live(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("The finest triangle size refinement will ever produce. Smaller captures finer " + "relief; also stops runaway subdivision at a sharp texture step, where the " + "surface never becomes flat."), + m_imgui->scaled(20.f)); + } + + // The budget. Refinement is worst-error-first, so a run that hits it has still spent its + // triangles on the biggest deviations - raising it buys detail, it does not redistribute it. + if (ImGui::SliderInt((_u8L("Added triangles (k)") + "##subdivbudget").c_str(), &m_subdivide_budget_k, 10, 2000)) { + m_subdivide_budget_k = std::clamp(m_subdivide_budget_k, 10, 2000); + preview_live(); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How many thousand triangles the refinement may add. It always splits the " + "worst-fitting triangle first, so a run that uses the whole budget has still spent " + "it where it shows most - raise this if the preview still looks too coarse."), + m_imgui->scaled(20.f)); + ImGui::PopItemWidth(); + + if (m_subdivide_editing && m_subdivide_preview_tris > 0) + m_imgui->text(Slic3r::format(_u8L("Preview: %1% triangles"), m_subdivide_preview_tris)); } else { ImGui::PushItemWidth(m_imgui->scaled(8.4f)); if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 0, 5)) { @@ -3808,7 +3924,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::SameLine(); if (m_imgui->button(_u8L("Done"))) { m_subdivide_editing = false; - m_subdivide_preview_count = -1; + m_subdivide_preview_tris = -1; m_subdivide_preview_glmodel.reset(); m_parent.set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 018e1f00c8..a4807eb61d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -302,7 +302,7 @@ private: // committed, i.e. the most expensive thing the panel can do, on every Apply. int m_subdivide_count = 1; bool m_subdivide_editing = false; - int m_subdivide_preview_count = -1; // the count m_subdivide_preview_glmodel was built for + int m_subdivide_preview_tris = -1; // triangle count of the previewed result, shown in the panel GLModel m_subdivide_preview_glmodel; void rebuild_subdivide_preview(); void render_subdivide_preview(); @@ -314,6 +314,20 @@ private: // are painted), so the region survives the subdivision instead of being dropped. bool m_subdivide_adaptive = false; float m_subdivide_target_mm = 0.f; // 0 = not yet seeded; filled from the mesh on first show + // Feature-adaptive sub-mode: put the triangles where the *displaced surface* bends (texture + // curvature) rather than spreading them evenly. `detail_mm` is the chord-error tolerance ("Detail" + // slider: how far the true surface may sit off the flat triangle before it is split); the target + // above stays in play as a coarse baseline ("Max edge"), and `min_edge_mm` is the hard floor + // ("Min edge"). See subdivide_mesh_adaptive(). + bool m_subdivide_feature = false; + float m_subdivide_detail_mm = 0.05f; + float m_subdivide_min_edge_mm = 0.1f; + // How many thousand triangles refinement may *add* (the mesh's own count is added on before it is + // passed as subdivide_mesh_adaptive()'s absolute cap, so the control still means something on a + // dense model). Refinement is worst-error-first, so hitting the budget still yields the best mesh + // that many triangles can buy - and it is what keeps a fine "Detail" over a noisy texture from + // turning into an out-of-memory, or an unrenderable preview wireframe. + int m_subdivide_budget_k = 200; void subdivide_model_adaptive(); // Fills `region` (per current-mesh triangle, 1 = refine) from the union of every layer's painted // area. If `painted_tri` is non-null, also fills, per layer, the fully-painted triangles to carry diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index ed45ec66e6..6b3420845e 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -1,6 +1,7 @@ #define NOMINMAX #include +#include #include #include @@ -270,11 +271,20 @@ TEST_CASE("TextureDisplacement: adaptive subdivision is conformal and region-res { std::vector region(cube.indices.size(), 1); std::vector source; - const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 3.f, 12, &source); + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 3.f, 100000, &source); CHECK(out.indices.size() > cube.indices.size()); // it actually refined CHECK(every_edge_used_twice(out)); // ... without opening a single crack + // Refinement runs to completion, not for a fixed number of passes: with the whole mesh in the + // region and budget to spare, *every* edge really does end up at or below the target. This is + // the regression that matters - an earlier version quietly stopped a long way short, having + // spent its pass budget grading the coarse surroundings. + float worst = 0.f; + for (const auto &t : out.indices) + worst = std::max(worst, longest_edge(out, t)); + CHECK(worst <= 3.f); + REQUIRE(source.size() == out.indices.size()); for (int s : source) CHECK((s >= 0 && s < int(cube.indices.size()))); // every child names a real parent @@ -296,28 +306,133 @@ TEST_CASE("TextureDisplacement: adaptive subdivision is conformal and region-res REQUIRE(region_count > 0); std::vector source; - const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 2.f, 12, &source); + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 2.f, 100000, &source); CHECK(out.indices.size() > cube.indices.size()); CHECK(every_edge_used_twice(out)); // the refined/coarse seam has no T-junction - // The region's own triangles came down in size; count how big the largest region-sourced - // output triangle is versus the largest region-sourced input triangle. + // Inside the region the target is actually met - refinement is not cut short by a pass budget. + // Outside it, only the graded transition band conformality requires is touched, so plenty of + // the unpainted mesh is still coarser than the target: the region was not a suggestion. float max_in = 0.f, max_out = 0.f; - for (size_t i = 0; i < cube.indices.size(); ++i) - if (region[i]) - max_in = std::max(max_in, longest_edge(cube, cube.indices[i])); - for (size_t i = 0; i < out.indices.size(); ++i) - if (region[source[i]]) - max_out = std::max(max_out, longest_edge(out, out.indices[i])); - CHECK(max_out < max_in); // refinement genuinely happened inside the region + for (size_t i = 0; i < out.indices.size(); ++i) { + float &acc = region[source[i]] ? max_in : max_out; + acc = std::max(acc, longest_edge(out, out.indices[i])); + } + CHECK(max_in <= 2.f); + CHECK(max_out > 2.f); + + std::vector all(cube.indices.size(), 1); + const indexed_triangle_set whole = subdivide_mesh_adaptive(cube, all, 2.f, 100000); + CHECK(out.indices.size() < whole.indices.size()); // ... and it cost less than doing the lot + } + + SECTION("the triangle budget caps the result and still leaves a conformal mesh") + { + std::vector region(cube.indices.size(), 1); + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 0.05f, /*max_triangles*/ 500); + CHECK(out.indices.size() <= 500); + CHECK(out.indices.size() > cube.indices.size()); // it spent the budget rather than giving up + CHECK(every_edge_used_twice(out)); // stopping on the budget is not a crack } SECTION("an empty region is a no-op") { std::vector region(cube.indices.size(), 0); - const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 1.f, 12, nullptr); + const indexed_triangle_set out = subdivide_mesh_adaptive(cube, region, 1.f, 100000, nullptr); CHECK(out.indices.size() == cube.indices.size()); CHECK(out.vertices.size() == cube.vertices.size()); } } + +TEST_CASE("TextureDisplacement: feature-adaptive subdivision follows curvature, not slope", "[TextureDisplacement]") +{ + // A flat sheet, tessellated into a regular grid to give the bisector something to work with. + indexed_triangle_set plane; + plane.vertices = { { 0.f, 0.f, 0.f }, { 1.f, 0.f, 0.f }, { 1.f, 1.f, 0.f }, { 0.f, 1.f, 0.f } }; + plane.indices = { { 0, 1, 2 }, { 0, 2, 3 } }; + const indexed_triangle_set grid = subdivide_mesh_uniform(plane, 0.15f, 5); // ~uniform grid of small triangles + REQUIRE(grid.indices.size() > 32); + + const std::vector region(grid.indices.size(), 1); + + auto longest_edge = [](const indexed_triangle_set &its, const stl_triangle_vertex_indices &t) { + float m = 0.f; + for (int e = 0; e < 3; ++e) + m = std::max(m, (its.vertices[t[e]] - its.vertices[t[(e + 1) % 3]]).norm()); + return m; + }; + auto centroid_xy = [](const indexed_triangle_set &its, const stl_triangle_vertex_indices &t) { + return Vec2f((its.vertices[t[0]].x() + its.vertices[t[1]].x() + its.vertices[t[2]].x()) / 3.f, + (its.vertices[t[0]].y() + its.vertices[t[1]].y() + its.vertices[t[2]].y()) / 3.f); + }; + + SECTION("a sharp bump refines densely at its center and leaves flat corners coarse") + { + // A tight Gaussian bump at the sheet's center: strong curvature near (0.5, 0.5), flat far away. + HeightFieldSampler bump = [](const Vec3f &p, const Vec3f &) { + const float r2 = (p.x() - 0.5f) * (p.x() - 0.5f) + (p.y() - 0.5f) * (p.y() - 0.5f); + return 1.0f * std::exp(-r2 / 0.02f); + }; + + // Baseline max edge 0.3 is coarser than the grid's own edges, so the baseline adds nothing + // here - this isolates the *curvature* contribution (the grid already meets the baseline). + std::vector source; + const indexed_triangle_set out = + subdivide_mesh_adaptive(grid, region, /*max edge*/ 0.3f, 200000, &source, bump, /*tol*/ 0.02f, + /*min_edge*/ 0.01f); + + CHECK(out.indices.size() > grid.indices.size()); // the bump forced real refinement + + // The largest triangle near the bump's center must be much smaller than the largest in a flat + // corner - i.e. triangles went where the curvature is, not spread evenly. + float near_max = 0.f, far_max = 0.f; + for (const auto &t : out.indices) { + const Vec2f c = centroid_xy(out, t); + const float r = (c - Vec2f(0.5f, 0.5f)).norm(); + const float len = longest_edge(out, t); + if (r < 0.1f) + near_max = std::max(near_max, len); + else if (r > 0.45f) + far_max = std::max(far_max, len); + } + REQUIRE(near_max > 0.f); + REQUIRE(far_max > 0.f); + CHECK(near_max < far_max); // finer at the hill than on the flats + } + + SECTION("a linear ramp has zero curvature and is left untouched") + { + // Height varies, but linearly - a flat triangle represents it exactly, so the chord error is + // zero everywhere and nothing should be split. This is the case a gradient-based criterion + // would wrongly over-refine. + HeightFieldSampler ramp = [](const Vec3f &p, const Vec3f &) { return 2.0f * p.x(); }; + + // Same coarse baseline (0.3) that the grid already meets, so any split would be curvature- + // driven - and a ramp has none. + const indexed_triangle_set out = + subdivide_mesh_adaptive(grid, region, /*max edge*/ 0.3f, 200000, nullptr, ramp, /*tol*/ 0.02f, + /*min_edge*/ 0.01f); + + CHECK(out.indices.size() == grid.indices.size()); // not one extra triangle + } + + SECTION("the max-edge baseline still applies in feature mode") + { + // A height field that is flat everywhere the four sample points of a coarse triangle happen to + // land, but not in between - the aliasing case where a chord test alone reports no error and + // refinement stalls before it ever starts. The baseline is what stops that: it guarantees a + // sampling density fine enough for the curvature test to see the texture at all. + HeightFieldSampler flat = [](const Vec3f &, const Vec3f &) { return 0.f; }; + + const indexed_triangle_set out = + subdivide_mesh_adaptive(grid, region, /*max edge*/ 0.03f, 200000, nullptr, flat, /*tol*/ 0.02f, + /*min_edge*/ 0.001f); + + CHECK(out.indices.size() > grid.indices.size()); + float worst = 0.f; + for (const auto &t : out.indices) + worst = std::max(worst, longest_edge(out, t)); + CHECK(worst <= 0.03f); + } +} From b753cf22163cd6696a12f6afc223f809f6f88f97 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 29 Jul 2026 08:44:16 +0200 Subject: [PATCH 17/89] Displace the painted patch border and add post-process smoothing --- TEXTURE_DISPLACEMENT.md | 68 ++++- src/libslic3r/Model.hpp | 10 +- src/libslic3r/TextureDisplacement.cpp | 130 +++++++++- src/libslic3r/TextureDisplacement.hpp | 74 +++++- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 142 +++++++++++ .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 7 + .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 4 +- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 1 + .../Jobs/TextureDisplacementPreviewJob.cpp | 2 +- .../Jobs/TextureDisplacementPreviewJob.hpp | 1 + tests/libslic3r/test_texture_displacement.cpp | 235 ++++++++++++++++-- 11 files changed, 623 insertions(+), 51 deletions(-) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index 680c984998..c30592de77 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -35,16 +35,21 @@ semantics). ### Bake algorithm (`libslic3r/TextureDisplacement.cpp`) -`build_texture_displacement(base_mesh, layers, facets_data)` is **accumulate-then-displace, and -topology-preserving**: the returned mesh has exactly the input's vertices and triangles, in the same -order - only the positions of displaced vertices differ. +`build_texture_displacement(base_mesh, layers, facets_data, options)` is **accumulate-then-displace, +and topology-preserving**: the returned mesh has exactly the input's vertices and triangles, in the +same order - only the positions of displaced vertices differ. 1. `its_compactify_vertices()` on a copy of the input. In practice a no-op (it only drops *unreferenced* vertices, and preserves the order and indices of the rest). It is there to guarantee the index alignment step 3 depends on. 2. Area-weighted vertex normals of the **undisplaced** mesh, computed once. Every layer both projects and displaces along these, so a vertex covered by several layers moves along one single - well-defined direction. + well-defined direction. Where the paint does *not* cover every triangle around a vertex, the normal + is recomputed from the painted triangles alone (the union over all layers, so it stays one direction + per vertex). On the rim of a fully painted top face the whole-mesh normal is the 45 degrees bisector + it shares with the side wall, and displacing along that flares the rim outwards instead of raising + it. Interior vertices are unaffected - all their triangles are painted, so the two coincide. Paint + coverage per original triangle comes straight off `TriangleSplittingData::triangles_to_split`. 3. For each layer in slot order: deserialize its stored paint mask into a `TriangleSelector` against the **base mesh** (never against a previous layer's output), then `selector.get_facets_strict(ENFORCER)` → the painted patch. Two facts are exploited: @@ -57,16 +62,55 @@ order - only the positions of displaced vertices differ. referenced ones in order. Combined with step 1, **selector vertex index `i` is our vertex `i`**. Split vertices live past the end of our array and are simply skipped - they sit on the paint boundary anyway (splitting only happens at partial coverage), so they would be pinned regardless. -4. A vertex used by at least one **unpainted** triangle is a boundary vertex - pinned, never - displaced (its final position is ambiguous, it belongs to both regions). Only vertices used - exclusively by painted triangles get displaced. This is what keeps bakes seamless with zero - remeshing/hole-filling at the seam. +4. A vertex used by at least one **unpainted** triangle is a border vertex. Whether it moves is + `TextureDisplacementOptions::displace_border`, and it **does by default**. The original design + pinned it, justified as stopping the patch tearing away from the surrounding surface - which stopped + being true the moment the bake became topology-preserving. There is no seam to tear: a border vertex + is *one* vertex shared by both regions, and moving it simply tilts the unpainted triangles that use + it. What pinning actually does is clamp the outermost ring of relief to zero, so on a fully painted + face the pattern collapses into a ring of steep ramps right at the edge - the reported "it doesn't + extrude at the border" artifact. Pinning is kept as an option for the case where the relief must not + spill past the paint at all. Either way the border still drives the `edge_smoothing` falloff. 5. Per interior vertex: sample the height texture (`sample_layer_height()`, see Projection methods) and fold `height * depth_mm * (invert ? -1 : 1)` into that vertex's running total via the layer's `TextureBlendMode` (see Blend modes). A `visited` set makes each layer fold in exactly **once** per vertex, no matter how many of the patch's triangles share it - otherwise a Multiply/Subtract layer would apply two or three times over depending on local triangle fan-out. 6. Finally, move each touched vertex along its (step 2) normal by its accumulated total. +7. Optionally (`TextureDisplacementOptions::smooth_*`) relax the result - see Post-process smoothing. + +### Post-process smoothing + +`smooth_mesh_vertices(mesh, movable, strength, iterations)` - plain Laplacian relaxation, run after all +layers have been folded in, restricted to the vertices flagged in `movable`. Each pass moves a movable +vertex a `strength` fraction of the way to the average of its one-ring, read from a **snapshot** of the +previous pass so the result does not depend on vertex order (a Gauss-Seidel sweep would smooth several +times as hard at the end of the array as at the start). Neighbours come from a CSR-style adjacency built +once per call. + +Its job is to round off the hard steps a bitmap height map leaves behind, which is a different knob from +`TextureDisplacementLayer::smoothing` - that blurs the *height map* before it is ever sampled, this +relaxes the *geometry* afterwards. Topology-preserving, like the bake. + +Two ways in, sharing one set of settings on the volume: +- The **"Smooth result"** checkbox + "Smoothing (%)" / "Passes" ride along with Preview and Bake. + `movable` is exactly the set of vertices the displacement moved, so the untouched part of the model + keeps its exact geometry and the ring just outside the displaced set anchors the relaxation (the + relief cannot creep outward). +- **"Smooth baked mesh now"** (`GLGizmoTextureDisplacement::smooth_model()`) applies the same settings to + the volume's *committed* geometry, for relief that is already baked in. `movable` there is the painted + triangles' vertices. Because smoothing never touches the triangle list, this is the one geometry + operation in the gizmo that keeps **every** paint channel verbatim - it saves and restores the eight + texture-displacement masks around `set_mesh()` rather than remapping or dropping them. + +**"Ignore outer ring"** (`smooth_skip_border`, **on by default**) drops the patch's own outermost ring of +vertices from `movable`. That ring's neighbours *outside* the paint never move, so relaxing it drags the +rim of the relief back down toward the flat surface and the pattern comes out half-melted exactly where +it meets the edge - crisp everywhere else, which is what makes it look like a bug rather than a setting. +Held out, the border keeps the full depth the texture asked for and only the interior relaxes. Turning it +off softens the outer edge deliberately (a blunter version of the per-layer edge-smoothing falloff). +Note this is the *smoothing* rim, a separate question from whether that rim is displaced at all +(`displace_border`, step 4 above) - the two are independent and both default to "keep the border sharp". ### Blend modes @@ -451,8 +495,9 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward **libslic3r (core, no GUI dependency):** - `src/libslic3r/TextureDisplacement.hpp/.cpp` - data model, bake algorithm, projection methods, - tiling, subdivision (uniform + adaptive longest-edge bisection). See doc comments throughout, - they're kept accurate and up to date. + tiling, subdivision (uniform + adaptive longest-edge bisection), post-process smoothing + (`smooth_mesh_vertices()`), and `TextureDisplacementOptions` (the whole-stack settings). See doc + comments throughout, they're kept accurate and up to date. - `src/libslic3r/MeshBoolean.hpp/.cpp` - added `parameterize_lscm()` and `remesh_isotropic()` in the `cgal` sub-namespace, reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for @@ -461,7 +506,8 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward LSCM_parameterizer_3, parameterize}.h`. No new dependency - CGAL 5.6.3 is already vendored and the `Surface_mesh_parameterization` package headers were already present, just unused before now. - `src/libslic3r/Model.hpp/.cpp` - the 8 named `FacetsAnnotation` fields + accessor, - `texture_displacement_layers`, and all the mirrored touch points (see Data model above). + `texture_displacement_layers`, `texture_displacement_options`, and all the mirrored touch points + (see Data model above). **GUI:** - `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` - the gizmo. Panel controls: dock/ diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a8c6ac4d02..f29cf0d8a7 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -938,6 +938,11 @@ public: // see build_texture_displacement()); it only reflects UI insertion order. std::vector texture_displacement_layers; + // Whole-stack displacement settings (border handling, post-process smoothing) - see + // TextureDisplacementOptions. They live beside the layers rather than on one of them because + // they are not a property of any single layer. + TextureDisplacementOptions texture_displacement_options; + // Save painting data before reset_extra_facets() discards it. // Used for replacing mesh without losing painting data. // Only for model parts (not modifiers/connectors). @@ -1190,6 +1195,7 @@ private: texture_displacement_facets_4(other.texture_displacement_facets_4), texture_displacement_facets_5(other.texture_displacement_facets_5), texture_displacement_facets_6(other.texture_displacement_facets_6), texture_displacement_facets_7(other.texture_displacement_facets_7), texture_displacement_layers(other.texture_displacement_layers), + texture_displacement_options(other.texture_displacement_options), cut_info(other.cut_info), text_configuration(other.text_configuration), emboss_shape(other.emboss_shape) { assert(this->id().valid()); @@ -1288,7 +1294,7 @@ private: cereal::load_by_value(ar, f); mesh_changed |= tf != f.timestamp(); } - ar(texture_displacement_layers); + ar(texture_displacement_layers, texture_displacement_options); cereal::load_by_value(ar, config); cereal::load(ar, text_configuration); cereal::load(ar, emboss_shape); @@ -1312,7 +1318,7 @@ private: cereal::save_by_value(ar, fuzzy_skin_facets); for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) cereal::save_by_value(ar, texture_displacement_facet(i)); - ar(texture_displacement_layers); + ar(texture_displacement_layers, texture_displacement_options); cereal::save_by_value(ar, config); cereal::save(ar, text_configuration); cereal::save(ar, emboss_shape); diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 1d8df784e7..71a8b8001f 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -1117,7 +1117,8 @@ std::vector patch_boundary_distance(const indexed_triangle_set &patch, co indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, - const TextureDisplacementFacetsData &facets_data) + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options) { indexed_triangle_set mesh = base_mesh; // TriangleSelector's vertex array starts with the mesh's own vertices (any extra ones, created @@ -1144,10 +1145,51 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set // rather than against whatever the previous layer left behind. That is what lets all the layers // be evaluated independently and merged per vertex, instead of having to re-mesh and remap the // paint masks between them (see the header for why that earlier design was dropped). - const std::vector vertex_normals = texture_displacement_vertex_normals(mesh); + std::vector vertex_normals = texture_displacement_vertex_normals(mesh); + + // ... with one correction, applied where the painted area does not cover every triangle around a + // vertex: there the direction to move in is the normal of the *painted* surface, not of the whole + // mesh. On the rim of a fully painted top face the whole-mesh normal is the 45 degrees bisector + // between the face and the side wall it meets, so displacing along it flares the rim outwards + // instead of raising it. Taken over the union of every layer's paint (triangles_to_split is + // exactly the set of original triangles a layer's brush touched - serialize() records an entry for + // each one that is split or carries a non-default state), so a vertex still has one single + // direction however many layers cover it. Interior vertices are unaffected: all their triangles + // are painted, so the two normals coincide. + { + std::vector painted_face(mesh.indices.size(), 0); + bool any_paint = false; + for (const TextureDisplacementLayer *layer : ordered_layers) + for (const TriangleSelector::TriangleBitStreamMapping &m : facets_data[size_t(layer->slot)].triangles_to_split) + if (size_t(m.triangle_idx) < mesh.indices.size()) { + painted_face[size_t(m.triangle_idx)] = 1; + any_paint = true; + } + if (any_paint) { + std::vector painted_normals(mesh.vertices.size(), Vec3f::Zero()); + for (size_t i = 0; i < mesh.indices.size(); ++i) { + if (!painted_face[i]) + continue; + const stl_triangle_vertex_indices &t = mesh.indices[i]; + const Vec3f fn = (mesh.vertices[t[1]] - mesh.vertices[t[0]]).cross(mesh.vertices[t[2]] - mesh.vertices[t[0]]); + for (int k = 0; k < 3; ++k) + painted_normals[size_t(t[k])] += fn; // area-weighted, same convention as the full normals + } + for (size_t v = 0; v < vertex_normals.size(); ++v) + if (const float l = painted_normals[v].norm(); l > 1e-8f) + vertex_normals[v] = painted_normals[v] / l; + // else: no painted triangle touches this vertex, so it will not be displaced anyway - + // leave the whole-mesh normal in place rather than zeroing it. + } + } std::vector displacement(mesh.vertices.size(), 0.f); std::vector displaced(mesh.vertices.size(), false); + // Union, over every layer, of that layer's patch border - the vertices the post-process smoothing + // holds when TextureDisplacementOptions::smooth_skip_border is set. A vertex on any patch's edge + // counts, which is the conservative choice: hold it rather than let one layer's smoothing melt the + // rim another layer put there. + std::vector on_patch_border(mesh.vertices.size(), false); bool any_displacement = false; const TriangleMesh selector_mesh(mesh); @@ -1171,13 +1213,19 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set // compactify above, it is our own. const indexed_triangle_set rest = selector.get_facets_strict(EnforcerBlockerType::NONE); - // A vertex used by even one *unpainted* triangle sits on this layer's boundary: it belongs - // to both the painted patch and the untouched surface, so displacing it would tear the two - // apart. Pinning it is what keeps the result seamless with no remeshing at the seam. + // A vertex used by even one *unpainted* triangle sits on this layer's boundary. It is still + // needed either way - the edge-smoothing falloff measures distance from it - but whether it is + // held flat is now the user's call (TextureDisplacementOptions::displace_border), because + // nothing can tear: the bake is topology-preserving, so a border vertex is one vertex shared + // by both regions and moving it just tilts the unpainted triangles that use it. std::vector is_boundary(patch.vertices.size(), false); for (const stl_triangle_vertex_indices &tri : rest.indices) - for (int i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) { is_boundary[tri[i]] = true; + if (tri[i] < int(mesh.vertices.size())) + on_patch_border[size_t(tri[i])] = true; + } + const bool pin_boundary = !options.displace_border; // Only the Cylindrical/Spherical methods need these; Triplanar blends each vertex's own // normal and LSCM solves the patch globally. @@ -1246,7 +1294,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set const int vi = tri[i]; // Split vertices the brush introduced live past the end of our own vertex array; // they carry no displacement of their own and are not part of the output mesh. - if (vi >= int(mesh.vertices.size()) || is_boundary[vi] || visited[vi]) + if (vi >= int(mesh.vertices.size()) || (pin_boundary && is_boundary[vi]) || visited[vi]) continue; visited[vi] = true; @@ -1277,6 +1325,19 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (displaced[vi]) mesh.vertices[vi] += vertex_normals[vi] * displacement[vi]; + // Post-process relaxation of what the height maps left behind, restricted to the vertices that + // actually moved - the untouched part of the model keeps its exact geometry, and the ring of + // vertices just outside the displaced set stays put and anchors the smoothing so the relief does + // not creep outward. `smooth_skip_border` additionally holds the patch's own outermost ring, whose + // neighbours are those pinned outsiders: relaxing it would drag the rim of the relief back down and + // leave the pattern looking half-melted right where it meets the edge. + if (options.smooth_enabled && options.smooth_strength > 0.f && options.smooth_iterations > 0) { + std::vector movable(mesh.vertices.size(), 0); + for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) + movable[vi] = (displaced[vi] && !(options.smooth_skip_border && on_patch_border[vi])) ? 1 : 0; + smooth_mesh_vertices(mesh, movable, options.smooth_strength, options.smooth_iterations); + } + return mesh; } @@ -1286,7 +1347,60 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume) for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); - return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data); + return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data, + volume.texture_displacement_options); +} + +void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector &movable, float strength, + int iterations) +{ + if (iterations <= 0 || mesh.vertices.empty() || movable.size() != mesh.vertices.size()) + return; + strength = std::clamp(strength, 0.f, 1.f); + if (strength <= 0.f) + return; + if (std::none_of(movable.begin(), movable.end(), [](uint8_t m) { return m != 0; })) + return; + + // One-ring neighbours as a CSR-style pair of arrays: counted, prefix-summed, then filled. A + // triangle contributes each of its edges to both endpoints, so a shared edge is listed once per + // incident triangle - the duplicates are harmless here, they just weight an interior edge the same + // way from both sides, and dropping them would cost a sort per vertex for no visible difference. + const size_t nv = mesh.vertices.size(); + std::vector start(nv + 1, 0); + for (const stl_triangle_vertex_indices &t : mesh.indices) + for (int e = 0; e < 3; ++e) { + ++start[size_t(t[e]) + 1]; + ++start[size_t(t[(e + 1) % 3]) + 1]; + } + for (size_t v = 0; v < nv; ++v) + start[v + 1] += start[v]; + const size_t total_refs = size_t(start[nv]); + std::vector nbr(total_refs, 0); + std::vector fill(start.begin(), start.begin() + nv); + for (const stl_triangle_vertex_indices &t : mesh.indices) + for (int e = 0; e < 3; ++e) { + const int a = t[e], b = t[(e + 1) % 3]; + nbr[size_t(fill[size_t(a)]++)] = b; + nbr[size_t(fill[size_t(b)]++)] = a; + } + + // Read every pass from a snapshot of the previous one, so the result does not depend on the order + // vertices happen to be visited in (a Gauss-Seidel sweep would smooth several times as hard at the + // end of the array as at the start). + std::vector prev; + for (int it = 0; it < iterations; ++it) { + prev = mesh.vertices; + for (size_t v = 0; v < nv; ++v) { + if (!movable[v] || start[v] == start[v + 1]) + continue; + Vec3f sum = Vec3f::Zero(); + for (int k = start[v]; k < start[v + 1]; ++k) + sum += prev[size_t(nbr[size_t(k)])]; + const Vec3f avg = sum / float(start[v + 1] - start[v]); + mesh.vertices[v] = prev[v] + (avg - prev[v]) * strength; + } + } } HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 39e3ce634f..6a824b2054 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -295,6 +295,47 @@ struct TextureDisplacementLayer } }; +// Settings that apply to the whole layer stack rather than to one layer, held per ModelVolume next +// to texture_displacement_layers and consumed by build_texture_displacement(). +struct TextureDisplacementOptions +{ + // Whether the painted patch's *border* vertices - the ones also used by unpainted triangles - + // are displaced along with the rest, or pinned flat. + // + // Pinning them was originally justified as keeping the patch from tearing away from the + // surrounding surface. That reasoning no longer applies: since the bake became + // topology-preserving it only ever *moves* the input's own vertices, so a border vertex is one + // vertex shared by both regions and moving it simply tilts the unpainted triangles that use it - + // nothing can come apart. What pinning actually does is clamp the outermost ring of the relief to + // zero, which on a fully painted face collapses the pattern into a ring of steep ramps right at + // the edge (the "it doesn't extrude at the border" artifact). Displacing it is the default; + // pinning is kept for the case where the relief must not spill past the paint at all. + bool displace_border = true; + + // Optional Laplacian relaxation of the displaced surface, run after all layers have been folded + // in - a post-process, not a texture filter (TextureDisplacementLayer::smoothing blurs the height + // map instead, before it is ever sampled). Rounds off the hard steps a bitmap height map leaves + // behind. Restricted to vertices the displacement actually moved, so the rest of the model keeps + // its exact geometry. `smooth_strength` in [0, 1] is how far each pass moves a vertex toward the + // average of its neighbours. + bool smooth_enabled = false; + float smooth_strength = 0.3f; + int smooth_iterations = 2; + + // Hold the painted patch's outermost ring of vertices out of the smoothing. Those vertices sit + // next to unpainted ones that are pinned by definition, so relaxing them drags the rim of the + // relief back down toward the undisplaced surface - the pattern looks half-melted exactly where it + // meets the edge, however crisp the rest of it is. Excluding them keeps the border extruded at + // full depth and smooths only the interior. On by default; turn it off to soften the outer edge + // deliberately (which is a blunter version of the per-layer edge-smoothing falloff). + bool smooth_skip_border = true; + + template void serialize(Archive &ar) + { + ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border); + } +}; + // Decoded 8-bit grayscale height sample, independent of any GUI/OpenGL texture object so it can // be evaluated from a background bake Job as well as from GUI-side preview code. struct DecodedHeightTexture @@ -473,9 +514,14 @@ using TextureDisplacementFacetsData = std::array &layers, - const TextureDisplacementFacetsData &facets_data); + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options = {}); -// Convenience overload for main-thread callers: extracts the mesh/layers/paint data from `volume` -// and forwards to the overload above. +// Convenience overload for main-thread callers: extracts the mesh/layers/paint data/options from +// `volume` and forwards to the overload above. indexed_triangle_set build_texture_displacement(const ModelVolume &volume); +// Laplacian relaxation of `mesh` in place, restricted to the vertices flagged in `movable` (sized to +// the mesh's vertex count; anything else is held exactly where it is and still acts as an anchor for +// its neighbours). Each of `iterations` passes moves a movable vertex a `strength` fraction of the +// way to the average of the vertices it shares an edge with, computed from the positions at the +// start of that pass so the result does not depend on vertex order. +// +// Topology-preserving like the bake itself, so it composes with it: this is what "smooth the relief +// after displacing it" runs, and it is also safe to run standalone on an already baked mesh. +// `strength` is clamped to [0, 1]; 0 iterations, an empty/mis-sized `movable`, or an all-false one +// leave the mesh untouched. +void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector &movable, float strength, + int iterations); + // Returns a scalar height (in mm - a displacement magnitude) at a surface point, given that point's // position and interpolated normal. This is what feature-adaptive subdivision samples to decide // where the displaced surface has *curvature* worth spending triangles on. Called serially from the diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index ea489364aa..78886f6be8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -1192,6 +1192,7 @@ void GLGizmoTextureDisplacement::rebuild_preview() TextureDisplacementPreviewInput input; input.base_mesh = mv->mesh().its; input.layers = mv->texture_displacement_layers; + input.options = mv->texture_displacement_options; for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = mv->texture_displacement_facet(i).get_data(); @@ -2836,6 +2837,90 @@ void GLGizmoTextureDisplacement::subdivide_model_adaptive() m_parent.set_as_dirty(); } +void GLGizmoTextureDisplacement::smooth_model() +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) + return; + const TextureDisplacementOptions &opts = mv->texture_displacement_options; + if (opts.smooth_strength <= 0.f || opts.smooth_iterations <= 0) + return; + + update_model_object(); // flush any in-progress stroke, so the painted region below is current + + // Movable = the vertices of the painted triangles, so the first ring of purely unpainted vertices + // outside them stays put and anchors the result. Restricted rather than whole-model on purpose: + // this runs on geometry that has already been baked, and relaxing the whole thing would quietly + // round off every unrelated feature on the part. With "Ignore outer ring" the patch's own rim is + // held as well - see TextureDisplacementOptions::smooth_skip_border. + const indexed_triangle_set &its = mv->mesh().its; + std::vector painted_face(its.indices.size(), 0); + bool any = false; + for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) + for (const TriangleSelector::TriangleBitStreamMapping &m : mv->texture_displacement_facet(slot).get_data().triangles_to_split) + if (size_t(m.triangle_idx) < its.indices.size()) { + painted_face[size_t(m.triangle_idx)] = 1; + any = true; + } + if (!any) { + show_error(nullptr, _u8L("Paint the area you want to smooth first - smoothing only touches the " + "painted part of the model.")); + return; + } + std::vector movable(its.vertices.size(), 0); + for (size_t i = 0; i < its.indices.size(); ++i) + if (painted_face[i]) + for (int k = 0; k < 3; ++k) + movable[size_t(its.indices[i][k])] = 1; + if (opts.smooth_skip_border) + for (size_t i = 0; i < its.indices.size(); ++i) + if (!painted_face[i]) + for (int k = 0; k < 3; ++k) + movable[size_t(its.indices[i][k])] = 0; // also used by unpainted geometry -> the rim + if (std::none_of(movable.begin(), movable.end(), [](uint8_t m) { return m != 0; })) { + // Every painted vertex is on the patch's rim, so "Ignore outer ring" leaves nothing to move. + show_error(nullptr, _u8L("Nothing to smooth - the painted area is only one triangle deep, so with " + "\"Ignore outer ring\" on there are no interior vertices to relax.")); + return; + } + + indexed_triangle_set smoothed = its; + { + wxBusyCursor wait; + smooth_mesh_vertices(smoothed, movable, opts.smooth_strength, opts.smooth_iterations); + } + + Plater *plater = wxGetApp().plater(); + Plater::TakeSnapshot snapshot(plater, _u8L("Smooth texture displacement"), UndoRedo::SnapshotType::GizmoAction); + + // No save/restore-painting dance here, unlike subdivide and remesh: smoothing only moves vertices, + // it does not touch the triangle list, so every paint channel still refers to exactly the triangles + // it did before. set_mesh() clears the extra facets, so this saves and puts back the + // texture-displacement masks verbatim - no remap needed, and none of them is lost. + std::optional saved_painting = mv->save_painting(); + std::array saved_texture; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + saved_texture[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + + mv->set_mesh(TriangleMesh(std::move(smoothed))); + mv->set_new_unique_id(); + mv->calculate_convex_hull(); + mv->restore_painting(saved_painting); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + mv->texture_displacement_facet(i).set_data(std::move(saved_texture[size_t(i)])); + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), mo); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + plater->changed_object(*mo); + update_from_model_object(false); + m_parent.set_as_dirty(); +} + void GLGizmoTextureDisplacement::remesh_model() { ModelVolume *mv = texture_volume(); @@ -3769,6 +3854,63 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } // (The "Add layer" button now lives next to the "Texture layers" heading, as an icon.) + // Settings for the whole stack rather than one layer, so they sit outside the layer list. + if (mv != nullptr) { + TextureDisplacementOptions &opts = mv->texture_displacement_options; + ImGui::Separator(); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Displace up to the border").c_str(), &opts.displace_border); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Move the outermost ring of painted vertices as well, so the relief runs all the " + "way to the edge of the painted area. Turn this off to hold that ring flat, which " + "keeps the displacement strictly inside the paint but flattens the pattern right " + "at the border."), + m_imgui->scaled(20.f)); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Smooth result").c_str(), &opts.smooth_enabled); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Relax the displaced surface after the textures have been applied, to round off " + "the hard steps a bitmap height map leaves behind. Only the vertices the " + "displacement moved are touched. This is geometry smoothing - the per-layer " + "\"Blur\" slider instead softens the height map before it is sampled."), + m_imgui->scaled(20.f)); + if (opts.smooth_enabled) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + float percent = opts.smooth_strength * 100.f; + if (m_imgui->slider_float(std::string(_u8L("Smoothing (%)")) + "##dispsmooth", &percent, 1.f, 100.f, "%.0f", 1.f)) { + opts.smooth_strength = std::clamp(percent / 100.f, 0.01f, 1.f); + m_preview_params_dirty = true; + } + if (ImGui::SliderInt((_u8L("Passes") + "##dispsmoothit").c_str(), &opts.smooth_iterations, 1, 10)) { + opts.smooth_iterations = std::clamp(opts.smooth_iterations, 1, 10); + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How many relaxation passes to run. More passes reach further across the " + "surface; strength controls how much each one moves a vertex."), + m_imgui->scaled(20.f)); + ImGui::PopItemWidth(); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Ignore outer ring").c_str(), &opts.smooth_skip_border); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Leave the outermost ring of painted vertices out of the smoothing. Their " + "neighbours outside the paint never move, so relaxing them drags the relief " + "back down and the pattern comes out half-melted right at the edge. Turn this " + "off only if you want that edge softened on purpose."), + m_imgui->scaled(20.f)); + + // The settings above ride along with Preview/Bake. This button is for geometry that has + // *already* been baked, where there is no displacement left to fold the smoothing into. + if (m_imgui->button(_u8L("Smooth baked mesh now"))) + smooth_model(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Apply the smoothing above to the model's real geometry right now, using the " + "painted area to decide what to touch. For relief that is already baked in - " + "unbaked displacement is smoothed by Bake itself."), + m_imgui->scaled(20.f)); + } + } + ImGui::Separator(); m_imgui->text(_u8L("Not enough vertices for fine detail?")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index a4807eb61d..1f3c6bcc73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -335,6 +335,13 @@ private: bool collect_paint_region(std::vector ®ion, std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> *painted_tri) const; + // Runs the volume's TextureDisplacementOptions smoothing over the *already committed* geometry, + // restricted to the painted area. The same settings are folded into Preview/Bake automatically; + // this is the escape hatch for relief that has already been baked in, where there is no + // displacement pass left to attach them to. Topology-preserving, so unlike subdivide and remesh it + // keeps every paint channel - including texture displacement - exactly as it was. + void smooth_model(); + // Isotropic remeshing (CGAL) to even out wildly varying triangle sizes so displacement has a // consistent density to work with. Target edge length in mm; 0 means "not yet initialised", filled // with the mesh's mean edge length the first time the control is shown. Like subdivide, it replaces diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 155760709d..ae7e4dc3dd 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -24,7 +24,8 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) // Only ever touches m_input (captured by value before this job was queued) and local state - // never the live Model - so this is safe to run concurrently with the UI thread. - m_result = TriangleMesh(build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data)); + m_result = TriangleMesh(build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, + m_input.options)); } void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &eptr) @@ -78,6 +79,7 @@ void queue_texture_displacement_bake(const ModelVolume &volume, std::function layers; TextureDisplacementFacetsData facets_data; + TextureDisplacementOptions options; }; // Bakes a volume's painted texture-displacement layers into real mesh geometry in the background, diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index 48b031dcc3..555ed570b1 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -16,7 +16,7 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) // Only ever touches m_input (captured by value before this job was queued) and local state - // never the live Model - so this is safe to run concurrently with the UI thread. - m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data); + m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, m_input.options); } void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr &eptr) diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp index 93d66bcfb2..ed59c1f302 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -19,6 +19,7 @@ struct TextureDisplacementPreviewInput indexed_triangle_set base_mesh; std::vector layers; TextureDisplacementFacetsData facets_data; + TextureDisplacementOptions options; }; // Computes the true (unbaked) displaced-mesh preview in the background. With several painted diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 6b3420845e..6f41c76bce 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -1,8 +1,10 @@ #define NOMINMAX #include +#include #include #include +#include #include #include "libslic3r/TextureDisplacement.hpp" @@ -35,6 +37,30 @@ static std::shared_ptr> make_flat_gray_png(uint8_t va return std::make_shared>(std::move(bytes)); } +// A hard-edged black/white checkerboard, the worst case for a height map: every texel boundary is a +// step, which is precisely the relief the post-process smoothing exists to round off. +static std::shared_ptr> make_checkerboard_png(size_t w = 16, size_t h = 16) +{ + std::vector pixels(w * h); + for (size_t y = 0; y < h; ++y) + for (size_t x = 0; x < w; ++x) + pixels[y * w + x] = ((x / 2 + y / 2) % 2) ? 255 : 0; + const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("texdisp_test_%%%%%%%%.png"); + REQUIRE(Slic3r::png::write_gray_to_file(tmp_path.string(), w, h, pixels)); + + std::vector bytes; + { + std::ifstream ifs(tmp_path.string(), std::ios::binary); + bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp_path, ec); + + REQUIRE_FALSE(bytes.empty()); + return std::make_shared>(std::move(bytes)); +} + TEST_CASE("TextureDisplacement: decode_height_texture round-trips an 8-bit grayscale PNG", "[TextureDisplacement]") { TextureDisplacementLayer layer; @@ -188,15 +214,15 @@ TEST_CASE("TextureDisplacement: the lowest layer ignores its blend mode", "[Text CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(2.0f, 1e-3f)); } -TEST_CASE("TextureDisplacement: boundary vertices shared with unpainted triangles are pinned", "[TextureDisplacement]") +TEST_CASE("TextureDisplacement: the patch border is displaced by default and pinned on request", "[TextureDisplacement]") { // A small triangle fan around a central vertex O, with 4 outer points A/B/C/D forming 4 - // triangles T0..T3 in the XY plane. Only T0, T1, T2 are painted, T3 is left unpainted: - // O: touches all 4 triangles (incl. unpainted T3) -> boundary, must NOT move - // A: touches T0 (painted) and T3 (unpainted) -> boundary, must NOT move - // D: touches T2 (painted) and T3 (unpainted) -> boundary, must NOT move - // B: touches only T0 and T1 (both painted) -> interior, SHOULD move - // C: touches only T1 and T2 (both painted) -> interior, SHOULD move + // triangles T0..T3 in the XY plane. Only T0, T1, T2 are painted, T3 is left unpainted, so: + // O: touches all 4 triangles (incl. unpainted T3) -> patch border + // A: touches T0 (painted) and T3 (unpainted) -> patch border + // D: touches T2 (painted) and T3 (unpainted) -> patch border + // B: touches only T0 and T1 (both painted) -> interior + // C: touches only T1 and T2 (both painted) -> interior indexed_triangle_set fan; fan.vertices = { {0.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {-1.f, 0.f, 0.f}, {0.f, -1.f, 0.f} }; fan.indices = { {0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1} }; @@ -217,22 +243,189 @@ TEST_CASE("TextureDisplacement: boundary vertices shared with unpainted triangle layer.tiling_scale = 5.0f; layer.image_data = make_flat_gray_png(255); - const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets); - - // Find each named vertex's post-bake position by matching the original (pinned vertices keep - // their exact original position; moved ones won't match any original position anymore). - auto still_at_original_position = [&](const Vec3f &original) { - for (const Vec3f &v : result.vertices) - if ((v - original).norm() < 1e-6f) - return true; - return false; + // The bake is topology-preserving, so it only ever moves fan's own vertices, in order. + auto moved = [&](const indexed_triangle_set &result, size_t i) { + return (result.vertices[i] - fan.vertices[i]).norm() > 1e-6f; }; - CHECK(still_at_original_position(fan.vertices[0])); // O: boundary - CHECK(still_at_original_position(fan.vertices[1])); // A: boundary - CHECK(still_at_original_position(fan.vertices[4])); // D: boundary - CHECK_FALSE(still_at_original_position(fan.vertices[2])); // B: interior, must have moved - CHECK_FALSE(still_at_original_position(fan.vertices[3])); // C: interior, must have moved + SECTION("by default the whole painted patch moves, border included") + { + const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets); + REQUIRE(result.vertices.size() == fan.vertices.size()); + for (size_t i = 0; i < fan.vertices.size(); ++i) + CHECK(moved(result, i)); + // ... straight along the painted surface's own normal (+Z here), by the full depth. Nothing + // has torn: the unpainted triangle T3 simply shares the moved vertices. + for (size_t i = 0; i < fan.vertices.size(); ++i) + CHECK_THAT(result.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f, 1e-3f)); + CHECK(result.indices == fan.indices); + } + + SECTION("pinning the border holds exactly the vertices an unpainted triangle also uses") + { + TextureDisplacementOptions options; + options.displace_border = false; + const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets, options); + CHECK_FALSE(moved(result, 0)); // O: border + CHECK_FALSE(moved(result, 1)); // A: border + CHECK_FALSE(moved(result, 4)); // D: border + CHECK(moved(result, 2)); // B: interior + CHECK(moved(result, 3)); // C: interior + } + +} + +TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", "[TextureDisplacement]") +{ + // A checkerboard height map on a fine grid gives a relief full of hard steps - exactly what the + // smoothing pass is for. Measure it as the spread of the displaced heights: relaxing the surface + // must pull the extremes in without touching the mesh's topology. + indexed_triangle_set plane; + plane.vertices = { { 0.f, 0.f, 0.f }, { 20.f, 0.f, 0.f }, { 20.f, 20.f, 0.f }, { 0.f, 20.f, 0.f } }; + plane.indices = { { 0, 1, 2 }, { 0, 2, 3 } }; + const indexed_triangle_set grid = subdivide_mesh_uniform(plane, 1.f, 6); + REQUIRE(grid.indices.size() > 256); + + // Paint only the central square, so the patch has a real border to test against. + std::vector painted(grid.indices.size(), 0); + const TriangleMesh grid_mesh(grid); + TriangleSelector selector(grid_mesh); + for (int i = 0; i < int(grid.indices.size()); ++i) { + const auto &t = grid.indices[i]; + float cx = 0.f, cy = 0.f; + for (int k = 0; k < 3; ++k) { + cx += grid.vertices[t[k]].x() / 3.f; + cy += grid.vertices[t[k]].y() / 3.f; + } + if (cx > 5.f && cx < 15.f && cy > 5.f && cy < 15.f) { + selector.set_facet(i, EnforcerBlockerType::ENFORCER); + painted[size_t(i)] = 1; + } + } + REQUIRE(std::count(painted.begin(), painted.end(), uint8_t(1)) > 32); + TextureDisplacementFacetsData facets{}; + facets[0] = selector.serialize(); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 2.0f; + layer.tiling_scale = 6.0f; + layer.image_data = make_checkerboard_png(); + + auto z_spread = [](const indexed_triangle_set &its) { + float lo = its.vertices.front().z(), hi = lo; + for (const Vec3f &v : its.vertices) { + lo = std::min(lo, v.z()); + hi = std::max(hi, v.z()); + } + return hi - lo; + }; + + const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets); + REQUIRE(z_spread(raw) > 1.f); // the checkerboard really did produce relief to smooth + + TextureDisplacementOptions options; + options.smooth_enabled = true; + options.smooth_strength = 0.5f; + options.smooth_iterations = 4; + + SECTION("it rounds off the steps without touching the topology") + { + const indexed_triangle_set smoothed = build_texture_displacement(grid, { layer }, facets, options); + CHECK(z_spread(smoothed) < z_spread(raw)); + CHECK(smoothed.indices == raw.indices); + CHECK(smoothed.vertices.size() == raw.vertices.size()); + } + + SECTION("\"ignore outer ring\" leaves the patch rim at the depth the texture asked for") + { + // The rim: vertices shared by a painted and an unpainted triangle. Their neighbours outside + // the paint never move, so relaxing them drags the relief back down toward the flat surface - + // which is what makes the pattern look half-melted right at the edge. + std::vector rim(grid.vertices.size(), 0), inside(grid.vertices.size(), 0); + for (size_t i = 0; i < grid.indices.size(); ++i) + for (int k = 0; k < 3; ++k) + (painted[i] ? inside : rim)[size_t(grid.indices[i][k])] = 1; + size_t rim_count = 0; + for (size_t v = 0; v < rim.size(); ++v) { + rim[v] = (rim[v] && inside[v]) ? 1 : 0; + rim_count += rim[v]; + } + REQUIRE(rim_count > 8); + + // The tallest point of the rim. Laplacian relaxation is a convex average, so no vertex can ever + // exceed the mesh's current maximum - and a rim vertex sitting at that maximum has at least one + // neighbour outside the paint at zero, so it must come down. That makes this a strict, not a + // statistical, comparison. + auto rim_peak = [&](const indexed_triangle_set &its) { + float peak = 0.f; + for (size_t v = 0; v < rim.size(); ++v) + if (rim[v]) + peak = std::max(peak, its.vertices[v].z()); + return peak; + }; + + options.smooth_skip_border = true; + const indexed_triangle_set kept = build_texture_displacement(grid, { layer }, facets, options); + options.smooth_skip_border = false; + const indexed_triangle_set relaxed = build_texture_displacement(grid, { layer }, facets, options); + + // Held out of the smoothing, the rim is bit-for-bit the un-smoothed displacement. + for (size_t v = 0; v < rim.size(); ++v) + if (rim[v]) + CHECK_THAT(kept.vertices[v].z(), WithinAbs(raw.vertices[v].z(), 1e-6f)); + CHECK(rim_peak(relaxed) < rim_peak(kept)); // ... and melted back down without it + // ... while the interior really was smoothed - this is not just "smoothing turned off". + CHECK_FALSE(kept.vertices == raw.vertices); + } +} + +TEST_CASE("TextureDisplacement: smooth_mesh_vertices holds everything outside its mask", "[TextureDisplacement]") +{ + // A single spike on a flat sheet: relaxing it must pull the spike down and leave every vertex + // that is not flagged movable at exactly the coordinates it started at. + indexed_triangle_set plane; + plane.vertices = { { 0.f, 0.f, 0.f }, { 8.f, 0.f, 0.f }, { 8.f, 8.f, 0.f }, { 0.f, 8.f, 0.f } }; + plane.indices = { { 0, 1, 2 }, { 0, 2, 3 } }; + indexed_triangle_set grid = subdivide_mesh_uniform(plane, 1.f, 4); + + // Raise one interior vertex, and let only it and its immediate neighbours move. + size_t spike = 0; + float best = std::numeric_limits::max(); + for (size_t i = 0; i < grid.vertices.size(); ++i) + if (const float d = (grid.vertices[i] - Vec3f(4.f, 4.f, 0.f)).norm(); d < best) { + best = d; + spike = i; + } + grid.vertices[spike].z() = 5.f; + + std::vector movable(grid.vertices.size(), 0); + movable[spike] = 1; + for (const auto &t : grid.indices) + for (int e = 0; e < 3; ++e) + if (size_t(t[e]) == spike) + for (int k = 0; k < 3; ++k) + movable[size_t(t[k])] = 1; + + const indexed_triangle_set before = grid; + smooth_mesh_vertices(grid, movable, 0.5f, 3); + + CHECK(grid.vertices[spike].z() < before.vertices[spike].z()); // the spike came down + CHECK(grid.vertices[spike].z() > 0.f); // but was not flattened outright + CHECK(grid.indices == before.indices); // topology untouched + for (size_t i = 0; i < grid.vertices.size(); ++i) + if (!movable[i]) + CHECK_THAT((grid.vertices[i] - before.vertices[i]).norm(), WithinAbs(0.f, 1e-9f)); + + // Guard rails: each of these must leave the mesh byte-identical. + for (const auto &noop : { std::make_pair(0.f, 3), std::make_pair(0.5f, 0) }) { + indexed_triangle_set copy = before; + smooth_mesh_vertices(copy, movable, noop.first, noop.second); + CHECK(copy.vertices == before.vertices); + } + indexed_triangle_set copy = before; + smooth_mesh_vertices(copy, std::vector(3, 1), 0.5f, 3); // mis-sized mask + CHECK(copy.vertices == before.vertices); } // Every undirected edge of a closed manifold mesh is shared by exactly two triangles. A T-junction From 8434223f96fd535e628e0f2ba3d45bd505914956 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 12 Aug 2026 10:14:38 +0200 Subject: [PATCH 18/89] Add Parallax preview & fix Undo\Redo history --- TEXTURE_DISPLACEMENT.md | 488 ++++++++++-------- .../shaders/110/texture_displacement_bump.fs | 47 +- .../shaders/140/texture_displacement_bump.fs | 80 ++- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 448 ++++++++++++---- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 52 +- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 64 ++- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 7 +- 7 files changed, 819 insertions(+), 367 deletions(-) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index c30592de77..b557bad8db 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -1,9 +1,7 @@ # Texture Displacement - Technical Notes -Branch: `feature/texture_displacement`. This document is a knowledge dump of the whole feature as -it stands: architecture, file map, algorithms, known bugs found and fixed (with root causes worth -remembering), and what's still deferred. Written so a fresh session (or a fresh pair of eyes) can -pick this up without re-deriving everything from scratch. +Branch: `feature/texture_displacement`. Reference for the feature as it stands: what it does, how the +algorithms work, and where the code lives. ## What it does @@ -14,24 +12,67 @@ A paint-style gizmo (`GLGizmoTextureDisplacement`) that lets you: (saved into `/textures/displacement/`, kept separate so app updates can't clobber it). - Combine overlapping layers with image-editor-style blend modes (Add/Subtract/Multiply/Divide). - Preview the true displaced result live, before baking (background job, not on the UI thread). -- Optionally preview via a fast GPU bump-map shader instead (no real geometry movement, just - shading) for a lighter-weight alternative. +- Preview via a fast GPU shader instead (no real geometry movement) for a lighter-weight alternative. - Bake into real mesh geometry on demand, restricted to the painted area only. -- Subdivide a low-poly model first so there are enough vertices to show fine detail. +- Remesh and subdivide so a low-poly model has enough vertices to show fine detail. - Unwrap a painted patch with a real CGAL LSCM parameterization and view it in a dedicated, dockable 2D "UV Editor" pane. +## Standard vs Pro mode + +A two-position slider in the panel header, right of the Dock/Undock button. + +**Pro** shows every mesh-preparation control; Remesh, Subdivide and Bake are run separately by the user, +in whatever order they like. + +**Standard** hides all of it and folds one fixed recipe into the Bake button, because a height map only +ever *moves vertices that already exist* - painting onto an imported 12-triangle box and pressing Bake +would otherwise do nothing visible. Standard's Bake is: + +1. `plan_remesh()` + `replace_mesh_keep_all_paint()` - isotropic remesh to 1 mm, sharp edges above 40 + degrees protected. Gives the subdivider an even starting density whatever the input looked like. +2. `plan_adaptive_subdivision()` + `apply_adaptive_subdivision()` - feature-adaptive refinement, max + edge 20 mm, detail 0.02 mm, min edge 0.02 mm. +3. `bake()` - the ordinary background displacement job. + +Both preparation stages are *planned* before the undo snapshot and *applied* after it, so a stage with +nothing to do is skipped without leaving an empty undo step. The standalone Pro buttons share the same +plan/apply split. + +**All three stages sit under one undo step.** `Plater::take_snapshot()` records the state *before* the +change, so a single snapshot taken at the top of `bake_standard()` means one Undo returns the mesh to +exactly what was imported. `TextureDisplacementBakeInput::take_snapshot` lets the caller say who owns +the undo step - true for the Pro-mode button, false for the pipeline, whose background job commits long +after that snapshot's scope has closed. + +The presets live in one place (`STD_*` constants) and `apply_standard_mode_presets()` pins the hidden +controls to them every frame while Standard is active, so the live preview cannot disagree with what +Bake will do. Switching to Standard also closes the subdivision preview, whose controls have just gone. + +One control survives into Standard: **"Added triangles (k)"**, the subdivision budget. It is deliberately +*not* pinned - pinning would fight the user's own slider every frame - because unlike the rest of the +recipe its right value depends on the part rather than on the method (a big model, or a fine texture, +simply needs more triangles). Default 1500. The widget is one lambda shared by both layouts. + +Standard remeshes *after* painting, so the remesh has to preserve paint: `ModelVolume::restore_painting()` +only remaps the four standard channels, so `replace_mesh_keep_all_paint()` additionally runs +`TriangleSelector::remap_painting()` over the eight texture-displacement masks. The Pro Remesh button +goes through the same helper. If the remap comes back empty the pipeline stops with a message rather +than baking a flat mesh. + ## Architecture ### Data model (per `ModelVolume`) Each of up to `TEXTURE_DISPLACEMENT_MAX_LAYERS` (8) layers gets its **own independent -`FacetsAnnotation`** paint mask - the exact same `TriangleSelector`/`FacetsAnnotation` machinery -every other paint gizmo (FdmSupports, Seam, MMU, FuzzySkin) already uses, just one full instance -per layer slot instead of one per volume. This is what makes "layered/blended" painting work for -free: the same triangle can be `ENFORCER` in layer 2's mask and layer 5's mask simultaneously, and -at bake/preview time each layer displaces the surface left by the previous one (image-editor-layer -semantics). +`FacetsAnnotation`** paint mask - the same `TriangleSelector`/`FacetsAnnotation` machinery every other +paint gizmo (FdmSupports, Seam, MMU, FuzzySkin) already uses, just one full instance per layer slot +instead of one per volume. This is what makes layered/blended painting work for free: the same triangle +can be `ENFORCER` in layer 2's mask and layer 5's mask simultaneously, and at bake/preview time each +layer displaces the surface left by the previous one (image-editor-layer semantics). + +Whole-stack settings (border handling, post-process smoothing) live beside the layers in +`texture_displacement_options` (`TextureDisplacementOptions`), since they belong to no single layer. ### Bake algorithm (`libslic3r/TextureDisplacement.cpp`) @@ -42,13 +83,13 @@ same order - only the positions of displaced vertices differ. 1. `its_compactify_vertices()` on a copy of the input. In practice a no-op (it only drops *unreferenced* vertices, and preserves the order and indices of the rest). It is there to guarantee the index alignment step 3 depends on. -2. Area-weighted vertex normals of the **undisplaced** mesh, computed once. Every layer both - projects and displaces along these, so a vertex covered by several layers moves along one single - well-defined direction. Where the paint does *not* cover every triangle around a vertex, the normal - is recomputed from the painted triangles alone (the union over all layers, so it stays one direction - per vertex). On the rim of a fully painted top face the whole-mesh normal is the 45 degrees bisector - it shares with the side wall, and displacing along that flares the rim outwards instead of raising - it. Interior vertices are unaffected - all their triangles are painted, so the two coincide. Paint +2. Area-weighted vertex normals of the **undisplaced** mesh, computed once. Every layer both projects + and displaces along these, so a vertex covered by several layers moves along one single well-defined + direction. Where the paint does *not* cover every triangle around a vertex, the normal is recomputed + from the painted triangles alone (the union over all layers, so it stays one direction per vertex): + on the rim of a fully painted top face the whole-mesh normal is the 45-degree bisector it shares with + the side wall, and displacing along that flares the rim outwards instead of raising it. Interior + vertices are unaffected - all their triangles are painted, so the two normals coincide. Paint coverage per original triangle comes straight off `TriangleSplittingData::triangles_to_split`. 3. For each layer in slot order: deserialize its stored paint mask into a `TriangleSelector` against the **base mesh** (never against a previous layer's output), then @@ -61,36 +102,33 @@ same order - only the positions of displaced vertices differ. brush stroke split a triangle are appended after them), and `get_facets_strict()` emits the referenced ones in order. Combined with step 1, **selector vertex index `i` is our vertex `i`**. Split vertices live past the end of our array and are simply skipped - they sit on the paint - boundary anyway (splitting only happens at partial coverage), so they would be pinned regardless. + boundary anyway (splitting only happens at partial coverage). 4. A vertex used by at least one **unpainted** triangle is a border vertex. Whether it moves is - `TextureDisplacementOptions::displace_border`, and it **does by default**. The original design - pinned it, justified as stopping the patch tearing away from the surrounding surface - which stopped - being true the moment the bake became topology-preserving. There is no seam to tear: a border vertex - is *one* vertex shared by both regions, and moving it simply tilts the unpainted triangles that use - it. What pinning actually does is clamp the outermost ring of relief to zero, so on a fully painted - face the pattern collapses into a ring of steep ramps right at the edge - the reported "it doesn't - extrude at the border" artifact. Pinning is kept as an option for the case where the relief must not - spill past the paint at all. Either way the border still drives the `edge_smoothing` falloff. + `TextureDisplacementOptions::displace_border`, and it does by default. Nothing can tear: the bake is + topology-preserving, so a border vertex is *one* vertex shared by both regions and moving it simply + tilts the unpainted triangles that use it. Pinning it instead clamps the outermost ring of relief to + zero, which on a fully painted face collapses the pattern into a ring of steep ramps at the edge; it + is kept as an option for when the relief must not spill past the paint at all. Either way the border + drives the `edge_smoothing` falloff. 5. Per interior vertex: sample the height texture (`sample_layer_height()`, see Projection methods) and fold `height * depth_mm * (invert ? -1 : 1)` into that vertex's running total via the layer's `TextureBlendMode` (see Blend modes). A `visited` set makes each layer fold in exactly **once** per vertex, no matter how many of the patch's triangles share it - otherwise a Multiply/Subtract layer would apply two or three times over depending on local triangle fan-out. -6. Finally, move each touched vertex along its (step 2) normal by its accumulated total. +6. Move each touched vertex along its (step 2) normal by its accumulated total. 7. Optionally (`TextureDisplacementOptions::smooth_*`) relax the result - see Post-process smoothing. ### Post-process smoothing -`smooth_mesh_vertices(mesh, movable, strength, iterations)` - plain Laplacian relaxation, run after all -layers have been folded in, restricted to the vertices flagged in `movable`. Each pass moves a movable -vertex a `strength` fraction of the way to the average of its one-ring, read from a **snapshot** of the -previous pass so the result does not depend on vertex order (a Gauss-Seidel sweep would smooth several -times as hard at the end of the array as at the start). Neighbours come from a CSR-style adjacency built -once per call. +`smooth_mesh_vertices(mesh, movable, strength, iterations)` - Laplacian relaxation, run after all layers +have been folded in, restricted to the vertices flagged in `movable`. Each pass moves a movable vertex a +`strength` fraction of the way to the average of its one-ring, read from a **snapshot** of the previous +pass so the result does not depend on vertex order (a Gauss-Seidel sweep would smooth several times as +hard at the end of the array as at the start). Neighbours come from a CSR-style adjacency built once per +call. Topology-preserving, like the bake. -Its job is to round off the hard steps a bitmap height map leaves behind, which is a different knob from -`TextureDisplacementLayer::smoothing` - that blurs the *height map* before it is ever sampled, this -relaxes the *geometry* afterwards. Topology-preserving, like the bake. +Its job is to round off the hard steps a bitmap height map leaves behind - a different knob from +`TextureDisplacementLayer::smoothing`, which blurs the *height map* before it is ever sampled. Two ways in, sharing one set of settings on the volume: - The **"Smooth result"** checkbox + "Smoothing (%)" / "Passes" ride along with Preview and Bake. @@ -103,14 +141,13 @@ Two ways in, sharing one set of settings on the volume: operation in the gizmo that keeps **every** paint channel verbatim - it saves and restores the eight texture-displacement masks around `set_mesh()` rather than remapping or dropping them. -**"Ignore outer ring"** (`smooth_skip_border`, **on by default**) drops the patch's own outermost ring of +**"Ignore outer ring"** (`smooth_skip_border`, on by default) drops the patch's own outermost ring of vertices from `movable`. That ring's neighbours *outside* the paint never move, so relaxing it drags the -rim of the relief back down toward the flat surface and the pattern comes out half-melted exactly where -it meets the edge - crisp everywhere else, which is what makes it look like a bug rather than a setting. -Held out, the border keeps the full depth the texture asked for and only the interior relaxes. Turning it -off softens the outer edge deliberately (a blunter version of the per-layer edge-smoothing falloff). -Note this is the *smoothing* rim, a separate question from whether that rim is displaced at all -(`displace_border`, step 4 above) - the two are independent and both default to "keep the border sharp". +rim of the relief down toward the flat surface and the pattern comes out half-melted where it meets the +edge. Held out, the border keeps the full depth the texture asked for and only the interior relaxes. +Turning it off softens the outer edge deliberately (a blunter version of the per-layer edge-smoothing +falloff). This is the *smoothing* rim, independent of whether that rim is displaced at all +(`displace_border`, step 4 above); both default to keeping the border sharp. ### Blend modes @@ -122,50 +159,46 @@ Add/Subtract are self-explanatory. Multiply/Divide are *scaling* operations and convention: they treat the layer's own value as a **factor relative to 1 mm**. That makes `depth_mm` a gain, and - the property that makes a Multiply layer usable as a mask - a layer with depth 1 mm sampling a white (1.0) texel multiplies by exactly 1, i.e. leaves the layers below unchanged. -Divide floors its divisor's magnitude at 0.05 - a black texel samples to *exactly* zero, so the -divisor really does hit zero in ordinary use, and an unbounded `1/0` would fling vertices thousands -of mm away and poison the mesh's bounding box (and every plate/print-volume check downstream). The -floor doubles as a cap on how far Divide can amplify the relief beneath it: at most 20×. +Divide floors its divisor's magnitude at 0.05: a black texel samples to *exactly* zero, so the divisor +really does hit zero in ordinary use, and an unbounded `1/0` would fling vertices thousands of mm away +and poison the mesh's bounding box (and every plate/print-volume check downstream). The floor doubles as +a cap on how far Divide can amplify the relief beneath it: at most 20×. The **lowest painted layer ignores its blend mode**: it has nothing beneath it, and Multiply/Divide -against an implicit zero base would annihilate (or blow up) it. Enforced in `build_texture_ -displacement()` (the first layer to reach a given vertex always folds in additively) and surfaced in -the UI, which labels that layer "Base layer" instead of offering a control that silently does nothing. +against an implicit zero base would annihilate (or blow up) it. Enforced in +`build_texture_displacement()` (the first layer to reach a given vertex always folds in additively) and +surfaced in the UI, which labels that layer "Base layer" instead of offering a control that does nothing. ### Projection methods -Four choices per layer (`TextureProjectionMethod`), all funneling through `apply_uv_transform()` +Five choices per layer (`TextureProjectionMethod`), all funneling through `apply_uv_transform()` (scale by `1/tiling_scale`, rotate by `rotation_deg`, add `offset`). They are dispatched by `sample_layer_height()`, which returns a **height**, not a UV - because Triplanar takes three texture samples per vertex and so has no single UV that represents it. - **Triplanar** (default) - samples the texture on all three world planes (`(y,z)`, `(x,z)`, `(x,y)`) - and blends the three by the vertex's own normal raised to `TRIPLANAR_BLEND_SHARPNESS` (4). - This is the fix for a real, user-reported bug. The previous version *hard-picked* the single axis - most aligned with the normal, which is discontinuous wherever that dominant axis flips: on a +X - face the planar coordinate is `(y, z)`, on a −Y face it is `(x, z)`, so at the shared edge `u` - jumps from `y_edge` to `x_edge`. On a box centred near the origin those two happen to **agree** at - the (+,+) and (−,−) corners and **differ by the full corner width** at the (+,−) and (−,+) corners - - which is exactly the "two bad corners, two good ones" symmetry that was observed. A weighted - blend is continuous across the transition by construction, since the weight of the axis being left - behind falls smoothly to zero. (Note this removes the hard *seam*; some cross-fade blurring in the - band right at a 90° edge is inherent to triplanar mapping. A genuinely seam-free wrap around a box - needs a real unwrap - that is what the LSCM mode is for.) + and blends the three by the vertex's own normal raised to `TRIPLANAR_BLEND_SHARPNESS` (4). Hard-picking + the single axis most aligned with the normal instead is discontinuous wherever that dominant axis + flips: on a +X face the planar coordinate is `(y, z)`, on a −Y face it is `(x, z)`, so at the shared + edge `u` jumps. A weighted blend is continuous across the transition by construction, since the weight + of the axis being left behind falls smoothly to zero. This removes the hard *seam*; some cross-fade + blurring in the band right at a 90° edge is inherent to triplanar mapping. A genuinely seam-free wrap + around a box needs a real unwrap - that is what the LSCM mode is for. - **Cylindrical** - wraps around an axis through the patch centroid, axis auto-picked as the world axis *least* aligned with the average normal (perpendicular to the outward radial normal, as a cylinder's own axis would be). `u = angle * local_radius` (arc length in mm), `v = distance along - axis`. Approximation, not an exact fit for arbitrary geometry. + axis`. An approximation, not an exact fit for arbitrary geometry, and the axis/centre are not + user-overridable. - **Spherical** - longitude/latitude around the centroid, scaled by local radius. Same caveat. - **LSCM** - real UV unwrap via `MeshBoolean::cgal::parameterize_lscm()` (CGAL's `Surface_mesh_parameterization` package, LSCM algorithm). Computed **once per patch** (not per-vertex like the others - it's a single global least-squares solve), then each vertex looks up its precomputed UV. Requires the patch to be a single topological disk (one connected component, - one boundary loop) - `compute_lscm_uvs()` returns empty and the layer silently falls back to - Triplanar if not (e.g. multiple disconnected painted islands, or a fully closed patch). - CGAL's parameterizer needs a mesh with no isolated/unreferenced vertices, but `get_facets_strict()` - returns the *whole* mesh's vertex array - so there's a compaction step - (`compact_patch_with_map()`) that builds a clean sub-mesh + an index map back to the original - (uncompacted) vertex numbering, purely local to this file. + one boundary loop) - `compute_lscm_uvs()` returns empty and the layer falls back to Triplanar if not + (e.g. multiple disconnected painted islands, or a fully closed patch). CGAL's parameterizer needs a + mesh with no isolated/unreferenced vertices, but `get_facets_strict()` returns the *whole* mesh's + vertex array - so `compact_patch_with_map()` builds a clean sub-mesh plus an index map back to the + original vertex numbering, purely local to this file. - **ViewProjected** ("From view") - a flat projection along a fixed direction captured from the 3D camera, like a slide projector. `capture_view_projection()` takes the camera's right/up axes, transforms them into the volume's *local* frame (so the projection rides along if the part is later @@ -174,7 +207,7 @@ texture samples per vertex and so has no single UV that represents it. projects `Vec2f(dot(pos, right), dot(pos, up))`. Single-valued per point, so - like LSCM but unlike blended Triplanar - the fast preview and UV-check overlay precompute it per vertex (`compute_layer_vertex_uvs()`) and drive the shader's `use_vertex_uv` path. Faces angled away from - the projector smear; that is inherent to view projection, not a bug. + the projector smear; that is inherent to view projection. Two companions to this mode: - **Projection frame overlay** (`TextureProjectorFrame`, see below) - a semi-transparent window @@ -202,35 +235,34 @@ untouched) still forces a re-solve. Like the paint masks, seams are mesh-index-s any topology change. Two ways to write to it: -- **Mark seam (manual, #9)** - a "Mark seams" click mode (`m_seam_edit_mode`) that suppresses - painting. A click raycasts the volume (`m_c->raycaster()->raycasters()[idx]->unproject_on_mesh()`, - `idx` = the volume's slot among model-part volumes), finds the facet's edge nearest the hit point, - and toggles it. Marked edges render as a red overlay (`render_seam_overlay()`), pulled toward the - camera so they read on top. This is the Blender mark-seam workflow. -- **Cut island (auto, #17)** - `cut_island()` takes the selected chart's triangles (back-mapped from - the unwrap via `source_vertex`), finds their 3D bounding box, and marks every edge that straddles - the mid-plane perpendicular to the longest axis. The re-unwrap then splits the chart across its - narrow waist - the "islands might be very long" case. Exposed as the UV pane's **Cut** button. +- **Mark seam (manual)** - a "Mark seams" click mode (`m_seam_edit_mode`) that suppresses painting. A + click raycasts the volume (`m_c->raycaster()->raycasters()[idx]->unproject_on_mesh()`, `idx` = the + volume's slot among model-part volumes), finds the facet's edge nearest the hit point, and toggles it. + Marked edges render as a red overlay (`render_seam_overlay()`), pulled toward the camera so they read + on top. This is the Blender mark-seam workflow. +- **Cut island (auto)** - `cut_island()` takes the selected chart's triangles (back-mapped from the + unwrap via `source_vertex`), finds their 3D bounding box, and marks every edge that straddles the + mid-plane perpendicular to the longest axis. The re-unwrap then splits the chart across its narrow + waist. Exposed as the UV pane's **Cut** button. ### UV-check overlays (checker / distortion) `resources/shaders/{110,140}/texture_displacement_uvcheck.{vs,fs}`, one shader with a `mode` uniform, drawn over the painted patch (`rebuild_uvcheck_mesh()`/`render_uvcheck_mesh()`, P3N3T2: `normal.x` = -distortion, `tex_coord` = uv), pulled forward with a polygon offset. **Checker** (#13) samples a -procedural checkerboard at the layer's uv (per-vertex for LSCM/ViewProjected, in-shader triplanar -otherwise) - squares that stay square mean low distortion. **Distortion** (#14) colours each triangle -blue→green→red by `log2(uv_area / surface_area)` centred on the patch's *median* stretch (so a -globally-scaled unwrap reads as uniformly ideal and only relative stretch shows), averaged to -vertices. A separate **Show mesh wireframe** toggle (#8) draws the whole volume's triangle edges, -rebuilt only when the vertex count changes (not per stroke). +distortion, `tex_coord` = uv), pulled forward with a polygon offset. **Checker** samples a procedural +checkerboard at the layer's uv (per-vertex for LSCM/ViewProjected, in-shader triplanar otherwise) - +squares that stay square mean low distortion. **Distortion** colours each triangle blue→green→red by +`log2(uv_area / surface_area)` centred on the patch's *median* stretch (so a globally-scaled unwrap +reads as uniformly ideal and only relative stretch shows), averaged to vertices. A separate **Show mesh +wireframe** toggle draws the whole volume's triangle edges, rebuilt only when the vertex count changes +(not per stroke). ### Tiling `DecodedHeightTexture::sample(uv, tile_enabled, tile_method)`. Two tile methods when enabled (Repeat, MirroredRepeat). **When `tile_enabled` is false, sampling outside `[0,1)` returns `0` -directly** - clamping the *coordinate* into range (what an earlier version did) instead smears the -border row/column of pixels outward to infinity in every direction, which is a real bug that was -reported and fixed (visually: streaky lines radiating out from the painted patch). +directly** rather than clamping the *coordinate* into range, which would smear the border row/column of +pixels outward to infinity in every direction (streaky lines radiating out from the painted patch). ### Subdivision — two modes @@ -242,16 +274,15 @@ no subdivision), Apply snaps back to 0. Drops texture-displacement paint (no rem `save_painting()`/`set_mesh()`/`restore_painting()` dance; the other four channels are remapped. **Adaptive (`subdivide_mesh_adaptive()`)** — refine **only the painted area**, by **Rivara longest-edge -bisection**. This is the algorithm that was "scoped out" originally for fear of the T-junction/crack -problem; it is safe because it is *conformal by construction*. Only **terminal** edges are ever bisected -- an edge that is the longest edge of *every* triangle sharing it - which splits both those triangles -along one shared midpoint at once, so a hanging node is never created. The edge to split for a triangle -that wants refining is found by **longest-edge propagation (LEPP)**: walk to the longest edge of -ever-longer-edged neighbours until a terminal one is reached, and bisect that. Edge length strictly -increases along the path (ties broken by mesh-vertex key, which both sides of an edge compute -identically), so the walk cannot cycle, and Rivara's result is that repeating it refines the original -triangle in a bounded number of bisections. The transition triangles it pulls in just outside the -painted patch are the graded band that makes the size change conformal. +bisection**, which is *conformal by construction*. Only **terminal** edges are ever bisected - an edge +that is the longest edge of *every* triangle sharing it - which splits both those triangles along one +shared midpoint at once, so a hanging node is never created. The edge to split for a triangle that wants +refining is found by **longest-edge propagation (LEPP)**: walk to the longest edge of ever-longer-edged +neighbours until a terminal one is reached, and bisect that. Edge length strictly increases along the +path (ties broken by mesh-vertex key, which both sides of an edge compute identically), so the walk +cannot cycle, and Rivara's result is that repeating it refines the original triangle in a bounded number +of bisections. The transition triangles it pulls in just outside the painted patch are the graded band +that makes the size change conformal. The win: a small decal on a big model no longer quadruples the *whole* model's triangle count. @@ -260,16 +291,11 @@ of sweeps: it holds every triangle that is over its criteria in a max-heap keyed it is, pops the worst, walks its LEPP, bisects, and re-scores. Edge adjacency (`nb[e]`, the triangle across each edge) is built **once** and maintained incrementally through each bisection, so the cost scales with the refined region rather than with the whole model. `max_triangles` is the only bound; -stopping on it leaves a perfectly valid, still-conformal mesh that spent its budget on the largest errors. +stopping on it leaves a perfectly valid, still-conformal mesh that spent its budget on the largest +errors. A fixed sweep count instead spends itself grading the *coarse surroundings* - whose edges are +the longest, so they win every terminal-edge contest - and never reaches the painted patch. -This shape replaced a first version that ran a fixed 12 sweeps, each rebuilding a whole-mesh edge map and -bisecting one terminal edge per active triangle. Two failure modes came out of that, and they are worth -remembering because they look like separate bugs and are not: the sweeps were consumed grading the -*coarse surroundings* (whose edges are the longest, so they win every terminal-edge contest), which both -**stopped refinement of the painted patch far short** of the requested detail and left the band outside -it looking wildly over-refined relative to the patch itself. - -**It carries the paint forward**, which is what makes it usable (uniform/remesh both drop paint). Because +**It carries the paint forward**, which is what makes it usable (uniform subdivide drops paint). Because the refinement is *driven by* the paint, the remap is trivial: `subdivide_mesh_adaptive()` fills an `out_source[new_tri] = input_tri` map (children inherit their parent), and the gizmo rebuilds each layer's mask on the new mesh - a new triangle is painted iff its source was fully painted in that @@ -277,32 +303,30 @@ layer. `collect_paint_region()` derives both: - the union refine-region: **exactly** the original triangles the brush touched, read straight off `TriangleSplittingData::triangles_to_split` (`serialize()` records an entry per original triangle that is either split - i.e. partially painted, the patch boundary - or carries a non-default state). No - dilation. An earlier version marked every triangle sharing a *vertex* with the patch, which drags in a - whole fan of huge unpainted neighbours and then refines *those* down to the resolution floor, since the - height field the detail test samples is not restricted to the painted area. The conformal closure - already grades the size change outward on its own; it does not need help. + dilation: marking every triangle that shares a *vertex* with the patch drags in a whole fan of huge + unpainted neighbours and refines *those* down to the resolution floor, since the height field the + detail test samples is not restricted to the painted area. The conformal closure already grades the + size change outward on its own. - the per-layer fully-painted-triangle sets (a `get_facets_strict(ENFORCER)` sub-triangle with all three *original* vertex indices == a whole, fully-painted original triangle; a partial stroke's sub-triangles always carry a split vertex). -The other four channels still ride the normal `restore_painting()` remap. Covered by a conformality unit -test (`every_edge_used_twice` on a partially-refined cube - an exact crack detector for a closed mesh), -plus tests that the target edge length is actually *reached* and that the budget caps the result without -opening a crack. +The other four channels ride the normal `restore_painting()` remap. -Both share the gizmo's Preview/Apply/Done flow; the **"Only painted area (adaptive)"** checkbox picks -the mode, and the adaptive preview follows the paint live (`rebuild_preview()` refreshes the wireframe -while the subdivide preview is open in adaptive mode). The panel shows the previewed triangle count. +Both modes share the gizmo's Preview/Apply/Done flow; the **"Only painted area (adaptive)"** checkbox +picks the mode, and the adaptive preview follows the paint live (`rebuild_preview()` refreshes the +wireframe while the subdivide preview is open in adaptive mode). The panel shows the previewed triangle +count. **Feature-adaptive (follow texture detail).** A sub-mode of adaptive (the **"Follow texture detail"** -checkbox) that puts triangles where the *displaced surface actually bends*, not evenly. The insight: -a flat region or a linear **ramp** needs no extra vertices (linear interpolation is exact for a ramp); -what needs them is **curvature** - the *second* derivative, not the gradient. So the extra predicate is a -**chord-error** test: sample the combined displacement at the triangle's three edge midpoints *and its -centroid* (sampling the interior is what catches a bump sitting inside a triangle, the blind spot of an -edge-only test) and take the largest departure from the flat triangle's barycentric interpolation. Refine -while that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp ⇒ untouched; high on -a bump/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The +checkbox) that puts triangles where the *displaced surface actually bends*, not evenly. A flat region or +a linear **ramp** needs no extra vertices (linear interpolation is exact for a ramp); what needs them is +**curvature** - the *second* derivative, not the gradient. So the extra predicate is a **chord-error** +test: sample the combined displacement at the triangle's three edge midpoints *and its centroid* +(sampling the interior is what catches a bump sitting inside a triangle, the blind spot of an edge-only +test) and take the largest departure from the flat triangle's barycentric interpolation. Refine while +that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp ⇒ untouched; high on a +bump/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The per-triangle error is cached and recomputed only for the children of a split. Four knobs bracket it, and all four matter: @@ -320,33 +344,40 @@ The height field is `make_combined_displacement_sampler()` - it mirrors `build_t per-layer setup (decode, patch centroid, cylinder axis, blend order, "lowest layer folds additively") but evaluated per point. Two deliberate simplifications, both erring toward *more* detail (safe - over-refinement is never a crack): every sampleable layer is sampled at every point (no per-point paint -test), and edge-smoothing falloff is ignored. Note the first one is *why* the refine region must not be -dilated - outside the paint the sampler still reports full relief. **LSCM layers are skipped** (no -per-point UV); a purely LSCM stack yields a null sampler and the code falls back to the length baseline -alone. Per-vertex heights are sampled lazily, so a small patch on a huge model never pays for the rest of -it. Covered by unit tests: a Gaussian bump refines densely at its center and leaves flat corners coarse, -a linear ramp produces *zero* extra triangles (the case a gradient criterion would over-refine), and a -flat field still honours the max-edge baseline. +test), and edge-smoothing falloff is ignored. The first is *why* the refine region must not be dilated - +outside the paint the sampler still reports full relief. **LSCM layers are skipped** (no per-point UV); a +purely LSCM stack yields a null sampler and the code falls back to the length baseline alone. Per-vertex +heights are sampled lazily, so a small patch on a huge model never pays for the rest of it. -### Fast bump preview (GPU-only, no CPU meshing) +### Fast preview (GPU-only, no CPU meshing) `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}`, registered as -`"texture_displacement_bump"`. Perturbs the *shading* normal from the height texture's local -gradient instead of moving geometry - active-layer-only, toggled via a "Fast preview (normal map)" -checkbox. Vertex format is `GLModel::Geometry::EVertexLayout::P3N3T2`: `normal.x` carries the -per-vertex paint weight (0/1) and `tex_coord` carries a precomputed texture UV, so it can use -`GLModel` normally instead of needing a hand-rolled VBO/VAO manager. Weight buffer is -rebuilt at the same cadence as the true-displacement preview (stroke-end/slider-release), using the -**live** `TriangleSelector` state (not the flushed model facets), so it doesn't lag by a full model -round-trip. +`"texture_displacement_bump"`. Shades the *displaced* surface without moving geometry - active-layer +only, selected from the View row, and the default when the gizmo opens (`m_use_bump_preview = true`). +Vertex format is `GLModel::Geometry::EVertexLayout::P3N3T2`: `normal.x` carries the per-vertex paint +weight (0/1), `normal.y` flags the UV island currently being dragged, and `tex_coord` carries a +precomputed texture UV, so it can use `GLModel` normally instead of a hand-rolled VBO/VAO manager. + +The mesh is **flat** (vertices not shared between triangles): every corner of a painted triangle gets +weight 1, every corner of an unpainted one weight 0. A coarse mesh needs that - one painted face of a raw +cube has no strictly-interior vertex, so per-vertex weighting would either bleed onto the neighbours or +vanish outright. Duplicating vertices costs no shading quality here because the shader takes its surface +normal from screen-space derivatives of position, not from a per-vertex normal. + +**Both preview meshes work in the patch's vertex space, not the mesh's.** Those agree only until a +*brush* stroke splits a triangle: `get_facets_strict()` then appends the split vertices, so the patch +array is longer. `rebuild_bump_preview_mesh()` and `rebuild_uvcheck_mesh()` therefore index +`patch.vertices` throughout. The weight buffer is rebuilt at the same cadence as the true-displacement +preview (stroke-end/slider-release) but from the **live** `TriangleSelector` state, not the flushed model +facets, so it does not lag by a full model round-trip. The perturbed normal is the analytic one for a height field `H = ±depth_mm · h(uv)` displaced along `N` over any orthonormal surface tangent pair `T`/`B`: N' = normalize(N − (dH/da)·T − (dH/db)·B), a = dot(p,T), b = dot(p,B) -The two slopes have to be genuine **mm-per-mm** derivatives for the preview's apparent depth to -match the bake's - see bug #13. +The two slopes have to be genuine **mm-per-mm** derivatives for the preview's apparent depth to match +the bake's. **Two projection paths (`use_vertex_uv` uniform):** - **Triplanar (`use_vertex_uv = 0`)** - `uv` and the `T`/`B` axes are both derived in-shader from @@ -354,32 +385,58 @@ match the bake's - see bug #13. formed analytically. `T`/`B` are the projection's axis-aligned pair, exact only when the face is axis-aligned; the shader drops the along-normal component to keep the gradient in the surface. Here one `uv` unit is exactly `tiling_scale` mm, so the `1/tiling_scale` gradient factor is right. -- **Precomputed UV (`use_vertex_uv = 1`, used for LSCM)** - `uv` comes per-vertex from the CPU - (`compute_lscm_uvs(patch, layer)`, so island placement + tiling/rotation/offset are already folded +- **Precomputed UV (`use_vertex_uv = 1`, used for LSCM and ViewProjected)** - `uv` comes per-vertex from + the CPU (`compute_layer_vertex_uvs()`, so island placement + tiling/rotation/offset are already folded in), and the perturbed normal is built with **Mikkelsen's method** ("Bump Mapping Unparametrized Surfaces on the GPU"): the surface gradient taken directly from the screen-space derivatives of the - *sampled height* and position. **This makes no uv→mm scale assumption**, which is essential - - the first cut used the same global `1/tiling_scale` factor as triplanar and the depth came out - visibly wrong, because an LSCM map is **conformal, not isometric**: it is globally area-scaled but - the *local* mm-per-uv varies across the chart. `dFdx(h)` captures the true on-screen rate of change - however the chart is stretched. **This path is also what makes the fast preview follow the UV - editor: move an island and its uv - hence its bump - moves with it** (the bump mesh rebuilds on - drag-end, since `on_island_edited(finished)` → `rebuild_preview()` → `rebuild_bump_preview_mesh()`). - The branch is uniform (`use_vertex_uv` is a uniform) and the paint weight gates by multiply, so the + *sampled height* and position. **This makes no uv→mm scale assumption**, which is essential, because an + LSCM map is **conformal, not isometric**: it is globally area-scaled but the *local* mm-per-uv varies + across the chart, so a single global `1/tiling_scale` factor gets the apparent depth wrong. `dFdx(h)` + captures the true on-screen rate of change however the chart is stretched. This path is also what makes + the fast preview follow the UV editor: move an island and its uv - hence its shading - moves with it + (the mesh rebuilds on drag-end, `on_island_edited(finished)` → `rebuild_preview()` → + `rebuild_bump_preview_mesh()`). The branch is uniform and the paint weight gates by multiply, so the texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv → the `det≈0` guard skips it (a localised preview-only artifact, never in the bake). -Remaining deliberate approximation: the GPU sampler's wrap mode stands in for -`tile_enabled`/`tile_method`, so with tiling *off* the GPU repeats where the CPU returns 0 outside +**Parallax (triplanar path).** Perturbing the shading normal alone welds the pattern to the base surface: +it does not slide as the camera orbits, and does not get deeper as `depth_mm` grows. The triplanar path +therefore shades at the point the *displaced* surface would show at this pixel, found by **ray marching** +(parallax occlusion mapping). A point at ray parameter `s`, i.e. `P + V·s` (`P` the base point, `V` the +unit direction to the eye), sits at height `s·dot(V,n)` above the undisplaced surface. The displaced +surface lives in a shell between the extreme values of `amp·(h − midlevel)` - taken from both ends of +`h ∈ [0,1]`, so it holds for an inverted layer and a raised midlevel too, where the surface sits *below* +the undisplaced one. The march starts at the top of that shell, where the ray is outside the surface by +construction, and steps inward until the ray height drops below the sampled height. That crossing *is* +the visible point. + +Solving `Q = P + V·(H(Q)/dot(V,n))` by fixed-point iteration instead is geometrically exact but the +divisor goes to zero edge-on; the sample then lands a large fraction of a tile away and the iteration +oscillates, which reads as a second, flat copy of the pattern ghosted over the real one. Clamping the +step to one tile does not help - a tile-sized shift lands on the neighbouring tile, the same pattern +again. Offset limiting (stepping along the tangential part of `V`) is stable but understates parallax +enough that the relief still flattens as soon as the camera tilts. Marching has neither problem. + +The hit is interpolated between the last two samples, which keeps `PARALLAX_STEPS` (24) affordable, and +the whole march is skipped when sweeping the shell would move the sample point less than half a texel - +the head-on case, so the common view pays almost nothing. The 140 variant samples with +`textureLod(…, 0.0)` inside the loop, since implicit derivatives are undefined in non-uniform control +flow. Two uniforms exist for this: `midlevel` (parallax needs the real height, not just its derivative) +and `eye_model_pos` (the camera in the volume's local frame). + +Parallax cannot change the model's silhouette or cast shadows; the View row's Normal mode is one click +away for that. The LSCM path stays plain Mikkelsen bump - it has no closed-form uv, so there is no cheap +way to re-project a marched position. One further approximation: the GPU sampler's wrap mode stands in +for `tile_enabled`/`tile_method`, so with tiling *off* the GPU repeats where the CPU returns 0 outside `[0,1)`. ### On-canvas "Adjust Texture" gizmo -A per-active-layer toggle ("Adjust placement (drag on model)") that disables painting and shows a -flat pan panel (free 2D drag on both axes) plus two arrows along the patch's own U/V axes -(constrained single-axis drag). Anchored to the painted patch's centroid/average-normal -(`compute_layer_paint_anchor()`). Hit-testing is screen-space distance/point-to-segment (not real -3D ray intersection against the handle geometry) - simple and good enough at this handle size. +A per-active-layer toggle ("Adjust placement") that disables painting and shows a flat pan panel (free +2D drag on both axes) plus two arrows along the patch's own U/V axes (constrained single-axis drag). +Anchored to the painted patch's centroid/average-normal (`compute_layer_paint_anchor()`). Hit-testing is +screen-space distance/point-to-segment, not real 3D ray intersection against the handle geometry - simple +and good enough at this handle size. ### Projection frame overlay (ViewProjected) @@ -393,14 +450,14 @@ position and size *are* the placement, read on demand at Apply - which is also w visible-facet raycast runs. So dragging it is free and nothing recomputes until asked. Plain 2D (`wxPaintDC`), not a `wxGLCanvas`: a second GL canvas would have to share the app's one real -`wxGLContext`, the cause of bugs #10 and #14 below. It only ever draws a bitmap and a border. +`wxGLContext`. It only ever draws a bitmap and a border. -**The projective mapping (`apply_projection_frame()`)**. The frame defines a -**screen-space** rectangle, but the bake samples from a **local-space** position, so the two have to be -reconciled. `view_project_right/up` can only express an *affine* projection - exact under an -orthographic camera, but wrong under perspective, where the near end of a part projects larger than the -far end and no pair of axes reproduces that. So the layer instead stores a full projective map -(`view_project_matrix`, row-major 3×4, `uv = (row0·p̃/row2·p̃, row1·p̃/row2·p̃)`), built like this: +**The projective mapping (`apply_projection_frame()`)**. The frame defines a **screen-space** rectangle, +but the bake samples from a **local-space** position, so the two have to be reconciled. +`view_project_right/up` can only express an *affine* projection - exact under an orthographic camera, but +wrong under perspective, where the near end of a part projects larger than the far end and no pair of +axes reproduces that. So the layer instead stores a full projective map (`view_project_matrix`, row-major +3×4, `uv = (row0·p̃/row2·p̃, row1·p̃/row2·p̃)`), built like this: - `K = projection · view · (instance · volume)`, i.e. local → clip, the same product the renderer uses. Note `Camera::get_projection_matrix()` is typed `Transform3d` (nominally affine) but its perspective @@ -433,7 +490,7 @@ reopening keeps it where it was left. `UVEditorCanvas` (`src/slic3r/GUI/UVEditorCanvas.hpp/.cpp`) - a standalone `wxGLCanvas` rendering the flattened LSCM islands (per-island wireframe + outline + fill) over the height texture (background quad tiled across the whole unwrap), with mouse pan/zoom. It is wrapped in a **`UVEditorPanel`** -(same file) that adds a button row (Frame / Snap / Average scale) and a status line +(same file) that adds a button row (Frame / Snap / Avg scale / Cut / Join / Unjoin) and a status line along the bottom naming the current gesture and the shortcuts in play. The *panel* is what is registered as a `wxAuiPaneInfo` pane on `Plater`'s `m_aui_mgr`; `Plater::show_uv_editor(bool)` shows/hides it (deferred via `CallAfter`, since the gizmo calls it mid-3D-frame), and @@ -445,51 +502,24 @@ same call `View3D`/`Preview`/`AssembleView` make) rather than creating an indepe `"flat"`/`"flat_texture"` shaders and `GLModel` as-is, instead of needing its own shader compilation/VBO management. -**Geometry is uploaded once, in the unwrap's own (raw, mm) coordinates**, one `GLModel` set per -island; each island is then drawn through its own 2x3 affine (`island_transform_matrix()` composed -with the layer's tiling/rotation/offset) passed as the `flat` shader's `view_model_matrix`. A -drag updates one matrix per island and touches no vertex -buffer - `on_island_edited(!finished)` calls only `set_island_transforms()`, and the full -`set_islands()` rebuild happens solely when the unwrap itself changes (`unwrap_changed` in -`update_uv_editor()`). +**Geometry is uploaded once, in the unwrap's own (raw, mm) coordinates**, one `GLModel` set per island; +each island is then drawn through its own 2x3 affine (`island_transform_matrix()` composed with the +layer's tiling/rotation/offset) passed as the `flat` shader's `view_model_matrix`. A drag updates one +matrix per island and touches no vertex buffer - `on_island_edited(!finished)` calls only +`set_island_transforms()`, and the full `set_islands()` rebuild happens solely when the unwrap itself +changes (`unwrap_changed` in `update_uv_editor()`). **Gestures** (canvas-owned, reported to the gizmo as incremental deltas via `IslandEditFn`): left-drag = move, right-drag or **R** = rotate (hold **Shift** to snap to 15° steps - quantised on the *cumulative* rotation, not each delta, so it doesn't judder, and accumulated incrementally so it survives crossing ±180°), **S** = scale (R/S modal, click/Enter to confirm, Esc to cancel), wheel = zoom about the cursor, middle-drag = pan, **Home**/**F** = frame all. Scale writes -`TextureIsland::scale`; "Average scale" (`average_island_scales()`) sets every island to the mean, so +`TextureIsland::scale`; "Avg scale" (`average_island_scales()`) sets every island to the mean, so one island scaled by hand can be matched back to its neighbours' texel density. **Snap** (canvas-owned `m_snap_enabled`, toggled from the toolbar) sticks a dragged island's nearest boundary vertex onto a neighbouring island's at drag-*end* only - a magnet that re-applies mid-drag is very hard to pull out -of. Toolbar commands the canvas can't service itself (Average scale) are forwarded to the gizmo via -`CommandFn`; view-only ones (Frame, Snap) it handles directly. - -## Known limitations / deferred work - -- **No `.3mf` serialization** for texture-displacement paint data or texture assets. A background - agent attempted this in an earlier session, hit its own usage limit mid-edit, and left - `bbs_3mf.cpp` with an undefined forward-declared function; that partial edit was reverted rather - than shipped broken. Practical impact: **baked** geometry round-trips fine (it's just an ordinary - part of the mesh via the existing mesh serialization path) - what does *not* survive a project - save/reload is any *unbaked* paint stroke and texture layer definition. -- **No remap-across-topology-change** for texture-displacement paint (`ModelObject::split()`, mesh - boolean ops, Simplify, uniform subdivide, and remesh all drop it via `reset_extra_facets()`). The - other four paint channels (supported/seam/mmu/fuzzy) do get remapped in these cases. The lone - exception is **adaptive subdivide**, which carries texture-displacement paint forward itself via its - source map (see the Subdivision section) - a targeted remap that only works because the operation is - driven by the paint. -- **Cylindrical/Spherical axis/center are auto-picked heuristically**, not user-controllable - no - UI to override the auto-detected wrap axis if it picks the "wrong" one for an odd shape. -- **Fast preview covers the active layer only** — and is now the **default** view when the gizmo - opens (`m_use_bump_preview = true`): it is the instant, no-CPU-meshing preview, so it is the better - first impression while painting. The exact true-displacement view is one click away in the View row. -- **Displacement resolution is capped by the mesh's own vertex density.** Baking only ever *moves* - existing vertices (it never inserts any), so a coarse patch cannot show fine texture detail no - matter how high-resolution the height map is - that is what the subdivision controls are for - (uniform, adaptive, or feature-adaptive; see the Subdivision section). Since the rewrite the bake is - topology-preserving, so this is now a hard, explicit property rather than something partly papered - over by the old per-layer re-meshing. +of. Toolbar commands the canvas can't service itself (Avg scale, Cut, Join, Unjoin) are forwarded to the +gizmo via `CommandFn`; view-only ones (Frame, Snap) it handles directly. ## File map @@ -498,23 +528,18 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward tiling, subdivision (uniform + adaptive longest-edge bisection), post-process smoothing (`smooth_mesh_vertices()`), and `TextureDisplacementOptions` (the whole-stack settings). See doc comments throughout, they're kept accurate and up to date. -- `src/libslic3r/MeshBoolean.hpp/.cpp` - added `parameterize_lscm()` and `remesh_isotropic()` - in the `cgal` sub-namespace, - reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for - mesh boolean ops. New CGAL includes: `Polygon_mesh_processing/border.h`, +- `src/libslic3r/MeshBoolean.hpp/.cpp` - `parameterize_lscm()` and `remesh_isotropic()` in the `cgal` + sub-namespace, reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already + there for mesh boolean ops. CGAL includes: `Polygon_mesh_processing/border.h`, `Polygon_mesh_processing/connected_components.h`, `Surface_mesh_parameterization/{Error_code, - LSCM_parameterizer_3, parameterize}.h`. No new dependency - CGAL 5.6.3 is already vendored and - the `Surface_mesh_parameterization` package headers were already present, just unused before now. + LSCM_parameterizer_3, parameterize}.h`. No new dependency - CGAL 5.6.3 is already vendored and the + `Surface_mesh_parameterization` package headers were already present. - `src/libslic3r/Model.hpp/.cpp` - the 8 named `FacetsAnnotation` fields + accessor, `texture_displacement_layers`, `texture_displacement_options`, and all the mirrored touch points (see Data model above). **GUI:** -- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` - the gizmo. Panel controls: dock/ - undock toggle, brush/face/connected-area selection mode + "select whole model" button, per-layer - texture picker + depth/tiling/rotation/invert/tile-mode/projection-mode/blend-mode controls, - "Adjust placement" toggle (on-canvas gizmo), "Fast preview (normal map)" toggle, "Subdivide model" - button, Add layer/Erase all/Bake. +- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` - the gizmo and its whole panel. - `src/slic3r/GUI/TextureLibrary.hpp/.cpp` - scans the shipped + user texture folders, imports an arbitrary image into the user folder (converting it to the 8-bit grayscale PNG libslic3r decodes), and loads a library file's bytes for a layer. The image→grayscale-PNG conversion lives here, on the @@ -534,18 +559,23 @@ of. Toolbar commands the canvas can't service itself (Average scale) are forward - `src/slic3r/GUI/Plater.hpp/.cpp` - `uv_editor_canvas` member, AUI pane registration, `get_uv_editor_canvas()`/`show_uv_editor()`. - `src/slic3r/GUI/GLShadersManager.cpp` - registers `"texture_displacement_bump"`. -- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` - the bump-preview shader. +- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` - the fast-preview shader. - `src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp` - `PainterGizmoType::TEXTURE_DISPLACEMENT`. - `src/slic3r/GUI/Gizmos/GLGizmosManager.hpp/.cpp` - `EType::TextureDisplacement` registration. -- `src/slic3r/GUI/ImGuiWrapper.cpp` - the light-mode checkmark-color fix -**Tests:** `tests/libslic3r/test_texture_displacement.cpp` - **run and passing** (7 cases, 116 -assertions). Covers `decode_height_texture` round-trip, empty-layer no-op, full-cube uniform -displacement, boundary-vertex pinning on a hand-built fan mesh, and - added with the bake rewrite - -a regression test that a **second layer over the same area actually contributes**, -a table-driven check of all four blend modes, and that the lowest layer ignores its -blend mode. `BUILD_TESTS` is `OFF` in the checked-in build cache; flip it on to run them: +## Tests + +`tests/libslic3r/test_texture_displacement.cpp`. Covers `decode_height_texture` round-trip, empty-layer +no-op, full-cube uniform displacement, a second layer over the same area contributing, all four blend +modes (table-driven), the lowest layer ignoring its blend mode, border displace/pin, post-process +smoothing and its mask guarantees, and adaptive subdivision: conformality (`every_edge_used_twice` on a +partially-refined cube - an exact crack detector for a closed mesh), the target edge length actually +being reached, the triangle budget capping the result without opening a crack, curvature-driven +refinement (a Gaussian bump refines at its centre, a linear ramp adds nothing), and the max-edge +baseline. + +`BUILD_TESTS` is `OFF` in the checked-in build cache; flip it on to run them: cmake -S . -B build -DBUILD_TESTS=ON cmake --build build --config Release --target libslic3r_tests -- -m - ./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand \ No newline at end of file + ./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index 75a2baa290..d569b42d0c 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -5,6 +5,9 @@ #define INTENSITY_CORRECTION 0.6 +#define PARALLAX_STEPS 24 +#define H_AT(uv) texture2D(height_tex, uv).r + const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); #define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) #define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) @@ -30,6 +33,8 @@ uniform float tiling_scale; uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; +uniform float midlevel; // the height that means "don't move"; needed by the parallax step +uniform vec3 eye_model_pos; // camera position in this volume's local space, for the view ray uniform bool use_vertex_uv; // 2x3 affine (lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the dragged island's uv; see the // 140 variant. Identity when nothing is dragged. @@ -96,10 +101,50 @@ void main() if (abs(det) > 1e-12) triangle_normal = normalize(triangle_normal - (dHdx * R1 + dHdy * R2) / det); } else if (weight > 0.0) { - vec2 uv = project_uv(model_pos.xyz, triangle_normal); vec3 t, b; projection_axes(triangle_normal, t, b); + // Parallax occlusion mapping: march the view ray through the height shell and shade at the + // first point where it drops below the displaced surface (see header). + float amp = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); + vec3 view_dir = normalize(eye_model_pos - model_pos.xyz); + float v_dot_n = dot(view_dir, triangle_normal); + vec2 uv = project_uv(model_pos.xyz, triangle_normal); + + // The shell the displaced surface lives inside, as signed heights along the normal. Taken from + // both ends of h in [0, 1] so it stays correct for an inverted layer or a raised midlevel, + // where the surface sits *below* the undisplaced one. + float h_end_a = amp * (0.0 - midlevel); + float h_end_b = amp * (1.0 - midlevel); + float h_hi = max(h_end_a, h_end_b); + float h_lo = min(h_end_a, h_end_b); + // How far, in mm, sweeping the ray across the shell slides the sample point sideways. Below half + // a texel there is no parallax to find and the march would be pure cost - which is the common + // case of looking straight down at a surface. + float sweep = length(view_dir - triangle_normal * v_dot_n) * (h_hi - h_lo) / max(v_dot_n, 1e-4); + if (v_dot_n > 0.05 && sweep > 0.5 * tiling_scale * height_tex_texel.x) { + // A point at ray parameter s (model_pos + view_dir * s) sits at height s * v_dot_n above the + // undisplaced surface. Start at the top of the shell, where the ray is outside the surface + // by construction, and step inward; the crossing is what this pixel actually sees. + float s = h_hi / v_dot_n; + float ds = (h_hi - h_lo) / (v_dot_n * float(PARALLAX_STEPS)); + vec2 prev_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + float prev_gap = h_hi - amp * (H_AT(prev_uv) - midlevel); // >= 0 by construction + for (int i = 0; i < PARALLAX_STEPS; ++i) { + s -= ds; + vec2 cur_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + float gap = s * v_dot_n - amp * (H_AT(cur_uv) - midlevel); + if (gap <= 0.0) { + // Crossed between the last two samples - interpolating the hit is what stops it + // quantising to the step size, and so what keeps the step count affordable. + uv = mix(prev_uv, cur_uv, clamp(prev_gap / max(prev_gap - gap, 1e-6), 0.0, 1.0)); + break; + } + prev_uv = cur_uv; + prev_gap = gap; + } + } + float hL = texture2D(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; float hR = texture2D(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; float hD = texture2D(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 58f79722aa..17b550d5e6 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -19,7 +19,36 @@ // Two projection paths: // * Triplanar (use_vertex_uv = 0): uv and the tangent axes are derived in-shader from the dominant // normal axis, mirroring libslic3r's project_planar()/apply_uv_transform(), and the slope is -// formed analytically (there is a closed-form uv, so 1 uv unit is exactly tiling_scale mm). +// formed analytically (there is a closed-form uv, so 1 uv unit is exactly tiling_scale mm). This +// path also runs a parallax step before shading, see below. +// +// Parallax. A pure bump map perturbs shading only, so the pattern is welded to the base surface: it +// does not shift as the camera orbits and it does not get any deeper as depth_mm grows, which is +// exactly when the preview stops reading as real geometry. The triplanar path therefore shades at the +// point the *displaced* surface would show at this pixel rather than at the pixel's own base position. +// +// Two cheaper formulations were tried first and both are wrong here, which is worth recording: +// * Solving Q = P + V * (H(Q) / dot(V, n)) by fixed-point iteration. Geometrically exact, but the +// divisor goes to zero edge-on, and an unbounded step is not a small error - the sample lands a +// large fraction of a tile away and the iteration oscillates instead of converging. It reads as a +// *second, flat copy* of the pattern ghosted over the real one. Clamping the step to one tile does +// not help either: a tile-sized shift lands on the neighbouring tile, which is the same pattern. +// * Offset limiting (Welsh): step along the tangential part of V, whose length caps the shift at one +// depth. Stable and cheap, but it understates parallax by exactly the factor that matters - the +// relief still flattens as soon as the camera tilts, which is the complaint it was meant to fix. +// +// So this ray-marches instead (parallax occlusion mapping). A point at ray parameter s, i.e. P + V * s, +// sits at height s * dot(V, n) above the undisplaced surface. The displaced surface lives in a shell +// between the extreme values of amp * (h - midlevel); the march starts at the top of that shell, where +// the ray is outside the surface by construction, and steps inward until the ray height falls below the +// sampled height. That crossing *is* the visible point - no divergence, no ghosting, and parallax stays +// correct at any angle. The hit is interpolated between the last two samples, which is what keeps +// PARALLAX_STEPS low enough to afford. The march is skipped when sweeping the shell would move the +// sample point less than half a texel (the head-on case), so the common view pays almost nothing. +// +// The gradient/shading below is evaluated at the resulting uv, so the relief both slides correctly +// under camera motion and visibly deepens with depth_mm. What it still cannot do is change the +// model's silhouette or cast shadows; for that, switch the View row to Normal. // * Precomputed uv (use_vertex_uv = 1, used for LSCM): uv comes per-vertex from the CPU (the LSCM // unwrap with island placement + tiling/rotation/offset already folded in), and the perturbed // normal is built with Mikkelsen's method -- the surface gradient taken straight from the @@ -31,6 +60,11 @@ #define INTENSITY_CORRECTION 0.6 +#define PARALLAX_STEPS 24 +// Explicit LOD: the march samples inside non-uniform control flow, where implicit +// derivatives are undefined. +#define H_AT(uv) textureLod(height_tex, uv, 0.0).r + // normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); #define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) @@ -58,6 +92,8 @@ uniform float tiling_scale; uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; +uniform float midlevel; // the height that means "don't move"; needed by the parallax step +uniform vec3 eye_model_pos; // camera position in this volume's local space, for the view ray uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM) // A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of // the island currently being dragged in the UV editor (island_active > 0.5). Identity when nothing is @@ -144,10 +180,50 @@ void main() // Triplanar path: uv and the tangent axes are reconstructed in-shader from the dominant // normal component (see header). The gradient is expressed analytically because there is a // closed-form uv here, unlike the LSCM case. - vec2 uv = project_uv(model_pos.xyz, triangle_normal); vec3 t, b; projection_axes(triangle_normal, t, b); + // Parallax occlusion mapping: march the view ray through the height shell and shade at the + // first point where it drops below the displaced surface (see header). + float amp = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); + vec3 view_dir = normalize(eye_model_pos - model_pos.xyz); + float v_dot_n = dot(view_dir, triangle_normal); + vec2 uv = project_uv(model_pos.xyz, triangle_normal); + + // The shell the displaced surface lives inside, as signed heights along the normal. Taken from + // both ends of h in [0, 1] so it stays correct for an inverted layer or a raised midlevel, + // where the surface sits *below* the undisplaced one. + float h_end_a = amp * (0.0 - midlevel); + float h_end_b = amp * (1.0 - midlevel); + float h_hi = max(h_end_a, h_end_b); + float h_lo = min(h_end_a, h_end_b); + // How far, in mm, sweeping the ray across the shell slides the sample point sideways. Below half + // a texel there is no parallax to find and the march would be pure cost - which is the common + // case of looking straight down at a surface. + float sweep = length(view_dir - triangle_normal * v_dot_n) * (h_hi - h_lo) / max(v_dot_n, 1e-4); + if (v_dot_n > 0.05 && sweep > 0.5 * tiling_scale * height_tex_texel.x) { + // A point at ray parameter s (model_pos + view_dir * s) sits at height s * v_dot_n above the + // undisplaced surface. Start at the top of the shell, where the ray is outside the surface + // by construction, and step inward; the crossing is what this pixel actually sees. + float s = h_hi / v_dot_n; + float ds = (h_hi - h_lo) / (v_dot_n * float(PARALLAX_STEPS)); + vec2 prev_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + float prev_gap = h_hi - amp * (H_AT(prev_uv) - midlevel); // >= 0 by construction + for (int i = 0; i < PARALLAX_STEPS; ++i) { + s -= ds; + vec2 cur_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + float gap = s * v_dot_n - amp * (H_AT(cur_uv) - midlevel); + if (gap <= 0.0) { + // Crossed between the last two samples - interpolating the hit is what stops it + // quantising to the step size, and so what keeps the step count affordable. + uv = mix(prev_uv, cur_uv, clamp(prev_gap / max(prev_gap - gap, 1e-6), 0.0, 1.0)); + break; + } + prev_uv = cur_uv; + prev_gap = gap; + } + } + float hL = texture(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; float hR = texture(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; float hD = texture(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 78886f6be8..579d9a5792 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -747,9 +747,13 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() if (patch.indices.empty()) return; - const indexed_triangle_set &base = mv->mesh().its; - if (base.vertices.size() != patch.vertices.size()) - return; // shouldn't happen: get_facets_strict() always returns the full vertex array + // No "patch.vertices.size() == mesh vertex count" check here, and that is the point: a *brush* + // stroke splits triangles, so the selector appends split vertices and the patch array is longer + // than the mesh's. An earlier version bailed out on that as "shouldn't happen", which meant the + // bump model was never built while brushing and render_painter_gizmo() silently fell back to the + // Normal (true-displacement) preview - Fast looked broken for brush and fine for Face/Connected + // area, because only the brush splits. Everything below indexes the patch's own vertex array, so + // the extra vertices are simply carried through. // Unpainted triangles, so the surrounding surface still renders (the render path hides the real // model in bump mode). get_facets_strict() returns the same vertex array whatever state is asked. @@ -762,7 +766,7 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() // mesh rebuilds (on drag end) with them. The other projections keep projecting in-shader. const TextureDisplacementLayer *active = active_layer(); std::vector vertex_uv = active != nullptr ? compute_layer_vertex_uvs(patch, *active) : std::vector{}; - m_bump_preview_uses_vertex_uv = vertex_uv.size() == base.vertices.size(); + m_bump_preview_uses_vertex_uv = vertex_uv.size() == patch.vertices.size(); if (!m_bump_preview_uses_vertex_uv) vertex_uv.clear(); @@ -945,6 +949,13 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f); shader->set_uniform("uv_offset", layer->offset); shader->set_uniform("invert", layer->invert); + // The parallax step in the triplanar path needs the real height, not just its gradient, so it + // needs the midlevel the bake subtracts - and the camera position in the volume's own local space, + // to build the view ray it walks along. Without the parallax the pattern is welded to the base + // surface: it does not slide as the camera orbits and does not deepen with depth_mm, which is + // exactly when the fast preview stops looking like geometry. + shader->set_uniform("midlevel", layer->midlevel); + shader->set_uniform("eye_model_pos", Vec3f((trafo_matrix.inverse() * camera.get_position()).cast())); // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than // projecting; see rebuild_bump_preview_mesh(). shader->set_uniform("use_vertex_uv", m_bump_preview_uses_vertex_uv); @@ -970,9 +981,11 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() const indexed_triangle_set patch = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER); if (patch.indices.empty()) return; - const indexed_triangle_set &base = mv->mesh().its; - if (base.vertices.size() != patch.vertices.size()) - return; + // Everything below works in the *patch's* vertex space, not the mesh's. Those agree only until a + // brush stroke splits a triangle, after which the patch array is longer - and since patch triangle + // indices are used to index it, reading the mesh's array instead would run off the end. An earlier + // version guarded that by bailing out, which quietly disabled the Checker and Distortion overlays + // for anything painted with the brush. const TextureDisplacementLayer *layer = active_layer(); if (layer == nullptr) return; @@ -980,16 +993,16 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() // The checker samples wherever the projection puts it; the projections the shader can't // reconstruct (LSCM, ViewProjected) get a precomputed per-vertex uv, the rest project in-shader. std::vector uv = compute_layer_vertex_uvs(patch, *layer); - const bool have_uvs = uv.size() == base.vertices.size(); + const bool have_uvs = uv.size() == patch.vertices.size(); m_uvcheck_uses_vertex_uv = have_uvs; // Per-vertex area distortion in [0,1] (0.5 == ideal), only when both requested and possible. - std::vector distortion(base.vertices.size(), 0.5f); + std::vector distortion(patch.vertices.size(), 0.5f); if (m_uv_check_mode == UVCheckMode::Distortion && have_uvs) { std::vector tri_log(patch.indices.size(), 0.f); for (size_t f = 0; f < patch.indices.size(); ++f) { const stl_triangle_vertex_indices &t = patch.indices[f]; - const float a3 = 0.5f * (base.vertices[t[1]] - base.vertices[t[0]]).cross(base.vertices[t[2]] - base.vertices[t[0]]).norm(); + const float a3 = 0.5f * (patch.vertices[t[1]] - patch.vertices[t[0]]).cross(patch.vertices[t[2]] - patch.vertices[t[0]]).norm(); const Vec2f e0 = uv[t[1]] - uv[t[0]]; const Vec2f e1 = uv[t[2]] - uv[t[0]]; const float a2 = 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x()); @@ -1003,8 +1016,8 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() std::nth_element(sorted.begin(), sorted.begin() + sorted.size() / 2, sorted.end()); median = sorted[sorted.size() / 2]; } - std::vector sum(base.vertices.size(), 0.f); - std::vector cnt(base.vertices.size(), 0); + std::vector sum(patch.vertices.size(), 0.f); + std::vector cnt(patch.vertices.size(), 0); for (size_t f = 0; f < patch.indices.size(); ++f) { // +/- 2 stops (4x stretch either way) spans the full blue->red range. const float d = std::clamp(0.5f + (tri_log[f] - median) / 4.f, 0.f, 1.f); @@ -1020,10 +1033,10 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; - init_data.reserve_vertices(base.vertices.size()); + init_data.reserve_vertices(patch.vertices.size()); init_data.reserve_indices(patch.indices.size() * 3); - for (size_t vi = 0; vi < base.vertices.size(); ++vi) - init_data.add_vertex(base.vertices[vi], Vec3f(distortion[vi], 0.f, 0.f), + for (size_t vi = 0; vi < patch.vertices.size(); ++vi) + init_data.add_vertex(patch.vertices[vi], Vec3f(distortion[vi], 0.f, 0.f), have_uvs ? uv[vi] : Vec2f::Zero()); for (const stl_triangle_vertex_indices &tri : patch.indices) init_data.add_triangle(unsigned(tri[0]), unsigned(tri[1]), unsigned(tri[2])); @@ -2753,51 +2766,82 @@ bool GLGizmoTextureDisplacement::collect_paint_region( return any_paint; } -void GLGizmoTextureDisplacement::subdivide_model_adaptive() +bool GLGizmoTextureDisplacement::plan_adaptive_subdivision(const ModelVolume &mv, SubdivisionPlan &out) const { - ModelVolume *mv = texture_volume(); - ModelObject *mo = m_c->selection_info()->model_object(); - if (mv == nullptr || mo == nullptr || m_subdivide_target_mm <= 0.f) - return; + if (m_subdivide_target_mm <= 0.f) + return false; - update_model_object(); // flush any in-progress stroke into the committed masks first - - std::vector region; - std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> painted_tri; - if (!collect_paint_region(region, &painted_tri)) { - show_error(nullptr, _u8L("Paint the area you want to subdivide first - adaptive subdivision only " - "refines where you have painted.")); - return; - } + std::vector region; + if (!collect_paint_region(region, &out.painted_tri)) + return false; // Feature-adaptive: sample the combined displacement so refinement follows texture curvature. A - // null sampler (only LSCM layers, or nothing decodable) falls back to the uniform target below. + // null sampler (only LSCM layers, or nothing decodable) falls back to the length baseline alone. HeightFieldSampler sampler; if (m_subdivide_feature) { TextureDisplacementFacetsData facets{}; for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) - facets[size_t(i)] = mv->texture_displacement_facet(i).get_data(); - sampler = make_combined_displacement_sampler(mv->mesh().its, mv->texture_displacement_layers, facets); + facets[size_t(i)] = mv.texture_displacement_facet(i).get_data(); + sampler = make_combined_displacement_sampler(mv.mesh().its, mv.texture_displacement_layers, facets); } - // Do the (potentially slow) refinement before taking the snapshot, so a no-op leaves no empty - // undo step - mirrors remesh_model(). // "Min edge" is a feature-mode control (it is the floor the curvature test refines down to); in // plain adaptive mode the target edge length is the only criterion, so the floor must not be // allowed to silently override a target the user set below it. const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f; const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f; - std::vector source; - indexed_triangle_set refined; { wxBusyCursor wait; // The budget slider is "triangles the refinement may *add*", so the model's own count is the // baseline - otherwise the control would be meaningless (or a dead end) on a dense model. - refined = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, - int(mv->mesh().its.indices.size()) + m_subdivide_budget_k * 1000, - &source, sampler, tol, floor); + out.refined = subdivide_mesh_adaptive(mv.mesh().its, region, m_subdivide_target_mm, + int(mv.mesh().its.indices.size()) + m_subdivide_budget_k * 1000, + &out.source, sampler, tol, floor); } - if (refined.indices.size() == mv->mesh().its.indices.size()) { + return out.refined.indices.size() != mv.mesh().its.indices.size(); +} + +void GLGizmoTextureDisplacement::apply_adaptive_subdivision(ModelVolume &mv, SubdivisionPlan &&plan) +{ + // Other paint channels ride across via the standard remap; texture-displacement paint is rebuilt + // below from the plan's source map, which is the whole point of driving this by the paint. + std::optional saved_painting = mv.save_painting(); + mv.set_mesh(TriangleMesh(std::move(plan.refined))); + mv.set_new_unique_id(); + mv.calculate_convex_hull(); + mv.restore_painting(saved_painting); // resets extra facets (incl. texture-displacement) + remaps the rest + + // Carry each layer's paint onto the new mesh: a new triangle is painted iff its source triangle + // was fully painted in that layer. Children inherit their parent's source, so this is exact. + for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { + if (plan.painted_tri[size_t(slot)].empty()) + continue; + TriangleSelector sel(mv.mesh()); + for (size_t i = 0; i < plan.source.size(); ++i) + if (plan.painted_tri[size_t(slot)][size_t(plan.source[i])]) + sel.set_facet(int(i), EnforcerBlockerType::ENFORCER); + mv.texture_displacement_facet(slot).set(sel); + } +} + +void GLGizmoTextureDisplacement::subdivide_model_adaptive() +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr) + return; + + update_model_object(); // flush any in-progress stroke into the committed masks first + + if (!mv->is_texture_displacement_painted()) { + show_error(nullptr, _u8L("Paint the area you want to subdivide first - adaptive subdivision only " + "refines where you have painted.")); + return; + } + + // Plan before the snapshot, so a no-op leaves no empty undo step - mirrors remesh_model(). + SubdivisionPlan plan; + if (!plan_adaptive_subdivision(*mv, plan)) { show_error(nullptr, _u8L("Nothing to subdivide - the painted area already meets the target edge " "length and detail tolerance, or the triangle budget is already used up.")); return; @@ -2805,26 +2849,7 @@ void GLGizmoTextureDisplacement::subdivide_model_adaptive() Plater *plater = wxGetApp().plater(); Plater::TakeSnapshot snapshot(plater, _u8L("Adaptive subdivide for texture displacement"), UndoRedo::SnapshotType::GizmoAction); - - // Other paint channels ride across via the standard remap; texture-displacement paint is rebuilt - // by hand below from the source map, which is the whole point of driving this by the paint. - std::optional saved_painting = mv->save_painting(); - mv->set_mesh(TriangleMesh(std::move(refined))); // refined is not needed past here; source carries the paint map - mv->set_new_unique_id(); - mv->calculate_convex_hull(); - mv->restore_painting(saved_painting); // resets extra facets (incl. texture-displacement) + remaps the rest - - // Carry each layer's paint onto the new mesh: a new triangle is painted iff its source triangle - // was fully painted in that layer. Children inherit their parent's source, so this is exact. - for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { - if (painted_tri[slot].empty()) - continue; - TriangleSelector sel(mv->mesh()); - for (size_t i = 0; i < source.size(); ++i) - if (painted_tri[slot][source[i]]) - sel.set_facet(int(i), EnforcerBlockerType::ENFORCER); - mv->texture_displacement_facet(slot).set(sel); - } + apply_adaptive_subdivision(*mv, std::move(plan)); if (ObjectList *obj_list = wxGetApp().obj_list()) { const ModelObjectPtrs &objs = plater->model().objects; @@ -2921,6 +2946,62 @@ void GLGizmoTextureDisplacement::smooth_model() m_parent.set_as_dirty(); } +void GLGizmoTextureDisplacement::replace_mesh_keep_all_paint(ModelVolume &mv, TriangleMesh &&new_mesh) +{ + const indexed_triangle_set old_its = mv.mesh().its; + std::array saved_texture; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + saved_texture[size_t(i)] = mv.texture_displacement_facet(i).get_data(); + std::optional saved_painting = mv.save_painting(); + + mv.set_mesh(std::move(new_mesh)); + mv.set_new_unique_id(); + mv.calculate_convex_hull(); + mv.restore_painting(saved_painting); // clears the extra facets, then remaps the four standard channels + + // ... and the same spatial remap for the texture-displacement masks, which restore_painting() knows + // nothing about. Without this a remesh would wipe the texture paint, which is fine for a standalone + // "Remesh" click but fatal for Standard mode's pipeline: it remeshes *after* the user has painted, + // and the subdivision and displacement that follow are both driven by that paint. + const Transform3d to_source = Slic3r::Geometry::translation_transform(mv.mesh().get_init_shift()); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) { + if (saved_texture[size_t(i)].bitstream.empty()) + continue; + TriangleSelector::TriangleSplittingData remapped = + TriangleSelector::remap_painting(old_its, saved_texture[size_t(i)], mv.mesh().its, to_source, + {}); // no existing paint to merge with: set_mesh() just cleared it + if (!remapped.bitstream.empty()) + mv.texture_displacement_facet(i).set_data(std::move(remapped)); + } +} + +bool GLGizmoTextureDisplacement::plan_remesh(const ModelVolume &mv, float target_edge_mm, float sharp_angle_deg, + TriangleMesh &out) +{ + if (target_edge_mm <= 0.f) + return false; + + // CGAL isotropic remeshing can be slow on a big mesh, which is why this is separated from applying + // it: the caller runs it before taking the snapshot so a failure leaves no empty undo step. + const indexed_triangle_set &src = mv.mesh().its; + indexed_triangle_set remeshed; + { + wxBusyCursor wait; + remeshed = MeshBoolean::cgal::remesh_isotropic(src, double(target_edge_mm), 3, double(sharp_angle_deg)); + } + // remesh_isotropic() signals failure by handing the input straight back, so compare against it + // structurally. Vertex count alone is not enough: a remesh that only redistributes triangles at + // roughly the current density legitimately lands on the same count, and treating that as failure + // meant a perfectly good result got thrown away with an error message. + if (remeshed.indices.empty() || + (remeshed.vertices.size() == src.vertices.size() && remeshed.indices.size() == src.indices.size() && + remeshed.indices == src.indices)) + return false; + + out = TriangleMesh(std::move(remeshed)); + return true; +} + void GLGizmoTextureDisplacement::remesh_model() { ModelVolume *mv = texture_volume(); @@ -2928,39 +3009,18 @@ void GLGizmoTextureDisplacement::remesh_model() if (mv == nullptr || mo == nullptr || m_remesh_target_edge_mm <= 0.f) return; - Plater *plater = wxGetApp().plater(); - - // CGAL isotropic remeshing can be slow on a big mesh; do it before taking the snapshot so a failure - // (it returns the input unchanged) doesn't leave an empty undo step. - const indexed_triangle_set &src = mv->mesh().its; - indexed_triangle_set remeshed; - { - wxBusyCursor wait; - remeshed = MeshBoolean::cgal::remesh_isotropic(mv->mesh().its, double(m_remesh_target_edge_mm), 3, - m_remesh_keep_sharp_edges ? double(m_remesh_sharp_angle_deg) : 0.0); - } - // remesh_isotropic() signals failure by handing the input straight back, so compare against it - // structurally. Vertex count alone is not enough: a remesh that only redistributes triangles at - // roughly the current density legitimately lands on the same count, and treating that as failure - // meant a perfectly good result got thrown away with an error message. - const bool unchanged = remeshed.indices.empty() || - (remeshed.vertices.size() == src.vertices.size() && remeshed.indices.size() == src.indices.size() && - remeshed.indices == src.indices); - if (unchanged) { + TriangleMesh remeshed; + if (!plan_remesh(*mv, m_remesh_target_edge_mm, m_remesh_keep_sharp_edges ? m_remesh_sharp_angle_deg : 0.f, + remeshed)) { show_error(nullptr, _u8L("Remeshing did not change the model. It may be non-manifold (open edges or " "edges shared by more than two triangles), or the target edge length may " "already be met.")); return; } + Plater *plater = wxGetApp().plater(); Plater::TakeSnapshot snapshot(plater, _u8L("Remesh model for texture displacement"), UndoRedo::SnapshotType::GizmoAction); - // Same save/replace/restore-painting dance as subdivide: texture-displacement paint has no remap - // across a topology change, so it is dropped rather than left pointing at triangles that moved. - std::optional saved_painting = mv->save_painting(); - mv->set_mesh(TriangleMesh(std::move(remeshed))); - mv->set_new_unique_id(); - mv->calculate_convex_hull(); - mv->restore_painting(saved_painting); + replace_mesh_keep_all_paint(*mv, std::move(remeshed)); if (ObjectList *obj_list = wxGetApp().obj_list()) { const ModelObjectPtrs &objs = plater->model().objects; @@ -3075,7 +3135,7 @@ GLTexture *GLGizmoTextureDisplacement::get_layer_thumbnail(const TextureDisplace return m_thumbnails[slot].get(); } -void GLGizmoTextureDisplacement::bake() +void GLGizmoTextureDisplacement::bake(bool own_snapshot) { ModelVolume *mv = texture_volume(); if (!mv || m_bake_in_progress) @@ -3101,7 +3161,112 @@ void GLGizmoTextureDisplacement::bake() if (m_state == On && m_c->selection_info() && m_c->selection_info()->model_object()) update_from_model_object(false); m_parent.set_as_dirty(); - }); + }, own_snapshot); +} + +// Standard mode's fixed recipe. These are both the values the hidden controls are pinned to and what +// bake_standard() drives its remesh and subdivision with - one set of numbers, so the preview cannot +// disagree with the bake. Chosen to be safe on an arbitrary imported part rather than optimal on any +// particular one: a 1 mm isotropic remesh gives the subdivider an even starting density whatever the +// input looked like, and the subdivision then spends up to 1.5 M triangles chasing texture curvature +// down to a 0.02 mm floor, which is finer than any FDM nozzle will resolve. The triangle budget is not +// here: it is the one control Standard mode still shows, so it belongs to the user (its default is +// m_subdivide_budget_k's initialiser). +static constexpr float STD_REMESH_EDGE_MM = 1.0f; +static constexpr float STD_REMESH_SHARP_DEG = 40.f; +static constexpr float STD_SUBDIV_MAX_EDGE_MM = 20.f; +static constexpr float STD_SUBDIV_DETAIL_MM = 0.02f; +static constexpr float STD_SUBDIV_MIN_EDGE_MM = 0.02f; + +bool GLGizmoTextureDisplacement::apply_standard_mode_presets(ModelVolume *mv) +{ + bool changed = false; + const auto pin = [&changed](auto &field, auto value) { + if (field != value) { + field = value; + changed = true; + } + }; + + if (mv != nullptr) { + pin(mv->texture_displacement_options.displace_border, true); + pin(mv->texture_displacement_options.smooth_enabled, false); + } + pin(m_subdivide_adaptive, true); + pin(m_subdivide_feature, true); + pin(m_subdivide_target_mm, STD_SUBDIV_MAX_EDGE_MM); + pin(m_subdivide_detail_mm, STD_SUBDIV_DETAIL_MM); + pin(m_subdivide_min_edge_mm, STD_SUBDIV_MIN_EDGE_MM); + // Deliberately *not* pinned: the triangle budget stays visible and editable in Standard mode, so + // pinning it would fight the user's own slider every frame. + pin(m_remesh_target_edge_mm, STD_REMESH_EDGE_MM); + pin(m_remesh_keep_sharp_edges, true); + pin(m_remesh_sharp_angle_deg, STD_REMESH_SHARP_DEG); + return changed; +} + +void GLGizmoTextureDisplacement::bake_standard() +{ + ModelVolume *mv = texture_volume(); + ModelObject *mo = m_c->selection_info()->model_object(); + if (mv == nullptr || mo == nullptr || m_bake_in_progress) + return; + + update_model_object(); // flush the active layer's in-progress strokes before anything reads them + if (!mv->is_texture_displacement_painted()) { + show_error(nullptr, _u8L("Nothing is painted, there is nothing to bake.")); + return; + } + apply_standard_mode_presets(mv); // belt and braces: never bake with values the panel is not showing + + // Both stages are planned before the snapshot so a stage that has nothing to do is simply skipped. + // Skipping is normal, not a failure: a mesh that is already even needs no remesh, and one that is + // already fine enough for the texture needs no subdivision. + TriangleMesh remeshed; + const bool do_remesh = plan_remesh(*mv, STD_REMESH_EDGE_MM, STD_REMESH_SHARP_DEG, remeshed); + + Plater *plater = wxGetApp().plater(); + { + // ONE undo step for the whole pipeline. take_snapshot() records the state *before* the change, + // so a single Undo goes all the way back to the untouched mesh - which is the only thing "undo + // the bake" can sensibly mean when the bake is also what prepared the geometry. The background + // displacement job is told not to add its own (see queue_texture_displacement_bake's + // take_snapshot), because an undo step landing between the subdivision and the displacement + // leaves a mesh with 1.5 M extra triangles and no relief on it - and baking again from there + // subdivides that mesh a second time. + Plater::TakeSnapshot snapshot(plater, _u8L("Bake texture displacement"), + UndoRedo::SnapshotType::GizmoAction); + if (do_remesh) + replace_mesh_keep_all_paint(*mv, std::move(remeshed)); + + // The remesh carries the paint across spatially, but if that remap came back empty the rest of + // the pipeline has nothing to work from - stop here rather than silently baking a flat mesh. + if (!mv->is_texture_displacement_painted()) { + show_error(nullptr, _u8L("The painted area could not be transferred onto the remeshed model. Undo, " + "then switch to Pro mode to prepare the mesh before painting.")); + return; + } + + // Planning the subdivision has to happen inside the snapshot because it reads the mesh the + // remesh just produced. It is the expensive step, but by here we are committed anyway. + SubdivisionPlan plan; + if (plan_adaptive_subdivision(*mv, plan)) + apply_adaptive_subdivision(*mv, std::move(plan)); + } + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), mo); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + plater->changed_object(*mo); + update_from_model_object(false); // reload selectors against the prepared mesh + carried paint + m_parent.set_as_dirty(); + + // ... and finally the displacement itself, in the background exactly as the Pro-mode button does - + // except that it commits into the snapshot taken above instead of pushing another one. + bake(/* own_snapshot */ false); } void GLGizmoTextureDisplacement::render_paint_cursor_hint() @@ -3237,6 +3402,42 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->tooltip(_u8L("Detach this panel so it can be dragged anywhere over the 3D view, or dock it " "back beside the toolbar."), m_imgui->scaled(20.f)); + + // Standard / Pro, right-aligned on the header row. A two-position slider rather than a checkbox + // because it is a mode, not an option: Standard hides every mesh-preparation control and folds the + // whole recipe into Bake, Pro shows all of it and hands the ordering to the user. + { + const float mode_w = m_imgui->scaled(6.2f); + ImGui::SameLine(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - mode_w)); + ImGui::PushItemWidth(mode_w); + // The format string carries no conversion, so ImGui prints it verbatim as the slider's label - + // which is how a two-position slider gets word labels instead of "0" and "1". + const std::string mode_label = pro_mode() ? _u8L("Pro") : _u8L("Standard"); + if (ImGui::SliderInt("##panel_mode", &m_panel_mode, 0, 1, mode_label.c_str())) { + m_panel_mode = std::clamp(m_panel_mode, 0, 1); + if (!pro_mode()) { + // Leaving the subdivision preview open would strand a wireframe whose controls just + // disappeared, so close it as part of the switch. + m_subdivide_editing = false; + m_subdivide_preview_tris = -1; + m_subdivide_preview_glmodel.reset(); + if (apply_standard_mode_presets(mv)) + m_preview_params_dirty = true; + } + m_parent.set_as_dirty(); + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Standard - paint, then press Bake: the mesh is remeshed, refined where the " + "texture bends, and displaced in one step.\n\nPro - every mesh-preparation " + "control is shown and you run Remesh, Subdivide and Bake yourself, in the " + "order you want."), + m_imgui->scaled(20.f)); + } + // Pinned every frame while Standard is active, so what Preview shows is always what Bake will do. + if (!pro_mode() && apply_standard_mode_presets(mv)) + m_preview_params_dirty = true; + ImGui::Separator(); // Selection mode: which of TriangleSelector's existing click/brush mechanisms drives painting. @@ -3854,8 +4055,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } // (The "Add layer" button now lives next to the "Texture layers" heading, as an icon.) - // Settings for the whole stack rather than one layer, so they sit outside the layer list. - if (mv != nullptr) { + // Settings for the whole stack rather than one layer, so they sit outside the layer list. Standard + // mode pins them (see apply_standard_mode_presets()) instead of showing them. + if (pro_mode() && mv != nullptr) { TextureDisplacementOptions &opts = mv->texture_displacement_options; ImGui::Separator(); @@ -3912,7 +4114,34 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } ImGui::Separator(); - m_imgui->text(_u8L("Not enough vertices for fine detail?")); + m_imgui->text(_u8L("Subdivision")); + + // The triangle budget is the one subdivision control Standard mode keeps: its right value depends + // on the part rather than on the recipe (a big model, or a fine texture, simply needs more of them), + // and raising or lowering it is safe without understanding anything else in this section. Shared + // with the Pro layout below so there is one definition of the widget. + const auto budget_slider = [this]() { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (ImGui::SliderInt((_u8L("Added triangles (k)") + "##subdivbudget").c_str(), &m_subdivide_budget_k, 10, 2000)) { + m_subdivide_budget_k = std::clamp(m_subdivide_budget_k, 10, 2000); + if (m_subdivide_editing) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("How many thousand triangles the refinement may add. It always splits the " + "worst-fitting triangle first, so a run that uses the whole budget has still spent " + "it where it shows most - raise this if the result still looks too coarse."), + m_imgui->scaled(20.f)); + }; + + // Everything else from here to the Bake row is mesh preparation, which is exactly what Standard + // mode takes over: it pins those to one recipe and runs them from the Bake button (see + // bake_standard()), so showing them would only invite changing numbers that get overwritten. + if (!pro_mode()) + budget_slider(); + if (pro_mode()) { if (ImGui::Checkbox(_u8L("Only painted area (adaptive)").c_str(), &m_subdivide_adaptive)) { if (m_subdivide_editing) @@ -3996,18 +4225,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->scaled(20.f)); } - // The budget. Refinement is worst-error-first, so a run that hits it has still spent its - // triangles on the biggest deviations - raising it buys detail, it does not redistribute it. - if (ImGui::SliderInt((_u8L("Added triangles (k)") + "##subdivbudget").c_str(), &m_subdivide_budget_k, 10, 2000)) { - m_subdivide_budget_k = std::clamp(m_subdivide_budget_k, 10, 2000); - preview_live(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How many thousand triangles the refinement may add. It always splits the " - "worst-fitting triangle first, so a run that uses the whole budget has still spent " - "it where it shows most - raise this if the preview still looks too coarse."), - m_imgui->scaled(20.f)); ImGui::PopItemWidth(); + budget_slider(); if (m_subdivide_editing && m_subdivide_preview_tris > 0) m_imgui->text(Slic3r::format(_u8L("Preview: %1% triangles"), m_subdivide_preview_tris)); @@ -4074,7 +4293,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // Remesh: even out uneven triangle sizes (CGAL isotropic remeshing). GPU Delaunay isn't practical // here, but this delivers the same goal - a consistent triangle size across the whole model. - m_imgui->text(_u8L("Uneven triangle sizes?")); + m_imgui->text(_u8L("Remeshing")); if (m_remesh_target_edge_mm <= 0.f && mv != nullptr) { // Seed the target with the model's current mean edge length, so the default is a sensible // "make everything about the size it already averages". @@ -4114,9 +4333,12 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Rebuilds the whole model with triangles close to this edge length - splitting the big " "ones and merging the small ones - so displacement has an even density to work with. " - "Replaces the geometry and clears any not-yet-baked paint (already-baked bumps are kept)."), + "Replaces the geometry; your paint is carried onto the new triangles spatially, so it " + "survives (already-baked bumps are kept too)."), m_imgui->scaled(20.f)); + } // pro_mode() + ImGui::Separator(); m_imgui->disabled_begin(mv == nullptr || !mv->is_texture_displacement_painted()); @@ -4136,9 +4358,23 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::SameLine(); m_imgui->disabled_begin(m_bake_in_progress || mv == nullptr || !mv->is_texture_displacement_painted()); - if (m_imgui->button(m_bake_in_progress ? _L("Baking...") : m_desc.at("bake"))) - bake(); + if (m_imgui->button(m_bake_in_progress ? _L("Baking...") : m_desc.at("bake"))) { + // Standard mode's Bake is the whole pipeline (remesh -> refine -> displace); Pro's is only the + // displacement, because there the user has already prepared the mesh with the controls above. + if (pro_mode()) + bake(); + else + bake_standard(); + } m_imgui->disabled_end(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(pro_mode() ? + _u8L("Turn the painted height maps into real geometry, by moving the vertices that are " + "already there. Use Subdivide first if the mesh is too coarse to show the detail.") : + _u8L("Turn the painted height maps into real geometry. The mesh is remeshed to an even " + "density and refined where the texture bends first, so the detail has vertices to " + "land on - all in one step."), + m_imgui->scaled(20.f)); ImGui::Separator(); if (m_imgui->button(_L("Close"))) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 1f3c6bcc73..29fc28a114 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -67,7 +67,51 @@ private: void add_texture_layer(); void remove_texture_layer(int slot); void set_active_layer(int slot); // flushes the previous layer's edits, then reloads selectors - void bake(); + // `own_snapshot` false when the caller has already taken an undo step that is meant to cover the + // displacement too - see bake_standard(). + void bake(bool own_snapshot = true); + + // Standard (0) vs Pro (1), driven by the two-position slider in the panel header. + // + // Pro is the panel as it has always been: every geometry-preparation control is visible and the + // user drives Remesh, Subdivide and Bake themselves, in whatever order they like. Standard hides + // all of that, pins it to one fixed recipe, and folds it into the Bake button - paint, press Bake, + // done - so the common case does not require knowing that a height map can only move vertices that + // already exist. Nothing about Pro changed when Standard was added. + int m_panel_mode = 0; + bool pro_mode() const { return m_panel_mode != 0; } + // Pins every control Standard mode hides to its preset value. Idempotent, called each frame while + // Standard is active so what Preview shows is always what Bake will do. Returns true if it actually + // changed something, so the caller can invalidate the preview. + bool apply_standard_mode_presets(ModelVolume *mv); + // Standard mode's Bake: remesh to an even density, refine where the texture bends, then displace. + // The order matters and is the whole reason this is one button - a height map can only move + // existing vertices, so the mesh has to be prepared first, and remeshing after painting would drop + // the paint if it were not remapped across (see replace_mesh_keep_all_paint()). + void bake_standard(); + + // Replaces `mv`'s mesh and carries *every* paint channel onto it, texture displacement included - + // the four standard channels via ModelVolume::restore_painting(), the eight texture-displacement + // masks via the same TriangleSelector::remap_painting() spatial remap, which restore_painting() + // does not cover. Shared by Remesh and by Standard mode's pipeline. + static void replace_mesh_keep_all_paint(ModelVolume &mv, TriangleMesh &&new_mesh); + // Remesh and adaptive Subdivide are each split into a *plan* (the heavy geometry work, touching + // nothing) and an *apply* (pure mutation). That split is what lets both the standalone buttons and + // Standard mode's one-button pipeline run the expensive part **before** taking the undo snapshot, + // so a run that turns out to be a no-op does not leave an empty undo step behind - and it keeps + // snapshot ownership with the caller, which matters because the buttons want one snapshot per click + // while the pipeline wants a single one around remesh + subdivide together. + struct SubdivisionPlan + { + indexed_triangle_set refined; + std::vector source; // new tri -> input tri + std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> painted_tri; // per layer, per input tri + }; + // False means "nothing to refine" and `out` must not be used. + bool plan_adaptive_subdivision(const ModelVolume &mv, SubdivisionPlan &out) const; + static void apply_adaptive_subdivision(ModelVolume &mv, SubdivisionPlan &&plan); + // False means the remesh failed or changed nothing (CGAL signals failure by handing the input back). + static bool plan_remesh(const ModelVolume &mv, float target_edge_mm, float sharp_angle_deg, TriangleMesh &out); // Marks every facet of every model-part volume as painted for the currently active layer - // "whole model" as an alternative to brushing/clicking every triangle by hand. @@ -327,7 +371,7 @@ private: // dense model). Refinement is worst-error-first, so hitting the budget still yields the best mesh // that many triangles can buy - and it is what keeps a fine "Detail" over a noisy texture from // turning into an out-of-memory, or an unrenderable preview wireframe. - int m_subdivide_budget_k = 200; + int m_subdivide_budget_k = 1500; void subdivide_model_adaptive(); // Fills `region` (per current-mesh triangle, 1 = refine) from the union of every layer's painted // area. If `painted_tri` is non-null, also fills, per layer, the fully-painted triangles to carry @@ -345,7 +389,9 @@ private: // Isotropic remeshing (CGAL) to even out wildly varying triangle sizes so displacement has a // consistent density to work with. Target edge length in mm; 0 means "not yet initialised", filled // with the mesh's mean edge length the first time the control is shown. Like subdivide, it replaces - // the geometry and drops not-yet-baked paint (no remap across a topology change). + // the geometry, but unlike subdivide it keeps every paint channel: replace_mesh_keep_all_paint() + // remaps the texture-displacement masks spatially, which is also what lets Standard mode remesh + // *after* the user has painted. float m_remesh_target_edge_mm = 0.f; // Dihedral angle above which an edge counts as a hard feature and is held fixed by the remesher. // Off by default would round every sharp edge off, so this is on; 0 disables the protection. diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index ae7e4dc3dd..7d6119ce30 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -41,41 +41,55 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept Plater *plater = wxGetApp().plater(); - Plater::TakeSnapshot snapshot(plater, _u8L("Bake texture displacement"), UndoRedo::SnapshotType::GizmoAction); + const auto commit = [this, plater]() { + ModelVolume *volume = get_model_volume(m_input.volume_id, plater->model().objects); + if (volume == nullptr) + return; - ModelVolume *volume = get_model_volume(m_input.volume_id, plater->model().objects); - if (volume == nullptr) - return; + volume->set_mesh(std::move(m_result)); + volume->set_new_unique_id(); + volume->calculate_convex_hull(); - volume->set_mesh(std::move(m_result)); - volume->set_new_unique_id(); - volume->calculate_convex_hull(); + // Clear the paint mask of every layer that was actually baked so a repeat bake (or the paint + // overlay) doesn't act on triangles that no longer represent the same unbaked surface. The + // texture layer definitions themselves (and paint outside the baked area, if any) are left + // untouched so the user can keep sculpting with the same textures. + for (const TextureDisplacementLayer &layer : m_input.layers) + if (!layer.empty() && layer.slot >= 0 && layer.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) + volume->texture_displacement_facet(layer.slot).reset(); - // Clear the paint mask of every layer that was actually baked so a repeat bake (or the paint - // overlay) doesn't act on triangles that no longer represent the same unbaked surface. The - // texture layer definitions themselves (and paint outside the baked area, if any) are left - // untouched so the user can keep sculpting with the same textures. - for (const TextureDisplacementLayer &layer : m_input.layers) - if (!layer.empty() && layer.slot >= 0 && layer.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) - volume->texture_displacement_facet(layer.slot).reset(); + ModelObject *object = volume->get_object(); + if (object == nullptr) + return; - ModelObject *object = volume->get_object(); - if (object == nullptr) - return; + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), object); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } - if (ObjectList *obj_list = wxGetApp().obj_list()) { - const ModelObjectPtrs &objs = plater->model().objects; - auto it = std::find(objs.begin(), objs.end(), object); - if (it != objs.end()) - obj_list->update_info_items(size_t(it - objs.begin())); + plater->changed_object(*object); + }; + + // Standard mode's Bake is remesh -> subdivide -> displace under a single snapshot, and this job runs + // long after that snapshot's scope has closed. Adding one here would put an undo step *between* the + // subdivision and the displacement: the first Undo would land on a mesh carrying every added + // triangle and no relief at all, and pressing Bake again from there would subdivide that mesh a + // second time. So the caller says who owns the undo step. + if (m_input.take_snapshot) { + Plater::TakeSnapshot snapshot(plater, _u8L("Bake texture displacement"), UndoRedo::SnapshotType::GizmoAction); + commit(); + } else { + commit(); } - - plater->changed_object(*object); } -void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished) +void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished, + bool take_snapshot) { TextureDisplacementBakeInput input; + input.take_snapshot = take_snapshot; input.volume_id = volume.id(); input.base_mesh = volume.mesh().its; input.layers = volume.texture_displacement_layers; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp index 6fca093897..bf0fcaa8bd 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -22,6 +22,10 @@ struct TextureDisplacementBakeInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Whether this job pushes its own undo step when it commits. False when the caller has already + // taken one that is meant to cover the displacement as well - Standard mode's Bake, which remeshes + // and subdivides first and has to undo as a single action. + bool take_snapshot = true; }; // Bakes a volume's painted texture-displacement layers into real mesh geometry in the background, @@ -45,7 +49,8 @@ private: // the app's UI job worker. `on_finished` is always called once the job settles (success, failure, // or cancellation), so the caller can clear its own "bake in progress" UI state. Must be called // from the main thread. -void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished); +void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished, + bool take_snapshot = true); } // namespace Slic3r::GUI From 3aa48abcb7ae62e5062bd6015357aa01e2ae6090 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 20 Aug 2026 07:14:21 +0200 Subject: [PATCH 19/89] Measure post-process smoothing by edge energy instead of height spread --- tests/libslic3r/test_texture_displacement.cpp | 113 +++++++++--------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 6f41c76bce..5978418ae2 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -278,15 +278,13 @@ TEST_CASE("TextureDisplacement: the patch border is displaced by default and pin TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", "[TextureDisplacement]") { // A checkerboard height map on a fine grid gives a relief full of hard steps - exactly what the - // smoothing pass is for. Measure it as the spread of the displaced heights: relaxing the surface - // must pull the extremes in without touching the mesh's topology. + // smoothing pass is for. Only the central square is painted, so the patch has a real border. indexed_triangle_set plane; plane.vertices = { { 0.f, 0.f, 0.f }, { 20.f, 0.f, 0.f }, { 20.f, 20.f, 0.f }, { 0.f, 20.f, 0.f } }; plane.indices = { { 0, 1, 2 }, { 0, 2, 3 } }; const indexed_triangle_set grid = subdivide_mesh_uniform(plane, 1.f, 6); REQUIRE(grid.indices.size() > 256); - // Paint only the central square, so the patch has a real border to test against. std::vector painted(grid.indices.size(), 0); const TriangleMesh grid_mesh(grid); TriangleSelector selector(grid_mesh); @@ -310,19 +308,19 @@ TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", layer.slot = 0; layer.depth_mm = 2.0f; layer.tiling_scale = 6.0f; - layer.image_data = make_checkerboard_png(); - auto z_spread = [](const indexed_triangle_set &its) { - float lo = its.vertices.front().z(), hi = lo; - for (const Vec3f &v : its.vertices) { - lo = std::min(lo, v.z()); - hi = std::max(hi, v.z()); - } - return hi - lo; - }; - - const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets); - REQUIRE(z_spread(raw) > 1.f); // the checkerboard really did produce relief to smooth + // The patch rim: vertices shared by a painted and an unpainted triangle - exactly the set + // TextureDisplacementOptions::smooth_skip_border holds out of the relaxation. + std::vector rim(grid.vertices.size(), 0), inside(grid.vertices.size(), 0); + for (size_t i = 0; i < grid.indices.size(); ++i) + for (int k = 0; k < 3; ++k) + (painted[i] ? inside : rim)[size_t(grid.indices[i][k])] = 1; + size_t rim_count = 0; + for (size_t v = 0; v < rim.size(); ++v) { + rim[v] = (rim[v] && inside[v]) ? 1 : 0; + rim_count += rim[v]; + } + REQUIRE(rim_count > 8); TextureDisplacementOptions options; options.smooth_enabled = true; @@ -331,52 +329,59 @@ TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", SECTION("it rounds off the steps without touching the topology") { - const indexed_triangle_set smoothed = build_texture_displacement(grid, { layer }, facets, options); - CHECK(z_spread(smoothed) < z_spread(raw)); - CHECK(smoothed.indices == raw.indices); - CHECK(smoothed.vertices.size() == raw.vertices.size()); - } + layer.image_data = make_checkerboard_png(); - SECTION("\"ignore outer ring\" leaves the patch rim at the depth the texture asked for") - { - // The rim: vertices shared by a painted and an unpainted triangle. Their neighbours outside - // the paint never move, so relaxing them drags the relief back down toward the flat surface - - // which is what makes the pattern look half-melted right at the edge. - std::vector rim(grid.vertices.size(), 0), inside(grid.vertices.size(), 0); - for (size_t i = 0; i < grid.indices.size(); ++i) - for (int k = 0; k < 3; ++k) - (painted[i] ? inside : rim)[size_t(grid.indices[i][k])] = 1; - size_t rim_count = 0; - for (size_t v = 0; v < rim.size(); ++v) { - rim[v] = (rim[v] && inside[v]) ? 1 : 0; - rim_count += rim[v]; - } - REQUIRE(rim_count > 8); - - // The tallest point of the rim. Laplacian relaxation is a convex average, so no vertex can ever - // exceed the mesh's current maximum - and a rim vertex sitting at that maximum has at least one - // neighbour outside the paint at zero, so it must come down. That makes this a strict, not a - // statistical, comparison. - auto rim_peak = [&](const indexed_triangle_set &its) { - float peak = 0.f; - for (size_t v = 0; v < rim.size(); ++v) - if (rim[v]) - peak = std::max(peak, its.vertices[v].z()); - return peak; + // Dirichlet energy over the mesh's edges. Laplacian relaxation is gradient descent on exactly + // this, so it is the quantity guaranteed to fall - unlike the min/max spread of z, which is + // pinned by whichever vertices are held (the unpainted surface at zero, and by default the + // patch rim as well) and so need not move at all. + auto roughness = [](const indexed_triangle_set &its) { + double e = 0.0; + for (const auto &t : its.indices) + for (int k = 0; k < 3; ++k) { + const double d = double(its.vertices[t[k]].z()) - double(its.vertices[t[(k + 1) % 3]].z()); + e += d * d; + } + return e; }; - options.smooth_skip_border = true; - const indexed_triangle_set kept = build_texture_displacement(grid, { layer }, facets, options); + const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets); + REQUIRE(roughness(raw) > 0.0); // the checkerboard really did produce relief to smooth + + const indexed_triangle_set smoothed = build_texture_displacement(grid, { layer }, facets, options); + CHECK(roughness(smoothed) < roughness(raw)); + CHECK(smoothed.indices == raw.indices); // ... without touching the topology + CHECK(smoothed.vertices.size() == raw.vertices.size()); + for (size_t v = 0; v < rim.size(); ++v) // ... and the held rim is bit-identical + if (rim[v]) + CHECK_THAT(smoothed.vertices[v].z(), WithinAbs(raw.vertices[v].z(), 1e-6f)); + } + + SECTION("\"ignore outer ring\" decides whether the patch rim relaxes") + { + // A flat white texture makes this exact rather than statistical: every painted vertex is + // displaced to precisely depth_mm, so a movable interior vertex sees nothing but neighbours at + // its own height and cannot move, while every rim vertex has at least one neighbour outside the + // paint pinned at zero and so must come down the moment it is allowed to. + layer.image_data = make_flat_gray_png(255); + const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets); + + options.smooth_skip_border = true; + const indexed_triangle_set kept = build_texture_displacement(grid, { layer }, facets, options); + options.smooth_skip_border = false; const indexed_triangle_set relaxed = build_texture_displacement(grid, { layer }, facets, options); - // Held out of the smoothing, the rim is bit-for-bit the un-smoothed displacement. + // Asserted on z alone, not on the whole position: relaxation averages all three coordinates, + // and while the tangential drift cancels by symmetry on a regular grid it does so only up to + // floating-point summation order, which is not something to pin down across three platforms. + // Height is what the option is about and it is exact - every neighbour of a movable vertex sits + // at the same height, so its own height cannot move. for (size_t v = 0; v < rim.size(); ++v) - if (rim[v]) - CHECK_THAT(kept.vertices[v].z(), WithinAbs(raw.vertices[v].z(), 1e-6f)); - CHECK(rim_peak(relaxed) < rim_peak(kept)); // ... and melted back down without it - // ... while the interior really was smoothed - this is not just "smoothing turned off". - CHECK_FALSE(kept.vertices == raw.vertices); + if (rim[v]) { + CHECK_THAT(kept.vertices[v].z(), WithinAbs(raw.vertices[v].z(), 1e-6f)); // held + CHECK(relaxed.vertices[v].z() < raw.vertices[v].z()); // melted down + } } } From ca52c0831726cb1cfb9e6243d13df403c645432f Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 26 Aug 2026 09:07:26 +0200 Subject: [PATCH 20/89] Improve adaptive subdivision at border & fix some visual bugs --- .../shaders/110/texture_displacement_bump.fs | 13 +- .../shaders/140/texture_displacement_bump.fs | 13 +- src/libslic3r/MeshBoolean.cpp | 19 + src/libslic3r/TextureDisplacement.cpp | 185 ++++-- src/libslic3r/TextureDisplacement.hpp | 48 +- src/libslic3r/TriangleSelector.cpp | 11 +- src/libslic3r/TriangleSelector.hpp | 9 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 590 +++++++++++++++--- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 102 ++- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 +- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 28 +- .../Jobs/TextureDisplacementPreviewJob.cpp | 29 +- .../Jobs/TextureDisplacementPreviewJob.hpp | 14 + 13 files changed, 914 insertions(+), 153 deletions(-) diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index d569b42d0c..fe0e32ef96 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -30,6 +30,9 @@ uniform sampler2D height_tex; uniform vec2 height_tex_texel; uniform float depth_mm; uniform float tiling_scale; +// Height map width / height. Scales the v axis so a non-square image keeps its proportions +// instead of being squeezed into a square tile - mirrors libslic3r's apply_uv_transform(). +uniform float tex_aspect; uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; @@ -70,7 +73,10 @@ vec2 project_uv(vec3 p, vec3 n) planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; float cs = cos(rotation_rad); float sn = sin(rotation_rad); - return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; + vec2 r = vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs); + // After the rotation, so the rotation stays a rotation rather than becoming a shear. + r.y *= tex_aspect; + return r + uv_offset; } void main() @@ -156,7 +162,10 @@ void main() float cs = cos(rotation_rad); float sn = sin(rotation_rad); - vec2 slope = amplitude * vec2(dh_duv.x * cs + dh_duv.y * sn, -dh_duv.x * sn + dh_duv.y * cs); + // One uv unit is tiling_scale mm along u but tiling_scale / tex_aspect mm along v, so the v + // component of the gradient carries the extra factor before being rotated back into t/b. + vec2 g = vec2(dh_duv.x, dh_duv.y * tex_aspect); + vec2 slope = amplitude * vec2(g.x * cs + g.y * sn, -g.x * sn + g.y * cs); vec3 gradient = slope.x * t + slope.y * b; gradient -= triangle_normal * dot(triangle_normal, gradient); diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 17b550d5e6..351452f0e9 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -89,6 +89,9 @@ uniform sampler2D height_tex; uniform vec2 height_tex_texel; // (1/width, 1/height) of height_tex uniform float depth_mm; uniform float tiling_scale; +// Height map width / height. Scales the v axis so a non-square image keeps its proportions +// instead of being squeezed into a square tile - mirrors libslic3r's apply_uv_transform(). +uniform float tex_aspect; uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; @@ -136,7 +139,10 @@ vec2 project_uv(vec3 p, vec3 n) planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; float cs = cos(rotation_rad); float sn = sin(rotation_rad); - return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; + vec2 r = vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs); + // After the rotation, so the rotation stays a rotation rather than becoming a shear. + r.y *= tex_aspect; + return r + uv_offset; } void main() @@ -240,7 +246,10 @@ void main() // gradient back into the axes' frame. float cs = cos(rotation_rad); float sn = sin(rotation_rad); - vec2 slope = amplitude * vec2(dh_duv.x * cs + dh_duv.y * sn, -dh_duv.x * sn + dh_duv.y * cs); + // One uv unit is tiling_scale mm along u but tiling_scale / tex_aspect mm along v, so the v + // component of the gradient carries the extra factor before being rotated back into t/b. + vec2 g = vec2(dh_duv.x, dh_duv.y * tex_aspect); + vec2 slope = amplitude * vec2(g.x * cs + g.y * sn, -g.x * sn + g.y * cs); vec3 gradient = slope.x * t + slope.y * b; gradient -= triangle_normal * dot(triangle_normal, gradient); diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 6a143b0a37..bf248e101e 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -356,6 +356,25 @@ std::optional> parameterize_lscm(const indexed_triangle_set & if (border == halfedge_descriptor()) return std::nullopt; // no boundary at all -- a closed patch, which isn't a disk either + // ...and exactly one boundary loop. One connected component is not enough on its own: a patch with + // a hole in it (paint a ring, or erase the middle of a stroke) is a single component with two + // loops, and LSCM will happily "parameterize" it into an overlapping, folded-over chart rather + // than fail. Walk the border halfedges and check every one of them belongs to the longest loop. + { + std::size_t border_halfedges = 0; + for (halfedge_descriptor h : halfedges(cgal_mesh)) + if (is_border(h, cgal_mesh)) + ++border_halfedges; + std::size_t loop_length = 0; + halfedge_descriptor h = border; + do { + ++loop_length; + h = next(h, cgal_mesh); + } while (h != border && loop_length <= border_halfedges); + if (loop_length != border_halfedges) + return std::nullopt; // more than one boundary loop -- not a topological disk + } + using Point_2 = EpicKernel::Point_2; using UV_pmap = _EpicMesh::Property_map; UV_pmap uv_map = cgal_mesh.add_property_map("h:uv", Point_2(0, 0)).first; diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 71a8b8001f..1a044ae956 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -13,6 +13,9 @@ #include #include +#include +#include + #include "MeshBoolean.hpp" #include "Model.hpp" #include "PNGReadWrite.hpp" @@ -897,7 +900,7 @@ std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const Tex return per_vertex; } -Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer) +Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer, float aspect) { const float scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f; const Vec2f scaled = planar * scale; @@ -905,7 +908,20 @@ Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &la const float rad = layer.rotation_deg * float(M_PI) / 180.f; const float cs = std::cos(rad); const float sn = std::sin(rad); - const Vec2f rotated(scaled.x() * cs - scaled.y() * sn, scaled.x() * sn + scaled.y() * cs); + Vec2f rotated(scaled.x() * cs - scaled.y() * sn, scaled.x() * sn + scaled.y() * cs); + + // Non-square textures. Without this the [0,1] square of uv covers the whole image whatever its + // proportions, so a 2:1 image is squeezed into a square tile and every feature in it comes out + // half as wide as it should be. `tiling_scale` is the tile's size along u; the tile is + // `tiling_scale * height / width` mm along v, which is exactly what keeps texels square - so + // dividing v by that extent is the same as multiplying it by width / height. A square texture has + // aspect 1 and is untouched, which is why this changes nothing for the shipped library. + // + // Applied after the rotation, not before: scaling one axis of an already-rotated coordinate is a + // shear, and doing it the other way round would make "Rotation" skew the pattern instead of + // turning it. + if (aspect > 0.f && aspect != 1.f) + rotated.y() *= aspect; return rotated + layer.offset; } @@ -954,8 +970,12 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp if (texture.empty()) return 0.f; + // width / height of the height map, so a non-square image keeps its proportions (see + // apply_uv_transform()). Every projection except the projective "from view" one funnels through + // here, so this one line is what makes them all aspect-correct. + const float aspect = (texture.height > 0) ? float(texture.width) / float(texture.height) : 1.f; auto sample_at = [&](const Vec2f &planar) { - return texture.sample(apply_uv_transform(planar, layer), layer.tile_enabled, layer.tile_method); + return texture.sample(apply_uv_transform(planar, layer, aspect), layer.tile_enabled, layer.tile_method); }; // Precomputed per-patch LSCM solve wins over the layer's own method (see the header): the @@ -1118,8 +1138,13 @@ std::vector patch_boundary_distance(const indexed_triangle_set &patch, co indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, - const TextureDisplacementOptions &options) + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress) { + // Returns true to keep going. An aborted run returns {} (see the header): an empty mesh is the + // one result no caller can mistake for a finished bake and commit onto the volume. + const auto report = [&progress](int percent) { return !progress || progress(percent); }; + indexed_triangle_set mesh = base_mesh; // TriangleSelector's vertex array starts with the mesh's own vertices (any extra ones, created // where a brush stroke split a triangle, are appended after them), and get_facets_strict() @@ -1183,8 +1208,14 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set } } + if (!report(5)) + return {}; + std::vector displacement(mesh.vertices.size(), 0.f); - std::vector displaced(mesh.vertices.size(), false); + // uint8_t rather than std::vector: the sampling loop below writes these from several + // threads at once, and vector's bit packing makes writes to *distinct* elements a data + // race on the shared word. + std::vector displaced(mesh.vertices.size(), 0); // Union, over every layer, of that layer's patch border - the vertices the post-process smoothing // holds when TextureDisplacementOptions::smooth_skip_border is set. A vertex on any patch's edge // counts, which is the conservative choice: hold it rather than let one layer's smoothing melt the @@ -1193,7 +1224,21 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set bool any_displacement = false; const TriangleMesh selector_mesh(mesh); + // One selector for the whole stack, re-deserialized per layer. Its constructor computes + // its_face_neighbors() and its_face_normals() over the *entire* mesh, which on a subdivided model + // is by far the most expensive thing here - building a fresh one per layer paid that cost up to + // eight times over. reset() (what deserialize(..., true) calls) only rebuilds the vertex/triangle + // arrays; the neighbour and face-normal tables are immutable members and survive it. + TriangleSelector selector(selector_mesh); + bool selector_dirty = false; + + const int layer_count = std::max(int(ordered_layers.size()), 1); + int layer_index = 0; for (const TextureDisplacementLayer *layer : ordered_layers) { + // Progress spans 5..65% across the layers; the apply and smoothing passes take it from there. + if (!report(5 + (60 * layer_index++) / layer_count)) + return {}; + const TriangleSelector::TriangleSplittingData &data = facets_data[size_t(layer->slot)]; if (data.triangles_to_split.empty()) continue; @@ -1202,8 +1247,9 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (height.empty()) continue; - TriangleSelector selector(selector_mesh); - selector.deserialize(data, false); + // needs_reset only from the second layer on: the selector is already pristine on the first. + selector.deserialize(data, selector_dirty); + selector_dirty = true; const indexed_triangle_set patch = selector.get_facets_strict(EnforcerBlockerType::ENFORCER); if (patch.indices.empty()) @@ -1287,8 +1333,13 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set const float sign = layer->invert ? -1.f : 1.f; // A vertex may be reached by several of the patch's triangles; each must fold into the // running total exactly once, or a Multiply/Subtract layer would apply two or three times - // over depending on how many painted triangles happen to share the vertex. - std::vector visited(patch.vertices.size(), false); + // over depending on how many painted triangles happen to share the vertex. Collecting the + // unique list up front (cheap, one pass) is also what lets the expensive part - the texture + // sampling, which is three bilinear fetches plus three pow()s per vertex for triplanar - run + // in parallel below, instead of serially inside the triangle walk. + std::vector layer_vertices; + std::vector visited(patch.vertices.size(), 0); + layer_vertices.reserve(patch.vertices.size()); for (const stl_triangle_vertex_indices &tri : patch.indices) for (int i = 0; i < 3; ++i) { const int vi = tri[i]; @@ -1296,28 +1347,53 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set // they carry no displacement of their own and are not part of the output mesh. if (vi >= int(mesh.vertices.size()) || (pin_boundary && is_boundary[vi]) || visited[vi]) continue; - visited[vi] = true; + visited[vi] = 1; + layer_vertices.push_back(vi); + } + if (layer_vertices.empty()) + continue; - const Vec2f *lscm_uv = lscm_uvs.empty() ? nullptr : &lscm_uvs[size_t(vi)]; + std::vector sampled(layer_vertices.size(), 0.f); + tbb::parallel_for(tbb::blocked_range(0, layer_vertices.size()), + [&](const tbb::blocked_range &range) { + for (size_t k = range.begin(); k < range.end(); ++k) { + const size_t vi = size_t(layer_vertices[k]); + const Vec2f *lscm_uv = lscm_uvs.empty() ? nullptr : &lscm_uvs[vi]; const float h = sample_layer_height(height, *layer, mesh.vertices[vi], vertex_normals[vi], patch_centroid, patch_axis, lscm_uv); - // midlevel is the height that means "stay put", so anything below it displaces // *inwards* - see TextureDisplacementLayer::midlevel. At the default of 0 this is // exactly the old outward-only behaviour. - const float edge_w = edge_weight.empty() ? 1.f : edge_weight[size_t(vi)]; - const float signed_height = (h - layer->midlevel) * layer->depth_mm * sign * edge_w; - displacement[size_t(vi)] = blend_displacement(displacement[size_t(vi)], signed_height, - displaced[size_t(vi)] ? layer->blend_mode : TextureBlendMode::Add); - // The first layer to reach a vertex has nothing underneath it to blend with, so it - // always starts the total off additively - a Multiply/Divide against an implicit - // zero base would otherwise annihilate (or blow up) it, which is never what the - // user means by putting a mask on the bottom of the stack. - displaced[size_t(vi)] = true; - any_displacement = true; + sampled[k] = (h - layer->midlevel) * layer->depth_mm * sign; } + }); + + for (size_t k = 0; k < layer_vertices.size(); ++k) { + const size_t vi = size_t(layer_vertices[k]); + // The first layer to reach a vertex has nothing underneath it to blend with, so it + // always starts the total off additively - a Multiply/Divide against an implicit + // zero base would otherwise annihilate (or blow up) it, which is never what the + // user means by putting a mask on the bottom of the stack. + const float accumulated = displacement[vi]; + const float blended = blend_displacement(accumulated, sampled[k], + displaced[vi] ? layer->blend_mode : TextureBlendMode::Add); + // Edge smoothing fades this layer's *effect*, not its input. Scaling the input instead is + // only correct for Add/Subtract, whose neutral value is 0: on a Multiply layer a faded + // input approaches 0, which annihilates everything beneath it at the rim rather than + // leaving it alone, and on a Divide layer it approaches the 0.05 divisor floor, which + // amplifies the relief underneath by up to 20x exactly where it was meant to fade out. + // Interpolating the blended result back toward the accumulated total is the neutral + // element for every mode at once, and reduces to the old formula exactly for Add. + const float edge_w = edge_weight.empty() ? 1.f : edge_weight[vi]; + displacement[vi] = accumulated + (blended - accumulated) * edge_w; + displaced[vi] = 1; + } + any_displacement = true; } + if (!report(65)) + return {}; + if (!any_displacement) return mesh; @@ -1332,12 +1408,22 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set // neighbours are those pinned outsiders: relaxing it would drag the rim of the relief back down and // leave the pattern looking half-melted right where it meets the edge. if (options.smooth_enabled && options.smooth_strength > 0.f && options.smooth_iterations > 0) { + if (!report(70)) + return {}; std::vector movable(mesh.vertices.size(), 0); for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) movable[vi] = (displaced[vi] && !(options.smooth_skip_border && on_patch_border[vi])) ? 1 : 0; - smooth_mesh_vertices(mesh, movable, options.smooth_strength, options.smooth_iterations); + // The pass hook only *stops* the relaxation early; the report(99) below is what turns a + // cancellation into an empty (uncommittable) result, since a cancelled run keeps reporting + // cancelled. + smooth_mesh_vertices(mesh, movable, options.smooth_strength, options.smooth_iterations, + progress ? DisplacementProgressFn([&report, it = options.smooth_iterations](int pass) { + return report(70 + (29 * (pass + 1)) / std::max(it, 1)); + }) : DisplacementProgressFn{}); } + if (!report(99)) + return {}; return mesh; } @@ -1352,7 +1438,7 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume) } void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector &movable, float strength, - int iterations) + int iterations, const DisplacementProgressFn &on_pass) { if (iterations <= 0 || mesh.vertices.empty() || movable.size() != mesh.vertices.size()) return; @@ -1391,15 +1477,21 @@ void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector std::vector prev; for (int it = 0; it < iterations; ++it) { prev = mesh.vertices; - for (size_t v = 0; v < nv; ++v) { - if (!movable[v] || start[v] == start[v + 1]) - continue; - Vec3f sum = Vec3f::Zero(); - for (int k = start[v]; k < start[v + 1]; ++k) - sum += prev[size_t(nbr[size_t(k)])]; - const Vec3f avg = sum / float(start[v + 1] - start[v]); - mesh.vertices[v] = prev[v] + (avg - prev[v]) * strength; - } + // Each vertex reads only from `prev` and writes only its own slot, so the sweep parallelises + // with no synchronisation at all. + tbb::parallel_for(tbb::blocked_range(0, nv), [&](const tbb::blocked_range &range) { + for (size_t v = range.begin(); v < range.end(); ++v) { + if (!movable[v] || start[v] == start[v + 1]) + continue; + Vec3f sum = Vec3f::Zero(); + for (int k = start[v]; k < start[v + 1]; ++k) + sum += prev[size_t(nbr[size_t(k)])]; + const Vec3f avg = sum / float(start[v + 1] - start[v]); + mesh.vertices[v] = prev[v] + (avg - prev[v]) * strength; + } + }); + if (on_pass && !on_pass(it)) + return; // cancelled: leave the passes done so far in place, the caller decides what to do } } @@ -1552,7 +1644,8 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const std::vector &refine_region, float target_edge_length_mm, int max_triangles, std::vector *out_source, const HeightFieldSampler &sampler, - float chord_tolerance_mm, float min_edge_length_mm) + float chord_tolerance_mm, float min_edge_length_mm, + float border_edge_length_mm) { // Neighbour slots that are not a triangle index. constexpr int NB_BOUNDARY = -1; // open edge: terminal on its own, bisected from this side alone @@ -1590,12 +1683,13 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const bool feature_mode = bool(sampler) && chord_tolerance_mm > 0.f; const float min_floor_sq = min_edge_length_mm > 0.f ? min_edge_length_mm * min_edge_length_mm : 0.f; const float target_sq = target_edge_length_mm > 0.f ? target_edge_length_mm * target_edge_length_mm : 0.f; + const float border_sq = border_edge_length_mm > 0.f ? border_edge_length_mm * border_edge_length_mm : 0.f; // refine_region is indexed by input-triangle index, and every triangle's src stays in that range // (children inherit their parent's src), so a wrong size would be an out-of-bounds read. Guard it. if (refine_region.size() != mesh.indices.size() || int(tris.size()) + 2 > max_triangles) return emit(); - if (!feature_mode && target_sq <= 0.f) + if (!feature_mode && target_sq <= 0.f && border_sq <= 0.f) return emit(); // no criterion at all if (std::none_of(refine_region.begin(), refine_region.end(), [](uint8_t v) { return v != 0; })) return emit(); // nothing flagged: no-op @@ -1720,16 +1814,27 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // reach. Triangles outside the region always score 0 - they are only ever touched by the conformal // closure below, never refined on their own account. auto priority = [&](int ti) -> float { - const Tri &t = tris[ti]; - if (refine_region[t.src] == 0) + const Tri &t = tris[ti]; + const uint8_t flags = refine_region[t.src]; + if (flags == 0) return 0.f; const int le = longest_local(ti); const float ll = elen_sq(t.v[le], t.v[(le + 1) % 3]); if (ll <= min_floor_sq) return 0.f; // at the resolution floor - also what stops a sharp texture step going forever - float p = (target_sq > 0.f) ? ll / target_sq : 0.f; - if (feature_mode) - p = std::max(p, detail_error(ti) / chord_tolerance_mm); + float p = 0.f; + if (flags & REFINE_PAINTED) { + p = (target_sq > 0.f) ? ll / target_sq : 0.f; + if (feature_mode) + p = std::max(p, detail_error(ti) / chord_tolerance_mm); + } + // The band straddling the paint's edge, refined by plain edge length. Deliberately *not* run + // through detail_error(): outside the paint the sampler still reports full relief (it has no + // per-point paint test), so the chord test there would chase texture detail on a surface the + // bake is going to leave flat. Length alone is what this band needs - the error it is fixing + // is the size of the triangles spanning the displacement step, not the curvature of anything. + if ((flags & REFINE_BORDER) && border_sq > 0.f) + p = std::max(p, ll / border_sq); return p; }; diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 6a824b2054..74c590bbdb 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -22,6 +22,14 @@ namespace Slic3r { class ModelVolume; +// Bits of subdivide_mesh_adaptive()'s per-triangle `refine_region` mask. See that function. +static constexpr uint8_t REFINE_PAINTED = 1; +static constexpr uint8_t REFINE_BORDER = 2; + +// Progress/cancellation hook for the (potentially multi-second) bake. Called with a 0..100 +// percentage; return false to abort. See build_texture_displacement(). +using DisplacementProgressFn = std::function; + // Maximum number of simultaneous texture-displacement layers a single ModelVolume can hold. // Each layer owns its own paint mask (ModelVolume::texture_displacement_facet(slot)), so this // is also the number of independent EnforcerBlockerType selectors kept per volume. @@ -370,7 +378,11 @@ Vec2f project_planar(const Vec3f &position, const Vec3f &normal); // LSCM's per-patch UV solve through the same scale/rotate/offset controls as every other // projection method, without going through project_texture_displacement_uv()'s own dispatch // (which only knows how to compute the *analytic* methods from a single vertex + normal). -Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer); +// `aspect` is the height map's width / height. It scales the v axis so a non-square image is not +// squeezed into a square tile: `tiling_scale` is the tile's size along u, and the tile is +// `tiling_scale * height / width` mm along v, which keeps texels square. 1 (the default) is the +// square case and leaves the coordinate exactly as it always was. +Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer, float aspect = 1.f); // Applies a row-major 3x4 projective matrix (see TextureDisplacementLayer::view_project_matrix) to a // local-space point, writing the resulting texture uv. Returns false - and leaves `uv` untouched - @@ -532,10 +544,18 @@ using TextureDisplacementFacetsData = std::array &layers, const TextureDisplacementFacetsData &facets_data, - const TextureDisplacementOptions &options = {}); + const TextureDisplacementOptions &options = {}, + const DisplacementProgressFn &progress = {}); // Convenience overload for main-thread callers: extracts the mesh/layers/paint data/options from // `volume` and forwards to the overload above. @@ -551,8 +571,10 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume); // after displacing it" runs, and it is also safe to run standalone on an already baked mesh. // `strength` is clamped to [0, 1]; 0 iterations, an empty/mis-sized `movable`, or an all-false one // leave the mesh untouched. +// `on_pass`, when set, is called with the 0-based index of each completed pass; returning false stops +// the relaxation there, leaving the passes already done in place. void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector &movable, float strength, - int iterations); + int iterations, const DisplacementProgressFn &on_pass = {}); // Returns a scalar height (in mm - a displacement magnitude) at a surface point, given that point's // position and interpolated normal. This is what feature-adaptive subdivision samples to decide @@ -638,12 +660,30 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl // the input triangle that output triangle i descends from (children inherit their parent's index), so // a caller can carry per-triangle data - e.g. a paint mask - across the topology change without a // geometric remap. +// +// `refine_region` is a **bitmask** per input triangle, not a plain flag: +// bit 0 (REFINE_PAINTED) - inside the painted area: refine by the length baseline and, in feature +// mode, by the chord-error test. +// bit 1 (REFINE_BORDER) - inside the band straddling the paint's edge: refine by +// `border_edge_length_mm` alone. +// A value of 1 therefore means exactly what a plain 1 always meant, and 0 still means "never touch +// this triangle except through the conformal closure". +// +// The border band exists because the chord-error test is blind to the one discontinuity the bake +// actually creates. `make_combined_displacement_sampler()` evaluates the height field everywhere, +// with no per-point paint test, so where the paint *stops* it keeps reporting full relief - smooth +// and low-curvature - while the baked surface steps from full displacement to zero. The test sees no +// error there and leaves the transition at whatever density the input had, which is what turns the +// rim of an unpainted island into a ring of large, steeply tilted triangles. Refining that band by +// plain edge length is bounded (it is a thin ring, and a length target always terminates) and needs +// no paint-aware sampler. indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const std::vector &refine_region, float target_edge_length_mm, int max_triangles = 1000000, std::vector *out_source = nullptr, const HeightFieldSampler &sampler = nullptr, - float chord_tolerance_mm = 0.f, float min_edge_length_mm = 0.f); + float chord_tolerance_mm = 0.f, float min_edge_length_mm = 0.f, + float border_edge_length_mm = 0.f); } // namespace Slic3r diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 3b032cc57e..64a4c6d724 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1519,9 +1519,11 @@ void TriangleSelector::get_facets(std::vector& facets_per_ } } -indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType state) const +indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType state, std::vector *out_source) const { indexed_triangle_set out; + if (out_source) + out_source->clear(); size_t num_vertices = 0; for (const Vertex &v : m_vertices) @@ -1535,8 +1537,13 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta out.vertices.emplace_back(v.v); } - for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle) + for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle) { this->get_facets_strict_recursive(m_triangles[itriangle], m_neighbors[itriangle], state, out.indices); + // Everything the recursion just appended came from this original triangle, whatever depth it + // was split to. Recording it here keeps the recursive helpers untouched. + if (out_source) + out_source->resize(out.indices.size(), itriangle); + } for (auto &triangle : out.indices) for (int i = 0; i < 3; ++ i) diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 11517f5c6c..e11618095a 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -332,7 +332,14 @@ public: // Get facets at a given state. Don't triangulate T-joints. indexed_triangle_set get_facets(EnforcerBlockerType state) const; // Get facets at a given state. Triangulate T-joints. - indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const; + // Sub-triangles in `state`, with the *whole* mesh's referenced vertex array (only .indices is + // filtered by state, so two calls with different states share one indexing). + // + // `out_source`, when given, is filled parallel to the returned .indices with the index of the + // original mesh triangle each sub-triangle came from. That is what lets a caller carry partial + // paint - the pieces of a triangle a brush stroke only partly covered - across a refinement of + // the same surface, instead of having to round each source triangle to wholly painted or not. + indexed_triangle_set get_facets_strict(EnforcerBlockerType state, std::vector *out_source = nullptr) const; // Get edges around the selected area by seed fill. std::vector get_seed_fill_contour() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 579d9a5792..2fbaf7bf53 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -68,13 +68,43 @@ constexpr float ImGuiLogSlider = float(ImGuiSliderFlags_Logarithmic); // frequencies that would alias, and it cuts the VRAM these hold by ~16x as a bonus. constexpr int THUMBNAIL_MAX_PX = 128; -std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &decoded) +// Everything above is about drawing a ~48 px panel row, and none of it applies to the height texture +// the fast-preview *shader* samples: that one is magnified across the model, not minified into a +// row, and every texel it loses is relief the preview cannot show. It gets its own upload at (up to) +// this size, so the bump preview reads the same height field the bake does instead of a 128 px box +// blur of it - which is what made Fast look flatter and softer than the result it was previewing. +constexpr int HEIGHT_TEX_MAX_PX = 2048; + +// How many rings of vertex-adjacent triangles either side of the paint's edge join the border refine +// band (see collect_paint_region()). Two is enough to grade the size change without the band's own +// cost growing to matter: it is a ring around a perimeter, not an area. +constexpr int BORDER_BAND_RINGS = 2; + +// Is `p` inside triangle `t`, given that it already lies in the triangle's plane? Barycentric via the +// three sub-triangle cross products, compared against the whole triangle's normal. Used to carry a +// partly painted source triangle's coverage onto the children of a subdivision, which are coplanar +// with it by construction (subdivision only adds edge midpoints). +bool point_in_triangle_coplanar(const Vec3f &p, const std::array &t) +{ + const Vec3f n = (t[1] - t[0]).cross(t[2] - t[0]); + const float n2 = n.squaredNorm(); + if (n2 < 1e-20f) + return false; // degenerate: it covers no area, so nothing is inside it + // A small negative tolerance, scaled by the triangle, keeps a centroid sitting exactly on a shared + // edge from falling through the gap between two neighbouring pieces. + const float eps = -1e-4f * n2; + return (t[1] - t[0]).cross(p - t[0]).dot(n) >= eps && + (t[2] - t[1]).cross(p - t[1]).dot(n) >= eps && + (t[0] - t[2]).cross(p - t[2]).dot(n) >= eps; +} + +std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &decoded, int max_px = THUMBNAIL_MAX_PX) { if (decoded.empty()) return nullptr; // Preserve aspect; never upscale a texture that is already small. - const int scale = std::max(1, (std::max(decoded.width, decoded.height) + THUMBNAIL_MAX_PX - 1) / THUMBNAIL_MAX_PX); + const int scale = std::max(1, (std::max(decoded.width, decoded.height) + max_px - 1) / max_px); const int w = std::max(1, decoded.width / scale); const int h = std::max(1, decoded.height / scale); @@ -155,6 +185,12 @@ void GLGizmoTextureDisplacement::on_shutdown() m_parent.toggle_model_objects_visibility(true); m_preview_glmodel.reset(); m_bump_preview_glmodel.reset(); + m_paint_overlay_glmodel.reset(); + m_paint_overlay_dirty = false; + // Any preview still in flight is superseded: bumping the shared counter makes it abort at its next + // progress poll, and its completion handler then finds nothing to do. + m_preview_generation->fetch_add(1); + m_preview_job_pending = false; m_uvcheck_glmodel.reset(); m_wireframe_overlay_glmodel.reset(); m_wireframe_overlay_vcount = 0; @@ -221,32 +257,50 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() // // The bump preview is different: it never actually moves geometry (it's a shading trick), so // its depth is identical to the overlay's *everywhere*, not just in the unpainted area - the - // depth-biased overlay would win the depth test across the whole surface and hide the bump - // shading entirely. So the overlay is skipped for it; the bump shading itself is the only - // feedback in that mode (still fine for painting, since render_cursor() below shows the brush). + // depth-biased opaque overlay would win the depth test across the whole surface and hide the bump + // shading entirely. So render_triangles() is skipped for it. What is *not* skipped is + // render_paint_overlay(): leaving the bump shading as the only paint feedback meant a stroke that + // erased paint, or added it with no texture picked, changed nothing on screen until the whole + // preview rebuilt at stroke end - and in the true-displacement view the opaque overlay is hidden + // by the raised surface for the same reason. The translucent tint covers both cases. // Coalesced bump rebuild from an in-progress UV island drag (see on_island_edited): done here, at // most once per drawn frame, rather than synchronously in the UV canvas's mouse-move handler. if (m_use_bump_preview && m_bump_preview_dirty) { rebuild_bump_preview_mesh(); m_bump_preview_dirty = false; } - const bool use_bump = m_use_bump_preview && m_bump_preview_glmodel.is_initialized(); + // Same coalescing for the paint tint, but on its own flag: a stroke marks this every mouse move + // (see on_mouse()) and it only costs the painted patch, whereas the bump mesh also carries every + // unpainted triangle of the volume and stays on the stroke-end cadence. + if (m_paint_overlay_dirty) { + rebuild_paint_overlay(); + m_paint_overlay_dirty = false; + } + // is_initialized() alone is not enough: render_bump_preview_mesh() also needs an active layer + // with a decoded texture and a compiled shader, and bails silently without them. Hiding the real + // volume for a bump pass that then draws nothing is what made the model vanish - most obviously + // with zero layers, but equally with a layer that has no texture picked yet. + const bool use_bump = m_use_bump_preview && m_bump_preview_glmodel.is_initialized() && bump_preview_ready(); + const bool use_true_preview = !use_bump && m_preview_glmodel.is_initialized(); // In Checker/Distortion mode the UV-check overlay *is* the surface visualization the user is // looking at, so the opaque paint-selection highlight must not be drawn on top of it - same // reasoning as skipping it for the bump preview (see bug #12). Without this the painted area // covers the checker/heatmap and it can't be seen. const bool show_paint_overlay = m_uv_check_mode == UVCheckMode::None; + + // Hide the real volume only when something is actually going to be drawn in its place; otherwise + // put it back. Getting this wrong leaves an invisible model, so it is decided once, here, rather + // than per branch below. + m_parent.toggle_model_objects_visibility(true); + if (use_bump || use_true_preview) { + if (ModelVolume *mv = texture_volume()) + m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), + m_c->selection_info()->get_active_instance(), mv); + } + if (use_bump) { - m_parent.toggle_model_objects_visibility(true); - if (ModelVolume *mv = texture_volume()) - m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), - m_c->selection_info()->get_active_instance(), mv); render_bump_preview_mesh(); - } else if (m_preview_glmodel.is_initialized()) { - m_parent.toggle_model_objects_visibility(true); - if (ModelVolume *mv = texture_volume()) - m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), - m_c->selection_info()->get_active_instance(), mv); + } else if (use_true_preview) { render_preview_mesh(); if (show_paint_overlay) { @@ -259,6 +313,13 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() render_triangles(selection); } + // The translucent paint tint. Needed in the bump view because the opaque highlight above is + // skipped there, and in the true-displacement view because the displaced surface rises *above* + // the undisplaced overlay geometry and hides it exactly where the relief is strongest - in both + // cases leaving an erase stroke with no visible effect until the next full preview rebuild. + if (show_paint_overlay && (use_bump || use_true_preview)) + render_paint_overlay(); + // Diagnostic overlays, drawn on top of whatever preview is active (both pull toward the camera // with a polygon offset so they win the depth test against the coincident surface). if (m_uv_check_mode != UVCheckMode::None) @@ -290,7 +351,16 @@ bool GLGizmoTextureDisplacement::on_mouse(const wxMouseEvent &mouse_event) return on_mouse_seam(mouse_event); if (m_adjust_texture_mode) return on_mouse_adjust_texture(mouse_event); - return GLGizmoPainterBase::on_mouse(mouse_event); + + const bool handled = GLGizmoPainterBase::on_mouse(mouse_event); + // A consumed drag/click is a paint (or erase) event: the base class has already updated the live + // TriangleSelector, but nothing is flushed to the model - and so nothing rebuilds - until the + // stroke ends. Mark the tint stale so it follows the brush from the first frame instead. Only the + // flag is set here; the rebuild is coalesced to once per drawn frame in render_painter_gizmo(). + if (handled && (mouse_event.Dragging() || mouse_event.LeftDown() || mouse_event.RightDown() || + mouse_event.LeftUp() || mouse_event.RightUp())) + m_paint_overlay_dirty = true; + return handled; } bool GLGizmoTextureDisplacement::on_mouse_seam(const wxMouseEvent &mouse_event) @@ -707,11 +777,29 @@ void GLGizmoTextureDisplacement::render_preview_mesh() shader->stop_using(); } +float GLGizmoTextureDisplacement::layer_texture_aspect(const TextureDisplacementLayer &layer) +{ + // decode_height_texture() is cached on the image_data allocation, so this is a hash lookup rather + // than a PNG decode - cheap enough to call per rebuild. + const DecodedHeightTexture tex = decode_height_texture(layer); + return (tex.width > 0 && tex.height > 0) ? float(tex.width) / float(tex.height) : 1.f; +} + std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) const { - if (layer.projection_method == TextureProjectionMethod::LSCM) - return compute_lscm_uvs(patch, layer); // one final uv per patch vertex (0 where unassigned) + const float aspect = layer_texture_aspect(layer); + if (layer.projection_method == TextureProjectionMethod::LSCM) { + // compute_lscm_uvs() returns the unwrap's own (raw, mm) coordinates with the island placement + // folded in - it does *not* apply the layer's tiling/rotation/offset. The bake applies those + // on top (sample_layer_height()'s lscm branch runs the result through sample_at()), so the + // shader's precomputed-uv path has to as well, or the fast preview samples millimetre-valued + // coordinates as if they were uv and shows the texture at a wildly wrong scale. + std::vector uv = compute_lscm_uvs(patch, layer); + for (Vec2f &p : uv) + p = apply_uv_transform(p, layer, aspect); + return uv; + } if (layer.projection_method == TextureProjectionMethod::ViewProjected) { std::vector uv(patch.vertices.size()); for (size_t vi = 0; vi < patch.vertices.size(); ++vi) { @@ -725,7 +813,7 @@ std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const in } const Vec2f planar(patch.vertices[vi].dot(layer.view_project_right), patch.vertices[vi].dot(layer.view_project_up)); - uv[vi] = apply_uv_transform(planar, layer); + uv[vi] = apply_uv_transform(planar, layer, aspect); } return uv; } @@ -913,11 +1001,13 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() if (layer == nullptr || layer->empty()) return; - // Reuses the layer-list panel's already-decoded, already-uploaded GPU thumbnail (smoothing-aware), - // whose grayscale value lives in the R channel exactly as the shader samples it. Its width/height - // are read straight off the texture - decoding the PNG here every frame would re-run the smoothing - // blur on every camera move, which is what tanked the frame rate at high smoothing. - GLTexture *tex = get_layer_thumbnail(*layer); + // Full-resolution height upload (smoothing-aware), whose grayscale value lives in the R channel + // exactly as the shader samples it. Deliberately *not* the layer-list panel's thumbnail: that one + // is box-filtered down to 128 px for a ~48 px row, and feeding it to the shader cost the preview + // three quarters of the height map's detail - and, since height_tex_texel is derived from it, also + // flattened the shading gradient and made the parallax march skip itself at angles where it should + // run. Cached on the image_data pointer + smoothing, so no PNG is decoded per frame. + GLTexture *tex = get_layer_height_texture(*layer); if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0) return; @@ -942,10 +1032,36 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0); glsafe(::glActiveTexture(GL_TEXTURE0)); glsafe(::glBindTexture(GL_TEXTURE_2D, tex->get_id())); + // Match DecodedHeightTexture::sample()'s tiling. The sampler's wrap mode is the only place the + // GPU path can express this, and nothing ever set it - so it sat at GL_REPEAT no matter what the + // layer said: a MirroredRepeat layer previewed as a plain repeat, and a layer with tiling *off* + // previewed as an endless tiling where the bake produces one placement and nothing around it. + // CLAMP_TO_BORDER with a zero border is the exact analogue of sample()'s "outside [0,1) is 0". + // GL_CLAMP_TO_BORDER is desktop-GL only; on ES the nearest thing is CLAMP_TO_EDGE, which smears + // the border row instead of vanishing - still much closer to the bake than an endless repeat. +#if SLIC3R_OPENGL_ES + const GLint no_tile_wrap = GL_CLAMP_TO_EDGE; +#else + const GLint no_tile_wrap = GL_CLAMP_TO_BORDER; +#endif + const GLint wrap = !layer->tile_enabled ? no_tile_wrap : + (layer->tile_method == TextureTileMethod::MirroredRepeat) ? GL_MIRRORED_REPEAT : + GL_REPEAT; + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap)); +#if !SLIC3R_OPENGL_ES + if (wrap == GL_CLAMP_TO_BORDER) { + static const GLfloat border[4] = { 0.f, 0.f, 0.f, 0.f }; + glsafe(::glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border)); + } +#endif // !SLIC3R_OPENGL_ES shader->set_uniform("height_tex", 0); shader->set_uniform("height_tex_texel", Vec2f(1.f / float(tex->get_width()), 1.f / float(tex->get_height()))); shader->set_uniform("depth_mm", layer->depth_mm); shader->set_uniform("tiling_scale", layer->tiling_scale); + // Read off the uploaded texture rather than the decoded one: they are the same image, and this is + // the aspect the sampler will actually see. + shader->set_uniform("tex_aspect", float(tex->get_width()) / float(tex->get_height())); shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f); shader->set_uniform("uv_offset", layer->offset); shader->set_uniform("invert", layer->invert); @@ -969,6 +1085,100 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() shader->stop_using(); } +bool GLGizmoTextureDisplacement::bump_preview_ready() const +{ + // Mirrors render_bump_preview_mesh()'s own preconditions. Kept as a separate query because the + // caller has to know whether the bump pass will draw *before* it hides the real volume for it. + if (m_c->selection_info() == nullptr || m_c->selection_info()->model_object() == nullptr) + return false; + if (texture_volume() == nullptr) + return false; + const TextureDisplacementLayer *layer = active_layer(); + if (layer == nullptr || layer->empty()) + return false; + // The same texture render_bump_preview_mesh() will bind, not the panel thumbnail - the two are + // separate caches and either can fail on its own. + const GLTexture *tex = const_cast(this)->get_layer_height_texture(*layer); + if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0) + return false; + return wxGetApp().get_shader("texture_displacement_bump") != nullptr; +} + +void GLGizmoTextureDisplacement::rebuild_paint_overlay() +{ + m_paint_overlay_glmodel.reset(); + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || m_triangle_selectors.empty()) + return; + + // The *live* selector, so an in-progress stroke shows immediately - which is the whole point: + // this is the only feedback that a brush actually added or erased anything until the (much more + // expensive) preview catches up at stroke end. + const indexed_triangle_set patch = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER); + if (patch.indices.empty()) + return; + + // In the true-displacement view the surface on screen is the *raised* one, and a tint built on + // the flat base mesh would sink underneath it wherever the relief is deepest - which is precisely + // where the user is looking. The bake is topology-preserving (patch vertex i is mesh vertex i, see + // build_texture_displacement()), so the displaced positions can be read straight across. Vertices + // the brush split live past the end of that array and keep their flat position; they sit on the + // patch boundary, where the displacement is smallest anyway. + const std::vector *displaced = nullptr; + if (!m_use_bump_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() && + !m_preview_its.vertices.empty()) + displaced = &m_preview_its.vertices; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(patch.indices.size() * 3); + init_data.reserve_indices(patch.indices.size() * 3); + unsigned n = 0; + for (const stl_triangle_vertex_indices &tri : patch.indices) { + for (int i = 0; i < 3; ++i) { + const size_t idx = size_t(tri[i]); + init_data.add_vertex((displaced != nullptr && idx < displaced->size()) ? (*displaced)[idx] + : patch.vertices[idx]); + } + init_data.add_triangle(n, n + 1, n + 2); + n += 3; + } + m_paint_overlay_glmodel.init_from(std::move(init_data)); + // GLModel::render() re-sets "uniform_color" from this field just before drawing, so the colour + // has to be set here rather than as a uniform at draw time. + m_paint_overlay_glmodel.set_color(ColorRGBA(0.16f, 0.79f, 0.35f, 0.38f)); +} + +void GLGizmoTextureDisplacement::render_paint_overlay() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr || !m_paint_overlay_glmodel.is_initialized()) + return; + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + // Translucent, and pulled toward the camera so it wins the depth test against the coincident + // bump surface. Depth writes are off: this is a tint, and letting it own the depth buffer would + // make the wireframe and seam overlays drawn after it fight with geometry that is not really + // there. Blending is already enabled by render_painter_gizmo(). + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(-1.5f, -1.5f)); + glsafe(::glDepthMask(GL_FALSE)); + m_paint_overlay_glmodel.render(); + glsafe(::glDepthMask(GL_TRUE)); + glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); + shader->stop_using(); +} + void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() { m_uvcheck_glmodel.reset(); @@ -1178,10 +1388,12 @@ void GLGizmoTextureDisplacement::rebuild_preview() { // Bumped first: any in-flight job's result (captured generation from before this call) will // now compare unequal to m_preview_generation and be discarded when it completes, even if it - // finishes after the job queued below. - const uint64_t generation = ++m_preview_generation; + // finishes after the job queued below - and, since the counter is shared with the worker, that + // job also notices mid-run and aborts rather than computing a result nobody will use. + m_preview_generation->fetch_add(1); update_uv_editor(); rebuild_bump_preview_mesh(); + rebuild_paint_overlay(); rebuild_uvcheck_mesh(); rebuild_seam_overlay(); // The adaptive subdivision preview is driven by the painted area, so it has to follow the paint @@ -1194,13 +1406,43 @@ void GLGizmoTextureDisplacement::rebuild_preview() if (mv == nullptr || !mv->is_texture_displacement_painted()) { m_preview_glmodel.reset(); m_preview_its = indexed_triangle_set{}; // no displaced mesh; wireframe falls back to the base + m_preview_job_pending = false; refresh_wireframe(); return; } // In Fast/paint modes the wireframe follows the base mesh and can be built now; the true-displacement // view's wireframe needs the displaced mesh, which only exists once the job below completes. - if (m_use_bump_preview) + if (m_use_bump_preview) { refresh_wireframe(); + // Fast view: the shader *is* the preview, and m_preview_glmodel is never drawn. Running the + // full CPU displacement anyway - which is what happened on every stroke and slider release - + // was the single largest cost in the gizmo, and it bought nothing. The switch back to the + // true-displacement view queues it (see the View row in on_render_input_window()). + m_preview_job_pending = false; + return; + } + + queue_preview_job(); +} + +void GLGizmoTextureDisplacement::queue_preview_job() +{ + // A job in flight when the gizmo closes still runs its completion handler, which would otherwise + // happily queue the follow-up run it was holding - against a gizmo nobody is looking at any more. + if (m_state != On) + return; + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || !mv->is_texture_displacement_painted()) + return; + + // One in flight at a time; everything requested meanwhile collapses into a single follow-up run + // issued from the completion handler. See m_preview_job_running. + if (m_preview_job_running) { + m_preview_job_pending = true; + return; + } + + const uint64_t generation = m_preview_generation->load(); TextureDisplacementPreviewInput input; input.base_mesh = mv->mesh().its; @@ -1209,20 +1451,35 @@ void GLGizmoTextureDisplacement::rebuild_preview() for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + m_preview_job_running = true; auto &worker = wxGetApp().plater()->get_ui_job_worker(); - queue_job(worker, std::make_unique(std::move(input), generation, + queue_job(worker, std::make_unique(std::move(input), generation, m_preview_generation, [this](indexed_triangle_set its, uint64_t result_generation) { - if (result_generation != m_preview_generation) - return; // superseded by a newer edit while this was computing - m_preview_glmodel.reset(); - if (!its.indices.empty()) { + m_preview_job_running = false; + if (result_generation != m_preview_generation->load()) { + // Superseded while this was computing (it will have aborted early and come back + // empty). Whatever the newest state is, it still needs a run. + m_preview_job_pending = true; + } else if (its.indices.empty()) { + // Aborted or cancelled rather than finished - the handler runs on every outcome so + // the in-flight latch above always clears. Keep whatever preview is already on screen + // rather than blanking it; there is no new result to show, not a new empty one. + } else { + m_preview_glmodel.reset(); m_preview_glmodel.init_from(its); m_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); + // Keep the displaced mesh so the wireframe overlay can be drawn on it (the + // true-displacement view), then refresh the wireframe from it. + m_preview_its = std::move(its); + refresh_wireframe(); + // The paint tint rides the displaced surface in this view, so it follows the new mesh. + m_paint_overlay_dirty = true; + } + if (m_preview_job_pending) { + m_preview_job_pending = false; + if (!m_use_bump_preview) + queue_preview_job(); // no-ops if the gizmo has closed in the meantime } - // Keep the displaced mesh so the wireframe overlay can be drawn on it (the true-displacement - // view), then refresh the wireframe from it. - m_preview_its = std::move(its); - refresh_wireframe(); m_parent.set_as_dirty(); })); } @@ -1935,7 +2192,11 @@ Vec3f GLGizmoTextureDisplacement::adjust_handle_center(const TextureDisplacement // follows the cursor precisely, and is back on the anchor exactly when offset is zero. const float rad = layer.rotation_deg * float(M_PI) / 180.f; const float cs = std::cos(rad), sn = std::sin(rad); - const Vec2f unrotated(layer.offset.x() * cs + layer.offset.y() * sn, -layer.offset.x() * sn + layer.offset.y() * cs); + // Undo the non-square v scaling first - it is the last thing apply_uv_transform() does, so it is + // the first thing to come off on the way back. + const float aspect = layer_texture_aspect(layer); + const Vec2f o(layer.offset.x(), (aspect > 0.f) ? layer.offset.y() / aspect : layer.offset.y()); + const Vec2f unrotated(o.x() * cs + o.y() * sn, -o.x() * sn + o.y() * cs); const Vec2f planar = -unrotated * layer.tiling_scale; Vec3f u_axis, v_axis; @@ -2168,7 +2429,10 @@ bool GLGizmoTextureDisplacement::on_mouse_adjust_texture(const wxMouseEvent &mou const Vec2f delta_scaled = delta_planar * scale; const float rad = layer->rotation_deg * float(M_PI) / 180.f; const float cs = std::cos(rad), sn = std::sin(rad); - const Vec2f delta_rotated(delta_scaled.x() * cs - delta_scaled.y() * sn, delta_scaled.x() * sn + delta_scaled.y() * cs); + Vec2f delta_rotated(delta_scaled.x() * cs - delta_scaled.y() * sn, delta_scaled.x() * sn + delta_scaled.y() * cs); + // ...and the same v scaling apply_uv_transform() applies for a non-square texture, so the + // handle keeps tracking the cursor exactly instead of drifting on the v axis. + delta_rotated.y() *= layer_texture_aspect(*layer); // Increasing `offset` shifts which texel is sampled at a fixed world position, which // visually slides the pattern the *opposite* way - subtracting is this session's // best-effort reasoning about the direction that feels like "dragging the texture", @@ -2701,7 +2965,7 @@ void GLGizmoTextureDisplacement::subdivide_model() bool GLGizmoTextureDisplacement::collect_paint_region( std::vector ®ion, - std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> *painted_tri) const + std::array *paint) const { const ModelVolume *mv = texture_volume(); if (mv == nullptr) @@ -2711,21 +2975,24 @@ bool GLGizmoTextureDisplacement::collect_paint_region( const size_t nvert = its.vertices.size(); region.assign(ntri, 0); - if (painted_tri) - for (auto &pt : *painted_tri) - pt.clear(); + if (paint) + for (LayerPaintMap &pm : *paint) + pm = LayerPaintMap{}; - // Sorted-vertex-triple -> triangle index, so a fully-painted patch sub-triangle (which comes back - // with the original mesh's own three vertex indices) can be mapped to its source triangle. Only - // the paint carry-forward needs it, and the live subdivide preview calls this on every slider - // frame, so it is not built for the region-only path. - std::map, int> tri_by_verts; - if (painted_tri) - for (size_t i = 0; i < ntri; ++i) { - std::array k{ its.indices[i][0], its.indices[i][1], its.indices[i][2] }; - std::sort(k.begin(), k.end()); - tri_by_verts.emplace(k, int(i)); - } + // Twice the area of each source triangle, for the "is this one covered edge to edge" test below. + // Only the paint carry-forward needs it, and the live subdivide preview calls this on every + // slider frame, so it is not built for the region-only path. + const auto tri_area2 = [](const Vec3f &a, const Vec3f &b, const Vec3f &c) { + return (b - a).cross(c - a).norm(); + }; + std::vector source_area2; + if (paint) { + source_area2.resize(ntri); + for (size_t i = 0; i < ntri; ++i) + source_area2[i] = tri_area2(its.vertices[size_t(its.indices[i][0])], + its.vertices[size_t(its.indices[i][1])], + its.vertices[size_t(its.indices[i][2])]); + } bool any_paint = false; for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { @@ -2743,27 +3010,114 @@ bool GLGizmoTextureDisplacement::collect_paint_region( // subdivide_mesh_adaptive() already grades the size change outward on its own. for (const TriangleSelector::TriangleBitStreamMapping &m : data.triangles_to_split) if (size_t(m.triangle_idx) < ntri) - region[m.triangle_idx] = 1; + region[m.triangle_idx] |= REFINE_PAINTED; - if (painted_tri) { + if (paint) { TriangleSelector sel(mv->mesh()); sel.deserialize(data, false); - (*painted_tri)[slot].assign(ntri, 0); - for (const stl_triangle_vertex_indices &t : sel.get_facets_strict(EnforcerBlockerType::ENFORCER).indices) { - // A sub-triangle produced by a *partial* stroke always carries at least one appended - // (split) vertex, so "all three indices are original" is exactly the test for a whole, - // fully-painted triangle - the only kind whose paint can be inherited wholesale. - if (size_t(t[0]) >= nvert || size_t(t[1]) >= nvert || size_t(t[2]) >= nvert) + // get_facets_strict() now reports which source triangle each sub-triangle came from, which + // is what lets partial coverage be carried forward geometrically instead of being rounded + // away. Note a *fully* painted source can still come back as several sub-triangles (T-joint + // splits forced by a refined neighbour), so "wholly painted" is an area test, not a + // one-piece test. + std::vector src; + const indexed_triangle_set patch = sel.get_facets_strict(EnforcerBlockerType::ENFORCER, &src); + + LayerPaintMap &pm = (*paint)[slot]; + pm.full.assign(ntri, 0); + pm.part_start.assign(ntri + 1, 0); + + std::vector covered2(ntri, 0.f); + for (size_t j = 0; j < patch.indices.size() && j < src.size(); ++j) { + if (size_t(src[j]) >= ntri) continue; - std::array k{ t[0], t[1], t[2] }; - std::sort(k.begin(), k.end()); - if (auto it = tri_by_verts.find(k); it != tri_by_verts.end()) - (*painted_tri)[slot][it->second] = 1; + const stl_triangle_vertex_indices &t = patch.indices[j]; + covered2[size_t(src[j])] += tri_area2(patch.vertices[size_t(t[0])], patch.vertices[size_t(t[1])], + patch.vertices[size_t(t[2])]); + } + for (size_t i = 0; i < ntri; ++i) + pm.full[i] = (source_area2[i] > 0.f && covered2[i] >= 0.999f * source_area2[i]) ? 1 : 0; + + // Only partly covered sources need their pieces kept - a full one answers every query with + // "painted", and an untouched one with "not painted". + for (size_t j = 0; j < patch.indices.size() && j < src.size(); ++j) + if (size_t(src[j]) < ntri && !pm.full[size_t(src[j])]) + ++pm.part_start[size_t(src[j]) + 1]; + for (size_t i = 0; i < ntri; ++i) + pm.part_start[i + 1] += pm.part_start[i]; + pm.part.resize(size_t(pm.part_start[ntri])); + { + std::vector fill(pm.part_start.begin(), pm.part_start.begin() + ntri); + for (size_t j = 0; j < patch.indices.size() && j < src.size(); ++j) { + const size_t S = size_t(src[j]); + if (S >= ntri || pm.full[S]) + continue; + const stl_triangle_vertex_indices &t = patch.indices[j]; + pm.part[size_t(fill[S]++)] = { patch.vertices[size_t(t[0])], patch.vertices[size_t(t[1])], + patch.vertices[size_t(t[2])] }; + } } } any_paint = true; } - return any_paint; + if (!any_paint) + return false; + + // The band straddling the paint's edge. The bake steps the surface from full displacement to zero + // across it, and nothing else in the refinement criteria can see that step: the chord-error + // sampler has no per-point paint test, so just outside the paint it keeps reporting the same + // smooth height field and reports no error at all. Left alone, the transition therefore stays at + // whatever density the input had - which is what makes the rim of an unpainted island a ring of + // big, steeply tilted triangles. + // + // Seeded from the vertices shared by a painted and an unpainted triangle (the actual edge of the + // paint) and grown outward over vertex adjacency, so the band covers both sides of the step. + if (BORDER_BAND_RINGS > 0) { + // Vertex -> incident triangles, CSR-style (counted, prefix-summed, filled). This runs on every + // frame of the subdivide preview's sliders, so it must not allocate a small vector per vertex. + std::vector vstart(nvert + 1, 0); + for (size_t i = 0; i < ntri; ++i) + for (int k = 0; k < 3; ++k) + if (size_t(its.indices[i][k]) < nvert) + ++vstart[size_t(its.indices[i][k]) + 1]; + for (size_t v = 0; v < nvert; ++v) + vstart[v + 1] += vstart[v]; + std::vector vtri(size_t(vstart[nvert]), 0); + { + std::vector fill(vstart.begin(), vstart.begin() + nvert); + for (size_t i = 0; i < ntri; ++i) + for (int k = 0; k < 3; ++k) + if (size_t(its.indices[i][k]) < nvert) + vtri[size_t(fill[size_t(its.indices[i][k])]++)] = int(i); + } + + // A vertex used by both a painted and an unpainted triangle sits exactly on the paint's edge. + std::vector ring_vertex(nvert, 0); + for (size_t v = 0; v < nvert; ++v) { + bool painted = false, unpainted = false; + for (int k = vstart[v]; k < vstart[v + 1]; ++k) + ((region[size_t(vtri[size_t(k)])] & REFINE_PAINTED) ? painted : unpainted) = true; + ring_vertex[v] = (painted && unpainted) ? 1 : 0; + } + + for (int ring = 0; ring < BORDER_BAND_RINGS; ++ring) { + std::vector next = ring_vertex; + for (size_t v = 0; v < nvert; ++v) { + if (!ring_vertex[v]) + continue; + for (int k = vstart[v]; k < vstart[v + 1]; ++k) { + const size_t t = size_t(vtri[size_t(k)]); + region[t] |= REFINE_BORDER; + // Grow through this triangle's other corners, for the following ring. + for (int c = 0; c < 3; ++c) + if (size_t(its.indices[t][c]) < nvert) + next[size_t(its.indices[t][c])] = 1; + } + } + ring_vertex.swap(next); + } + } + return true; } bool GLGizmoTextureDisplacement::plan_adaptive_subdivision(const ModelVolume &mv, SubdivisionPlan &out) const @@ -2772,7 +3126,7 @@ bool GLGizmoTextureDisplacement::plan_adaptive_subdivision(const ModelVolume &mv return false; std::vector region; - if (!collect_paint_region(region, &out.painted_tri)) + if (!collect_paint_region(region, &out.paint)) return false; // Feature-adaptive: sample the combined displacement so refinement follows texture curvature. A @@ -2796,7 +3150,7 @@ bool GLGizmoTextureDisplacement::plan_adaptive_subdivision(const ModelVolume &mv // baseline - otherwise the control would be meaningless (or a dead end) on a dense model. out.refined = subdivide_mesh_adaptive(mv.mesh().its, region, m_subdivide_target_mm, int(mv.mesh().its.indices.size()) + m_subdivide_budget_k * 1000, - &out.source, sampler, tol, floor); + &out.source, sampler, tol, floor, m_subdivide_border_mm); } return out.refined.indices.size() != mv.mesh().its.indices.size(); } @@ -2811,15 +3165,33 @@ void GLGizmoTextureDisplacement::apply_adaptive_subdivision(ModelVolume &mv, Sub mv.calculate_convex_hull(); mv.restore_painting(saved_painting); // resets extra facets (incl. texture-displacement) + remaps the rest - // Carry each layer's paint onto the new mesh: a new triangle is painted iff its source triangle - // was fully painted in that layer. Children inherit their parent's source, so this is exact. + // Carry each layer's paint onto the new mesh. Children inherit their parent's source triangle, and + // subdivision only ever adds edge midpoints, so every new triangle lies inside its source and on + // the same surface - which means the source's painted *pieces* can be queried directly by point + // containment. That is what keeps a brush outline smooth: rounding each source to wholly painted + // or not instead leaves a ragged fringe of isolated triangles along any curved boundary, and the + // refined mesh then reproduces that fringe exactly rather than hiding it. + const indexed_triangle_set &new_its = mv.mesh().its; for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { - if (plan.painted_tri[size_t(slot)].empty()) + const LayerPaintMap &pm = plan.paint[size_t(slot)]; + if (pm.empty()) continue; TriangleSelector sel(mv.mesh()); - for (size_t i = 0; i < plan.source.size(); ++i) - if (plan.painted_tri[size_t(slot)][size_t(plan.source[i])]) + for (size_t i = 0; i < plan.source.size() && i < new_its.indices.size(); ++i) { + const size_t S = size_t(plan.source[i]); + if (S >= pm.full.size()) + continue; + bool painted = pm.full[S] != 0; + if (!painted && pm.part_start[S] != pm.part_start[S + 1]) { + const stl_triangle_vertex_indices &t = new_its.indices[i]; + const Vec3f centroid = (new_its.vertices[size_t(t[0])] + new_its.vertices[size_t(t[1])] + + new_its.vertices[size_t(t[2])]) / 3.f; + for (int k = pm.part_start[S]; k < pm.part_start[S + 1] && !painted; ++k) + painted = point_in_triangle_coplanar(centroid, pm.part[size_t(k)]); + } + if (painted) sel.set_facet(int(i), EnforcerBlockerType::ENFORCER); + } mv.texture_displacement_facet(slot).set(sel); } } @@ -3063,7 +3435,7 @@ void GLGizmoTextureDisplacement::rebuild_subdivide_preview() const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f; its = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, int(mv->mesh().its.indices.size()) + m_subdivide_budget_k * 1000, - nullptr, sampler, tol, floor); + nullptr, sampler, tol, floor, m_subdivide_border_mm); } else { if (m_subdivide_count < 1) return; @@ -3135,6 +3507,26 @@ GLTexture *GLGizmoTextureDisplacement::get_layer_thumbnail(const TextureDisplace return m_thumbnails[slot].get(); } +GLTexture *GLGizmoTextureDisplacement::get_layer_height_texture(const TextureDisplacementLayer &layer) +{ + if (layer.empty()) + return nullptr; + + // One slot, not one per layer: the bump shader only ever shades the *active* layer, so a single + // full-resolution upload is enough and the VRAM cost stays at one texture rather than eight. + if (m_height_tex && m_height_tex_source == layer.image_data.get() && m_height_tex_smoothing == layer.smoothing) + return m_height_tex.get(); + + std::unique_ptr texture = upload_height_thumbnail(decode_height_texture(layer), HEIGHT_TEX_MAX_PX); + if (!texture) + return nullptr; + + m_height_tex = std::move(texture); + m_height_tex_source = layer.image_data.get(); + m_height_tex_smoothing = layer.smoothing; + return m_height_tex.get(); +} + void GLGizmoTextureDisplacement::bake(bool own_snapshot) { ModelVolume *mv = texture_volume(); @@ -3177,6 +3569,10 @@ static constexpr float STD_REMESH_SHARP_DEG = 40.f; static constexpr float STD_SUBDIV_MAX_EDGE_MM = 20.f; static constexpr float STD_SUBDIV_DETAIL_MM = 0.02f; static constexpr float STD_SUBDIV_MIN_EDGE_MM = 0.02f; +// Edge length the band straddling the paint's edge is refined to. This is the one number that decides +// how clean the rim of an unpainted island looks: the bake steps the surface from full displacement to +// zero across that band, and nothing else in the criteria can see the step (see collect_paint_region()). +static constexpr float STD_SUBDIV_BORDER_MM = 0.4f; bool GLGizmoTextureDisplacement::apply_standard_mode_presets(ModelVolume *mv) { @@ -3197,6 +3593,7 @@ bool GLGizmoTextureDisplacement::apply_standard_mode_presets(ModelVolume *mv) pin(m_subdivide_target_mm, STD_SUBDIV_MAX_EDGE_MM); pin(m_subdivide_detail_mm, STD_SUBDIV_DETAIL_MM); pin(m_subdivide_min_edge_mm, STD_SUBDIV_MIN_EDGE_MM); + pin(m_subdivide_border_mm, STD_SUBDIV_BORDER_MM); // Deliberately *not* pinned: the triangle budget stays visible and editable in Standard mode, so // pinning it would fight the user's own slider every frame. pin(m_remesh_target_edge_mm, STD_REMESH_EDGE_MM); @@ -3226,6 +3623,7 @@ void GLGizmoTextureDisplacement::bake_standard() const bool do_remesh = plan_remesh(*mv, STD_REMESH_EDGE_MM, STD_REMESH_SHARP_DEG, remeshed); Plater *plater = wxGetApp().plater(); + bool paint_transfer_failed = false; { // ONE undo step for the whole pipeline. take_snapshot() records the state *before* the change, // so a single Undo goes all the way back to the untouched mesh - which is the only thing "undo @@ -3241,17 +3639,19 @@ void GLGizmoTextureDisplacement::bake_standard() // The remesh carries the paint across spatially, but if that remap came back empty the rest of // the pipeline has nothing to work from - stop here rather than silently baking a flat mesh. - if (!mv->is_texture_displacement_painted()) { - show_error(nullptr, _u8L("The painted area could not be transferred onto the remeshed model. Undo, " - "then switch to Pro mode to prepare the mesh before painting.")); - return; - } + // Note this cannot just `return`: the remesh above has already replaced the volume's mesh, so + // the scene and the gizmo's own TriangleSelectors still have to be brought back into step with + // it below. Returning from here left the gizmo painting and raycasting against a mesh that no + // longer existed. + paint_transfer_failed = !mv->is_texture_displacement_painted(); // Planning the subdivision has to happen inside the snapshot because it reads the mesh the // remesh just produced. It is the expensive step, but by here we are committed anyway. - SubdivisionPlan plan; - if (plan_adaptive_subdivision(*mv, plan)) - apply_adaptive_subdivision(*mv, std::move(plan)); + if (!paint_transfer_failed) { + SubdivisionPlan plan; + if (plan_adaptive_subdivision(*mv, plan)) + apply_adaptive_subdivision(*mv, std::move(plan)); + } } if (ObjectList *obj_list = wxGetApp().obj_list()) { @@ -3264,6 +3664,12 @@ void GLGizmoTextureDisplacement::bake_standard() update_from_model_object(false); // reload selectors against the prepared mesh + carried paint m_parent.set_as_dirty(); + if (paint_transfer_failed) { + show_error(nullptr, _u8L("The painted area could not be transferred onto the remeshed model. Undo, " + "then switch to Pro mode to prepare the mesh before painting.")); + return; + } + // ... and finally the displacement itself, in the background exactly as the Pro-mode button does - // except that it commits into the snapshot taken above instead of pushing another one. bake(/* own_snapshot */ false); @@ -3526,6 +3932,12 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float rebuild_uvcheck_mesh(); if (m_use_bump_preview) rebuild_bump_preview_mesh(); + else + // The Fast view skips the CPU displacement entirely (see rebuild_preview()), so + // leaving it means m_preview_glmodel may be stale or absent - ask for it now. + queue_preview_job(); + // ...and the paint tint between the base and the displaced surface, for the same reason. + m_paint_overlay_dirty = true; refresh_wireframe(); // Normal<->Fast swaps the wireframe between displaced and base mesh update_uv_editor(); // mirror the checker / distortion heatmap into the UV pane too (#7) m_parent.set_as_dirty(); @@ -4225,6 +4637,20 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->scaled(20.f)); } + // Applies in both adaptive sub-modes: it is not a texture-detail criterion, it is about the + // step the bake puts at the paint's edge, which exists whether or not "Follow texture detail" + // is on. 0 turns the band off and restores the old behaviour. + if (m_imgui->slider_float(std::string(_u8L("Edge detail (mm)")) + "##subdivborder", &m_subdivide_border_mm, + 0.f, 5.f, "%.3f")) + preview_live(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangle size along the boundary of the painted area. The relief drops back to the " + "flat surface across that boundary, and the triangles spanning the drop are what you " + "see as a jagged rim around an unpainted region - smaller values make the outline " + "cleaner. Costs triangles along the outline only, not over the whole area. " + "0 turns it off."), + m_imgui->scaled(20.f)); + ImGui::PopItemWidth(); budget_slider(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 29fc28a114..7874f2656f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -10,6 +10,7 @@ #include "slic3r/GUI/TextureLibrary.hpp" #include +#include #include #include #include @@ -101,11 +102,26 @@ private: // so a run that turns out to be a no-op does not leave an empty undo step behind - and it keeps // snapshot ownership with the caller, which matters because the buttons want one snapshot per click // while the pipeline wants a single one around remesh + subdivide together. + // How one layer's paint sits on the pre-subdivision mesh, precise enough to carry across the + // refinement without rounding each source triangle to wholly painted or not. + // + // Rounding is what made the outline of a painted region come out ragged: a source triangle near a + // smooth brush boundary is wholly painted essentially at random, so "painted iff the source was + // full" turns a clean curve into a noisy fringe of isolated painted and unpainted triangles - and + // once the border band refines the mesh there, that fringe is reproduced faithfully instead of + // being blurred away by coarse geometry. + struct LayerPaintMap + { + std::vector full; // per source triangle: covered edge to edge + std::vector part_start; // CSR offsets into `part`, size (source tris + 1) + std::vector> part; // painted pieces of partly covered source triangles + bool empty() const { return full.empty(); } + }; struct SubdivisionPlan { - indexed_triangle_set refined; - std::vector source; // new tri -> input tri - std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> painted_tri; // per layer, per input tri + indexed_triangle_set refined; + std::vector source; // new tri -> input tri + std::array paint; // per layer }; // False means "nothing to refine" and `out` must not be used. bool plan_adaptive_subdivision(const ModelVolume &mv, SubdivisionPlan &out) const; @@ -162,10 +178,20 @@ private: // remapped (texture-displacement paint has no remap-across-topology-change support yet). void subdivide_model(); + // The layer height map's width / height, for apply_uv_transform()'s non-square handling. 1 when + // there is no usable texture. + static float layer_texture_aspect(const TextureDisplacementLayer &layer); + // Returns a cached GPU thumbnail of layer's texture (decoding + uploading it the first time it // is requested, or whenever its image_data changes), or nullptr if it has no usable texture. + // Panel-sized: box-filtered down to THUMBNAIL_MAX_PX, which is right for a list row and wrong for + // anything the shader samples - see get_layer_height_texture(). GLTexture *get_layer_thumbnail(const TextureDisplacementLayer &layer); + // The same texture at full resolution, for the fast-preview shader. One slot, shared by whichever + // layer is active, because that is the only one the bump shader ever shades. + GLTexture *get_layer_height_texture(const TextureDisplacementLayer &layer); + // A texture from the picker's library (see slic3r/GUI/TextureLibrary.hpp), read and uploaded // once and then kept for the gizmo's lifetime. The decoded bytes are held alongside the GPU // thumbnail so that picking the texture can hand the layer this very same image_data buffer - @@ -366,18 +392,33 @@ private: bool m_subdivide_feature = false; float m_subdivide_detail_mm = 0.05f; float m_subdivide_min_edge_mm = 0.1f; + // Edge length the band straddling the paint's boundary is refined to (0 = leave it alone). Applies + // in both adaptive sub-modes, because it is not a texture-detail criterion: the bake steps the + // surface from full displacement to zero across that boundary whatever the texture is doing, and + // the chord-error test cannot see that step at all - its sampler has no per-point paint test, so + // just outside the paint it goes on reporting the same smooth height field. Without this the + // transition keeps the input's density and the rim of an unpainted island comes out as a ring of + // large, steeply tilted triangles. See collect_paint_region() and subdivide_mesh_adaptive(). + float m_subdivide_border_mm = 0.4f; // How many thousand triangles refinement may *add* (the mesh's own count is added on before it is // passed as subdivide_mesh_adaptive()'s absolute cap, so the control still means something on a // dense model). Refinement is worst-error-first, so hitting the budget still yields the best mesh // that many triangles can buy - and it is what keeps a fine "Detail" over a noisy texture from // turning into an out-of-memory, or an unrenderable preview wireframe. - int m_subdivide_budget_k = 1500; + // + // The default used to be 1500 (i.e. +1.5 M triangles), which is what made Standard mode's Bake + // take minutes: every stage after the subdivision - the displacement itself, the convex hull, the + // GLModel upload, and the re-slice changed_object() triggers - then runs on a mesh two orders of + // magnitude denser than the input. 300k is still far finer than any FDM nozzle resolves at the + // 0.02 mm detail tolerance Standard uses, and the slider goes to 2000 for anyone who wants more. + int m_subdivide_budget_k = 300; void subdivide_model_adaptive(); - // Fills `region` (per current-mesh triangle, 1 = refine) from the union of every layer's painted - // area. If `painted_tri` is non-null, also fills, per layer, the fully-painted triangles to carry - // forward. Returns false when nothing is painted at all. Shared by the preview and the commit. + // Fills `region` (per current-mesh triangle, a REFINE_* bitmask) from the union of every layer's + // painted area plus the band straddling its edge. If `paint` is non-null, also fills the per-layer + // coverage map the subdivision carries forward - the expensive half, skipped by the live preview, + // which only needs the region. Returns false when nothing is painted at all. bool collect_paint_region(std::vector ®ion, - std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> *painted_tri) const; + std::array *paint) const; // Runs the volume's TextureDisplacementOptions smoothing over the *already committed* geometry, // restricted to the painted area. The same settings are folded into Preview/Bake automatically; @@ -417,6 +458,25 @@ private: // and the 3D view. The rebuild is instead coalesced to once per 3D frame (render_painter_gizmo). bool m_bump_preview_dirty = false; GLModel m_bump_preview_glmodel; + + // Translucent tint over the active layer's painted triangles, drawn on top of whichever preview + // is showing. The base painter's own opaque paint highlight (render_triangles()) cannot be used + // in either preview mode - it is coincident with the surface and simply covers it - so the only + // paint feedback the gizmo had was the relief itself, which meant erasing showed nothing at all + // until the stroke ended and the whole preview rebuilt. This is that feedback: cheap (the painted + // patch only), translucent (the preview stays visible through it) and rebuilt live during a + // stroke. + GLModel m_paint_overlay_glmodel; + // Set on every paint event, cleared when the overlay is rebuilt in render_painter_gizmo(). Kept + // separate from m_bump_preview_dirty so a stroke refreshes only the small painted patch per frame, + // not the bump mesh (which also carries every *unpainted* triangle of the volume). + bool m_paint_overlay_dirty = false; + void rebuild_paint_overlay(); + void render_paint_overlay(); + // Whether render_bump_preview_mesh() would actually draw something. Checked before the real volume + // is hidden: with no layer, no texture or no shader the bump path draws nothing, and hiding the + // volume for it left the model invisible. + bool bump_preview_ready() const; // Whether the current bump mesh carries a precomputed per-vertex uv (LSCM) that the shader // should sample at directly, rather than projecting in-shader. Set by rebuild_bump_preview_mesh(). bool m_bump_preview_uses_vertex_uv = false; @@ -507,16 +567,36 @@ private: // Bumped on every rebuild_preview() call; a background TextureDisplacementPreviewJob's result // is only applied if this hasn't moved on since the job was queued (see rebuild_preview()), // so a burst of edits can't have an earlier, now-stale job clobber a later one's result. - uint64_t m_preview_generation = 0; + // + // Shared with the worker thread (hence the atomic) so a running job can notice mid-computation + // that it has been superseded and abort, instead of running to completion for a result that will + // only be discarded on arrival. + std::shared_ptr> m_preview_generation = std::make_shared>(0); + // At most one preview job is ever queued. The UI job worker is a single FIFO queue shared with + // Bake (and with arrange/orient/send), and rebuild_preview() is called on every stroke end, every + // slider release and - with "Auto update" on - every frame of a slider drag. Queuing one full + // displacement per call built a backlog that took minutes to drain: the preview appeared frozen, + // and a Bake pressed afterwards sat behind the whole queue. So a request made while a job is in + // flight is recorded here and issued once that job settles, collapsing any number of edits into a + // single follow-up run. + bool m_preview_job_running = false; + bool m_preview_job_pending = false; + void queue_preview_job(); // Per-slot GPU thumbnail cache for the layer list panel, keyed by the image_data pointer that // was current the last time each thumbnail was built (see get_layer_thumbnail()). std::array, TEXTURE_DISPLACEMENT_MAX_LAYERS> m_thumbnails; std::array m_thumbnail_source{}; - // The smoothing each cached thumbnail was built at, so a smoothing change re-uploads it (and the - // fast/bump preview, which samples this texture, actually shows the blur). + // The smoothing each cached thumbnail was built at, so a smoothing change re-uploads it. std::array m_thumbnail_smoothing{}; + // Full-resolution height texture for the bump shader, keyed the same way (see + // get_layer_height_texture()). A smoothing change re-uploads it, so the fast preview shows the + // blur the bake will apply. + std::unique_ptr m_height_tex; + const void *m_height_tex_source = nullptr; + float m_height_tex_smoothing = -1.f; + // Library textures the picker has shown at least once, keyed by file path (see LibraryTexture). std::map m_library_textures; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 0977e388b5..28846ca822 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -166,7 +166,11 @@ void GLGizmosManager::switch_gizmos_icon_filename() gizmo->set_icon_filename(m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg"); break; case(EType::TextureDisplacement): - gizmo->set_icon_filename(m_is_dark ? "toolbar_fuzzy_skin_paint_dark.svg" : "toolbar_fuzzy_skin_paint.svg"); + // One shared icon in both themes (no dedicated dark variant yet) - but it must still be + // *this* gizmo's icon. Handing it the fuzzy-skin one here quietly replaced the icon set at + // construction, so the toolbar ended up showing two identical fuzzy-skin buttons after any + // light/dark switch. + gizmo->set_icon_filename("toolbar_texture_displacement.svg"); break; case(EType::MeshBoolean): gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"); diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 7d6119ce30..91493a9173 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -20,12 +20,34 @@ TextureDisplacementBakeJob::TextureDisplacementBakeJob(TextureDisplacementBakeIn void TextureDisplacementBakeJob::process(Ctl &ctl) { - ctl.update_status(0, _u8L("Baking texture displacement")); + const std::string status = _u8L("Baking texture displacement"); + ctl.update_status(1, status); // Only ever touches m_input (captured by value before this job was queued) and local state - // never the live Model - so this is safe to run concurrently with the UI thread. - m_result = TriangleMesh(build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, - m_input.options)); + // + // The progress hook matters for more than cosmetics: the framework's progress notification only + // grows a close button once it reaches 100%, so a job that reports 0 and nothing else leaves an + // uncloseable notification pinned on screen. It also carries the Cancel button's effect into the + // bake, which on a subdivided mesh can run for several seconds. + int last_reported = 1; + m_result = TriangleMesh(build_texture_displacement( + m_input.base_mesh, m_input.layers, m_input.facets_data, m_input.options, + [&ctl, &status, &last_reported](int percent) { + if (ctl.was_canceled()) + return false; + // The notification repaints (and wakes the idle loop) on every call, so only push a + // message when the displayed integer percentage actually moves. + if (percent > last_reported) { + last_reported = percent; + ctl.update_status(percent, status); + } + return true; + })); + + // Always finish at 100: this is what closes the notification. Reported even on cancel, where + // build_texture_displacement() returns an empty mesh and finalize() commits nothing. + ctl.update_status(100, status); } void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &eptr) diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index 555ed570b1..44bcf37725 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -5,25 +5,44 @@ namespace Slic3r::GUI { TextureDisplacementPreviewJob::TextureDisplacementPreviewJob(TextureDisplacementPreviewInput &&input, uint64_t generation, + std::shared_ptr> current_generation, std::function on_finished) - : m_input(std::move(input)), m_generation(generation), m_on_finished(std::move(on_finished)) + : m_input(std::move(input)), m_generation(generation), m_current_generation(std::move(current_generation)), + m_on_finished(std::move(on_finished)) { } void TextureDisplacementPreviewJob::process(Ctl &ctl) { - ctl.update_status(0, _u8L("Computing texture displacement preview")); + // No ctl.update_status() anywhere in here on purpose - see the class comment. A preview is + // invisible bookkeeping; the only thing on screen should be the preview itself. // Only ever touches m_input (captured by value before this job was queued) and local state - // never the live Model - so this is safe to run concurrently with the UI thread. - m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, m_input.options); + m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, m_input.options, + [this, &ctl](int) { + // Bail the moment this preview stops being the current + // one; build_texture_displacement() then returns an + // empty mesh and finalize() drops it. + return !ctl.was_canceled() && + (!m_current_generation || + m_current_generation->load() == m_generation); + }); } void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr &eptr) { - if (canceled || eptr || !m_on_finished) + if (!m_on_finished) return; - m_on_finished(std::move(m_result), m_generation); + // The handler must run on *every* outcome, cancellation included, because the caller uses it to + // clear its "a job is in flight" latch. Returning early on `canceled` - which is what a cancel_all() + // from Plater (project load/close, app exit) delivers - left that latch stuck true and no preview + // was ever queued again for the rest of the session. An empty result is the caller's signal that + // nothing usable came back; it already handles that. + if (canceled || eptr) + m_on_finished(indexed_triangle_set{}, m_generation); + else + m_on_finished(std::move(m_result), m_generation); } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp index ed59c1f302..f9e2a9f389 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -1,8 +1,10 @@ #ifndef slic3r_TextureDisplacementPreviewJob_hpp_ #define slic3r_TextureDisplacementPreviewJob_hpp_ +#include #include #include +#include #include #include "libslic3r/TextureDisplacement.hpp" @@ -27,6 +29,10 @@ struct TextureDisplacementPreviewInput // to run synchronously on every paint stroke and parameter tweak and made editing feel slow with // more than one or two layers. Unlike Bake, this never touches the live Model - a preview is // purely informational, there is nothing to commit. +// +// Deliberately reports no status: the Job framework turns the first update_status() call into an +// on-screen progress notification, and a preview firing one on every stroke and slider release +// buried the user in notifications that only closed at 100%. class TextureDisplacementPreviewJob : public Job { public: @@ -35,7 +41,14 @@ public: // current generation when the job completes, so that a burst of edits queuing several of // these jobs in a row can't have an earlier, now-stale result clobber a later one that // finishes first. + // + // `current_generation` is the caller's live counter, shared with the worker thread. The job + // polls it *while computing* and aborts as soon as it no longer matches - so a preview that has + // already been superseded stops burning CPU instead of running to completion for a result that + // will only be thrown away. That matters because the UI job worker runs one job at a time in FIFO + // order: without it, a Bake queued behind a handful of stale previews waits for every one of them. TextureDisplacementPreviewJob(TextureDisplacementPreviewInput &&input, uint64_t generation, + std::shared_ptr> current_generation, std::function on_finished); void process(Ctl &ctl) override; @@ -44,6 +57,7 @@ public: private: TextureDisplacementPreviewInput m_input; uint64_t m_generation; + std::shared_ptr> m_current_generation; indexed_triangle_set m_result; std::function m_on_finished; }; From e4c570a7b7f5943cd469002010ca491ad392ef5e Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 27 Aug 2026 09:20:02 +0200 Subject: [PATCH 21/89] Move computation to background thread & resolve freeze --- src/libslic3r/TextureDisplacement.cpp | 14 +- src/libslic3r/TextureDisplacement.hpp | 40 +- src/slic3r/CMakeLists.txt | 2 + .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 574 +++++++++++------- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 80 ++- .../Jobs/TextureDisplacementPrepareJob.cpp | 117 ++++ .../Jobs/TextureDisplacementPrepareJob.hpp | 72 +++ 7 files changed, 650 insertions(+), 249 deletions(-) create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 1a044ae956..e1a03a9eaf 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -1645,7 +1645,8 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, float target_edge_length_mm, int max_triangles, std::vector *out_source, const HeightFieldSampler &sampler, float chord_tolerance_mm, float min_edge_length_mm, - float border_edge_length_mm) + float border_edge_length_mm, + const DisplacementProgressFn &progress) { // Neighbour slots that are not a triangle index. constexpr int NB_BOUNDARY = -1; // open edge: terminal on its own, bisected from this side alone @@ -1948,7 +1949,18 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // Every iteration either drops one satisfied triangle from the queue or performs exactly one // bisection, and bisections are capped by the triangle budget, so this always terminates. + // Progress is reported against the triangle budget, which is what the loop is bounded by. Polled + // rather than pushed on every bisection: a refinement spends its budget in hundreds of thousands + // of them, and every hook call wakes the UI's idle loop to repaint the notification. + const int start_tris = int(tris.size()); + const int budget_tris = std::max(1, max_triangles - start_tris); + int next_poll = start_tris; while (!queue.empty() && int(tris.size()) + 2 <= max_triangles) { + if (progress && int(tris.size()) >= next_poll) { + next_poll = int(tris.size()) + std::max(1024, budget_tris / 100); + if (!progress(std::clamp((int(tris.size()) - start_tris) * 100 / budget_tris, 0, 100))) + break; // still conformal - whole bisections only; the caller decides whether to keep it + } const int ti = queue.top().second; queue.pop(); if (priority(ti) <= 1.f) diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 74c590bbdb..10e30f9788 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -677,13 +677,51 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl // rim of an unpainted island into a ring of large, steeply tilted triangles. Refining that band by // plain edge length is bounded (it is a thin ring, and a length target always terminates) and needs // no paint-aware sampler. +// +// `progress`, when given, is called with a 0..100 percentage of the triangle budget spent; returning +// false stops the refinement early. What it hands back then is still a complete, conformal mesh - the +// loop only ever finishes whole bisections - so a caller that wants to discard it has to do so itself. indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const std::vector &refine_region, float target_edge_length_mm, int max_triangles = 1000000, std::vector *out_source = nullptr, const HeightFieldSampler &sampler = nullptr, float chord_tolerance_mm = 0.f, float min_edge_length_mm = 0.f, - float border_edge_length_mm = 0.f); + float border_edge_length_mm = 0.f, + const DisplacementProgressFn &progress = nullptr); + +// The recipe for getting a mesh ready to receive displacement: even out the triangle density, then +// refine it where the texture bends. Either stage is skipped when its target is <= 0. Pure data, and +// the whole of it, so the preparation can be handed to a background job instead of running on the UI +// thread - see GLGizmoTextureDisplacement::prepare_mesh(). +struct TextureDisplacementPrepareParams +{ + // Isotropic remesh (CGAL). The target is clamped against the part's own surface area before it is + // used, so a value that would produce millions of triangles cannot be asked for by accident. + float remesh_edge_mm = 0.f; + float remesh_sharp_deg = 0.f; // 0 = do not protect sharp edges + + // Adaptive (Rivara) subdivision of the painted area. See subdivide_mesh_adaptive(). + float subdiv_target_mm = 0.f; // "Max edge": the length baseline, and the only criterion when + // subdiv_feature is off + float subdiv_detail_mm = 0.f; // "Detail": chord tolerance, feature mode only + float subdiv_min_edge_mm = 0.f; // "Min edge": the floor under both, feature mode only + float subdiv_border_mm = 0.f; // "Edge detail": the band straddling the paint's edge, 0 = off + bool subdiv_feature = false; // follow texture curvature, not just edge length + int subdiv_added_triangles = 0; // budget, *added* to the mesh's own count +}; + +// What a preparation run produced. An empty `mesh` means there was nothing to do and the caller must +// commit nothing - which is not a failure: a mesh that is already even needs no remesh, and one that +// is already fine enough for the texture needs no subdivision. +struct TextureDisplacementPrepareResult +{ + indexed_triangle_set mesh; + TextureDisplacementFacetsData masks; + // The remesh landed but no layer's paint survived being carried onto it. Nothing is committed: + // everything downstream is driven by that paint, so baking on would bake a flat mesh. + bool paint_lost = false; +}; } // namespace Slic3r diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4a9f20b8dc..6cdb530296 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -327,6 +327,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/SLAImportJob.hpp GUI/Jobs/TextureDisplacementBakeJob.cpp GUI/Jobs/TextureDisplacementBakeJob.hpp + GUI/Jobs/TextureDisplacementPrepareJob.cpp + GUI/Jobs/TextureDisplacementPrepareJob.hpp GUI/Jobs/TextureDisplacementPreviewJob.cpp GUI/Jobs/TextureDisplacementPreviewJob.hpp GUI/Jobs/ThreadSafeQueue.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 2fbaf7bf53..bff54e791e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -2,6 +2,7 @@ #include +#include "libslic3r/AABBTreeIndirect.hpp" #include "libslic3r/MeshBoolean.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Utils.hpp" @@ -23,11 +24,13 @@ #include "slic3r/GUI/TextureProjectorFrame.hpp" #include "slic3r/GUI/UVEditorCanvas.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp" +#include "slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "GLGizmoUtils.hpp" #include +#include #include #include #include @@ -98,6 +101,117 @@ bool point_in_triangle_coplanar(const Vec3f &p, const std::array &t) (t[0] - t[2]).cross(p - t[2]).dot(n) >= eps; } +// Carries one texture-displacement paint mask from `src_mesh` onto `dst_mesh` - a different +// tessellation of the same surface (an isotropic remesh, in practice). +// +// TriangleSelector::remap_painting(), which ModelVolume::restore_painting() uses for the four +// standard paint channels, is not usable here. It runs one select_patch() flood fill per +// (painted sub-triangle x overlapping target facet) pair, and select_patch() splits the *target's* +// triangles down to a 0.02-0.05 mm edge limit along every cursor boundary, behind an +// O(target triangles) visited-set allocation per call. A coarse import painted with a fine brush +// carries tens of thousands of painted sub-triangles, so that is tens of millions of sub-triangles +// created on the target - which is what made Remesh, and Standard mode's Bake (which remeshes for +// you), appear to hang rather than finish. +// +// Each target triangle here asks one question instead: is the point on the source surface closest +// to my centroid inside a painted piece? One AABB-tree query per target triangle, whole facets +// only, no splitting - O(target log source). `src_tree` is built over `src_mesh.its` by the caller +// and shared across the layers; `dst_to_src` brings the target's vertices into the source's frame. +TriangleSelector::TriangleSplittingData remap_texture_paint_spatial( + const TriangleMesh &src_mesh, const TriangleSelector::TriangleSplittingData &src_data, + const AABBTreeIndirect::Tree3f &src_tree, const TriangleMesh &dst_mesh, const Vec3f &dst_to_src) +{ + const indexed_triangle_set &src = src_mesh.its; + const indexed_triangle_set &dst = dst_mesh.its; + const size_t ntri = src.indices.size(); + if (src_data.bitstream.empty() || ntri == 0 || dst.indices.empty() || src_tree.empty()) + return {}; + + // The painted patch, split into per-source-triangle pieces - the same shape + // collect_paint_region() builds, and for the same reason: a source triangle the brush only + // partly covered has to answer "is this point painted" geometrically rather than be rounded to + // painted-or-not, which leaves a ragged fringe along any curved brush boundary. + TriangleSelector src_sel(src_mesh); + src_sel.deserialize(src_data, false); + std::vector piece_src; + const indexed_triangle_set patch = src_sel.get_facets_strict(EnforcerBlockerType::ENFORCER, &piece_src); + if (patch.indices.empty()) + return {}; + + const auto tri_area2 = [](const Vec3f &a, const Vec3f &b, const Vec3f &c) { + return (b - a).cross(c - a).norm(); + }; + std::vector src_area2(ntri, 0.f), covered2(ntri, 0.f); + for (size_t i = 0; i < ntri; ++i) + src_area2[i] = tri_area2(src.vertices[size_t(src.indices[i][0])], src.vertices[size_t(src.indices[i][1])], + src.vertices[size_t(src.indices[i][2])]); + for (size_t j = 0; j < patch.indices.size() && j < piece_src.size(); ++j) { + if (size_t(piece_src[j]) >= ntri) + continue; + const stl_triangle_vertex_indices &t = patch.indices[j]; + covered2[size_t(piece_src[j])] += tri_area2(patch.vertices[size_t(t[0])], patch.vertices[size_t(t[1])], + patch.vertices[size_t(t[2])]); + } + std::vector full(ntri, 0); + for (size_t i = 0; i < ntri; ++i) + full[i] = (src_area2[i] > 0.f && covered2[i] >= 0.999f * src_area2[i]) ? 1 : 0; + + // CSR pieces, kept for partly covered sources only - a full one answers every query "painted". + std::vector part_start(ntri + 1, 0); + for (size_t j = 0; j < patch.indices.size() && j < piece_src.size(); ++j) + if (size_t(piece_src[j]) < ntri && !full[size_t(piece_src[j])]) + ++part_start[size_t(piece_src[j]) + 1]; + for (size_t i = 0; i < ntri; ++i) + part_start[i + 1] += part_start[i]; + std::vector> part; + part.resize(size_t(part_start[ntri])); // not a constructor call: `vector p(size_t(x[n]));` parses + // as a function declaration, and every use of `part` below + // then fails with something that does not mention the cause. + { + std::vector fill(part_start.begin(), part_start.begin() + ntri); + for (size_t j = 0; j < patch.indices.size() && j < piece_src.size(); ++j) { + const size_t S = size_t(piece_src[j]); + if (S >= ntri || full[S]) + continue; + const stl_triangle_vertex_indices &t = patch.indices[j]; + part[size_t(fill[S]++)] = { patch.vertices[size_t(t[0])], patch.vertices[size_t(t[1])], + patch.vertices[size_t(t[2])] }; + } + } + + // Classify in parallel, then write the mask serially - TriangleSelector is not thread safe. + std::vector painted(dst.indices.size(), 0); + tbb::parallel_for(tbb::blocked_range(0, dst.indices.size()), + [&](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + const stl_triangle_vertex_indices &t = dst.indices[i]; + const Vec3f centroid = (dst.vertices[size_t(t[0])] + dst.vertices[size_t(t[1])] + + dst.vertices[size_t(t[2])]) / 3.f + dst_to_src; + size_t hit = 0; + Vec3f hit_pos = Vec3f::Zero(); + if (AABBTreeIndirect::squared_distance_to_indexed_triangle_set(src.vertices, src.indices, src_tree, + centroid, hit, hit_pos) < 0.f || + hit >= ntri) + continue; + if (full[hit]) { + painted[i] = 1; + continue; + } + for (int k = part_start[hit]; k < part_start[hit + 1]; ++k) + if (point_in_triangle_coplanar(hit_pos, part[size_t(k)])) { + painted[i] = 1; + break; + } + } + }); + + TriangleSelector dst_sel(dst_mesh); + for (size_t i = 0; i < painted.size(); ++i) + if (painted[i]) + dst_sel.set_facet(int(i), EnforcerBlockerType::ENFORCER); + return dst_sel.serialize(); +} + std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &decoded, int max_px = THUMBNAIL_MAX_PX) { if (decoded.empty()) @@ -2964,13 +3078,11 @@ void GLGizmoTextureDisplacement::subdivide_model() } bool GLGizmoTextureDisplacement::collect_paint_region( + const TriangleMesh &mesh, const TextureDisplacementFacetsData &facets, std::vector ®ion, - std::array *paint) const + std::array *paint) { - const ModelVolume *mv = texture_volume(); - if (mv == nullptr) - return false; - const indexed_triangle_set &its = mv->mesh().its; + const indexed_triangle_set &its = mesh.its; const size_t ntri = its.indices.size(); const size_t nvert = its.vertices.size(); @@ -2996,7 +3108,7 @@ bool GLGizmoTextureDisplacement::collect_paint_region( bool any_paint = false; for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { - const TriangleSelector::TriangleSplittingData &data = mv->texture_displacement_facet(slot).get_data(); + const TriangleSelector::TriangleSplittingData &data = facets[size_t(slot)]; if (!TriangleSelector::has_facets(data, EnforcerBlockerType::ENFORCER)) continue; @@ -3013,7 +3125,7 @@ bool GLGizmoTextureDisplacement::collect_paint_region( region[m.triangle_idx] |= REFINE_PAINTED; if (paint) { - TriangleSelector sel(mv->mesh()); + TriangleSelector sel(mesh); sel.deserialize(data, false); // get_facets_strict() now reports which source triangle each sub-triangle came from, which // is what lets partial coverage be carried forward geometrically instead of being rounded @@ -3120,87 +3232,146 @@ bool GLGizmoTextureDisplacement::collect_paint_region( return true; } -bool GLGizmoTextureDisplacement::plan_adaptive_subdivision(const ModelVolume &mv, SubdivisionPlan &out) const +TextureDisplacementFacetsData GLGizmoTextureDisplacement::masks_after_subdivision( + const TriangleMesh &new_mesh, const std::vector &source, + const std::array &paint) { - if (m_subdivide_target_mm <= 0.f) - return false; - - std::vector region; - if (!collect_paint_region(region, &out.paint)) - return false; - - // Feature-adaptive: sample the combined displacement so refinement follows texture curvature. A - // null sampler (only LSCM layers, or nothing decodable) falls back to the length baseline alone. - HeightFieldSampler sampler; - if (m_subdivide_feature) { - TextureDisplacementFacetsData facets{}; - for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) - facets[size_t(i)] = mv.texture_displacement_facet(i).get_data(); - sampler = make_combined_displacement_sampler(mv.mesh().its, mv.texture_displacement_layers, facets); - } - - // "Min edge" is a feature-mode control (it is the floor the curvature test refines down to); in - // plain adaptive mode the target edge length is the only criterion, so the floor must not be - // allowed to silently override a target the user set below it. - const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f; - const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f; - { - wxBusyCursor wait; - // The budget slider is "triangles the refinement may *add*", so the model's own count is the - // baseline - otherwise the control would be meaningless (or a dead end) on a dense model. - out.refined = subdivide_mesh_adaptive(mv.mesh().its, region, m_subdivide_target_mm, - int(mv.mesh().its.indices.size()) + m_subdivide_budget_k * 1000, - &out.source, sampler, tol, floor, m_subdivide_border_mm); - } - return out.refined.indices.size() != mv.mesh().its.indices.size(); -} - -void GLGizmoTextureDisplacement::apply_adaptive_subdivision(ModelVolume &mv, SubdivisionPlan &&plan) -{ - // Other paint channels ride across via the standard remap; texture-displacement paint is rebuilt - // below from the plan's source map, which is the whole point of driving this by the paint. - std::optional saved_painting = mv.save_painting(); - mv.set_mesh(TriangleMesh(std::move(plan.refined))); - mv.set_new_unique_id(); - mv.calculate_convex_hull(); - mv.restore_painting(saved_painting); // resets extra facets (incl. texture-displacement) + remaps the rest - - // Carry each layer's paint onto the new mesh. Children inherit their parent's source triangle, and - // subdivision only ever adds edge midpoints, so every new triangle lies inside its source and on - // the same surface - which means the source's painted *pieces* can be queried directly by point - // containment. That is what keeps a brush outline smooth: rounding each source to wholly painted - // or not instead leaves a ragged fringe of isolated triangles along any curved boundary, and the - // refined mesh then reproduces that fringe exactly rather than hiding it. - const indexed_triangle_set &new_its = mv.mesh().its; + // Children inherit their parent's source triangle, and subdivision only ever adds edge midpoints, + // so every new triangle lies inside its source and on the same surface - which means the source's + // painted *pieces* can be queried directly by point containment. That is what keeps a brush outline + // smooth: rounding each source to wholly painted or not instead leaves a ragged fringe of isolated + // triangles along any curved boundary, and the refined mesh then reproduces that fringe exactly + // rather than hiding it. + TextureDisplacementFacetsData out{}; + const indexed_triangle_set &its = new_mesh.its; for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { - const LayerPaintMap &pm = plan.paint[size_t(slot)]; + const LayerPaintMap &pm = paint[size_t(slot)]; if (pm.empty()) continue; - TriangleSelector sel(mv.mesh()); - for (size_t i = 0; i < plan.source.size() && i < new_its.indices.size(); ++i) { - const size_t S = size_t(plan.source[i]); + TriangleSelector sel(new_mesh); + for (size_t i = 0; i < source.size() && i < its.indices.size(); ++i) { + const size_t S = size_t(source[i]); if (S >= pm.full.size()) continue; bool painted = pm.full[S] != 0; if (!painted && pm.part_start[S] != pm.part_start[S + 1]) { - const stl_triangle_vertex_indices &t = new_its.indices[i]; - const Vec3f centroid = (new_its.vertices[size_t(t[0])] + new_its.vertices[size_t(t[1])] + - new_its.vertices[size_t(t[2])]) / 3.f; + const stl_triangle_vertex_indices &t = its.indices[i]; + const Vec3f centroid = (its.vertices[size_t(t[0])] + its.vertices[size_t(t[1])] + + its.vertices[size_t(t[2])]) / 3.f; for (int k = pm.part_start[S]; k < pm.part_start[S + 1] && !painted; ++k) painted = point_in_triangle_coplanar(centroid, pm.part[size_t(k)]); } if (painted) sel.set_facet(int(i), EnforcerBlockerType::ENFORCER); } - mv.texture_displacement_facet(slot).set(sel); + out[size_t(slot)] = sel.serialize(); } + return out; +} + +TextureDisplacementFacetsData GLGizmoTextureDisplacement::facets_data_of(const ModelVolume &mv) +{ + TextureDisplacementFacetsData out{}; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + out[size_t(i)] = mv.texture_displacement_facet(i).get_data(); + return out; +} + +TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( + const indexed_triangle_set &base, const TextureDisplacementFacetsData &masks, + const std::vector &layers, const TextureDisplacementPrepareParams ¶ms, + const DisplacementProgressFn &progress) +{ + TextureDisplacementPrepareResult out; + const auto report = [&progress](int pct) { return !progress || progress(pct); }; + + TriangleMesh mesh(base); + TextureDisplacementFacetsData current = masks; + bool changed = false; + bool had_paint = false; + for (const TriangleSelector::TriangleSplittingData &m : masks) + had_paint = had_paint || TriangleSelector::has_facets(m, EnforcerBlockerType::ENFORCER); + if (!report(1)) + return out; + + // 1. Even out the triangle density, and carry the paint onto the result. Everything downstream is + // driven by that paint, so losing it is a hard stop rather than something to bake around - and + // stopping here, before anything is committed, leaves the user's model exactly as it was. + if (params.remesh_edge_mm > 0.f) { + indexed_triangle_set remeshed; + if (plan_remesh(mesh.its, params.remesh_edge_mm, params.remesh_sharp_deg, remeshed)) { + TriangleMesh new_mesh(std::move(remeshed)); + const AABBTreeIndirect::Tree3f old_tree = + AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices); + TextureDisplacementFacetsData carried{}; + bool any = false; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) { + // Both meshes are the volume's own local frame, so there is no shift between them. + carried[size_t(i)] = + remap_texture_paint_spatial(mesh, current[size_t(i)], old_tree, new_mesh, Vec3f::Zero()); + any = any || !carried[size_t(i)].bitstream.empty(); + } + mesh = std::move(new_mesh); + current = std::move(carried); + changed = true; + if (had_paint && !any) { + out.paint_lost = true; + return out; + } + } + } + if (!report(50)) + return {}; + + // 2. Refine where the texture bends - the painted area only, plus the graded band the conformal + // closure pulls in around it. + if (params.subdiv_target_mm > 0.f) { + std::vector region; + std::array paint; + if (collect_paint_region(mesh, current, region, &paint)) { + // Feature-adaptive: sample the combined displacement so refinement follows texture + // curvature. A null sampler (only LSCM layers, or nothing decodable) falls back to the + // length baseline alone. + HeightFieldSampler sampler; + if (params.subdiv_feature) + sampler = make_combined_displacement_sampler(mesh.its, layers, current); + // "Min edge" is a feature-mode control (it is the floor the curvature test refines down + // to); in plain adaptive mode the target edge length is the only criterion, so the floor + // must not be allowed to silently override a target the user set below it. + const float tol = params.subdiv_feature ? params.subdiv_detail_mm : 0.f; + const float floor = params.subdiv_feature ? params.subdiv_min_edge_mm : 0.f; + bool aborted = false; + std::vector source; + // The budget is "triangles the refinement may *add*", so the mesh's own count is the + // baseline - otherwise the control would be meaningless (or a dead end) on a dense model. + indexed_triangle_set refined = + subdivide_mesh_adaptive(mesh.its, region, params.subdiv_target_mm, + int(mesh.its.indices.size()) + params.subdiv_added_triangles, &source, + sampler, tol, floor, params.subdiv_border_mm, [&](int pct) { + aborted = !report(50 + pct / 2); + return !aborted; + }); + if (aborted) + return {}; + if (refined.indices.size() != mesh.its.indices.size()) { + mesh = TriangleMesh(std::move(refined)); + current = masks_after_subdivision(mesh, source, paint); + changed = true; + } + } + } + if (!report(100) || !changed) + return out; // an empty result: nothing to commit, which is not a failure + + out.mesh = std::move(mesh.its); + out.masks = std::move(current); + return out; } void GLGizmoTextureDisplacement::subdivide_model_adaptive() { ModelVolume *mv = texture_volume(); - ModelObject *mo = m_c->selection_info()->model_object(); - if (mv == nullptr || mo == nullptr) + if (mv == nullptr || m_subdivide_target_mm <= 0.f) return; update_model_object(); // flush any in-progress stroke into the committed masks first @@ -3211,27 +3382,16 @@ void GLGizmoTextureDisplacement::subdivide_model_adaptive() return; } - // Plan before the snapshot, so a no-op leaves no empty undo step - mirrors remesh_model(). - SubdivisionPlan plan; - if (!plan_adaptive_subdivision(*mv, plan)) { - show_error(nullptr, _u8L("Nothing to subdivide - the painted area already meets the target edge " - "length and detail tolerance, or the triangle budget is already used up.")); - return; - } - - Plater *plater = wxGetApp().plater(); - Plater::TakeSnapshot snapshot(plater, _u8L("Adaptive subdivide for texture displacement"), UndoRedo::SnapshotType::GizmoAction); - apply_adaptive_subdivision(*mv, std::move(plan)); - - if (ObjectList *obj_list = wxGetApp().obj_list()) { - const ModelObjectPtrs &objs = plater->model().objects; - auto it = std::find(objs.begin(), objs.end(), mo); - if (it != objs.end()) - obj_list->update_info_items(size_t(it - objs.begin())); - } - plater->changed_object(*mo); - update_from_model_object(false); // reload selectors/preview against the new mesh + carried paint - m_parent.set_as_dirty(); + TextureDisplacementPrepareParams params; + params.subdiv_target_mm = m_subdivide_target_mm; + params.subdiv_detail_mm = m_subdivide_detail_mm; + params.subdiv_min_edge_mm = m_subdivide_min_edge_mm; + params.subdiv_border_mm = m_subdivide_border_mm; + params.subdiv_feature = m_subdivide_feature; + params.subdiv_added_triangles = m_subdivide_budget_k * 1000; + queue_prepare(params, _u8L("Adaptive subdivide for texture displacement"), /* then_bake */ false, + _u8L("Nothing to subdivide - the painted area already meets the target edge length and " + "detail tolerance, or the triangle budget is already used up.")); } void GLGizmoTextureDisplacement::smooth_model() @@ -3318,49 +3478,35 @@ void GLGizmoTextureDisplacement::smooth_model() m_parent.set_as_dirty(); } -void GLGizmoTextureDisplacement::replace_mesh_keep_all_paint(ModelVolume &mv, TriangleMesh &&new_mesh) -{ - const indexed_triangle_set old_its = mv.mesh().its; - std::array saved_texture; - for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) - saved_texture[size_t(i)] = mv.texture_displacement_facet(i).get_data(); - std::optional saved_painting = mv.save_painting(); - - mv.set_mesh(std::move(new_mesh)); - mv.set_new_unique_id(); - mv.calculate_convex_hull(); - mv.restore_painting(saved_painting); // clears the extra facets, then remaps the four standard channels - - // ... and the same spatial remap for the texture-displacement masks, which restore_painting() knows - // nothing about. Without this a remesh would wipe the texture paint, which is fine for a standalone - // "Remesh" click but fatal for Standard mode's pipeline: it remeshes *after* the user has painted, - // and the subdivision and displacement that follow are both driven by that paint. - const Transform3d to_source = Slic3r::Geometry::translation_transform(mv.mesh().get_init_shift()); - for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) { - if (saved_texture[size_t(i)].bitstream.empty()) - continue; - TriangleSelector::TriangleSplittingData remapped = - TriangleSelector::remap_painting(old_its, saved_texture[size_t(i)], mv.mesh().its, to_source, - {}); // no existing paint to merge with: set_mesh() just cleared it - if (!remapped.bitstream.empty()) - mv.texture_displacement_facet(i).set_data(std::move(remapped)); - } -} - -bool GLGizmoTextureDisplacement::plan_remesh(const ModelVolume &mv, float target_edge_mm, float sharp_angle_deg, - TriangleMesh &out) +bool GLGizmoTextureDisplacement::plan_remesh(const indexed_triangle_set &src, float target_edge_mm, + float sharp_angle_deg, indexed_triangle_set &out) { if (target_edge_mm <= 0.f) return false; - // CGAL isotropic remeshing can be slow on a big mesh, which is why this is separated from applying - // it: the caller runs it before taking the snapshot so a failure leaves no empty undo step. - const indexed_triangle_set &src = mv.mesh().its; - indexed_triangle_set remeshed; - { - wxBusyCursor wait; - remeshed = MeshBoolean::cgal::remesh_isotropic(src, double(target_edge_mm), 3, double(sharp_angle_deg)); + // Its cost and memory grow with the *square* of 1/target_edge, and it runs single-threaded on the + // UI thread, so the target has to be bounded against the part's actual surface area rather than + // taken at face value. Standard mode asks for 1 mm whatever it is handed, and the slider goes down + // to 0.1 mm: on a 150 mm part those are ~500 k and ~50 M triangles respectively - minutes to hours + // of frozen UI, which is indistinguishable from a hang. Raising the target instead still gives the + // subdivider the even density it needs, and the subdivision that follows is where the detail was + // always going to come from anyway. An equilateral triangle of edge L covers sqrt(3)/4 * L^2. + static constexpr double REMESH_MAX_TRIANGLES = 150000.0; + double area = 0.0; + for (const stl_triangle_vertex_indices &tri : src.indices) + area += 0.5 * double((src.vertices[size_t(tri[1])] - src.vertices[size_t(tri[0])]) + .cross(src.vertices[size_t(tri[2])] - src.vertices[size_t(tri[0])]) + .norm()); + if (const double budget_edge = std::sqrt(area / (0.4330127 * REMESH_MAX_TRIANGLES)); + budget_edge > double(target_edge_mm)) { + BOOST_LOG_TRIVIAL(info) << "Texture displacement: remesh target raised from " << target_edge_mm + << " mm to " << budget_edge << " mm to stay within " + << int(REMESH_MAX_TRIANGLES) << " triangles."; + target_edge_mm = float(budget_edge); } + + indexed_triangle_set remeshed = + MeshBoolean::cgal::remesh_isotropic(src, double(target_edge_mm), 3, double(sharp_angle_deg)); // remesh_isotropic() signals failure by handing the input straight back, so compare against it // structurally. Vertex count alone is not enough: a remesh that only redistributes triangles at // roughly the current density legitimately lands on the same count, and treating that as failure @@ -3370,39 +3516,23 @@ bool GLGizmoTextureDisplacement::plan_remesh(const ModelVolume &mv, float target remeshed.indices == src.indices)) return false; - out = TriangleMesh(std::move(remeshed)); + out = std::move(remeshed); return true; } void GLGizmoTextureDisplacement::remesh_model() { - ModelVolume *mv = texture_volume(); - ModelObject *mo = m_c->selection_info()->model_object(); - if (mv == nullptr || mo == nullptr || m_remesh_target_edge_mm <= 0.f) + if (texture_volume() == nullptr || m_remesh_target_edge_mm <= 0.f) return; - TriangleMesh remeshed; - if (!plan_remesh(*mv, m_remesh_target_edge_mm, m_remesh_keep_sharp_edges ? m_remesh_sharp_angle_deg : 0.f, - remeshed)) { - show_error(nullptr, _u8L("Remeshing did not change the model. It may be non-manifold (open edges or " - "edges shared by more than two triangles), or the target edge length may " - "already be met.")); - return; - } + update_model_object(); // the paint rides across the remesh, so flush any in-progress stroke first - Plater *plater = wxGetApp().plater(); - Plater::TakeSnapshot snapshot(plater, _u8L("Remesh model for texture displacement"), UndoRedo::SnapshotType::GizmoAction); - replace_mesh_keep_all_paint(*mv, std::move(remeshed)); - - if (ObjectList *obj_list = wxGetApp().obj_list()) { - const ModelObjectPtrs &objs = plater->model().objects; - auto it = std::find(objs.begin(), objs.end(), mo); - if (it != objs.end()) - obj_list->update_info_items(size_t(it - objs.begin())); - } - plater->changed_object(*mo); - update_from_model_object(false); - m_parent.set_as_dirty(); + TextureDisplacementPrepareParams params; + params.remesh_edge_mm = m_remesh_target_edge_mm; + params.remesh_sharp_deg = m_remesh_keep_sharp_edges ? m_remesh_sharp_angle_deg : 0.f; + queue_prepare(params, _u8L("Remesh model for texture displacement"), /* then_bake */ false, + _u8L("Remeshing did not change the model. It may be non-manifold (open edges or edges " + "shared by more than two triangles), or the target edge length may already be met.")); } void GLGizmoTextureDisplacement::rebuild_subdivide_preview() @@ -3419,16 +3549,13 @@ void GLGizmoTextureDisplacement::rebuild_subdivide_preview() if (m_subdivide_adaptive) { if (m_subdivide_target_mm <= 0.f) return; - std::vector region; - if (!collect_paint_region(region, nullptr)) + const TextureDisplacementFacetsData facets = facets_data_of(*mv); + std::vector region; + if (!collect_paint_region(mv->mesh(), facets, region, nullptr)) return; // nothing painted yet: nothing to preview HeightFieldSampler sampler; - if (m_subdivide_feature) { - TextureDisplacementFacetsData facets{}; - for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) - facets[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + if (m_subdivide_feature) sampler = make_combined_displacement_sampler(mv->mesh().its, mv->texture_displacement_layers, facets); - } // Feature mode: curvature (Detail tolerance) on top of the Max-edge baseline, down to the // Min-edge floor. Plain adaptive: tol 0, so only the target-edge-length criterion applies. const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f; @@ -3605,8 +3732,7 @@ bool GLGizmoTextureDisplacement::apply_standard_mode_presets(ModelVolume *mv) void GLGizmoTextureDisplacement::bake_standard() { ModelVolume *mv = texture_volume(); - ModelObject *mo = m_c->selection_info()->model_object(); - if (mv == nullptr || mo == nullptr || m_bake_in_progress) + if (mv == nullptr || m_bake_in_progress || m_prepare_in_progress) return; update_model_object(); // flush the active layer's in-progress strokes before anything reads them @@ -3616,63 +3742,70 @@ void GLGizmoTextureDisplacement::bake_standard() } apply_standard_mode_presets(mv); // belt and braces: never bake with values the panel is not showing - // Both stages are planned before the snapshot so a stage that has nothing to do is simply skipped. - // Skipping is normal, not a failure: a mesh that is already even needs no remesh, and one that is - // already fine enough for the texture needs no subdivision. - TriangleMesh remeshed; - const bool do_remesh = plan_remesh(*mv, STD_REMESH_EDGE_MM, STD_REMESH_SHARP_DEG, remeshed); + // The whole recipe in one go. Either stage having nothing to do is normal, not a failure - a mesh + // that is already even needs no remesh, one that is already fine enough for the texture needs no + // subdivision - so no "nothing changed" message here: it goes straight on to the displacement. + TextureDisplacementPrepareParams params; + params.remesh_edge_mm = STD_REMESH_EDGE_MM; + params.remesh_sharp_deg = STD_REMESH_SHARP_DEG; + params.subdiv_target_mm = STD_SUBDIV_MAX_EDGE_MM; + params.subdiv_detail_mm = STD_SUBDIV_DETAIL_MM; + params.subdiv_min_edge_mm = STD_SUBDIV_MIN_EDGE_MM; + params.subdiv_border_mm = STD_SUBDIV_BORDER_MM; + params.subdiv_feature = true; + params.subdiv_added_triangles = m_subdivide_budget_k * 1000; + queue_prepare(params, _u8L("Bake texture displacement"), /* then_bake */ true, {}); +} - Plater *plater = wxGetApp().plater(); - bool paint_transfer_failed = false; - { - // ONE undo step for the whole pipeline. take_snapshot() records the state *before* the change, - // so a single Undo goes all the way back to the untouched mesh - which is the only thing "undo - // the bake" can sensibly mean when the bake is also what prepared the geometry. The background - // displacement job is told not to add its own (see queue_texture_displacement_bake's - // take_snapshot), because an undo step landing between the subdivision and the displacement - // leaves a mesh with 1.5 M extra triangles and no relief on it - and baking again from there - // subdivides that mesh a second time. - Plater::TakeSnapshot snapshot(plater, _u8L("Bake texture displacement"), - UndoRedo::SnapshotType::GizmoAction); - if (do_remesh) - replace_mesh_keep_all_paint(*mv, std::move(remeshed)); - - // The remesh carries the paint across spatially, but if that remap came back empty the rest of - // the pipeline has nothing to work from - stop here rather than silently baking a flat mesh. - // Note this cannot just `return`: the remesh above has already replaced the volume's mesh, so - // the scene and the gizmo's own TriangleSelectors still have to be brought back into step with - // it below. Returning from here left the gizmo painting and raycasting against a mesh that no - // longer existed. - paint_transfer_failed = !mv->is_texture_displacement_painted(); - - // Planning the subdivision has to happen inside the snapshot because it reads the mesh the - // remesh just produced. It is the expensive step, but by here we are committed anyway. - if (!paint_transfer_failed) { - SubdivisionPlan plan; - if (plan_adaptive_subdivision(*mv, plan)) - apply_adaptive_subdivision(*mv, std::move(plan)); - } - } - - if (ObjectList *obj_list = wxGetApp().obj_list()) { - const ModelObjectPtrs &objs = plater->model().objects; - auto it = std::find(objs.begin(), objs.end(), mo); - if (it != objs.end()) - obj_list->update_info_items(size_t(it - objs.begin())); - } - plater->changed_object(*mo); - update_from_model_object(false); // reload selectors against the prepared mesh + carried paint - m_parent.set_as_dirty(); - - if (paint_transfer_failed) { - show_error(nullptr, _u8L("The painted area could not be transferred onto the remeshed model. Undo, " - "then switch to Pro mode to prepare the mesh before painting.")); +void GLGizmoTextureDisplacement::queue_prepare(const TextureDisplacementPrepareParams ¶ms, + const std::string &snapshot_name, bool then_bake, + const std::string &unchanged_msg) +{ + ModelVolume *mv = texture_volume(); + if (mv == nullptr || m_prepare_in_progress || m_bake_in_progress) return; - } - // ... and finally the displacement itself, in the background exactly as the Pro-mode button does - - // except that it commits into the snapshot taken above instead of pushing another one. - bake(/* own_snapshot */ false); + TextureDisplacementPrepareInput input; + input.volume_id = mv->id(); + input.base_mesh = mv->mesh().its; + input.masks = facets_data_of(*mv); + input.layers = mv->texture_displacement_layers; + input.params = params; + input.snapshot_name = snapshot_name; + + m_prepare_in_progress = true; + queue_texture_displacement_prepare(std::move(input), [this, then_bake, unchanged_msg]( + TextureDisplacementPrepareOutcome outcome) { + m_prepare_in_progress = false; + // The commit replaced the volume's mesh (new id, new topology) without changing the object's id + // or volume count, which is not something GLGizmoPainterBase::data_changed() can detect - so the + // reload is explicit, exactly as it is after a bake. Done for every outcome: even a run that + // committed nothing may have left the panel showing a stale triangle count. + if (m_state == On && m_c->selection_info() && m_c->selection_info()->model_object()) + update_from_model_object(false); + m_parent.set_as_dirty(); + + switch (outcome) { + case TextureDisplacementPrepareOutcome::Failed: + return; // cancelled, or the volume went away while the job ran - say nothing, do nothing + case TextureDisplacementPrepareOutcome::PaintLost: + show_error(nullptr, _u8L("The painted area could not be carried onto the remeshed model, so " + "nothing was changed. Switch to Pro mode and remesh before painting.")); + return; + case TextureDisplacementPrepareOutcome::Unchanged: + if (!unchanged_msg.empty()) + show_error(nullptr, unchanged_msg); + break; + case TextureDisplacementPrepareOutcome::Committed: + break; + } + // ... and then the displacement itself, in the background exactly as the Pro-mode button does. + // It commits into the snapshot the prepare opened - but only if the prepare opened one: a run + // that found nothing to do took none, and a bake chained onto that has to push its own or it + // would not be undoable at all. + if (then_bake) + bake(/* own_snapshot */ outcome == TextureDisplacementPrepareOutcome::Unchanged); + }); } void GLGizmoTextureDisplacement::render_paint_cursor_hint() @@ -4686,7 +4819,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "commit it, or Done to leave the model as it is."), m_imgui->scaled(20.f)); } else { - m_imgui->disabled_begin(!subdivide_ready); + m_imgui->disabled_begin(!subdivide_ready || m_prepare_in_progress || m_bake_in_progress); if (m_imgui->button(_u8L("Apply"))) { if (m_subdivide_adaptive) { subdivide_model_adaptive(); // refines only the painted area, carrying the paint forward @@ -4752,7 +4885,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->scaled(20.f)); } - m_imgui->disabled_begin(mv == nullptr); + m_imgui->disabled_begin(mv == nullptr || m_prepare_in_progress || m_bake_in_progress); if (m_imgui->button(_u8L("Remesh"))) remesh_model(); m_imgui->disabled_end(); @@ -4783,8 +4916,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->disabled_end(); ImGui::SameLine(); - m_imgui->disabled_begin(m_bake_in_progress || mv == nullptr || !mv->is_texture_displacement_painted()); - if (m_imgui->button(m_bake_in_progress ? _L("Baking...") : m_desc.at("bake"))) { + m_imgui->disabled_begin(m_bake_in_progress || m_prepare_in_progress || mv == nullptr || + !mv->is_texture_displacement_painted()); + if (m_imgui->button(m_prepare_in_progress ? _L("Preparing...") : + m_bake_in_progress ? _L("Baking...") : + m_desc.at("bake"))) { // Standard mode's Bake is the whole pipeline (remesh -> refine -> displace); Pro's is only the // displacement, because there the user has already prepared the mesh with the controls above. if (pro_mode()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 7874f2656f..92461fdd1b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -32,6 +32,24 @@ class GLGizmoTextureDisplacement : public GLGizmoPainterBase public: GLGizmoTextureDisplacement(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + // The whole of mesh preparation - remesh, carry the paint across, refine where the texture bends - + // as one pure function over plain data: no ModelVolume, no Model, no GUI, no undo. That is what lets + // TextureDisplacementPrepareJob run it on the job worker instead of on the UI thread, where CGAL's + // remesher and a several-hundred-thousand-triangle refinement together freeze the window for tens + // of seconds with nothing to look at and no way to cancel. + // + // `progress` is called with 0..100 and aborts the run when it returns false; an aborted run reports + // an empty result. An empty result is also how "nothing needed doing" is reported - see + // TextureDisplacementPrepareResult. + static TextureDisplacementPrepareResult prepare_mesh(const indexed_triangle_set &base, + const TextureDisplacementFacetsData &masks, + const std::vector &layers, + const TextureDisplacementPrepareParams ¶ms, + const DisplacementProgressFn &progress); + // The volume's eight texture-displacement masks, gathered into the array every pure function here + // (and every job input) takes. + static TextureDisplacementFacetsData facets_data_of(const ModelVolume &mv); + void render_painter_gizmo() override; // Intercepts mouse input while "Adjust Texture" mode is on (dragging the on-canvas offset/ @@ -88,20 +106,26 @@ private: // Standard mode's Bake: remesh to an even density, refine where the texture bends, then displace. // The order matters and is the whole reason this is one button - a height map can only move // existing vertices, so the mesh has to be prepared first, and remeshing after painting would drop - // the paint if it were not remapped across (see replace_mesh_keep_all_paint()). + // the paint if it were not carried across (see prepare_mesh()). void bake_standard(); - // Replaces `mv`'s mesh and carries *every* paint channel onto it, texture displacement included - - // the four standard channels via ModelVolume::restore_painting(), the eight texture-displacement - // masks via the same TriangleSelector::remap_painting() spatial remap, which restore_painting() - // does not cover. Shared by Remesh and by Standard mode's pipeline. - static void replace_mesh_keep_all_paint(ModelVolume &mv, TriangleMesh &&new_mesh); - // Remesh and adaptive Subdivide are each split into a *plan* (the heavy geometry work, touching - // nothing) and an *apply* (pure mutation). That split is what lets both the standalone buttons and - // Standard mode's one-button pipeline run the expensive part **before** taking the undo snapshot, - // so a run that turns out to be a no-op does not leave an empty undo step behind - and it keeps - // snapshot ownership with the caller, which matters because the buttons want one snapshot per click - // while the pipeline wants a single one around remesh + subdivide together. + // Queues one prepare_mesh() run on the job worker and commits its result when it lands. Every + // mesh-preparation button goes through here - Pro's Remesh, Pro's adaptive Subdivide and Standard's + // Bake differ only in which stages `params` enables and in what happens afterwards: + // - `snapshot_name` is the single undo step the commit opens. With `then_bake` it is also the step + // the displacement job that follows commits into, rather than pushing its own - an undo landing + // between the two would leave a mesh carrying every added triangle and no relief on it, and + // baking again from there would prepare it a second time. + // - `unchanged_msg`, when not empty, is shown if the run had nothing to do. Standard's Bake passes + // nothing: a mesh that already meets the criteria is not an error there, it just goes straight + // on to the displacement. + void queue_prepare(const TextureDisplacementPrepareParams ¶ms, const std::string &snapshot_name, + bool then_bake, const std::string &unchanged_msg); + // Set from queue_prepare() until its job's result has been committed. Distinct from + // m_bake_in_progress because Standard's Bake sets both in turn, and because every button that would + // read or replace the mesh has to stay disabled for the whole of it. + bool m_prepare_in_progress = false; + // How one layer's paint sits on the pre-subdivision mesh, precise enough to carry across the // refinement without rounding each source triangle to wholly painted or not. // @@ -117,17 +141,16 @@ private: std::vector> part; // painted pieces of partly covered source triangles bool empty() const { return full.empty(); } }; - struct SubdivisionPlan - { - indexed_triangle_set refined; - std::vector source; // new tri -> input tri - std::array paint; // per layer - }; - // False means "nothing to refine" and `out` must not be used. - bool plan_adaptive_subdivision(const ModelVolume &mv, SubdivisionPlan &out) const; - static void apply_adaptive_subdivision(ModelVolume &mv, SubdivisionPlan &&plan); - // False means the remesh failed or changed nothing (CGAL signals failure by handing the input back). - static bool plan_remesh(const ModelVolume &mv, float target_edge_mm, float sharp_angle_deg, TriangleMesh &out); + // Rebuilds every layer's mask on a subdivided mesh from `source` (new triangle -> the input triangle + // it descends from) and the pre-subdivision coverage in `paint`. + static TextureDisplacementFacetsData masks_after_subdivision( + const TriangleMesh &new_mesh, const std::vector &source, + const std::array &paint); + // False means the remesh failed or changed nothing (CGAL signals failure by handing the input back), + // and `out` must not be used. `target_edge_mm` is a request rather than a promise: it is clamped + // against the part's surface area first, because CGAL's cost grows with the square of 1/target. + static bool plan_remesh(const indexed_triangle_set &src, float target_edge_mm, float sharp_angle_deg, + indexed_triangle_set &out); // Marks every facet of every model-part volume as painted for the currently active layer - // "whole model" as an alternative to brushing/clicking every triangle by hand. @@ -417,8 +440,9 @@ private: // painted area plus the band straddling its edge. If `paint` is non-null, also fills the per-layer // coverage map the subdivision carries forward - the expensive half, skipped by the live preview, // which only needs the region. Returns false when nothing is painted at all. - bool collect_paint_region(std::vector ®ion, - std::array *paint) const; + static bool collect_paint_region(const TriangleMesh &mesh, const TextureDisplacementFacetsData &facets, + std::vector ®ion, + std::array *paint); // Runs the volume's TextureDisplacementOptions smoothing over the *already committed* geometry, // restricted to the painted area. The same settings are folded into Preview/Bake automatically; @@ -430,9 +454,9 @@ private: // Isotropic remeshing (CGAL) to even out wildly varying triangle sizes so displacement has a // consistent density to work with. Target edge length in mm; 0 means "not yet initialised", filled // with the mesh's mean edge length the first time the control is shown. Like subdivide, it replaces - // the geometry, but unlike subdivide it keeps every paint channel: replace_mesh_keep_all_paint() - // remaps the texture-displacement masks spatially, which is also what lets Standard mode remesh - // *after* the user has painted. + // the geometry, but unlike subdivide it keeps every paint channel: prepare_mesh() carries the + // texture-displacement masks across spatially, which is also what lets Standard mode remesh *after* + // the user has painted. float m_remesh_target_edge_mm = 0.f; // Dihedral angle above which an edge counts as a hard feature and is held fixed by the remesher. // Off by default would round every sharp edge off, so this is on; 0 disables the protection. diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp new file mode 100644 index 0000000000..5b6cdd246a --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp @@ -0,0 +1,117 @@ +#include "TextureDisplacementPrepareJob.hpp" + +#include +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/TriangleSelector.hpp" + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" +#include "slic3r/Utils/UndoRedo.hpp" + +namespace Slic3r::GUI { + +TextureDisplacementPrepareJob::TextureDisplacementPrepareJob( + TextureDisplacementPrepareInput &&input, std::function on_finished) + : m_input(std::move(input)), m_on_finished(std::move(on_finished)) +{ +} + +void TextureDisplacementPrepareJob::process(Ctl &ctl) +{ + const std::string status = _u8L("Preparing mesh for texture displacement"); + ctl.update_status(1, status); + + // Only ever touches m_input (captured by value before this job was queued) and local state - never + // the live Model - so this is safe to run concurrently with the UI thread. + // + // The progress hook is not just cosmetic: the framework's notification only grows a close button + // once it reaches 100%, and below that it shows Cancel, which is wired through here. Reporting is + // throttled to whole percentage points because every call repaints the notification and wakes the + // idle loop. + int last_reported = 1; + m_result = GLGizmoTextureDisplacement::prepare_mesh(m_input.base_mesh, m_input.masks, m_input.layers, + m_input.params, + [&ctl, &status, &last_reported](int percent) { + if (ctl.was_canceled()) + return false; + if (percent > last_reported) { + last_reported = percent; + ctl.update_status(percent, status); + } + return true; + }); + + ctl.update_status(100, status); // always finish at 100: this is what closes the notification +} + +void TextureDisplacementPrepareJob::finalize(bool canceled, std::exception_ptr &eptr) +{ + TextureDisplacementPrepareOutcome outcome = TextureDisplacementPrepareOutcome::Failed; + struct OnExit + { + std::function fn; + const TextureDisplacementPrepareOutcome *outcome; + ~OnExit() { if (fn) fn(*outcome); } + } on_exit{m_on_finished, &outcome}; + + if (canceled || eptr) + return; + if (m_result.paint_lost) { + outcome = TextureDisplacementPrepareOutcome::PaintLost; + return; + } + if (m_result.mesh.indices.empty()) { + outcome = TextureDisplacementPrepareOutcome::Unchanged; + return; + } + + Plater *plater = wxGetApp().plater(); + ModelVolume *volume = get_model_volume(m_input.volume_id, plater->model().objects); + // The lookup doubles as a staleness check: anything that replaces a volume's mesh also gives it a + // new id (set_new_unique_id()), so a prepare computed from a mesh that has since been replaced - + // by an undo, another bake, or a boolean - simply fails to find its volume and commits nothing. + if (volume == nullptr) + return; + ModelObject *object = volume->get_object(); + if (object == nullptr) + return; + + { + Plater::TakeSnapshot snapshot(plater, m_input.snapshot_name, UndoRedo::SnapshotType::GizmoAction); + + // The four standard paint channels ride across on ModelVolume's own spatial remap. The eight + // texture-displacement masks do not go through it - prepare_mesh() has already carried them, + // exactly, from the source triangles the refinement records - so they are put back after + // restore_painting(), which resets every extra facet before remapping the channels it knows. + std::optional saved_painting = volume->save_painting(); + volume->set_mesh(TriangleMesh(std::move(m_result.mesh))); + volume->set_new_unique_id(); + volume->calculate_convex_hull(); + volume->restore_painting(saved_painting); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + volume->texture_displacement_facet(i).set_data(std::move(m_result.masks[size_t(i)])); + + if (ObjectList *obj_list = wxGetApp().obj_list()) { + const ModelObjectPtrs &objs = plater->model().objects; + auto it = std::find(objs.begin(), objs.end(), object); + if (it != objs.end()) + obj_list->update_info_items(size_t(it - objs.begin())); + } + plater->changed_object(*object); + } + outcome = TextureDisplacementPrepareOutcome::Committed; +} + +void queue_texture_displacement_prepare(TextureDisplacementPrepareInput &&input, + std::function on_finished) +{ + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::make_unique(std::move(input), std::move(on_finished))); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp new file mode 100644 index 0000000000..90d723d88f --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp @@ -0,0 +1,72 @@ +#ifndef slic3r_TextureDisplacementPrepareJob_hpp_ +#define slic3r_TextureDisplacementPrepareJob_hpp_ + +#include +#include +#include + +#include "Job.hpp" + +#include "libslic3r/ObjectID.hpp" +#include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TriangleMesh.hpp" + +namespace Slic3r { +class ModelVolume; +} + +namespace Slic3r::GUI { + +// Getting a mesh ready to receive displacement - the isotropic remesh, carrying the paint onto it, and +// the adaptive refinement - off the UI thread. +// +// All three used to run inline behind a wxBusyCursor, which on any part big enough to matter meant tens +// of seconds with the window not repainting and no way to stop it: CGAL's remesher is single threaded +// and its cost grows with the square of 1/target_edge, and the refinement that follows spends a budget +// of hundreds of thousands of triangles. Indistinguishable from a hang, and reported as one. +// +// The work itself is GLGizmoTextureDisplacement::prepare_mesh(), which is pure - it takes an +// indexed_triangle_set and the layers' masks and returns new ones, touching no Model and no GUI - so all +// this job adds is the worker thread, the progress notification with its Cancel button, and the commit. +enum class TextureDisplacementPrepareOutcome +{ + Committed, // the volume now carries the prepared mesh and the paint carried onto it + Unchanged, // nothing needed doing - the mesh already met the criteria; the model was not touched + PaintLost, // the remesh landed but no layer's paint survived it, so nothing was committed + Failed, // cancelled, threw, or the volume went away while the job ran +}; + +struct TextureDisplacementPrepareInput +{ + ObjectID volume_id; + indexed_triangle_set base_mesh; + TextureDisplacementFacetsData masks; + std::vector layers; + TextureDisplacementPrepareParams params; + // The undo step the commit opens. Standard mode's Bake names it after the bake, because the + // displacement job that follows commits into this same step rather than pushing its own. + std::string snapshot_name; +}; + +class TextureDisplacementPrepareJob : public Job +{ + TextureDisplacementPrepareInput m_input; + TextureDisplacementPrepareResult m_result; + std::function m_on_finished; + +public: + TextureDisplacementPrepareJob(TextureDisplacementPrepareInput &&input, + std::function on_finished); + + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; +}; + +// `on_finished` runs on the UI thread once the result has been committed (or found not to need +// committing), and always runs exactly once. +void queue_texture_displacement_prepare(TextureDisplacementPrepareInput &&input, + std::function on_finished); + +} // namespace Slic3r::GUI + +#endif // slic3r_TextureDisplacementPrepareJob_hpp_ From ae45d7b78a9715e5eea0b45f189c2f6eff7e3e4f Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Mon, 31 Aug 2026 07:58:48 +0200 Subject: [PATCH 22/89] Add color suport for textures --- .../shaders/110/texture_displacement_bump.fs | 69 ++- .../shaders/140/texture_displacement_bump.fs | 69 ++- src/libslic3r/TextureDisplacement.cpp | 519 ++++++++++++++++-- src/libslic3r/TextureDisplacement.hpp | 200 ++++++- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 467 +++++++++++++++- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 91 +++ .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 43 +- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 12 +- .../Jobs/TextureDisplacementPrepareJob.cpp | 2 +- .../Jobs/TextureDisplacementPrepareJob.hpp | 5 + .../Jobs/TextureDisplacementPreviewJob.cpp | 24 +- .../Jobs/TextureDisplacementPreviewJob.hpp | 18 +- src/slic3r/GUI/TextureLibrary.cpp | 75 ++- tests/libslic3r/test_texture_displacement.cpp | 213 +++++++ 14 files changed, 1728 insertions(+), 79 deletions(-) diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index fe0e32ef96..4686668467 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -21,6 +21,15 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); const vec3 ZERO = vec3(0.0, 0.0, 0.0); uniform vec4 uniform_color; +// The printable palette, in **CIELAB** as well as RGB, and how many entries are real. Lab because the +// match has to be perceptual - the same reason the CPU side uses CIEDE2000 - and converting the +// palette once on the CPU is what lets the fragment shader match with a plain squared distance. +// Count 0 means nothing is colouring, and every fragment falls back to uniform_color as before. +uniform vec3 palette_lab[64]; +uniform vec3 palette_rgb[64]; +uniform int palette_count; +uniform sampler2D color_tex; // the layer's colour image, sampled at the same uv as the height +uniform bool has_color_tex; uniform bool volume_mirrored; uniform mat4 view_model_matrix; @@ -79,6 +88,47 @@ vec2 project_uv(vec3 p, vec3 n) return r + uv_offset; } +// sRGB -> CIELAB, matching slic3r/Utils/ColorSpaceConvert's RGB2Lab so this picks the same entry the +// bake does. +vec3 srgb_to_lab(vec3 c) +{ + vec3 v = vec3(c.r > 0.04045 ? pow((c.r + 0.055) / 1.055, 2.4) : c.r / 12.92, + c.g > 0.04045 ? pow((c.g + 0.055) / 1.055, 2.4) : c.g / 12.92, + c.b > 0.04045 ? pow((c.b + 0.055) / 1.055, 2.4) : c.b / 12.92); + vec3 xyz = vec3(dot(v, vec3(0.4124, 0.3576, 0.1805)) / 0.95047, + dot(v, vec3(0.2126, 0.7152, 0.0722)), + dot(v, vec3(0.0193, 0.1192, 0.9505)) / 1.08883); + vec3 f = vec3(xyz.x > 0.008856 ? pow(xyz.x, 1.0 / 3.0) : (7.787 * xyz.x) + 16.0 / 116.0, + xyz.y > 0.008856 ? pow(xyz.y, 1.0 / 3.0) : (7.787 * xyz.y) + 16.0 / 116.0, + xyz.z > 0.008856 ? pow(xyz.z, 1.0 / 3.0) : (7.787 * xyz.z) + 16.0 / 116.0); + return vec3(116.0 * f.y - 16.0, 500.0 * (f.x - f.y), 200.0 * (f.y - f.z)); +} + +// Nearest printable colour to a sampled one. Quantizing per *fragment* rather than per facet is the +// whole point of this path: it shows the image at the texture's resolution instead of the mesh's, +// which is what you need while choosing a texture and placing it. The Normal view is where the +// facet-resolution truth - what actually bakes - is shown. +// +// Squared distance in Lab (CIE76) rather than the CPU's CIEDE2000: the two agree except on near-ties, +// and CIEDE2000 per fragment across 64 entries is not worth its cost in a preview. +vec3 quantize_to_palette(vec3 rgb) +{ + vec3 lab = srgb_to_lab(rgb); + int best = 0; + float bd = 1.0e20; + for (int i = 0; i < 64; ++i) { + if (i >= palette_count) + break; + vec3 d = lab - palette_lab[i]; + float d2 = dot(d, d); + if (d2 < bd) { + bd = d2; + best = i; + } + } + return palette_rgb[best]; +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -88,6 +138,12 @@ void main() if (volume_mirrored) triangle_normal = -triangle_normal; + // Where the colour is read from. Both branches below already compute the uv this fragment's + // *height* came from - including the parallax-marched one on the triplanar path - and the colour + // has to follow it exactly, or the colour would slide off the relief as the camera orbits. + vec2 color_uv = vec2(0.0); + bool have_uv = false; + if (use_vertex_uv) { // Mikkelsen surface-gradient bump; see the 140 variant for the full rationale. Scale-exact // for a conformal LSCM map (no global 1/tiling assumption), and gated by the paint weight @@ -95,6 +151,8 @@ void main() vec2 uv = (island_active > 0.5) ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr : vertex_uv; + color_uv = uv; + have_uv = true; float h = texture2D(height_tex, uv).r; float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); vec3 sigmaS = dFdx(model_pos.xyz); @@ -151,6 +209,9 @@ void main() } } + color_uv = uv; // after the parallax march, so colour and relief stay registered + have_uv = true; + float hL = texture2D(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; float hR = texture2D(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; float hD = texture2D(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; @@ -183,5 +244,11 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + // Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour. + // Only the albedo - the specular term (intensity.y) stays white - so a coloured fragment reads as + // the same material under the same light, and the relief this preview exists to show is unaffected. + vec3 albedo = uniform_color.rgb; + if (palette_count > 0 && has_color_tex && have_uv && weight > 0.0) + albedo = quantize_to_palette(texture2D(color_tex, color_uv).rgb); + gl_FragColor = vec4(vec3(intensity.y) + albedo * intensity.x, uniform_color.a); } diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 351452f0e9..4f8e3c8186 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -80,6 +80,15 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); const vec3 ZERO = vec3(0.0, 0.0, 0.0); uniform vec4 uniform_color; +// The printable palette, in **CIELAB** as well as RGB, and how many entries are real. Lab because the +// match has to be perceptual - the same reason the CPU side uses CIEDE2000 - and converting the +// palette once on the CPU is what lets the fragment shader match with a plain squared distance. +// Count 0 means nothing is colouring, and every fragment falls back to uniform_color as before. +uniform vec3 palette_lab[64]; +uniform vec3 palette_rgb[64]; +uniform int palette_count; +uniform sampler2D color_tex; // the layer's colour image, sampled at the same uv as the height +uniform bool has_color_tex; uniform bool volume_mirrored; uniform mat4 view_model_matrix; @@ -145,6 +154,47 @@ vec2 project_uv(vec3 p, vec3 n) return r + uv_offset; } +// sRGB -> CIELAB, matching slic3r/Utils/ColorSpaceConvert's RGB2Lab so this picks the same entry the +// bake does. +vec3 srgb_to_lab(vec3 c) +{ + vec3 v = vec3(c.r > 0.04045 ? pow((c.r + 0.055) / 1.055, 2.4) : c.r / 12.92, + c.g > 0.04045 ? pow((c.g + 0.055) / 1.055, 2.4) : c.g / 12.92, + c.b > 0.04045 ? pow((c.b + 0.055) / 1.055, 2.4) : c.b / 12.92); + vec3 xyz = vec3(dot(v, vec3(0.4124, 0.3576, 0.1805)) / 0.95047, + dot(v, vec3(0.2126, 0.7152, 0.0722)), + dot(v, vec3(0.0193, 0.1192, 0.9505)) / 1.08883); + vec3 f = vec3(xyz.x > 0.008856 ? pow(xyz.x, 1.0 / 3.0) : (7.787 * xyz.x) + 16.0 / 116.0, + xyz.y > 0.008856 ? pow(xyz.y, 1.0 / 3.0) : (7.787 * xyz.y) + 16.0 / 116.0, + xyz.z > 0.008856 ? pow(xyz.z, 1.0 / 3.0) : (7.787 * xyz.z) + 16.0 / 116.0); + return vec3(116.0 * f.y - 16.0, 500.0 * (f.x - f.y), 200.0 * (f.y - f.z)); +} + +// Nearest printable colour to a sampled one. Quantizing per *fragment* rather than per facet is the +// whole point of this path: it shows the image at the texture's resolution instead of the mesh's, +// which is what you need while choosing a texture and placing it. The Normal view is where the +// facet-resolution truth - what actually bakes - is shown. +// +// Squared distance in Lab (CIE76) rather than the CPU's CIEDE2000: the two agree except on near-ties, +// and CIEDE2000 per fragment across 64 entries is not worth its cost in a preview. +vec3 quantize_to_palette(vec3 rgb) +{ + vec3 lab = srgb_to_lab(rgb); + int best = 0; + float bd = 1.0e20; + for (int i = 0; i < 64; ++i) { + if (i >= palette_count) + break; + vec3 d = lab - palette_lab[i]; + float d2 = dot(d, d); + if (d2 < bd) { + bd = d2; + best = i; + } + } + return palette_rgb[best]; +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -154,6 +204,12 @@ void main() if (volume_mirrored) triangle_normal = -triangle_normal; + // Where the colour is read from. Both branches below already compute the uv this fragment's + // *height* came from - including the parallax-marched one on the triplanar path - and the colour + // has to follow it exactly, or the colour would slide off the relief as the camera orbits. + vec2 color_uv = vec2(0.0); + bool have_uv = false; + if (use_vertex_uv) { // Precomputed-uv (LSCM) path - Mikkelsen's surface-gradient bump ("Bump Mapping // Unparametrized Surfaces on the GPU"). The perturbed normal is derived straight from the @@ -171,6 +227,8 @@ void main() vec2 uv = (island_active > 0.5) ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr : vertex_uv; + color_uv = uv; + have_uv = true; float h = texture(height_tex, uv).r; float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); vec3 sigmaS = dFdx(model_pos.xyz); @@ -230,6 +288,9 @@ void main() } } + color_uv = uv; // after the parallax march, so colour and relief stay registered + have_uv = true; + float hL = texture(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r; float hR = texture(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r; float hD = texture(height_tex, uv - vec2(0.0, height_tex_texel.y)).r; @@ -267,5 +328,11 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - out_color = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); + // Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour. + // Only the albedo - the specular term (intensity.y) stays white - so a coloured fragment reads as + // the same material under the same light, and the relief this preview exists to show is unaffected. + vec3 albedo = uniform_color.rgb; + if (palette_count > 0 && has_color_tex && have_uv && weight > 0.0) + albedo = quantize_to_palette(texture(color_tex, color_uv).rgb); + out_color = vec4(vec3(intensity.y) + albedo * intensity.x, uniform_color.a); } diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index e1a03a9eaf..74485c7919 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -23,10 +23,11 @@ namespace Slic3r { -float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTileMethod tile_method) const +bool DecodedHeightTexture::texel_tap(const Vec2f &uv, bool tile_enabled, TextureTileMethod tile_method, + TexelTap &tap) const { if (empty()) - return 0.f; + return false; auto repeat01 = [](float x) { x = std::fmod(x, 1.f); @@ -43,7 +44,7 @@ float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTi // Outside the single, non-repeating placement entirely: no texture there, not "smeared // edge pixel" - clamping the *coordinate* to [0, 1] would otherwise keep returning the // border row/column's height forever in every direction, stretching it out to infinity. - return 0.f; + return false; float u, v; if (!tile_enabled) { @@ -59,14 +60,26 @@ float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTi const float fx = u * float(width); const float fy = v * float(height); - int x0 = std::clamp(int(std::floor(fx)), 0, width - 1); - int y0 = std::clamp(int(std::floor(fy)), 0, height - 1); + const int x0 = std::clamp(int(std::floor(fx)), 0, width - 1); + const int y0 = std::clamp(int(std::floor(fy)), 0, height - 1); // Neighbour for bilinear filtering: wrap for tiling methods, clamp at the edge otherwise (a // repeating neighbour would incorrectly blend against the opposite edge of the image). - const int x1 = tile_enabled ? (x0 + 1) % width : std::min(x0 + 1, width - 1); - const int y1 = tile_enabled ? (y0 + 1) % height : std::min(y0 + 1, height - 1); - const float tx = fx - std::floor(fx); - const float ty = fy - std::floor(fy); + tap.x0 = x0; + tap.y0 = y0; + tap.x1 = tile_enabled ? (x0 + 1) % width : std::min(x0 + 1, width - 1); + tap.y1 = tile_enabled ? (y0 + 1) % height : std::min(y0 + 1, height - 1); + tap.tx = fx - std::floor(fx); + tap.ty = fy - std::floor(fy); + return true; +} + +float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTileMethod tile_method) const +{ + TexelTap tap; + if (!texel_tap(uv, tile_enabled, tile_method, tap)) + return 0.f; + const int x0 = tap.x0, y0 = tap.y0, x1 = tap.x1, y1 = tap.y1; + const float tx = tap.tx, ty = tap.ty; auto at = [this](int x, int y) { return float(pixels[size_t(y) * size_t(width) + size_t(x)]) / 255.f; }; const float top = at(x0, y0) * (1.f - tx) + at(x1, y0) * tx; @@ -74,6 +87,21 @@ float DecodedHeightTexture::sample(const Vec2f &uv, bool tile_enabled, TextureTi return top * (1.f - ty) + bottom * ty; } +Vec3f DecodedHeightTexture::sample_color(const Vec2f &uv, bool tile_enabled, TextureTileMethod tile_method) const +{ + TexelTap tap; + if (!has_color() || !texel_tap(uv, tile_enabled, tile_method, tap)) + return Vec3f::Zero(); + + auto at = [this](int x, int y) { + const size_t i = (size_t(y) * size_t(width) + size_t(x)) * 3; + return Vec3f(float(rgb[i]) / 255.f, float(rgb[i + 1]) / 255.f, float(rgb[i + 2]) / 255.f); + }; + const Vec3f top = at(tap.x0, tap.y0) * (1.f - tap.tx) + at(tap.x1, tap.y0) * tap.tx; + const Vec3f bottom = at(tap.x0, tap.y1) * (1.f - tap.tx) + at(tap.x1, tap.y1) * tap.tx; + return top * (1.f - tap.ty) + bottom * tap.ty; +} + namespace { // Decoding a PNG (zlib inflate + defilter) is real work, and image_data never changes in place // once assigned to a layer (a new texture always gets a brand new image_data), so the decoded @@ -156,18 +184,40 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer if (!have_raw) { const png::ReadBuf rbuf{ layer.image_data->data(), layer.image_data->size() }; if (!png::is_png(rbuf)) - // Only 8-bit grayscale PNG height maps are supported. The GUI is responsible for - // converting any imported image (jpg, color png, ...) to that format on import, so this - // code never needs a dependency on wxWidgets/libjpeg to decode arbitrary user images. + // PNG only. The GUI converts any other imported format (jpg, bmp, ...) on import, so this + // code needs no dependency on wxWidgets/libjpeg to read arbitrary user images. return result; png::ImageGreyscale img; - if (!png::decode_png(rbuf, img) || img.cols == 0 || img.rows == 0) - return result; + if (png::decode_png(rbuf, img) && img.cols > 0 && img.rows > 0) { + // The shipped library, and anything imported before colour was kept. + result.width = int(img.cols); + result.height = int(img.rows); + result.pixels = std::move(img.buf); + } else { + // A colour source: keep the colour, and take the height from its luminance. The + // coefficients are wxImage::ConvertToGreyscale()'s, which is what the importer used to + // apply on the way in - so a texture that used to be flattened to grey at import time + // displaces identically now that its colour is preserved. + png::ImageColorscale col; + if (!png::decode_colored_png(rbuf, col) || col.cols == 0 || col.rows == 0 || + col.bytes_per_pixel < 3) + return result; - result.width = int(img.cols); - result.height = int(img.rows); - result.pixels = std::move(img.buf); + const size_t n = size_t(col.cols) * size_t(col.rows); + const size_t bpp = size_t(col.bytes_per_pixel); + result.width = int(col.cols); + result.height = int(col.rows); + result.pixels.resize(n); + result.rgb.resize(n * 3); + for (size_t i = 0; i < n; ++i) { + const uint8_t r = col.buf[i * bpp], g = col.buf[i * bpp + 1], b = col.buf[i * bpp + 2]; + result.rgb[i * 3] = r; + result.rgb[i * 3 + 1] = g; + result.rgb[i * 3 + 2] = b; + result.pixels[i] = uint8_t(std::lround(0.299 * r + 0.587 * g + 0.114 * b)); + } + } std::lock_guard lock(g_decoded_texture_cache.mutex); // Opportunistically drop entries for image_data that no longer exists anywhere, so the @@ -184,6 +234,20 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer const int max_radius = std::clamp(int(std::lround(0.02f * std::min(result.width, result.height))), 1, 32); const int radius = std::max(1, int(std::lround(layer.smoothing * float(max_radius)))); smooth_height_pixels(result.pixels, result.width, result.height, radius); + // Colour gets the same blur, per channel. It is the same knob for the same reason: detail in + // the image finer than the mesh can carry is noise either way, and low-passing it here is the + // cheapest place to remove it - one blur of the texture, rather than a fight per triangle. + if (result.has_color()) { + const size_t n = size_t(result.width) * size_t(result.height); + std::vector channel(n); + for (int c = 0; c < 3; ++c) { + for (size_t i = 0; i < n; ++i) + channel[i] = result.rgb[i * 3 + size_t(c)]; + smooth_height_pixels(channel, result.width, result.height, radius); + for (size_t i = 0; i < n; ++i) + result.rgb[i * 3 + size_t(c)] = channel[i]; + } + } } return result; } @@ -1029,6 +1093,109 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp w.z() * sample_at(Vec2f(position.x(), position.y())); } +bool sample_layer_color(const DecodedHeightTexture &texture, const TextureDisplacementLayer &layer, + const Vec3f &position, const Vec3f &normal, Vec3f &out, const Vec3f &patch_center, + const Vec3f &patch_axis, const Vec2f *lscm_uv) +{ + if (!texture.has_color()) + return false; + + // Deliberately a transcription of sample_layer_height()'s dispatch rather than a shared template: + // the two differ in what "nothing here" means. Height returns 0, which is a perfectly good height + // (no displacement); colour has no such neutral value - black is a colour - so every path that + // returns 0 there has to report false here instead, and the caller leaves the triangle uncoloured. + const float aspect = (texture.height > 0) ? float(texture.width) / float(texture.height) : 1.f; + auto sample_at = [&](const Vec2f &planar) { + return texture.sample_color(apply_uv_transform(planar, layer, aspect), layer.tile_enabled, + layer.tile_method); + }; + // Outside a non-tiled placement there is no texture at all - the same hard edge sample() gives the + // height. Checked explicitly because sample_color() reports it as black, which is a real colour. + auto covered = [&](const Vec2f &planar) { + if (layer.tile_enabled) + return true; + const Vec2f uv = apply_uv_transform(planar, layer, aspect); + return uv.x() >= 0.f && uv.x() < 1.f && uv.y() >= 0.f && uv.y() < 1.f; + }; + + if (lscm_uv != nullptr) { + if (!covered(*lscm_uv)) + return false; + out = sample_at(*lscm_uv); + return true; + } + + switch (layer.projection_method) { + case TextureProjectionMethod::Cylindrical: { + const Vec2f p = project_cylindrical(position, patch_center, patch_axis); + if (!covered(p)) + return false; + out = sample_at(p); + return true; + } + case TextureProjectionMethod::Spherical: { + const Vec2f p = project_spherical(position, patch_center); + if (!covered(p)) + return false; + out = sample_at(p); + return true; + } + case TextureProjectionMethod::ViewProjected: + if (layer.view_project_projective) { + // The frame's own rectangle is the placement, so no apply_uv_transform() - see + // sample_layer_height(). A point behind the projector has no uv, hence no colour. + Vec2f uv; + if (!project_uv_projective(layer.view_project_matrix, position, uv)) + return false; + if (!layer.tile_enabled && (uv.x() < 0.f || uv.x() >= 1.f || uv.y() < 0.f || uv.y() >= 1.f)) + return false; + out = texture.sample_color(uv, layer.tile_enabled, layer.tile_method); + return true; + } else { + const Vec2f p(position.dot(layer.view_project_right), position.dot(layer.view_project_up)); + if (!covered(p)) + return false; + out = sample_at(p); + return true; + } + case TextureProjectionMethod::LSCM: // no usable unwrap for this patch - fall back to Triplanar + case TextureProjectionMethod::Triplanar: + default: break; + } + + // Blended tri-planar, weighted exactly as the height is, so colour and relief stay registered + // across the cross-fade band at a 90-degree edge. + Vec3f w = normal.cwiseAbs(); + w = Vec3f(std::pow(w.x(), TRIPLANAR_BLEND_SHARPNESS), std::pow(w.y(), TRIPLANAR_BLEND_SHARPNESS), + std::pow(w.z(), TRIPLANAR_BLEND_SHARPNESS)); + const float w_sum = w.x() + w.y() + w.z(); + if (w_sum < 1e-8f) { + const Vec2f p(position.x(), position.y()); + if (!covered(p)) + return false; + out = sample_at(p); + return true; + } + w /= w_sum; + + // A blend of three planes is only "not covered" where *every* contributing plane is outside the + // placement; where some are, the covered ones are renormalised so the colour does not fade toward + // black at the edge of an untiled tri-planar layer. + const std::array planes = { Vec2f(position.y(), position.z()), Vec2f(position.x(), position.z()), + Vec2f(position.x(), position.y()) }; + Vec3f acc = Vec3f::Zero(); + float acc_w = 0.f; + for (int i = 0; i < 3; ++i) + if (w[i] > 0.f && covered(planes[size_t(i)])) { + acc += w[i] * sample_at(planes[size_t(i)]); + acc_w += w[i]; + } + if (acc_w <= 0.f) + return false; + out = acc / acc_w; + return true; +} + indexed_triangle_set extract_painted_patch(const indexed_triangle_set &base_mesh, const TriangleSelector::TriangleSplittingData &facet_data) { @@ -1135,11 +1302,71 @@ std::vector patch_boundary_distance(const indexed_triangle_set &patch, co } } // namespace +namespace { +// Majority filter over face adjacency: each triangle takes the most common colour among itself and +// the (up to three) triangles across its edges. Ties, and a triangle whose own colour is already the +// most common, keep what they had - so the filter only ever removes a facet that disagrees with its +// whole neighbourhood, and cannot drift a large region. +// +// Read from a snapshot of the previous pass, so the result does not depend on triangle order. +// Uncoloured triangles (-1) neither vote nor get voted on: the paint boundary is not noise. +void despeckle_triangle_colors(const indexed_triangle_set &mesh, std::vector &color, int passes) +{ + if (passes <= 0 || color.size() != mesh.indices.size()) + return; + const std::vector neighbors = its_face_neighbors(mesh); + if (neighbors.size() != mesh.indices.size()) + return; + + std::vector prev; + for (int pass = 0; pass < passes; ++pass) { + prev = color; + tbb::parallel_for(tbb::blocked_range(0, color.size()), + [&](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + if (prev[i] < 0) + continue; + // At most four candidates (self plus three neighbours), so counting by a linear scan + // is cheaper than any map. + int cand[4] = { prev[i], -1, -1, -1 }; + int count[4] = { 1, 0, 0, 0 }; + int n = 1; + for (int e = 0; e < 3; ++e) { + const int nb = neighbors[i][e]; + if (nb < 0 || size_t(nb) >= prev.size() || prev[size_t(nb)] < 0) + continue; + const int c = prev[size_t(nb)]; + int k = 0; + for (; k < n; ++k) + if (cand[k] == c) { + ++count[k]; + break; + } + if (k == n && n < 4) { + cand[n] = c; + count[n] = 1; + ++n; + } + } + // Strictly greater, so a tie leaves the triangle alone. + int best = 0; + for (int k = 1; k < n; ++k) + if (count[k] > count[best]) + best = k; + if (count[best] > count[0]) + color[i] = cand[best]; + } + }); + } +} +} // namespace + indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, const TextureDisplacementOptions &options, - const DisplacementProgressFn &progress) + const DisplacementProgressFn &progress, + const TextureColorRequest *color) { // Returns true to keep going. An aborted run returns {} (see the header): an empty mesh is the // one result no caller can mistake for a finished bake and commit onto the volume. @@ -1223,6 +1450,19 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set std::vector on_patch_border(mesh.vertices.size(), false); bool any_displacement = false; + // Colour is accumulated per *triangle*, not per vertex: it ends up in the volume's + // mmu_segmentation_facets, which assigns one filament to a whole facet. Layers are visited in + // ascending slot order, so a higher layer simply overwrites a lower one's colour where they + // overlap - the painter's-algorithm reading of a layer stack, and the one that matches how the + // panel lists them. + const bool want_color = color != nullptr && color->out_triangle != nullptr && bool(color->quantize); + // Palette indices, not filament indices: -1 for "no colour here". Kept in perceived-colour space + // for the whole pass so the despeckle filter below operates on what the eye sees, and the + // interleaving that turns a mixed entry into two real filaments happens once, at the very end. + std::vector triangle_palette; + if (want_color) + triangle_palette.assign(mesh.indices.size(), -1); + const TriangleMesh selector_mesh(mesh); // One selector for the whole stack, re-deserialized per layer. Its constructor computes // its_face_neighbors() and its_face_normals() over the *entire* mesh, which on a subdivided model @@ -1251,7 +1491,10 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set selector.deserialize(data, selector_dirty); selector_dirty = true; - const indexed_triangle_set patch = selector.get_facets_strict(EnforcerBlockerType::ENFORCER); + const bool color_this_layer = want_color && layer->color_enabled; + std::vector patch_source; // sub-triangle -> base mesh triangle, only built when colouring + const indexed_triangle_set patch = + selector.get_facets_strict(EnforcerBlockerType::ENFORCER, color_this_layer ? &patch_source : nullptr); if (patch.indices.empty()) continue; // get_facets_strict() returns the same vertex array whichever state is asked for (only the @@ -1309,6 +1552,65 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set compute_lscm_uvs(patch, *layer) : std::vector{}; + // Colour, if this layer carries any. Area-weighted over each base triangle's *painted* part, + // so a triangle the brush only clipped a corner off takes the colour of that corner rather + // than of the whole triangle's worth of texture - and so a triangle straddling a colour + // boundary lands on whichever side covers more of it, instead of on whichever sub-triangle + // happened to be emitted first. One quantize call per triangle, after the averaging. + if (color_this_layer) { + const DecodedHeightTexture &tex = height; + if (tex.has_color()) { + std::vector sum(mesh.indices.size(), Vec3f::Zero()); + std::vector sum_area(mesh.indices.size(), 0.f); + for (size_t j = 0; j < patch.indices.size() && j < patch_source.size(); ++j) { + const size_t S = size_t(patch_source[j]); + if (S >= mesh.indices.size()) + continue; + const stl_triangle_vertex_indices &t = patch.indices[j]; + const Vec3f &pa = patch.vertices[size_t(t[0])]; + const Vec3f &pb = patch.vertices[size_t(t[1])]; + const Vec3f &pc = patch.vertices[size_t(t[2])]; + const float area2 = (pb - pa).cross(pc - pa).norm(); + if (area2 <= 0.f) + continue; + const Vec3f centroid = (pa + pb + pc) / 3.f; + + // The normal the triplanar blend weights by, and the unwrap coordinate the LSCM + // path needs, both averaged over the sub-triangle's corners - the same quantities + // the per-vertex height sampling uses, evaluated at the centroid instead. + Vec3f n = Vec3f::Zero(); + Vec2f uv = Vec2f::Zero(); + bool have_uv = !lscm_uvs.empty(); + for (int k = 0; k < 3; ++k) { + const int vi = t[k]; + if (vi < int(vertex_normals.size())) + n += vertex_normals[size_t(vi)]; + if (have_uv && size_t(vi) < lscm_uvs.size()) + uv += lscm_uvs[size_t(vi)]; + else + have_uv = false; + } + n = (n.norm() > 1e-8f) ? Vec3f(n.normalized()) : average_normal; + uv /= 3.f; + + Vec3f rgb; + if (sample_layer_color(tex, *layer, centroid, n, rgb, patch_centroid, patch_axis, + have_uv ? &uv : nullptr)) { + sum[S] += area2 * rgb; + sum_area[S] += area2; + } + } + for (size_t i = 0; i < mesh.indices.size(); ++i) + if (sum_area[i] > 0.f) { + const int idx = color->quantize(sum[i] / sum_area[i]); + // A quantizer that declines this colour leaves whatever a lower layer put + // there, rather than punching a hole in it. + if (idx >= 0) + triangle_palette[i] = idx; + } + } + } + // Edge smoothing: a per-vertex weight in [0, 1] that fades the displacement to zero toward the // patch boundary. amount->0 leaves only the very edge softened; amount->1 fades the whole patch // flat. k = (1-a)/a turns the normalized boundary distance into that weight (see the header). @@ -1424,6 +1726,29 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (!report(99)) return {}; + if (want_color) { + // Despeckle in perceived-colour space, then resolve each entry to a real filament. The order + // matters both ways round: filtering after the interleave would erase the bands it is supposed + // to keep, and interleaving before the filter would have the filter treat two halves of one + // blended colour as a disagreement. + despeckle_triangle_colors(mesh, triangle_palette, color->despeckle_passes); + + std::vector out_color(mesh.indices.size(), 0); + for (size_t i = 0; i < mesh.indices.size(); ++i) { + if (triangle_palette[i] < 0) + continue; + const stl_triangle_vertex_indices &t = mesh.indices[i]; + const Vec3f centroid = (mesh.vertices[size_t(t[0])] + mesh.vertices[size_t(t[1])] + + mesh.vertices[size_t(t[2])]) / 3.f; + const int filament = color->resolve ? color->resolve(triangle_palette[i], centroid) + : triangle_palette[i]; + if (filament >= 0) + out_color[i] = uint8_t(std::min(filament + 1, 255)); + } + // Handed over only on a run that completed: every early return above is a cancellation, and + // the caller must not commit a half-computed colouring any more than a half-displaced mesh. + *color->out_triangle = std::move(out_color); + } return mesh; } @@ -1495,22 +1820,26 @@ void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector } } -HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, - const std::vector &layers, - const TextureDisplacementFacetsData &facets_data) +namespace { +// One decoded texture + placement per sampleable layer, in blend (slot) order. Held by shared_ptr so +// the returned closure owns it for as long as the subdivider keeps calling back. +struct PreparedLayer { + DecodedHeightTexture tex; + TextureDisplacementLayer layer; // a copy of the params (depth/tiling/rotation/offset/blend/...) + Vec3f center; // patch centroid, for Cylindrical/Spherical + Vec3f axis; // cylinder axis, for Cylindrical +}; + +// Shared by both point samplers, so the height field and the colour field can never disagree about +// where a layer is placed. `need_color` additionally drops layers that cannot contribute colour. +std::shared_ptr> prepare_sampleable_layers( + const indexed_triangle_set &base_mesh, const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, bool need_color) { - // One decoded texture + placement per sampleable layer, in blend (slot) order. Held by shared_ptr - // so the returned closure owns it for as long as the subdivider keeps calling back. - struct Prepared { - DecodedHeightTexture tex; - TextureDisplacementLayer layer; // a copy of the params (depth/tiling/rotation/offset/blend/...) - Vec3f center; // patch centroid, for Cylindrical/Spherical - Vec3f axis; // cylinder axis, for Cylindrical - }; - auto prepared = std::make_shared>(); + auto prepared = std::make_shared>(); if (base_mesh.indices.empty()) - return nullptr; + return prepared; std::vector ordered; for (const TextureDisplacementLayer &l : layers) @@ -1525,11 +1854,13 @@ HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set for (const TextureDisplacementLayer *layer : ordered) { if (layer->projection_method == TextureProjectionMethod::LSCM) continue; // no per-point UV -> not sampleable here (caller falls back to uniform for these) + if (need_color && !layer->color_enabled) + continue; const TriangleSelector::TriangleSplittingData &data = facets_data[size_t(layer->slot)]; if (data.triangles_to_split.empty()) continue; const DecodedHeightTexture tex = decode_height_texture(*layer); - if (tex.empty()) + if (tex.empty() || (need_color && !tex.has_color())) continue; TriangleSelector selector(selector_mesh); @@ -1563,14 +1894,47 @@ HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set prepared->push_back({ tex, *layer, centroid, axis }); } + return prepared; +} +} // namespace +ColorFieldSampler make_combined_color_sampler(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + ColorQuantizeFn quantize) +{ + if (!quantize) + return nullptr; + auto prepared = prepare_sampleable_layers(base_mesh, layers, facets_data, /* need_color */ true); + if (prepared->empty()) + return nullptr; + + return [prepared, quantize = std::move(quantize)](const Vec3f &pos, const Vec3f &normal) -> int { + // Last one wins: `prepared` is in ascending slot order and the bake lets a higher layer + // overwrite a lower one's colour, so the sampler has to resolve overlaps the same way. + int result = -1; + for (const PreparedLayer &p : *prepared) { + Vec3f rgb; + if (sample_layer_color(p.tex, p.layer, pos, normal, rgb, p.center, p.axis, nullptr)) + if (const int idx = quantize(rgb); idx >= 0) + result = idx; + } + return result; + }; +} + +HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data) +{ + auto prepared = prepare_sampleable_layers(base_mesh, layers, facets_data, /* need_color */ false); if (prepared->empty()) return nullptr; return [prepared](const Vec3f &pos, const Vec3f &normal) -> float { float total = 0.f; bool any = false; - for (const Prepared &p : *prepared) { + for (const PreparedLayer &p : *prepared) { const float h = sample_layer_height(p.tex, p.layer, pos, normal, p.center, p.axis, nullptr); const float sign = p.layer.invert ? -1.f : 1.f; const float signed_h = (h - p.layer.midlevel) * p.layer.depth_mm * sign; @@ -1646,7 +2010,8 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, std::vector *out_source, const HeightFieldSampler &sampler, float chord_tolerance_mm, float min_edge_length_mm, float border_edge_length_mm, - const DisplacementProgressFn &progress) + const DisplacementProgressFn &progress, + const ColorFieldSampler &color, float color_edge_length_mm) { // Neighbour slots that are not a triangle index. constexpr int NB_BOUNDARY = -1; // open edge: terminal on its own, bisected from this side alone @@ -1682,6 +2047,8 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // Feature-adaptive when a sampler and a positive tolerance are supplied; otherwise refinement is // driven by the length baseline alone. const bool feature_mode = bool(sampler) && chord_tolerance_mm > 0.f; + const bool color_mode = bool(color) && color_edge_length_mm > 0.f; + const float color_sq = color_edge_length_mm > 0.f ? color_edge_length_mm * color_edge_length_mm : 0.f; const float min_floor_sq = min_edge_length_mm > 0.f ? min_edge_length_mm * min_edge_length_mm : 0.f; const float target_sq = target_edge_length_mm > 0.f ? target_edge_length_mm * target_edge_length_mm : 0.f; const float border_sq = border_edge_length_mm > 0.f ? border_edge_length_mm * border_edge_length_mm : 0.f; @@ -1690,7 +2057,7 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // (children inherit their parent's src), so a wrong size would be an out-of-bounds read. Guard it. if (refine_region.size() != mesh.indices.size() || int(tris.size()) + 2 > max_triangles) return emit(); - if (!feature_mode && target_sq <= 0.f && border_sq <= 0.f) + if (!feature_mode && !color_mode && target_sq <= 0.f && border_sq <= 0.f) return emit(); // no criterion at all if (std::none_of(refine_region.begin(), refine_region.end(), [](uint8_t v) { return v != 0; })) return emit(); // nothing flagged: no-op @@ -1757,6 +2124,34 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, return vheight[v]; }; + // Per-vertex filament index, sampled lazily and cached the same way the heights are. Needs the + // vertex normals, which feature mode also builds - so colour mode builds them when it is on alone. + std::vector vcolor; + std::vector vcolor_valid; + if (color_mode) { + if (vnormal.empty()) { + vnormal.assign(verts.size(), Vec3f::Zero()); + for (const Tri &t : tris) { + const Vec3f fn = (verts[t.v[1]] - verts[t.v[0]]).cross(verts[t.v[2]] - verts[t.v[0]]); + for (int i = 0; i < 3; ++i) + vnormal[t.v[i]] += fn; + } + for (Vec3f &n : vnormal) { + const float l = n.norm(); + n = (l > 1e-12f) ? Vec3f(n / l) : Vec3f(Vec3f::UnitZ()); + } + } + vcolor.assign(verts.size(), -2); // -2 = not sampled yet; -1 = sampled, no colour there + vcolor_valid.assign(verts.size(), 0); + } + auto color_of = [&](int v) -> int { + if (!vcolor_valid[v]) { + vcolor[v] = color(verts[v], vnormal[v]); + vcolor_valid[v] = 1; + } + return vcolor[v]; + }; + auto elen_sq = [&](int a, int b) -> float { return (verts[a] - verts[b]).squaredNorm(); }; // The one edge of a triangle taken as its "longest": greatest squared length, exact ties broken by @@ -1788,6 +2183,38 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, std::vector tri_err; if (feature_mode) tri_err.assign(tris.size(), -1.f); + // True when this triangle straddles a colour boundary: its corners, its edge midpoints and its + // centroid do not all take the same filament. The midpoints and centroid matter for the same + // reason they do in detail_error() - a boundary can cross a triangle without separating any two of + // its corners. Cached per triangle; a split invalidates both children. + std::vector tri_color_split; // 0 = unknown, 1 = straddles, 2 = uniform + if (color_mode) + tri_color_split.assign(tris.size(), 0); + auto straddles_color = [&](int ti) -> bool { + if (tri_color_split[ti] != 0) + return tri_color_split[ti] == 1; + const Tri &t = tris[ti]; + const Vec3f pa = verts[t.v[0]], pb = verts[t.v[1]], pc = verts[t.v[2]]; + const Vec3f na = vnormal[t.v[0]], nb = vnormal[t.v[1]], nc = vnormal[t.v[2]]; + const int ca = color_of(t.v[0]); + bool split = color_of(t.v[1]) != ca || color_of(t.v[2]) != ca; + if (!split) { + static const float BARY[4][3] = { { 0.5f, 0.5f, 0.f }, { 0.f, 0.5f, 0.5f }, + { 0.5f, 0.f, 0.5f }, { 1.f / 3, 1.f / 3, 1.f / 3 } }; + for (const auto &w : BARY) { + Vec3f n = w[0] * na + w[1] * nb + w[2] * nc; + const float nl = n.norm(); + n = (nl > 1e-12f) ? Vec3f(n / nl) : na; + if (color(w[0] * pa + w[1] * pb + w[2] * pc, n) != ca) { + split = true; + break; + } + } + } + tri_color_split[ti] = split ? 1 : 2; + return split; + }; + auto detail_error = [&](int ti) -> float { if (tri_err[ti] >= 0.f) return tri_err[ti]; @@ -1828,6 +2255,10 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, p = (target_sq > 0.f) ? ll / target_sq : 0.f; if (feature_mode) p = std::max(p, detail_error(ti) / chord_tolerance_mm); + // Colour is per facet, so a colour boundary can only be drawn where there are edges along + // it. Length target, floored by min_edge_length_mm above, exactly like the border band. + if (color_mode && straddles_color(ti)) + p = std::max(p, ll / color_sq); } // The band straddling the paint's edge, refined by plain edge length. Deliberately *not* run // through detail_error(): outside the paint the sampler still reports full relief (it has no @@ -1878,13 +2309,19 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const int m = int(verts.size()); verts.push_back(0.5f * (verts[a] + verts[b])); - if (feature_mode) { + if (feature_mode || color_mode) { const Vec3f mn = vnormal[a] + vnormal[b]; const float ml = mn.norm(); vnormal.push_back(ml > 1e-12f ? Vec3f(mn / ml) : vnormal[a]); + } + if (feature_mode) { vheight.push_back(0.f); vheight_valid.push_back(0); } + if (color_mode) { + vcolor.push_back(-2); + vcolor_valid.push_back(0); + } // Near side: ti becomes (a, m, c), the new triangle is (m, b, c). Both keep the original // a->b->c winding. @@ -1903,6 +2340,10 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, tri_err.push_back(-1.f); tri_err[ti] = -1.f; } + if (color_mode) { + tri_color_split.push_back(0); + tri_color_split[ti] = 0; + } touched.assign({ ti, t2 }); if (n < 0) { @@ -1926,6 +2367,10 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, tri_err.push_back(-1.f); tri_err[n] = -1.f; } + if (color_mode) { + tri_color_split.push_back(0); + tri_color_split[n] = 0; + } // Stitch the two sides back together: whichever far child holds `a` borders the near child // that holds `a`. (Which one that is depends on how n happens to be wound.) diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 10e30f9788..7abf1d656c 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -274,6 +274,17 @@ struct TextureDisplacementLayer // it as the "Base" layer and hides the control). TextureBlendMode blend_mode = TextureBlendMode::Add; + // Colour this layer's painted area from the texture's own colours, on top of displacing by its + // height. Only meaningful when the texture actually has colour (DecodedHeightTexture::has_color()): + // the shipped library is grayscale, so this does nothing there. + // + // Colour lands in the volume's mmu_segmentation_facets - the same per-triangle filament assignment + // the MMU paint gizmo writes - so its resolution is the *mesh's*, not the image's, and a triangle + // gets exactly one filament. That is why the adaptive subdivision has a colour criterion of its + // own (see subdivide_mesh_adaptive()): without triangles along a colour boundary there is nothing + // for the boundary to be drawn on. + bool color_enabled = false; + bool empty() const { return !image_data || image_data->empty(); } template void save(Archive &ar) const @@ -283,7 +294,7 @@ struct TextureDisplacementLayer static_cast(tile_method), static_cast(projection_method), lscm_seam_angle_deg, islands, static_cast(blend_mode), midlevel, island_padding_mm, lscm_seam_edges, view_project_right, view_project_up, smoothing, edge_smoothing, edge_smoothing_amount, auto_connect_islands, island_groups, - lscm_uv_overrides, view_project_projective, view_project_matrix); + lscm_uv_overrides, view_project_projective, view_project_matrix, color_enabled); } template void load(Archive &ar) { @@ -295,7 +306,7 @@ struct TextureDisplacementLayer tile_method_int, projection_method_int, lscm_seam_angle_deg, islands, blend_mode_int, midlevel, island_padding_mm, lscm_seam_edges, view_project_right, view_project_up, smoothing, edge_smoothing, edge_smoothing_amount, auto_connect_islands, island_groups, lscm_uv_overrides, view_project_projective, - view_project_matrix); + view_project_matrix, color_enabled); image_data = blob.empty() ? nullptr : std::make_shared>(blob.begin(), blob.end()); tile_method = static_cast(tile_method_int); projection_method = static_cast(projection_method_int); @@ -303,6 +314,22 @@ struct TextureDisplacementLayer } }; +// How a *mixed* palette entry - one that names two filaments rather than one - is turned into real +// per-facet paint. An MMU extrudes one filament at a time, so an intermediate colour exists only by +// interleaving two of them finely enough that the eye does the blending. +enum class ColorMixMode : int +{ + // Horizontal bands: which of the two filaments a point takes depends on its height, so + // consecutive print layers alternate. This is how filament-blend prints actually work, and on a + // vertical-ish surface it reads as a genuinely smooth colour. On a near-horizontal surface a whole + // layer is one band, so the blend disappears - that is what XYDither is for. + ZBands = 0, + // An ordered (Bayer) checkerboard across the surface, at any orientation. Independent of layer + // height, but its cell is around the size of one facet, so a fine mix can read as texture rather + // than as a clean blend. + XYDither = 1, +}; + // Settings that apply to the whole layer stack rather than to one layer, held per ModelVolume next // to texture_displacement_layers and consumed by build_texture_displacement(). struct TextureDisplacementOptions @@ -338,33 +365,118 @@ struct TextureDisplacementOptions // deliberately (which is a blunter version of the per-layer edge-smoothing falloff). bool smooth_skip_border = true; + // Colour, all of which belongs to the stack rather than to any one layer: it is about how the + // printer will realise the colours, not about which image they came from. + + // Interleave pairs of filaments to get colours between them - so four loaded filaments offer far + // more than four colours. Off means every triangle takes one of the loaded filaments exactly. + bool color_mix_enabled = true; + ColorMixMode color_mix_mode = ColorMixMode::ZBands; + // Majority-filter passes over the assigned colours. See TextureColorRequest::despeckle_passes - + // this is the control for it, and 2 is enough to clear the salt-and-pepper an image with detail + // finer than the mesh leaves behind, without eating features that are genuinely a facet wide. + int color_despeckle = 2; + template void serialize(Archive &ar) { - ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border); + int mix_mode = int(color_mix_mode); + ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border, + color_mix_enabled, mix_mode, color_despeckle); + color_mix_mode = ColorMixMode(mix_mode); } }; -// Decoded 8-bit grayscale height sample, independent of any GUI/OpenGL texture object so it can -// be evaluated from a background bake Job as well as from GUI-side preview code. +// Decoded height (and, for a colour source image, colour) samples, independent of any GUI/OpenGL +// texture object so they can be evaluated from a background bake Job as well as from GUI-side +// preview code. struct DecodedHeightTexture { - std::vector pixels; // row-major, top-to-bottom, one byte per pixel + std::vector pixels; // height: row-major, top-to-bottom, one byte per pixel + // Colour: the same grid, three bytes per pixel, or empty when the source image was grayscale. + // A grayscale height map has no colour to give - `pixels` is not a colour, it is a height - so + // has_color() is what the whole colour feature keys off: a layer set to colour a model with a + // grayscale texture on it simply colours nothing. + std::vector rgb; int width = 0; int height = 0; bool empty() const { return width <= 0 || height <= 0 || pixels.empty(); } + bool has_color() const { return !empty() && rgb.size() == size_t(width) * size_t(height) * 3; } // Bilinearly sampled height in [0, 1] at a normalized uv coordinate. When tile_enabled is false, // a uv outside [0, 1) samples as 0 - the texture simply is not there, rather than its border // row/column being smeared outward forever (which is what clamping the coordinate would do, and // was a real reported bug). Callers rely on this to get a hard edge: it is how the projection // frame's border becomes the edge of the displacement. float sample(const Vec2f &uv, bool tile_enabled = true, TextureTileMethod tile_method = TextureTileMethod::Repeat) const; + // The same sample, in colour: linear RGB components in [0, 1]. Outside a non-tiled placement, and + // for a grayscale source, this is (0, 0, 0) - callers pair it with has_color() and with the + // height's own coverage rather than trying to read "no texture here" out of the colour itself. + Vec3f sample_color(const Vec2f &uv, bool tile_enabled = true, + TextureTileMethod tile_method = TextureTileMethod::Repeat) const; + + // Where a uv lands on the texel grid: the four texels of the bilinear tap and their weights. + // Shared by sample() and sample_color(), so a layer's height and its colour can never end up + // read from different places in the image. False means the uv is outside a non-tiled placement - + // no texture there at all (see sample()). + struct TexelTap + { + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + float tx = 0.f, ty = 0.f; + }; + bool texel_tap(const Vec2f &uv, bool tile_enabled, TextureTileMethod tile_method, TexelTap &out) const; }; -// Decode a layer's raw image bytes into sampleable grayscale height data. Returns an empty -// DecodedHeightTexture if image_data is empty or is not an 8-bit grayscale PNG. +// Decode a layer's raw image bytes into sampleable height data, plus colour when the source has any. +// Both 8-bit grayscale PNGs (the shipped library, and anything imported before colour was kept) and +// colour PNGs are accepted; for a colour source the height is its luminance, using the same +// coefficients wxImage::ConvertToGreyscale() uses, so a texture imported as colour displaces exactly +// as it did when the importer flattened it to grey on the way in. Returns an empty +// DecodedHeightTexture if image_data is empty or is not a PNG at all. DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer); +// Maps a linear RGB colour in [0, 1] to an index into the caller's palette, or -1 for "no colour". +// +// Deliberately a callback rather than a function here: matching a colour to a filament is a +// *perceptual* question (CIEDE2000 over CIELAB), and that machinery - slic3r/Utils/ColorSpaceConvert +// and GuiColor - lives on the GUI side along with the list of filaments actually loaded. libslic3r +// samples the image and decides *where* colour changes; the GUI decides *which* filament each colour +// is. See GLGizmoTextureDisplacement::make_palette_quantizer(). +using ColorQuantizeFn = std::function; + +// Resolves a palette index plus a surface position to the filament index that position should print +// in. A pure entry ignores the position; a mixed one interleaves its two filaments per ColorMixMode. +// +// Deliberately separate from ColorQuantizeFn, and deliberately *not* used by the subdivision's colour +// criterion: that criterion asks where the **perceived** colour changes, and must not see the +// interleaving. Refining on every band or dither-cell boundary would spend the whole triangle budget +// drawing a pattern the eye is supposed to blend away. +using ColorResolveFn = std::function; + +// One printable colour: either a loaded filament on its own, or a blend of two of them realised by +// interleaving (see ColorMixMode). Plain data, so it can be captured into a background job. +struct PrintableColor +{ + Vec3f rgb = Vec3f::Zero(); // what it looks like; for a mix, the perceptual average of the two + int a = 0; // filament index + int b = 0; // the second filament; == a for a pure entry + int num = 1; // a's share of the interleave, out of `den` + int den = 1; + bool is_mix() const { return a != b; } +}; + +// Everything needed to colour a mesh, captured on the main thread and handed to a job. An empty +// palette means nothing is colouring, which is the state every one of these paths starts in. +struct TextureColorSettings +{ + std::vector palette; + ColorMixMode mix_mode = ColorMixMode::ZBands; + float layer_height = 0.2f; // sizes the Z bands + float dither_cell_mm = 0.4f; // sizes the XY dither cells + int despeckle_passes = 2; + + bool empty() const { return palette.empty(); } +}; + // Raw dominant-axis planar projection of `position` (in mm, not yet scaled/rotated/offset by any // layer), dropping the axis position that best aligns with `normal`. Exposed on its own (rather // than only inline inside project_texture_displacement_uv()) so GUI code - the on-canvas @@ -409,6 +521,16 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp const Vec3f &patch_center = Vec3f::Zero(), const Vec3f &patch_axis = Vec3f::UnitZ(), const Vec2f *lscm_uv = nullptr); +// The same sample, in colour, through the identical projection/tiling/placement path - so a layer's +// colour lands on the model exactly where its relief does, whatever projection it is using. Returns +// false (leaving `out` untouched) when the texture has no colour, or when the point falls outside a +// non-tiled placement, or behind a projective "from view" projector: all three mean "this layer does +// not colour this point", which is different from "this layer colours it black". +bool sample_layer_color(const DecodedHeightTexture &texture, const TextureDisplacementLayer &layer, + const Vec3f &position, const Vec3f &normal, Vec3f &out, + const Vec3f &patch_center = Vec3f::Zero(), const Vec3f &patch_axis = Vec3f::UnitZ(), + const Vec2f *lscm_uv = nullptr); + // Area-weighted centroid and average normal of a layer's currently painted patch, in mesh-local // coordinates - the same measurements build_texture_displacement() uses to pick its dominant // projection axis. Used by the GUI to anchor the on-canvas "adjust texture placement" gizmo to @@ -551,11 +673,38 @@ using TextureDisplacementFacetsData = std::array palette index. Supplied by the GUI, which owns both the perceptual matching and the list + // of filaments actually loaded (see ColorQuantizeFn). + ColorQuantizeFn quantize; + // Palette index + position -> filament. Optional: without it a palette index is taken to be a + // filament index directly, which is the no-mixing case. + ColorResolveFn resolve; + // Majority-filter passes over the *perceived* colour, before any interleaving is resolved. + // + // Sampling a detailed image once per triangle leaves salt-and-pepper wherever the image's own + // detail is finer than the mesh: two neighbouring facets land either side of some contour and flip + // colour independently. Replacing each facet's colour with the most common one among itself and + // its edge neighbours removes exactly that, and leaves any feature wider than a facet alone. 0 + // turns it off. + int despeckle_passes = 0; + // Filled per *base mesh* triangle (the bake is topology-preserving, so this indexes the returned + // mesh too): the quantize callback's index plus one, or 0 for "this triangle takes no colour from + // the texture". The +1 is not arbitrary - it lines up with EnforcerBlockerType, where 0 is NONE + // ("use the volume's own filament") and 1..16 are Extruder1..16, so the caller can hand these + // straight to a TriangleSelector without a second mapping table. + std::vector *out_triangle = nullptr; +}; indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, const TextureDisplacementOptions &options = {}, - const DisplacementProgressFn &progress = {}); + const DisplacementProgressFn &progress = {}, + const TextureColorRequest *color = nullptr); // Convenience overload for main-thread callers: extracts the mesh/layers/paint data/options from // `volume` and forwards to the overload above. @@ -591,6 +740,22 @@ using HeightFieldSampler = std::function; + +// The colour counterpart of make_combined_displacement_sampler(), over the same layers, and skipping +// the same ones (LSCM has no per-point UV). Layers without color_enabled, and layers whose texture is +// grayscale, contribute nothing; a higher slot wins over a lower one where they overlap, matching the +// bake. Returns null when no layer can colour anything, in which case there is nothing to refine for. +ColorFieldSampler make_combined_color_sampler(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + ColorQuantizeFn quantize); + HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data); @@ -678,6 +843,15 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl // plain edge length is bounded (it is a thin ring, and a length target always terminates) and needs // no paint-aware sampler. // +// `color`, with a positive `color_edge_length_mm`, adds a third criterion inside the painted area: a +// triangle whose corners, edge midpoints and centroid do not all map to the *same* filament straddles +// a colour boundary, and is refined by plain edge length down to that target. Length rather than any +// error measure, for the same reason the border band uses length - the thing being fixed is the size +// of the triangles spanning a step, not the curvature of anything - and because a step's error never +// falls however fine the mesh gets, so only a length target (floored by min_edge_length_mm) is +// guaranteed to terminate. Without this a colour boundary lands on whatever triangles the *height* +// happened to need, which on a flat surface is none at all. +// // `progress`, when given, is called with a 0..100 percentage of the triangle budget spent; returning // false stops the refinement early. What it hands back then is still a complete, conformal mesh - the // loop only ever finishes whole bisections - so a caller that wants to discard it has to do so itself. @@ -688,7 +862,9 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const HeightFieldSampler &sampler = nullptr, float chord_tolerance_mm = 0.f, float min_edge_length_mm = 0.f, float border_edge_length_mm = 0.f, - const DisplacementProgressFn &progress = nullptr); + const DisplacementProgressFn &progress = nullptr, + const ColorFieldSampler &color = nullptr, + float color_edge_length_mm = 0.f); // The recipe for getting a mesh ready to receive displacement: even out the triangle density, then // refine it where the texture bends. Either stage is skipped when its target is <= 0. Pure data, and @@ -709,6 +885,10 @@ struct TextureDisplacementPrepareParams float subdiv_border_mm = 0.f; // "Edge detail": the band straddling the paint's edge, 0 = off bool subdiv_feature = false; // follow texture curvature, not just edge length int subdiv_added_triangles = 0; // budget, *added* to the mesh's own count + // Edge length triangles straddling a *colour* boundary are refined to, 0 = do not look at colour. + // Separate from the height criteria because colour lands per facet: a flat surface carrying a + // sharp colour edge needs triangles along that edge even though its height is perfectly smooth. + float subdiv_color_edge_mm = 0.f; }; // What a preparation run produced. An empty `mesh` means there was nothing to do and the caller must diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index bff54e791e..b0bfefddbd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -3,6 +3,8 @@ #include #include "libslic3r/AABBTreeIndirect.hpp" +#include "libslic3r/Color.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/MeshBoolean.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Utils.hpp" @@ -15,6 +17,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/GuiColor.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/MainFrame.hpp" // wxGetApp().mainframe, as the projector window's parent #include "slic3r/GUI/MsgDialog.hpp" @@ -212,6 +215,14 @@ TriangleSelector::TriangleSplittingData remap_texture_paint_spatial( return dst_sel.serialize(); } +// Edge of the RGB lookup cube make_palette_quantizer() builds. 24 gives 13824 cells - far finer than +// the difference between any two printable colours - and costs one DeltaE00 per cell per palette +// entry to fill. +constexpr int PALETTE_LUT_EDGE = 24; + +// Ceiling on the printable palette, which bounds that fill cost (and the shader's uniform array). +constexpr int PALETTE_MAX_ENTRIES = 64; + std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &decoded, int max_px = THUMBNAIL_MAX_PX) { if (decoded.empty()) @@ -247,6 +258,46 @@ std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &d return texture; } +// The same upload, of the texture's *colour* rather than its height, for the fast preview to quantize +// per fragment. Null for a grayscale texture - there is nothing to show. +// +// Box-filtered down like the height is, and for a sharper reason: the fast preview quantizes every +// fragment independently, so any texel-scale noise left in the image becomes a scatter of single-pixel +// colour flips on screen. Filtering on the way to the GPU is where that is cheapest to remove. +std::unique_ptr upload_color_texture(const DecodedHeightTexture &decoded, int max_px) +{ + if (!decoded.has_color()) + return nullptr; + + const int scale = std::max(1, (std::max(decoded.width, decoded.height) + max_px - 1) / max_px); + const int w = std::max(1, decoded.width / scale); + const int h = std::max(1, decoded.height / scale); + + std::vector rgba(size_t(w) * size_t(h) * 4); + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) { + const int x0 = x * decoded.width / w, x1 = std::max(x0 + 1, (x + 1) * decoded.width / w); + const int y0 = y * decoded.height / h, y1 = std::max(y0 + 1, (y + 1) * decoded.height / h); + unsigned int sum[3] = { 0, 0, 0 }; + unsigned int n = 0; + for (int sy = y0; sy < y1 && sy < decoded.height; ++sy) + for (int sx = x0; sx < x1 && sx < decoded.width; ++sx, ++n) { + const size_t si = (size_t(sy) * size_t(decoded.width) + size_t(sx)) * 3; + for (int c = 0; c < 3; ++c) + sum[c] += decoded.rgb[si + size_t(c)]; + } + const size_t di = (size_t(y) * size_t(w) + size_t(x)) * 4; + for (int c = 0; c < 3; ++c) + rgba[di + size_t(c)] = (n > 0) ? static_cast(sum[size_t(c)] / n) : 0; + rgba[di + 3] = 255; + } + + auto texture = std::make_unique(); + if (!texture->load_from_raw_data(std::move(rgba), (unsigned int) w, (unsigned int) h, false, false)) + return nullptr; + return texture; +} + // Intersects the camera ray through `mouse_pos` (screen coords) with the plane passing through // `plane_point_local`/`plane_normal_local` (mesh-local coords, transformed to world by `trafo`). // Returns false if the ray is parallel to the plane or the plane is behind the camera. @@ -887,7 +938,16 @@ void GLGizmoTextureDisplacement::render_preview_mesh() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_preview_glmodel.render(); + if (m_preview_color_runs.empty()) { + m_preview_glmodel.render(); + } else { + // set_color() writes the uniform the shader reads, so one call per group is all the + // per-triangle colour this needs. + for (const PreviewColorRun &run : m_preview_color_runs) { + m_preview_glmodel.set_color(run.color); + m_preview_glmodel.render(run.range, shader); + } + } shader->stop_using(); } @@ -972,6 +1032,13 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() if (!m_bump_preview_uses_vertex_uv) vertex_uv.clear(); + // Colour is quantized per *fragment* in the shader now (see the .fs), so this mesh carries no + // colour of its own - the palette and the colour texture are uniforms, and every pixel matches the + // image rather than the facet it landed on. What the *bake* will produce, at facet resolution, is + // what the Normal view shows. + m_bump_preview_palette = (active != nullptr && active->color_enabled) ? cached_palette() + : std::vector{}; + GLModel::Geometry init_data; // P3N3T2: normal.x carries the paint weight, tex_coord the precomputed uv (see the vertex shader). init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; @@ -1003,8 +1070,10 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() vcount += 3; } }; - emit_triangles(patch, 1.f); // painted -> bumped - emit_triangles(rest, 0.f); // untouched surface -> flat, so it still shows but isn't bumped + emit_triangles(patch, 1.f); // painted -> bumped, and coloured by the shader + // Untouched surface: flat, so it still shows but isn't bumped - and uncoloured, which is what the + // bake leaves it as (EnforcerBlockerType::NONE, i.e. the volume's own filament). + emit_triangles(rest, 0.f); m_bump_preview_glmodel.init_from(std::move(init_data)); // GLModel::render() unconditionally re-sets the shader's "uniform_color" from this internal @@ -1189,6 +1258,29 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than // projecting; see rebuild_bump_preview_mesh(). shader->set_uniform("use_vertex_uv", m_bump_preview_uses_vertex_uv); + + // The filament palette the mesh's per-triangle indices refer to. Count 0 means "no layer is + // colouring", and the shader keeps the model's own colour for every fragment. + // The printable palette, in RGB for display and in Lab for the match. Uploaded rather than + // matched on the CPU because the quantization is per fragment here. + const GLTexture *color_tex = get_layer_color_texture(*layer); + const int palette_count = + (color_tex != nullptr) ? int(std::min(m_bump_preview_palette.size(), size_t(PALETTE_MAX_ENTRIES))) : 0; + shader->set_uniform("palette_count", palette_count); + shader->set_uniform("has_color_tex", color_tex != nullptr); + for (int i = 0; i < palette_count; ++i) { + const Vec3f &rgb = m_bump_preview_palette[size_t(i)].rgb; + float l, a, b; + RGB2Lab(rgb.x() * 255.f, rgb.y() * 255.f, rgb.z() * 255.f, &l, &a, &b); + shader->set_uniform(("palette_rgb[" + std::to_string(i) + "]").c_str(), rgb); + shader->set_uniform(("palette_lab[" + std::to_string(i) + "]").c_str(), Vec3f(l, a, b)); + } + if (color_tex != nullptr) { + shader->set_uniform("color_tex", 1); + glsafe(::glActiveTexture(GL_TEXTURE1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint) color_tex->get_id())); + glsafe(::glActiveTexture(GL_TEXTURE0)); + } // The live UV-editor island drag rides this 2x3 affine (identity except mid-drag); only the flagged // island's vertices apply it, so a drag is a uniform update rather than a mesh rebuild. const Eigen::Matrix &d = m_bump_island_delta; @@ -1564,11 +1656,20 @@ void GLGizmoTextureDisplacement::queue_preview_job() input.options = mv->texture_displacement_options; for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = mv->texture_displacement_facet(i).get_data(); + // Captured here rather than read in the handler: get_extruders_colors() is main-thread state and + // the preview has to be grouped against the same palette it was computed with, not whatever is + // loaded by the time it lands. + input.color = color_settings_for(*mv); + // The filament list the result's indices refer to, captured with the job rather than read back + // when it lands - loading a filament meanwhile must not recolour a preview computed against a + // different list. + const std::vector filaments = m_palette_filaments; m_preview_job_running = true; auto &worker = wxGetApp().plater()->get_ui_job_worker(); queue_job(worker, std::make_unique(std::move(input), generation, m_preview_generation, - [this](indexed_triangle_set its, uint64_t result_generation) { + [this, filaments](TextureDisplacementPreviewResult result, uint64_t result_generation) { + indexed_triangle_set its = std::move(result.mesh); m_preview_job_running = false; if (result_generation != m_preview_generation->load()) { // Superseded while this was computing (it will have aborted early and come back @@ -1580,7 +1681,29 @@ void GLGizmoTextureDisplacement::queue_preview_job() // rather than blanking it; there is no new result to show, not a new empty one. } else { m_preview_glmodel.reset(); - m_preview_glmodel.init_from(its); + m_preview_color_runs.clear(); + if (result.triangle_color.size() == its.indices.size() && !filaments.empty()) { + // Group by *filament*, not by palette entry: what the bake wrote is the resolved + // filament, interleaving already applied, so this shows the real banding rather + // than the flat average the eye will turn it into. + indexed_triangle_set sorted; + sorted.vertices = its.vertices; + sorted.indices.reserve(its.indices.size()); + for (int want = 0; want <= int(filaments.size()); ++want) { + const size_t first = sorted.indices.size(); + for (size_t i = 0; i < its.indices.size(); ++i) + if (int(result.triangle_color[i]) == want) + sorted.indices.push_back(its.indices[i]); + if (sorted.indices.size() == first) + continue; + m_preview_color_runs.push_back( + { { first * 3, sorted.indices.size() * 3 }, + want == 0 ? GLVolume::NEUTRAL_COLOR : filaments[size_t(want - 1)] }); + } + m_preview_glmodel.init_from(sorted); + } else { + m_preview_glmodel.init_from(its); + } m_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); // Keep the displaced mesh so the wireframe overlay can be drawn on it (the // true-displacement view), then refresh the wireframe from it. @@ -3277,10 +3400,199 @@ TextureDisplacementFacetsData GLGizmoTextureDisplacement::facets_data_of(const M return out; } +bool GLGizmoTextureDisplacement::any_layer_colors(const ModelVolume &mv) +{ + for (const TextureDisplacementLayer &layer : mv.texture_displacement_layers) + if (layer.color_enabled && !layer.empty() && decode_height_texture(layer).has_color()) + return true; + return false; +} + +TextureColorSettings GLGizmoTextureDisplacement::color_settings_for(const ModelVolume &mv) +{ + TextureColorSettings out; + if (!any_layer_colors(mv)) + return out; // nothing is colouring: every colour path stays switched off + out.palette = cached_palette(); + out.mix_mode = mv.texture_displacement_options.color_mix_mode; + out.despeckle_passes = mv.texture_displacement_options.color_despeckle; + out.layer_height = print_layer_height(); + // The dither cell is tied to the colour-detail target: a cell much smaller than a facet cannot be + // drawn at all, and one much larger stops reading as a blend and starts reading as a check. + out.dither_cell_mm = std::max(m_subdivide_color_mm, 0.05f) * 2.f; + return out; +} + +const std::vector &GLGizmoTextureDisplacement::cached_palette() +{ + // Rebuilt only when the loaded filaments or the mixing setting actually change. The bump preview + // rebuilds on every paint stroke and the subdivide preview on every slider frame, and filling the + // quantizer's lookup cube for a 64-entry palette is tens of milliseconds - paying that per stroke + // is the difference between painting that keeps up and painting that stutters. + const ModelVolume *mv = texture_volume(); + const bool mixing = mv != nullptr && mv->texture_displacement_options.color_mix_enabled; + std::vector filaments = filament_palette(); + if (m_palette_cache.empty() || filaments != m_palette_filaments || mixing != m_palette_mixing) { + m_palette_filaments = std::move(filaments); + m_palette_mixing = mixing; + m_palette_cache = make_palette(m_palette_filaments, mixing); + m_palette_quantizer = make_palette_quantizer(m_palette_cache); + } + return m_palette_cache; +} + +std::vector GLGizmoTextureDisplacement::filament_palette() +{ + std::vector palette = wxGetApp().plater()->get_extruders_colors(); + // mmu_segmentation_facets encodes the filament in a 6-bit prefix code and stops at Extruder16. + if (palette.size() > size_t(EnforcerBlockerType::ExtruderMax)) + palette.resize(size_t(EnforcerBlockerType::ExtruderMax)); + return palette; +} + +float GLGizmoTextureDisplacement::print_layer_height() +{ + try { + const DynamicPrintConfig &cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config; + if (const ConfigOptionFloat *opt = cfg.option("layer_height"); opt != nullptr) + if (opt->value > 1e-3) + return float(opt->value); + } catch (...) { + } + return 0.2f; +} + +std::vector GLGizmoTextureDisplacement::make_palette( + const std::vector &filaments, bool mixing) +{ + std::vector out; + const int n = int(filaments.size()); + for (int i = 0; i < n; ++i) + out.push_back({ Vec3f(filaments[size_t(i)].r(), filaments[size_t(i)].g(), filaments[size_t(i)].b()), + i, i, 1, 1 }); + if (!mixing || n < 2) + return out; + + // How many intermediate steps each pair gets, chosen so the whole palette stays under + // PALETTE_MAX_ENTRIES. Fewer filaments means more room for mixes, which is also what you want: + // with two filaments the mixes are the only way to get anywhere, and with sixteen there is little + // point mixing at all. `den` is also the band/dither repeat, so a small one is a short pattern. + const int pairs = n * (n - 1) / 2; + int steps = 0; + for (int s = 5; s >= 1; --s) + if (n + pairs * s <= PALETTE_MAX_ENTRIES) { + steps = s; + break; + } + if (steps == 0) + return out; + const int den = steps + 1; + + for (int i = 0; i < n; ++i) + for (int j = i + 1; j < n; ++j) { + float la, aa, ba, lb, ab, bb; + RGB2Lab(filaments[size_t(i)].r() * 255.f, filaments[size_t(i)].g() * 255.f, + filaments[size_t(i)].b() * 255.f, &la, &aa, &ba); + RGB2Lab(filaments[size_t(j)].r() * 255.f, filaments[size_t(j)].g() * 255.f, + filaments[size_t(j)].b() * 255.f, &lb, &ab, &bb); + for (int k = 1; k <= steps; ++k) { + // k/den of filament i, the rest of j - averaged in Lab, which is what the eye does + // when the two are interleaved too finely to resolve. + const float t = float(k) / float(den); + float r, g, b; + Lab2RGB(la * t + lb * (1.f - t), aa * t + ab * (1.f - t), ba * t + bb * (1.f - t), &r, &g, &b); + out.push_back({ Vec3f(std::clamp(r / 255.f, 0.f, 1.f), std::clamp(g / 255.f, 0.f, 1.f), + std::clamp(b / 255.f, 0.f, 1.f)), + i, j, k, den }); + } + } + return out; +} + +ColorResolveFn GLGizmoTextureDisplacement::make_mix_resolver(const std::vector &palette, + ColorMixMode mode, float layer_height, + float cell_mm) +{ + if (palette.empty()) + return nullptr; + auto entries = std::make_shared>(palette); + const float band = std::max(layer_height, 0.01f); + const float cell = std::max(cell_mm, 0.01f); + + return [entries, mode, band, cell](int index, const Vec3f &pos) -> int { + if (index < 0 || size_t(index) >= entries->size()) + return -1; + const PaletteEntry &e = (*entries)[size_t(index)]; + if (!e.is_mix()) + return e.a; + + // Which of the two filaments this point falls on. Both patterns are *ordered*, never random: + // the eye blends a regular pattern into a flat colour, and turns a random one into noise. + if (mode == ColorMixMode::ZBands) { + // One band per print layer. floorf, not a cast, so this stays correct below z = 0. + const int slot = int(std::floor(pos.z() / band)); + const int phase = ((slot % e.den) + e.den) % e.den; + return phase < e.num ? e.a : e.b; + } + // Ordered 4x4 Bayer over the surface, indexed by position so the pattern is stable in space + // rather than in triangle order (which would move under any remesh, and read as noise). + static const int BAYER[16] = { 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5 }; + const int gx = ((int(std::floor(pos.x() / cell)) % 4) + 4) % 4; + const int gy = ((int(std::floor(pos.y() / cell)) % 4) + 4) % 4; + // A third axis would be ideal, but the two dominant ones are enough for a surface pattern and + // keep the cell square on the faces that matter. + const float threshold = (float(BAYER[gy * 4 + gx]) + 0.5f) / 16.f; + return (float(e.num) / float(e.den)) > threshold ? e.a : e.b; + }; +} + +ColorQuantizeFn GLGizmoTextureDisplacement::make_palette_quantizer(const std::vector &palette) +{ + if (palette.empty()) + return nullptr; + + // Lab once per entry, not once per lookup. + struct Lab { float l, a, b; }; + std::vector palette_lab(palette.size()); + for (size_t i = 0; i < palette.size(); ++i) + RGB2Lab(palette[i].rgb.x() * 255.f, palette[i].rgb.y() * 255.f, palette[i].rgb.z() * 255.f, + &palette_lab[i].l, &palette_lab[i].a, &palette_lab[i].b); + + constexpr int E = PALETTE_LUT_EDGE; + auto lut = std::make_shared>(size_t(E) * E * E, 0); + tbb::parallel_for(tbb::blocked_range(0, E), [&](const tbb::blocked_range &range) { + for (int r = range.begin(); r < range.end(); ++r) + for (int g = 0; g < E; ++g) + for (int b = 0; b < E; ++b) { + // Cell centre, so the quantization error is symmetric across the cell. + float l0, a0, b0; + RGB2Lab((r + 0.5f) / E * 255.f, (g + 0.5f) / E * 255.f, (b + 0.5f) / E * 255.f, &l0, &a0, &b0); + int best = 0; + float best_d = std::numeric_limits::max(); + for (size_t i = 0; i < palette_lab.size(); ++i) { + const float d = DeltaE00(l0, a0, b0, palette_lab[i].l, palette_lab[i].a, palette_lab[i].b); + if (d < best_d) { + best_d = d; + best = int(i); + } + } + (*lut)[(size_t(r) * E + size_t(g)) * E + size_t(b)] = uint8_t(best); + } + }); + + return [lut](const Vec3f &rgb) -> int { + constexpr int E = PALETTE_LUT_EDGE; + const int r = std::clamp(int(rgb.x() * E), 0, E - 1); + const int g = std::clamp(int(rgb.y() * E), 0, E - 1); + const int b = std::clamp(int(rgb.z() * E), 0, E - 1); + return int((*lut)[(size_t(r) * E + size_t(g)) * E + size_t(b)]); + }; +} + TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( const indexed_triangle_set &base, const TextureDisplacementFacetsData &masks, const std::vector &layers, const TextureDisplacementPrepareParams ¶ms, - const DisplacementProgressFn &progress) + const std::vector &palette, const DisplacementProgressFn &progress) { TextureDisplacementPrepareResult out; const auto report = [&progress](int pct) { return !progress || progress(pct); }; @@ -3335,6 +3647,13 @@ TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( HeightFieldSampler sampler; if (params.subdiv_feature) sampler = make_combined_displacement_sampler(mesh.its, layers, current); + // Colour boundaries need triangles of their own - the chord test cannot see them, since + // the height field is perfectly smooth across a change of filament. + ColorFieldSampler color; + if (params.subdiv_color_edge_mm > 0.f && !palette.empty()) + color = make_combined_color_sampler(mesh.its, layers, current, make_palette_quantizer(palette)); + // Note the sampler is built on the *quantizer* alone - the refinement follows perceived + // colour, never the interleaving that realises a mix (see ColorResolveFn). // "Min edge" is a feature-mode control (it is the floor the curvature test refines down // to); in plain adaptive mode the target edge length is the only criterion, so the floor // must not be allowed to silently override a target the user set below it. @@ -3350,7 +3669,8 @@ TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( sampler, tol, floor, params.subdiv_border_mm, [&](int pct) { aborted = !report(50 + pct / 2); return !aborted; - }); + }, + color, params.subdiv_color_edge_mm); if (aborted) return {}; if (refined.indices.size() != mesh.its.indices.size()) { @@ -3387,6 +3707,7 @@ void GLGizmoTextureDisplacement::subdivide_model_adaptive() params.subdiv_detail_mm = m_subdivide_detail_mm; params.subdiv_min_edge_mm = m_subdivide_min_edge_mm; params.subdiv_border_mm = m_subdivide_border_mm; + params.subdiv_color_edge_mm = m_subdivide_color_mm; params.subdiv_feature = m_subdivide_feature; params.subdiv_added_triangles = m_subdivide_budget_k * 1000; queue_prepare(params, _u8L("Adaptive subdivide for texture displacement"), /* then_bake */ false, @@ -3560,9 +3881,17 @@ void GLGizmoTextureDisplacement::rebuild_subdivide_preview() // Min-edge floor. Plain adaptive: tol 0, so only the target-edge-length criterion applies. const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f; const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f; + // Same colour criterion Apply will use, so the previewed wireframe is the mesh that commits. + ColorFieldSampler color; + if (m_subdivide_color_mm > 0.f && any_layer_colors(*mv)) { + cached_palette(); // refreshes m_palette_quantizer if the filaments changed + color = make_combined_color_sampler(mv->mesh().its, mv->texture_displacement_layers, facets, + m_palette_quantizer); + } its = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm, int(mv->mesh().its.indices.size()) + m_subdivide_budget_k * 1000, - nullptr, sampler, tol, floor, m_subdivide_border_mm); + nullptr, sampler, tol, floor, m_subdivide_border_mm, nullptr, color, + m_subdivide_color_mm); } else { if (m_subdivide_count < 1) return; @@ -3634,6 +3963,25 @@ GLTexture *GLGizmoTextureDisplacement::get_layer_thumbnail(const TextureDisplace return m_thumbnails[slot].get(); } +GLTexture *GLGizmoTextureDisplacement::get_layer_color_texture(const TextureDisplacementLayer &layer) +{ + if (layer.empty() || !layer.color_enabled) + return nullptr; + if (m_color_tex && m_color_tex_source == layer.image_data.get() && m_color_tex_smoothing == layer.smoothing) + return m_color_tex.get(); + + std::unique_ptr texture = upload_color_texture(decode_height_texture(layer), HEIGHT_TEX_MAX_PX); + if (!texture) { + m_color_tex.reset(); + m_color_tex_source = nullptr; + return nullptr; + } + m_color_tex = std::move(texture); + m_color_tex_source = layer.image_data.get(); + m_color_tex_smoothing = layer.smoothing; + return m_color_tex.get(); +} + GLTexture *GLGizmoTextureDisplacement::get_layer_height_texture(const TextureDisplacementLayer &layer) { if (layer.empty()) @@ -3670,7 +4018,7 @@ void GLGizmoTextureDisplacement::bake(bool own_snapshot) } m_bake_in_progress = true; - queue_texture_displacement_bake(*mv, [this]() { + queue_texture_displacement_bake(*mv, color_settings_for(*mv), [this]() { m_bake_in_progress = false; // Baking replaces the volume's mesh (new id, new topology) without changing the object's // id or volume count, so GLGizmoPainterBase::data_changed()'s usual change-detection never @@ -3700,6 +4048,10 @@ static constexpr float STD_SUBDIV_MIN_EDGE_MM = 0.02f; // how clean the rim of an unpainted island looks: the bake steps the surface from full displacement to // zero across that band, and nothing else in the criteria can see the step (see collect_paint_region()). static constexpr float STD_SUBDIV_BORDER_MM = 0.4f; +// Edge length a colour boundary is refined to. Finer than the border band because a colour edge is +// what the eye actually lands on - a stepped outline around a printed decal reads as a defect in a +// way a slightly coarse relief transition does not - and it costs triangles along an outline only. +static constexpr float STD_SUBDIV_COLOR_MM = 0.25f; bool GLGizmoTextureDisplacement::apply_standard_mode_presets(ModelVolume *mv) { @@ -3721,6 +4073,7 @@ bool GLGizmoTextureDisplacement::apply_standard_mode_presets(ModelVolume *mv) pin(m_subdivide_detail_mm, STD_SUBDIV_DETAIL_MM); pin(m_subdivide_min_edge_mm, STD_SUBDIV_MIN_EDGE_MM); pin(m_subdivide_border_mm, STD_SUBDIV_BORDER_MM); + pin(m_subdivide_color_mm, STD_SUBDIV_COLOR_MM); // Deliberately *not* pinned: the triangle budget stays visible and editable in Standard mode, so // pinning it would fight the user's own slider every frame. pin(m_remesh_target_edge_mm, STD_REMESH_EDGE_MM); @@ -3752,6 +4105,7 @@ void GLGizmoTextureDisplacement::bake_standard() params.subdiv_detail_mm = STD_SUBDIV_DETAIL_MM; params.subdiv_min_edge_mm = STD_SUBDIV_MIN_EDGE_MM; params.subdiv_border_mm = STD_SUBDIV_BORDER_MM; + params.subdiv_color_edge_mm = STD_SUBDIV_COLOR_MM; params.subdiv_feature = true; params.subdiv_added_triangles = m_subdivide_budget_k * 1000; queue_prepare(params, _u8L("Bake texture displacement"), /* then_bake */ true, {}); @@ -3772,6 +4126,8 @@ void GLGizmoTextureDisplacement::queue_prepare(const TextureDisplacementPrepareP input.layers = mv->texture_displacement_layers; input.params = params; input.snapshot_name = snapshot_name; + if (params.subdiv_color_edge_mm > 0.f) + input.color = color_settings_for(*mv); m_prepare_in_progress = true; queue_texture_displacement_prepare(std::move(input), [this, then_bake, unchanged_msg]( @@ -4216,6 +4572,84 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer->invert); + // Colour. Only offered for a texture that actually has some - the shipped library is + // grayscale, and a checkbox that silently does nothing on nine textures out of ten is + // worse than no checkbox. Disabled rather than hidden so it is clear the feature exists + // and what it wants. + { + const bool has_color = decode_height_texture(*layer).has_color(); + m_imgui->disabled_begin(!has_color); + bool color_enabled = layer->color_enabled && has_color; + if (ImGui::Checkbox(_u8L("Use image colours").c_str(), &color_enabled)) { + layer->color_enabled = color_enabled; + m_preview_params_dirty = true; + } + m_imgui->disabled_end(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(has_color ? + _u8L("Colours the painted area from the texture's own colours, as well as " + "displacing it. Each colour is matched to the closest printable " + "colour, and the result is written as multi-material paint - so the " + "area prints in those filaments. Everything you did not paint keeps " + "the object's own filament.") : + _u8L("This texture is a grayscale height map, so it has no colours to " + "apply. Import a colour image to use this."), + m_imgui->scaled(20.f)); + + // The rest of colour belongs to the whole stack, not to this layer, so it only appears + // once - under whichever layer turned colour on. + if (color_enabled && mv != nullptr) { + TextureDisplacementOptions &opts = mv->texture_displacement_options; + + if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled)) + m_preview_params_dirty = true; + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Interleave pairs of filaments to reach colours between them, so a few " + "filaments cover far more than a few colours. Off means every triangle " + "prints in one of the filaments exactly."), + m_imgui->scaled(20.f)); + + if (opts.color_mix_enabled) { + m_imgui->text(_u8L("Mix by")); + ImGui::SameLine(); + const std::string mix_z = _u8L("Layers"); + const std::string mix_xy = _u8L("Surface"); + const char *mix_items[] = { mix_z.c_str(), mix_xy.c_str() }; + int mix_mode = int(opts.color_mix_mode); + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (scoped_combo("##color_mix_mode", &mix_mode, mix_items, IM_ARRAYSIZE(mix_items))) { + opts.color_mix_mode = ColorMixMode(mix_mode); + m_preview_params_dirty = true; + } + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Layers: the two filaments alternate between print layers, which " + "blends smoothly on upright surfaces but disappears on flat-facing " + "ones, where a whole layer is a single band.\n" + "Surface: a fine checkerboard across the surface, which works at " + "any angle but can read as texture rather than as a blend."), + m_imgui->scaled(20.f)); + + const int count = int(cached_palette().size()); + ImGui::TextDisabled("%s", from_u8(Slic3r::format( + _u8L("%1% printable colours from %2% filaments"), count, + int(m_palette_filaments.size()))).ToUTF8().data()); + } + + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (ImGui::SliderInt(_u8L("Denoise").c_str(), &opts.color_despeckle, 0, 6)) + m_preview_params_dirty = true; + ImGui::PopItemWidth(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Removes stray single triangles of the wrong colour, which is what " + "detail in the image finer than the mesh leaves behind. Each step " + "replaces a triangle's colour with the one most of its neighbours " + "have, so features wider than a triangle are kept. Raise it if the " + "result looks speckled; lower it if fine detail is being eaten."), + m_imgui->scaled(20.f)); + } + } + // The lowest painted layer has nothing underneath it to combine with - it *is* the // base - so a blend mode would be meaningless (and Multiply/Divide against an implicit // zero would annihilate it). build_texture_displacement() forces the first layer to @@ -4784,6 +5218,21 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "0 turns it off."), m_imgui->scaled(20.f)); + // Only worth showing when a layer is actually colouring: with no colour there is no boundary + // for it to refine and the control would do nothing whatever it is set to. + if (mv != nullptr && any_layer_colors(*mv)) { + if (m_imgui->slider_float(std::string(_u8L("Colour detail (mm)")) + "##subdivcolor", + &m_subdivide_color_mm, 0.f, 5.f, "%.3f")) + preview_live(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangle size along a boundary between two colours. Colour is assigned per " + "triangle, so an edge between two filaments can only be as clean as the " + "triangles along it - and the relief criteria cannot see it at all, because " + "the surface is perfectly smooth across a change of colour. Costs triangles " + "along the boundaries only. 0 turns it off."), + m_imgui->scaled(20.f)); + } + ImGui::PopItemWidth(); budget_slider(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 92461fdd1b..93bbc61117 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -45,11 +45,80 @@ public: const TextureDisplacementFacetsData &masks, const std::vector &layers, const TextureDisplacementPrepareParams ¶ms, + const std::vector &palette, const DisplacementProgressFn &progress); // The volume's eight texture-displacement masks, gathered into the array every pure function here // (and every job input) takes. static TextureDisplacementFacetsData facets_data_of(const ModelVolume &mv); + using PaletteEntry = PrintableColor; + + // The printable palette: the loaded filaments (clamped to the sixteen mmu_segmentation_facets can + // address), plus - when `mixing` - every pair of them at evenly spaced ratios. + // + // Mixes are averaged in **CIELAB**, not RGB and not subtractively: two filaments interleaved too + // finely to resolve are averaged by the eye, which is what a perceptual space models. Yellow and + // blue banded together read as a desaturated grey-green, and that is what the preview must promise + // - blending them subtractively would show a green the printer cannot produce this way. + // + // How many ratios depends on how many filaments there are, so the palette stays bounded: the + // quantizer's lookup cube costs one DeltaE00 per cell per entry to fill, and with sixteen + // filaments there are already plenty of colours without mixing any of them. + static std::vector make_palette(const std::vector &filaments, bool mixing); + + // Maps an image colour to the closest entry of `palette`, perceptually (CIEDE2000 over CIELAB - a + // plain RGB distance picks visibly wrong filaments, most obviously between a saturated colour and + // a grey of similar brightness). + // + // Precomputed into a lookup cube rather than matched per call: the subdivision's colour criterion + // samples up to seven points per triangle and re-samples both children of every split, so a live + // match would dominate the refinement. The returned closure owns the cube, so it is safe to hand + // to a worker thread and outlives the palette it was built from. + static ColorQuantizeFn make_palette_quantizer(const std::vector &palette); + + // Turns a palette index plus a position into the filament to print there, interleaving the two + // filaments of a mixed entry per `mode`. `layer_height` sizes the Z bands; `cell_mm` the dither + // cells. See ColorResolveFn for why this is separate from the quantizer. + static ColorResolveFn make_mix_resolver(const std::vector &palette, ColorMixMode mode, + float layer_height, float cell_mm); + + // Everything the jobs need to colour with, for the current volume: palette, mix mode, layer + // height, despeckle. Empty when no layer is actually colouring. + TextureColorSettings color_settings_for(const ModelVolume &mv); + + // The printable palette for the current filaments and mixing setting, rebuilt only when either + // actually changes - see the definition for why that caching is not optional. + const std::vector &cached_palette(); + std::vector m_palette_cache; + std::vector m_palette_filaments; + bool m_palette_mixing = false; + ColorQuantizeFn m_palette_quantizer; + + // The loaded filaments, clamped to the sixteen mmu_segmentation_facets can address. + static std::vector filament_palette(); + // The print's layer height, which sizes ColorMixMode::ZBands. Falls back to 0.2 mm if it cannot be + // read - a wrong band size is a cosmetic error, not a reason to refuse to colour anything. + static float print_layer_height(); + + // The Normal preview's triangles, grouped by the filament they will print in. Colour is per facet + // and there are at most sixteen filaments, so the mesh is uploaded once with its index buffer + // sorted by colour and drawn as one GLModel::render(range) per group - which needs no per-vertex + // colour attribute, and so no change to GLModel's vertex layouts. + // + // The *index buffer* is what gets reordered, never m_preview_its: the paint overlay and the + // wireframe index into that by the volume's own triangle numbering (the bake is + // topology-preserving), and permuting it would silently misplace both. + struct PreviewColorRun + { + std::pair range; // into the GLModel's index buffer, in elements + ColorRGBA color; + }; + std::vector m_preview_color_runs; + // True if any of the volume's layers would actually colour something: colour turned on, and a + // texture that has colour to give. What decides whether a palette is captured into a job at all, + // and so whether the colour criterion and the mmu write ever run. + static bool any_layer_colors(const ModelVolume &mv); + void render_painter_gizmo() override; // Intercepts mouse input while "Adjust Texture" mode is on (dragging the on-canvas offset/ @@ -214,6 +283,9 @@ private: // The same texture at full resolution, for the fast-preview shader. One slot, shared by whichever // layer is active, because that is the only one the bump shader ever shades. GLTexture *get_layer_height_texture(const TextureDisplacementLayer &layer); + // The layer's colour texture for the fast preview's per-fragment quantization. Null when the + // layer is not colouring or its texture is grayscale. + GLTexture *get_layer_color_texture(const TextureDisplacementLayer &layer); // A texture from the picker's library (see slic3r/GUI/TextureLibrary.hpp), read and uploaded // once and then kept for the gizmo's lifetime. The decoded bytes are held alongside the GPU @@ -423,6 +495,13 @@ private: // transition keeps the input's density and the rim of an unpainted island comes out as a ring of // large, steeply tilted triangles. See collect_paint_region() and subdivide_mesh_adaptive(). float m_subdivide_border_mm = 0.4f; + // Edge length triangles straddling a *colour* boundary are refined to (0 = ignore colour). Its own + // control rather than a share of "Detail (mm)" because the two measure different things: Detail is + // a chord error in mm of surface deviation, this is a triangle size in mm along a step the chord + // test cannot see at all - the height field is perfectly smooth across a change of filament, so + // without this a colour boundary lands on whatever triangles the relief happened to need, which on + // a flat surface is none. Only ever costs anything where a boundary actually runs. + float m_subdivide_color_mm = 0.3f; // How many thousand triangles refinement may *add* (the mesh's own count is added on before it is // passed as subdivide_mesh_adaptive()'s absolute cap, so the control still means something on a // dense model). Refinement is worst-error-first, so hitting the budget still yields the best mesh @@ -504,6 +583,12 @@ private: // Whether the current bump mesh carries a precomputed per-vertex uv (LSCM) that the shader // should sample at directly, rather than projecting in-shader. Set by rebuild_bump_preview_mesh(). bool m_bump_preview_uses_vertex_uv = false; + // The palette the fast preview's per-triangle filament indices were built against, captured when + // the mesh was. Empty when the active layer is not colouring, which is what tells the shader to + // fall back to the model's own colour. Held rather than re-read at draw time so the indices baked + // into the mesh can never be resolved against a different set of filaments than they were computed + // from - loading a filament mid-session would otherwise recolour a stale preview at random. + std::vector m_bump_preview_palette; // GPU island drag: while an island is dragged in the UV editor, the bump mesh is baked once (with // the dragged island's vertices flagged, v_normal.y = 1) and then moved purely through the shader's @@ -621,6 +706,12 @@ private: const void *m_height_tex_source = nullptr; float m_height_tex_smoothing = -1.f; + // The same, for the layer's *colour*, which the fast preview quantizes per fragment so it shows + // the image at texel resolution rather than at the mesh's. Null for a grayscale texture. + std::unique_ptr m_color_tex; + const void *m_color_tex_source = nullptr; + float m_color_tex_smoothing = -1.f; + // Library textures the picker has shown at least once, keyed by file path (see LibraryTexture). std::map m_library_textures; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 91493a9173..eac60eb392 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -3,10 +3,12 @@ #include #include "libslic3r/Model.hpp" +#include "libslic3r/TriangleSelector.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/Utils/UndoRedo.hpp" @@ -30,6 +32,21 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) // grows a close button once it reaches 100%, so a job that reports 0 and nothing else leaves an // uncloseable notification pinned on screen. It also carries the Cancel button's effect into the // bake, which on a subdivided mesh can run for several seconds. + // Colour, when any layer asks for it, is computed in the same pass as the displacement: both need + // the same per-layer projection and UV work, and doing it twice would double the expensive part. + TextureColorRequest color_request; + TextureColorRequest *color = nullptr; + if (!m_input.color.empty()) { + color_request.quantize = GLGizmoTextureDisplacement::make_palette_quantizer(m_input.color.palette); + color_request.resolve = GLGizmoTextureDisplacement::make_mix_resolver( + m_input.color.palette, m_input.color.mix_mode, m_input.color.layer_height, + m_input.color.dither_cell_mm); + color_request.despeckle_passes = m_input.color.despeckle_passes; + color_request.out_triangle = &m_triangle_color; + if (color_request.quantize) + color = &color_request; + } + int last_reported = 1; m_result = TriangleMesh(build_texture_displacement( m_input.base_mesh, m_input.layers, m_input.facets_data, m_input.options, @@ -43,7 +60,8 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) ctl.update_status(percent, status); } return true; - })); + }, + color)); // Always finish at 100: this is what closes the notification. Reported even on cancel, where // build_texture_displacement() returns an empty mesh and finalize() commits nothing. @@ -72,6 +90,23 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept volume->set_new_unique_id(); volume->calculate_convex_hull(); + // Colour lands in mmu_segmentation_facets, merged *over* whatever is already painted there + // rather than replacing it: a triangle the texture does not colour keeps its existing filament, + // and one the user never painted at all stays at NONE, which already means "the volume's own + // filament". That is what confines the effect to the painted area without having to invent a + // colour for everything outside it. Safe to index straight onto the new mesh - the bake is + // topology-preserving, so triangle i is still triangle i. + if (!m_triangle_color.empty() && m_triangle_color.size() == volume->mesh().its.indices.size()) { + TriangleSelector selector(volume->mesh()); + const TriangleSelector::TriangleSplittingData &existing = volume->mmu_segmentation_facets.get_data(); + if (!existing.bitstream.empty()) + selector.deserialize(existing, false); + for (size_t i = 0; i < m_triangle_color.size(); ++i) + if (m_triangle_color[i] > 0) + selector.set_facet(int(i), EnforcerBlockerType(m_triangle_color[i])); + volume->mmu_segmentation_facets.set(selector); + } + // Clear the paint mask of every layer that was actually baked so a repeat bake (or the paint // overlay) doesn't act on triangles that no longer represent the same unbaked surface. The // texture layer definitions themselves (and paint outside the baked area, if any) are left @@ -107,10 +142,11 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept } } -void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished, - bool take_snapshot) +void queue_texture_displacement_bake(const ModelVolume &volume, const TextureColorSettings &color, + std::function on_finished, bool take_snapshot) { TextureDisplacementBakeInput input; + input.color = color; input.take_snapshot = take_snapshot; input.volume_id = volume.id(); input.base_mesh = volume.mesh().its; @@ -119,6 +155,7 @@ void queue_texture_displacement_bake(const ModelVolume &volume, std::functionget_ui_job_worker(); queue_job(worker, std::make_unique(std::move(input), std::move(on_finished))); } diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp index bf0fcaa8bd..cd3772ae47 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -4,6 +4,7 @@ #include #include +#include "libslic3r/Color.hpp" #include "libslic3r/ObjectID.hpp" #include "libslic3r/TextureDisplacement.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -22,6 +23,10 @@ struct TextureDisplacementBakeInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Captured on the main thread. Empty unless some layer is colouring, in which case the bake also + // writes the volume's mmu_segmentation_facets - the same per-triangle filament assignment the MMU + // paint gizmo writes - alongside the displaced geometry. + TextureColorSettings color; // Whether this job pushes its own undo step when it commits. False when the caller has already // taken one that is meant to cover the displacement as well - Standard mode's Bake, which remeshes // and subdivides first and has to undo as a single action. @@ -42,6 +47,9 @@ public: private: TextureDisplacementBakeInput m_input; TriangleMesh m_result; + // Per triangle of m_result: the filament to print it in, as an EnforcerBlockerType value + // (0 = leave alone). Empty unless a layer asked for colour. See TextureColorRequest. + std::vector m_triangle_color; std::function m_on_finished; }; @@ -49,8 +57,8 @@ private: // the app's UI job worker. `on_finished` is always called once the job settles (success, failure, // or cancellation), so the caller can clear its own "bake in progress" UI state. Must be called // from the main thread. -void queue_texture_displacement_bake(const ModelVolume &volume, std::function on_finished, - bool take_snapshot = true); +void queue_texture_displacement_bake(const ModelVolume &volume, const TextureColorSettings &color, + std::function on_finished, bool take_snapshot = true); } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp index 5b6cdd246a..4494594c7d 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp @@ -35,7 +35,7 @@ void TextureDisplacementPrepareJob::process(Ctl &ctl) // idle loop. int last_reported = 1; m_result = GLGizmoTextureDisplacement::prepare_mesh(m_input.base_mesh, m_input.masks, m_input.layers, - m_input.params, + m_input.params, m_input.color.palette, [&ctl, &status, &last_reported](int percent) { if (ctl.was_canceled()) return false; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp index 90d723d88f..efe6a8ce75 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp @@ -7,6 +7,7 @@ #include "Job.hpp" +#include "libslic3r/Color.hpp" #include "libslic3r/ObjectID.hpp" #include "libslic3r/TextureDisplacement.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -43,6 +44,10 @@ struct TextureDisplacementPrepareInput TextureDisplacementFacetsData masks; std::vector layers; TextureDisplacementPrepareParams params; + // Captured on the main thread. Empty when no layer is colouring, in which case the refinement + // skips the colour criterion entirely. Only the *quantizer* is used here: refinement follows + // perceived colour, never the interleaving that realises a mix. + TextureColorSettings color; // The undo step the commit opens. Standard mode's Bake names it after the bake, because the // displacement job that follows commits into this same step rather than pushing its own. std::string snapshot_name; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index 44bcf37725..adde26f884 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -1,12 +1,13 @@ #include "TextureDisplacementPreviewJob.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" namespace Slic3r::GUI { TextureDisplacementPreviewJob::TextureDisplacementPreviewJob(TextureDisplacementPreviewInput &&input, uint64_t generation, std::shared_ptr> current_generation, - std::function on_finished) + std::function on_finished) : m_input(std::move(input)), m_generation(generation), m_current_generation(std::move(current_generation)), m_on_finished(std::move(on_finished)) { @@ -19,7 +20,21 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) // Only ever touches m_input (captured by value before this job was queued) and local state - // never the live Model - so this is safe to run concurrently with the UI thread. - m_result = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, m_input.options, + TextureColorRequest color_request; + TextureColorRequest *color = nullptr; + if (!m_input.color.empty()) { + color_request.quantize = GLGizmoTextureDisplacement::make_palette_quantizer(m_input.color.palette); + color_request.resolve = GLGizmoTextureDisplacement::make_mix_resolver( + m_input.color.palette, m_input.color.mix_mode, m_input.color.layer_height, + m_input.color.dither_cell_mm); + color_request.despeckle_passes = m_input.color.despeckle_passes; + color_request.out_triangle = &m_result.triangle_color; + if (color_request.quantize) + color = &color_request; + } + + m_result.mesh = build_texture_displacement(m_input.base_mesh, m_input.layers, m_input.facets_data, + m_input.options, [this, &ctl](int) { // Bail the moment this preview stops being the current // one; build_texture_displacement() then returns an @@ -27,7 +42,8 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) return !ctl.was_canceled() && (!m_current_generation || m_current_generation->load() == m_generation); - }); + }, + color); } void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr &eptr) @@ -40,7 +56,7 @@ void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr & // was ever queued again for the rest of the session. An empty result is the caller's signal that // nothing usable came back; it already handles that. if (canceled || eptr) - m_on_finished(indexed_triangle_set{}, m_generation); + m_on_finished(TextureDisplacementPreviewResult{}, m_generation); else m_on_finished(std::move(m_result), m_generation); } diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp index f9e2a9f389..2a67b8c7cc 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -7,6 +7,7 @@ #include #include +#include "libslic3r/Color.hpp" #include "libslic3r/TextureDisplacement.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -22,6 +23,17 @@ struct TextureDisplacementPreviewInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Empty unless a layer is colouring, in which case the preview reports the filament per triangle + // alongside the mesh, so the Normal view shows what the bake will produce - interleaving included. + TextureColorSettings color; +}; + +// A preview result: the displaced mesh, and - when the input carried a palette - one filament index +// per triangle (an EnforcerBlockerType value; 0 means "no colour from the texture"). +struct TextureDisplacementPreviewResult +{ + indexed_triangle_set mesh; + std::vector triangle_color; }; // Computes the true (unbaked) displaced-mesh preview in the background. With several painted @@ -49,7 +61,7 @@ public: // order: without it, a Bake queued behind a handful of stale previews waits for every one of them. TextureDisplacementPreviewJob(TextureDisplacementPreviewInput &&input, uint64_t generation, std::shared_ptr> current_generation, - std::function on_finished); + std::function on_finished); void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &eptr) override; @@ -58,8 +70,8 @@ private: TextureDisplacementPreviewInput m_input; uint64_t m_generation; std::shared_ptr> m_current_generation; - indexed_triangle_set m_result; - std::function m_on_finished; + TextureDisplacementPreviewResult m_result; + std::function m_on_finished; }; } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/TextureLibrary.cpp b/src/slic3r/GUI/TextureLibrary.cpp index 67df12c32b..3af043a462 100644 --- a/src/slic3r/GUI/TextureLibrary.cpp +++ b/src/slic3r/GUI/TextureLibrary.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "libslic3r/PNGReadWrite.hpp" #include "libslic3r/Utils.hpp" @@ -47,6 +48,52 @@ void scan_dir(const boost::filesystem::path &dir, bool is_user, std::vector &out, std::string &error) +{ + if (image.GetWidth() <= 0 || image.GetHeight() <= 0) { + error = _u8L("The selected image is empty."); + return false; + } + wxMemoryOutputStream stream; + // Alpha would be lost on the way into DecodedHeightTexture anyway, and a partly transparent + // texture reading as black relief is worse than reading as its own colour over the background. + wxImage opaque = image; + if (opaque.HasAlpha()) + opaque.ClearAlpha(); + if (!opaque.SaveFile(stream, wxBITMAP_TYPE_PNG)) { + error = _u8L("Failed to prepare the texture for use."); + return false; + } + const size_t size = size_t(stream.GetLength()); + out.resize(size); + stream.CopyTo(out.data(), size); + if (out.empty()) { + error = _u8L("Failed to prepare the texture for use."); + return false; + } + return true; +} + // Slic3r::png only writes PNGs to a file, so the encode round-trips through a temp file rather than // staying in memory. It happens once per import / per texture pick, not per frame, so the I/O is // not worth avoiding with a second PNG encoder. @@ -87,14 +134,22 @@ bool encode_gray_png_bytes(const wxImage &image, std::vector &out return true; } +// Grayscale in, grayscale out; colour in, colour out. +bool encode_png_bytes(const wxImage &image, std::vector &out, std::string &error) +{ + return image_has_color(image) ? encode_color_png_bytes(image, out, error) + : encode_gray_png_bytes(image, out, error); +} + std::vector read_file_bytes(const std::string &path) { std::ifstream ifs(path, std::ios::binary); return std::vector(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); } -// True if these bytes are already the 8-bit grayscale PNG libslic3r can decode, i.e. can be stored -// on a layer as-is. Mirrors exactly what decode_height_texture() accepts. +// True if these bytes are a PNG libslic3r can decode, i.e. can be stored on a layer as-is. Mirrors +// exactly what decode_height_texture() accepts: 8-bit grayscale, or colour (whose luminance is the +// height and whose RGB is available to colour the model). bool is_supported_height_map(const std::vector &bytes) { if (bytes.empty()) @@ -102,8 +157,12 @@ bool is_supported_height_map(const std::vector &bytes) const png::ReadBuf rbuf{ bytes.data(), bytes.size() }; if (!png::is_png(rbuf)) return false; - png::ImageGreyscale img; - return png::decode_png(rbuf, img) && img.cols > 0 && img.rows > 0; + png::ImageGreyscale gray; + if (png::decode_png(rbuf, gray) && gray.cols > 0 && gray.rows > 0) + return true; + png::ImageColorscale color; + return png::decode_colored_png(rbuf, color) && color.cols > 0 && color.rows > 0 && + color.bytes_per_pixel >= 3; } std::vector g_library; @@ -153,7 +212,7 @@ std::optional import_texture_to_library(const std::string & } std::vector bytes; - if (!encode_gray_png_bytes(image, bytes, error)) + if (!encode_png_bytes(image, bytes, error)) return std::nullopt; // Never overwrite an existing texture (the user's or, if they picked the same name twice, their @@ -192,15 +251,15 @@ std::shared_ptr> load_texture_image_data(const std::s if (is_supported_height_map(bytes)) return std::make_shared>(std::move(bytes)); - // Not an 8-bit grayscale PNG (a colour image somebody copied into the folder by hand, say): - // convert it the same way an import would, but leave the file on disk alone. + // Not a PNG at all (a jpg somebody copied into the folder by hand, say): convert it the same way + // an import would - keeping its colour if it has any - but leave the file on disk alone. wxImage image; if (!image.LoadFile(from_u8(path)) || !image.IsOk()) { error = _u8L("Could not load the selected image."); return nullptr; } std::vector converted; - if (!encode_gray_png_bytes(image, converted, error)) + if (!encode_png_bytes(image, converted, error)) return nullptr; return std::make_shared>(std::move(converted)); } diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 5978418ae2..6d6fda7ca1 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -634,3 +634,216 @@ TEST_CASE("TextureDisplacement: feature-adaptive subdivision follows curvature, CHECK(worst <= 0.03f); } } + +// A 2x2 truecolour PNG: red, green / blue, white. Written out as bytes rather than encoded here +// because libslic3r only *writes* grayscale PNGs (png::write_gray_to_file) - which is also exactly +// why the colour path exists: the GUI importer stores colour images through wxImage instead. +static std::shared_ptr> make_rgb_png_2x2() +{ + static const unsigned char bytes[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x08, 0x02, 0x00, 0x00, 0x00, 0xfd, 0xd4, 0x9a, 0x73, 0x00, 0x00, 0x00, + 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xf8, 0xcf, 0xc0, 0xc0, + 0x00, 0xc2, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x1f, 0xee, 0x05, + 0xfb, 0x60, 0x6c, 0x70, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82, + }; + return std::make_shared>(std::begin(bytes), std::end(bytes)); +} + +TEST_CASE("TextureDisplacement: a colour texture decodes to both colour and height", "[TextureDisplacement]") +{ + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_rgb_png_2x2(); + + const DecodedHeightTexture tex = decode_height_texture(layer); + REQUIRE_FALSE(tex.empty()); + REQUIRE(tex.has_color()); + REQUIRE(tex.width == 2); + REQUIRE(tex.height == 2); + REQUIRE(tex.rgb.size() == 2 * 2 * 3); + + // Row-major, top-to-bottom: red, green / blue, white. + CHECK(tex.rgb[0] == 255); CHECK(tex.rgb[1] == 0); CHECK(tex.rgb[2] == 0); + CHECK(tex.rgb[3] == 0); CHECK(tex.rgb[4] == 255); CHECK(tex.rgb[5] == 0); + CHECK(tex.rgb[6] == 0); CHECK(tex.rgb[7] == 0); CHECK(tex.rgb[8] == 255); + CHECK(tex.rgb[9] == 255); CHECK(tex.rgb[10] == 255); CHECK(tex.rgb[11] == 255); + + // Height is the luminance, with wxImage::ConvertToGreyscale()'s coefficients - which is what + // makes a texture displace identically whether it was imported before or after colour was kept. + CHECK(int(tex.pixels[0]) == int(std::lround(0.299 * 255))); // red + CHECK(int(tex.pixels[1]) == int(std::lround(0.587 * 255))); // green + CHECK(int(tex.pixels[2]) == int(std::lround(0.114 * 255))); // blue + CHECK(int(tex.pixels[3]) == 255); // white +} + +TEST_CASE("TextureDisplacement: a grayscale texture reports no colour", "[TextureDisplacement]") +{ + // The shipped library is all grayscale, and has_color() is what the whole colour feature keys + // off - a height map must never look like it has colours to apply. + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_flat_gray_png(128); + + const DecodedHeightTexture tex = decode_height_texture(layer); + REQUIRE_FALSE(tex.empty()); + CHECK_FALSE(tex.has_color()); + CHECK(tex.rgb.empty()); + + Vec3f out(9.f, 9.f, 9.f); + CHECK_FALSE(sample_layer_color(tex, layer, Vec3f::Zero(), Vec3f::UnitZ(), out)); + CHECK(out.x() == 9.f); // left untouched on a false return +} + +// Two triangles making a 10x10 quad in the z=0 plane. +static indexed_triangle_set color_test_quad() +{ + indexed_triangle_set quad; + quad.vertices = { Vec3f(0, 0, 0), Vec3f(10, 0, 0), Vec3f(10, 10, 0), Vec3f(0, 10, 0) }; + quad.indices = { { 0, 1, 2 }, { 0, 2, 3 } }; + return quad; +} + +TEST_CASE("TextureDisplacement: colour is reported per triangle and only where painted", "[TextureDisplacement]") +{ + const indexed_triangle_set quad = color_test_quad(); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_rgb_png_2x2(); + layer.color_enabled = true; + layer.depth_mm = 0.f; // colour only, so this isolates the colour path from the geometry + layer.tiling_scale = 100.f; + layer.projection_method = TextureProjectionMethod::Triplanar; + + TriangleMesh mesh(quad); + TriangleSelector selector(mesh); + selector.set_facet(0, EnforcerBlockerType::ENFORCER); // only the first triangle + + TextureDisplacementFacetsData facets; + facets[0] = selector.serialize(); + + // A three-entry palette matched in plain RGB: all this test needs is *an* index. The perceptual + // matching is the GUI's (make_palette_quantizer), and is deliberately not under test here. + const std::array palette = { Vec3f(1, 0, 0), Vec3f(0, 1, 0), Vec3f(0, 0, 1) }; + TextureColorRequest request; + std::vector triangle_color; + request.out_triangle = &triangle_color; + request.quantize = [&palette](const Vec3f &rgb) { + int best = 0; + float bd = std::numeric_limits::max(); + for (int i = 0; i < 3; ++i) + if (const float d = (palette[size_t(i)] - rgb).squaredNorm(); d < bd) { + bd = d; + best = i; + } + return best; + }; + + const indexed_triangle_set out = build_texture_displacement(quad, { layer }, facets, {}, {}, &request); + + REQUIRE_FALSE(out.indices.empty()); + REQUIRE(triangle_color.size() == quad.indices.size()); + // The painted triangle takes a filament; the unpainted one is left at 0, which is + // EnforcerBlockerType::NONE - "use the volume's own filament". That is what confines the effect + // to the painted area without having to invent a colour for everything outside it. + CHECK(triangle_color[0] != 0); + CHECK(triangle_color[1] == 0); +} + +TEST_CASE("TextureDisplacement: a layer that is not colouring reports no colours", "[TextureDisplacement]") +{ + const indexed_triangle_set quad = color_test_quad(); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_rgb_png_2x2(); + layer.color_enabled = false; // the checkbox is off: colour stays off even on a colour texture + layer.depth_mm = 1.f; + + TriangleMesh mesh(quad); + TriangleSelector selector(mesh); + selector.set_facet(0, EnforcerBlockerType::ENFORCER); + selector.set_facet(1, EnforcerBlockerType::ENFORCER); + + TextureDisplacementFacetsData facets; + facets[0] = selector.serialize(); + + TextureColorRequest request; + std::vector triangle_color; + request.out_triangle = &triangle_color; + request.quantize = [](const Vec3f &) { return 0; }; + + build_texture_displacement(quad, { layer }, facets, {}, {}, &request); + + REQUIRE(triangle_color.size() == quad.indices.size()); + CHECK(triangle_color[0] == 0); + CHECK(triangle_color[1] == 0); +} + +TEST_CASE("TextureDisplacement: subdivision refines a colour boundary a flat height field hides", + "[TextureDisplacement]") +{ + // A cube with a flat height field, so *nothing* in the height criteria has any reason to refine + // it - which is exactly the case the colour criterion exists for. Closed, so every_edge_used_twice() + // is an exact crack detector: the colour criterion goes through the same conformal bisection as + // everything else and must not be able to open one. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const std::vector region(cube.indices.size(), REFINE_PAINTED); + + auto longest_edge = [](const indexed_triangle_set &its, const stl_triangle_vertex_indices &t) { + float m = 0.f; + for (int e = 0; e < 3; ++e) + m = std::max(m, (its.vertices[t[e]] - its.vertices[t[(e + 1) % 3]]).norm()); + return m; + }; + + // One filament on each side of x = 5: a step, with no gradient anywhere for a chord test to see. + ColorFieldSampler split_at_five = [](const Vec3f &p, const Vec3f &) { return p.x() < 5.f ? 0 : 1; }; + ColorFieldSampler all_one = [](const Vec3f &, const Vec3f &) { return 0; }; + + SECTION("a colour boundary gets triangles") + { + const indexed_triangle_set out = + subdivide_mesh_adaptive(cube, region, /*max edge*/ 0.f, 200000, nullptr, nullptr, 0.f, + /*min_edge*/ 0.05f, /*border*/ 0.f, nullptr, split_at_five, + /*colour edge*/ 0.5f); + + CHECK(out.indices.size() > cube.indices.size()); + CHECK(every_edge_used_twice(out)); // still watertight + + // Every triangle still straddling the boundary must be down at the target. + for (const auto &t : out.indices) { + bool straddles = false; + for (int i = 1; i < 3; ++i) + if ((out.vertices[t[i]].x() < 5.f) != (out.vertices[t[0]].x() < 5.f)) + straddles = true; + if (straddles) + CHECK(longest_edge(out, t) <= 0.5f + 1e-4f); + } + } + + SECTION("a uniform colour adds nothing") + { + const indexed_triangle_set out = + subdivide_mesh_adaptive(cube, region, /*max edge*/ 0.f, 200000, nullptr, nullptr, 0.f, + /*min_edge*/ 0.05f, /*border*/ 0.f, nullptr, all_one, + /*colour edge*/ 0.5f); + + CHECK(out.indices.size() == cube.indices.size()); + } + + SECTION("no colour sampler leaves the mesh alone") + { + // The regression this guards: the colour criterion must be inert when nothing is colouring, + // or every bake would start refining geometry for no reason. + const indexed_triangle_set out = + subdivide_mesh_adaptive(cube, region, /*max edge*/ 0.f, 200000, nullptr, nullptr, 0.f, + /*min_edge*/ 0.05f, /*border*/ 0.f, nullptr, nullptr, + /*colour edge*/ 0.5f); + + CHECK(out.indices.size() == cube.indices.size()); + } +} From 4a48fc770dce3ed4cc8f262d759460e11419ff28 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 3 Sep 2026 08:48:03 +0200 Subject: [PATCH 23/89] Add alternative backe pipeline --- src/libslic3r/CMakeLists.txt | 16 + .../TextureBake/TextureBakeDecimate.cpp | 478 ++++++++++++++++++ .../TextureBake/TextureBakeDecimate.hpp | 56 ++ .../TextureBake/TextureBakeDisplace.cpp | 268 ++++++++++ .../TextureBake/TextureBakeDisplace.hpp | 68 +++ .../TextureBake/TextureBakeIndex.cpp | 25 + .../TextureBake/TextureBakeIndex.hpp | 164 ++++++ src/libslic3r/TextureBake/TextureBakeMesh.cpp | 62 +++ src/libslic3r/TextureBake/TextureBakeMesh.hpp | 19 + .../TextureBake/TextureBakePipeline.cpp | 216 ++++++++ .../TextureBake/TextureBakePipeline.hpp | 98 ++++ .../TextureBake/TextureBakeRegularize.cpp | 380 ++++++++++++++ .../TextureBake/TextureBakeRegularize.hpp | 73 +++ .../TextureBake/TextureBakeRepair.cpp | 215 ++++++++ .../TextureBake/TextureBakeRepair.hpp | 48 ++ .../TextureBake/TextureBakeSubdivide.cpp | 414 +++++++++++++++ .../TextureBake/TextureBakeSubdivide.hpp | 83 +++ src/libslic3r/TextureDisplacement.cpp | 186 ++++++- src/libslic3r/TextureDisplacement.hpp | 14 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 53 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 5 +- 21 files changed, 2913 insertions(+), 28 deletions(-) create mode 100644 src/libslic3r/TextureBake/TextureBakeDecimate.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDecimate.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDisplace.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDisplace.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeIndex.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeIndex.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeMesh.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeMesh.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakePipeline.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakePipeline.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRegularize.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRegularize.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRepair.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRepair.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeSubdivide.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeSubdivide.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a0677e332e..56b2a1168f 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -464,6 +464,22 @@ set(lisbslic3r_sources TextConfiguration.hpp TextureDisplacement.cpp TextureDisplacement.hpp + TextureBake/TextureBakeIndex.cpp + TextureBake/TextureBakeIndex.hpp + TextureBake/TextureBakeSubdivide.cpp + TextureBake/TextureBakeSubdivide.hpp + TextureBake/TextureBakeRegularize.cpp + TextureBake/TextureBakeRegularize.hpp + TextureBake/TextureBakeDisplace.cpp + TextureBake/TextureBakeDisplace.hpp + TextureBake/TextureBakeDecimate.cpp + TextureBake/TextureBakeDecimate.hpp + TextureBake/TextureBakeRepair.cpp + TextureBake/TextureBakeRepair.hpp + TextureBake/TextureBakePipeline.cpp + TextureBake/TextureBakePipeline.hpp + TextureBake/TextureBakeMesh.cpp + TextureBake/TextureBakeMesh.hpp Thread.cpp Thread.hpp Time.cpp diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp new file mode 100644 index 0000000000..d4744f40a7 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -0,0 +1,478 @@ +#include "TextureBakeDecimate.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +// Symmetric 4x4 quadric, as its 10 upper-triangle values. +struct Quadric +{ + std::array q{}; + + void add_plane(double a, double b, double c, double d) + { + q[0] += a * a; q[1] += a * b; q[2] += a * c; q[3] += a * d; + q[4] += b * b; q[5] += b * c; q[6] += b * d; + q[7] += c * c; q[8] += c * d; + q[9] += d * d; + } + void operator+=(const Quadric &o) + { + for (int i = 0; i < 10; ++i) + q[size_t(i)] += o.q[size_t(i)]; + } + double eval(double x, double y, double z) const + { + return q[0] * x * x + 2 * q[1] * x * y + 2 * q[2] * x * z + 2 * q[3] * x + + q[4] * y * y + 2 * q[5] * y * z + 2 * q[6] * y + + q[7] * z * z + 2 * q[8] * z + q[9]; + } +}; + +double eval_sum(const std::vector &qs, int v1, int v2, const Vec3d &p) +{ + return qs[size_t(v1)].eval(p.x(), p.y(), p.z()) + qs[size_t(v2)].eval(p.x(), p.y(), p.z()); +} + +// The position minimising the summed quadric, if the system is well conditioned enough to trust. +bool solve_q(const std::vector &qs, int v1, int v2, Vec3d &out) +{ + const auto &A = qs[size_t(v1)].q; + const auto &B = qs[size_t(v2)].q; + const double a00 = A[0] + B[0], a01 = A[1] + B[1], a02 = A[2] + B[2]; + const double a11 = A[4] + B[4], a12 = A[5] + B[5], a22 = A[7] + B[7]; + const double b0 = -(A[3] + B[3]), b1 = -(A[6] + B[6]), b2 = -(A[8] + B[8]); + + const double det = a00 * (a11 * a22 - a12 * a12) - a01 * (a01 * a22 - a12 * a02) + + a02 * (a01 * a12 - a11 * a02); + const double max_el = std::max({ std::abs(a00), std::abs(a01), std::abs(a02), std::abs(a11), + std::abs(a12), std::abs(a22) }); + // Scaled with the matrix, so it means the same at any model scale. + const double threshold = max_el * max_el * max_el * 1e-10; + if (std::abs(det) < std::max(threshold, 1e-30)) + return false; + + const double inv = 1.0 / det; + out.x() = inv * (b0 * (a11 * a22 - a12 * a12) - a01 * (b1 * a22 - a12 * b2) + a02 * (b1 * a12 - a11 * b2)); + out.y() = inv * (a00 * (b1 * a22 - a12 * b2) - b0 * (a01 * a22 - a12 * a02) + a02 * (a01 * b2 - b1 * a02)); + out.z() = inv * (a00 * (a11 * b2 - b1 * a12) - a01 * (a01 * b2 - b1 * a02) + b0 * (a01 * a12 - a11 * a02)); + return true; +} + +Vec3d face_normal_unit(const std::vector &pos, int a, int b, int c) +{ + const Vec3d n = (pos[size_t(b)] - pos[size_t(a)]).cross(pos[size_t(c)] - pos[size_t(a)]); + const double len = n.norm(); + return (len > 0.0) ? Vec3d(n / len) : Vec3d::Zero(); +} + +// Versions are captured at push time; a mismatch on pop means a later collapse invalidated the entry. +// Lazy deletion, far cheaper than removing entries eagerly. +struct HeapEntry +{ + double cost; + int v1, v2; + uint32_t ver1, ver2; + Vec3d p; + bool operator>(const HeapEntry &o) const { return cost > o.cost; } +}; + +} // namespace + +DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool harvest_flat, + double harvest_tol, const std::vector &locked_faces, + const DecimateProgressFn &on_progress) +{ + DecimateResult result; + const size_t n = geometry.pos.size(); + if (n < 3) { + result.geometry = geometry; + return result; + } + + // The finest grid. Anything coarser fuses distinct fine-feature vertices on a displaced mesh, + // leaving it non-manifold before decimation starts and producing open edges afterwards. + QuantizedPointMap vert_map(WELD_GRID_DECIMATION, std::min(n, size_t(1) << 22)); + std::vector pos; + std::vector remap(n); + for (size_t i = 0; i < n; ++i) { + const int idx = vert_map.get_or_set(geometry.pos[i], int(pos.size())); + if (vert_map.inserted()) + pos.push_back(geometry.pos[i].cast()); + remap[i] = idx; + } + const size_t vert_count = pos.size(); + const size_t face_count = n / 3; + std::vector faces(face_count * 3); + for (size_t i = 0; i < n; ++i) + faces[i] = remap[i]; + + if (face_count <= target_triangles && !harvest_flat) { + result.geometry = geometry; + return result; + } + + // An edge with a locked endpoint never reaches the heap. + std::vector locked_vert; + size_t locked_face_count = 0; + if (!locked_faces.empty()) { + locked_vert.assign(vert_count, 0); + for (size_t f = 0; f < face_count && f < locked_faces.size(); ++f) { + if (!locked_faces[f]) + continue; + ++locked_face_count; + for (int k = 0; k < 3; ++k) + locked_vert[size_t(faces[f * 3 + size_t(k)])] = 1; + } + } + // With the locked faces alone at the target, chasing it would grind the free region to its guard + // limit for nothing - harvest only, and say so. + const bool locked_over_budget = + !locked_vert.empty() && face_count > target_triangles && locked_face_count >= target_triangles; + result.locked_over_budget = locked_over_budget; + if (locked_over_budget && !harvest_flat) { + result.geometry = geometry; + return result; + } + + std::vector quadrics(vert_count); + for (size_t f = 0; f < face_count; ++f) { + const int a = faces[f * 3], b = faces[f * 3 + 1], c = faces[f * 3 + 2]; + if (a < 0) + continue; + const Vec3d nrm = face_normal_unit(pos, a, b, c); + if (nrm.isZero()) + continue; + const double d = -nrm.dot(pos[size_t(a)]); + for (const int v : { a, b, c }) + quadrics[size_t(v)].add_plane(nrm.x(), nrm.y(), nrm.z(), d); + } + + // Two penalty planes per endpoint on a sharp interior edge, each perpendicular to one adjacent + // face and containing the edge, constraining the vertex to the crease line. + { + struct EdgeRec { int va, vb, f0, f1; uint8_t count; }; + std::vector edges; + QuantizedPointMap edge_idx(1.0, std::min(face_count * 3, size_t(1) << 22)); + for (size_t f = 0; f < face_count; ++f) { + if (faces[f * 3] < 0) + continue; + for (int e = 0; e < 3; ++e) { + const int va = faces[f * 3 + size_t(e)]; + const int vb = faces[f * 3 + size_t((e + 1) % 3)]; + const int lo = std::min(va, vb), hi = std::max(va, vb); + const int ei = edge_idx.get_or_set_key(lo, hi, 0, int(edges.size())); + if (edge_idx.inserted()) + edges.push_back({ lo, hi, int(f), -1, 1 }); + else if (edges[size_t(ei)].count == 1) { + edges[size_t(ei)].f1 = int(f); + edges[size_t(ei)].count = 2; + } else + // Non-manifold; never feeds a crease. + edges[size_t(ei)].count = 3; + } + } + + const double sqrt_w = std::sqrt(DECIMATE_CREASE_WEIGHT); + for (const EdgeRec &er : edges) { + if (er.count != 2) + continue; // boundary or non-manifold + const Vec3d n0 = face_normal_unit(pos, faces[size_t(er.f0) * 3], faces[size_t(er.f0) * 3 + 1], + faces[size_t(er.f0) * 3 + 2]); + const Vec3d n1 = face_normal_unit(pos, faces[size_t(er.f1) * 3], faces[size_t(er.f1) * 3 + 1], + faces[size_t(er.f1) * 3 + 2]); + if (n0.dot(n1) >= DECIMATE_CREASE_COS) + continue; // smooth enough to be no crease + + const Vec3d e = pos[size_t(er.vb)] - pos[size_t(er.va)]; + const double elen = e.norm(); + if (elen <= 0.0) + continue; + const Vec3d ed = e / elen; + for (const Vec3d &fn : { n0, n1 }) { + Vec3d pn = fn.cross(ed); + const double plen = pn.norm(); + if (plen < 1e-10) + continue; // edge parallel to the face normal + pn /= plen; + const double d = -pn.dot(pos[size_t(er.va)]); + // sqrt(w) on the inputs gives w times the accumulated products. + for (const int v : { er.va, er.vb }) + quadrics[size_t(v)].add_plane(pn.x() * sqrt_w, pn.y() * sqrt_w, pn.z() * sqrt_w, + d * sqrt_w); + } + } + } + + // Vertex-face incidence as intrusive linked lists of slots over flat arrays. + const size_t S = face_count * 3; + std::vector vf_head(vert_count, -1), slot_face(S), slot_vert(S), slot_next(S, -1), + slot_prev(S, -1), face_slot(S, -1); + for (size_t f = 0; f < face_count; ++f) + for (int k = 0; k < 3; ++k) { + const int s = int(f) * 3 + k; + const int v = faces[size_t(s)]; + slot_face[size_t(s)] = int(f); + slot_vert[size_t(s)] = v; + slot_next[size_t(s)] = vf_head[size_t(v)]; + slot_prev[size_t(s)] = -1; + if (vf_head[size_t(v)] >= 0) + slot_prev[size_t(vf_head[size_t(v)])] = s; + vf_head[size_t(v)] = s; + face_slot[size_t(s)] = s; + } + const auto unlink_slot = [&](int s) { + const int p = slot_prev[size_t(s)], nx = slot_next[size_t(s)]; + if (p >= 0) slot_next[size_t(p)] = nx; + else vf_head[size_t(slot_vert[size_t(s)])] = nx; + if (nx >= 0) slot_prev[size_t(nx)] = p; + }; + const auto move_slot = [&](int s, int nv) { + unlink_slot(s); + slot_next[size_t(s)] = vf_head[size_t(nv)]; + slot_prev[size_t(s)] = -1; + if (vf_head[size_t(nv)] >= 0) + slot_prev[size_t(vf_head[size_t(nv)])] = s; + vf_head[size_t(nv)] = s; + slot_vert[size_t(s)] = nv; + }; + + std::vector active(vert_count, 1); + std::vector version(vert_count, 0); + std::vector nb_stamp(vert_count, 0), lk_stamp(vert_count, 0); + uint32_t epoch = 1, lk_epoch = 1; + size_t active_faces = face_count; + + std::priority_queue, std::greater> heap; + + const auto push_edge = [&](int v1, int v2) { + Vec3d p; + if (!solve_q(quadrics, v1, v2, p)) { + const Vec3d mid = (pos[size_t(v1)] + pos[size_t(v2)]) * 0.5; + const double e1 = eval_sum(quadrics, v1, v2, pos[size_t(v1)]); + const double e2 = eval_sum(quadrics, v1, v2, pos[size_t(v2)]); + const double em = eval_sum(quadrics, v1, v2, mid); + const double emin = std::min({ e1, e2, em }); + const double etol = emin * 1e-2 + 1e-12; + // The midpoint when the three are near-equal, i.e. flat: it moves adjacent triangles + // least, so fewer normal flips and no stalling on coplanar geometry. + if (em <= emin + etol) p = mid; + else if (e1 <= e2) p = pos[size_t(v1)]; + else p = pos[size_t(v2)]; + } + // Where quadric costs are all near zero, shorter edges first keeps triangle quality up. + const double len2 = (pos[size_t(v2)] - pos[size_t(v1)]).squaredNorm(); + heap.push({ eval_sum(quadrics, v1, v2, p) + len2 * 1e-8, v1, v2, version[size_t(v1)], + version[size_t(v2)], p }); + }; + + { + QuantizedPointMap seed_seen(1.0, std::min(face_count * 3, size_t(1) << 22)); + for (size_t f = 0; f < face_count; ++f) { + if (faces[f * 3] < 0) + continue; + for (int e = 0; e < 3; ++e) { + const int va = faces[f * 3 + size_t(e)]; + const int vb = faces[f * 3 + size_t((e + 1) % 3)]; + if (!locked_vert.empty() && (locked_vert[size_t(va)] || locked_vert[size_t(vb)])) + continue; + seed_seen.get_or_set_key(std::min(va, vb), std::max(va, vb), 0, 1); + if (seed_seen.inserted()) + push_edge(va, vb); + } + } + } + + // 0 means a stale entry, 1 a boundary edge, 2 or more safe. + const auto shared_face_count = [&](int v1, int v2) { + int count = 0; + for (int s = vf_head[size_t(v1)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) + if (faces[size_t(f) * 3 + size_t(k)] == v2) { + if (++count >= 2) + return 2; + break; + } + } + return count; + }; + + // Safe only when the sole common neighbours of the endpoints are the apexes of the faces the edge + // already shares; any other would pile a third triangle onto an edge after the collapse. + const auto has_link_violation = [&](int v1, int v2, uint32_t ep) { + for (int s = vf_head[size_t(v1)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) + if (const int x = faces[size_t(f) * 3 + size_t(k)]; x != v1) + lk_stamp[size_t(x)] = ep; + } + int shared = 0; + for (int s = vf_head[size_t(v1)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + const int a = faces[size_t(f) * 3], b = faces[size_t(f) * 3 + 1], c = faces[size_t(f) * 3 + 2]; + if (a == v2 || b == v2 || c == v2) { + ++shared; + const int apex = (a != v1 && a != v2) ? a : (b != v1 && b != v2) ? b : c; + lk_stamp[size_t(apex)] = ep + 1; // a legal shared-face apex + } + } + if (shared > 2) + return true; // already non-manifold + for (int s = vf_head[size_t(v2)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) { + const int x = faces[size_t(f) * 3 + size_t(k)]; + if (x != v2 && x != v1 && lk_stamp[size_t(x)] == ep) + return true; + } + } + return false; + }; + + // Squared-dot, so no square root or division. Faces containing the other endpoint are the ones + // being removed, so they are skipped. + const auto check_flipped = [&](int vc, int vo, const Vec3d &np) { + for (int s = vf_head[size_t(vc)]; s >= 0; s = slot_next[size_t(s)]) { + const size_t f = size_t(slot_face[size_t(s)]); + if (faces[f * 3] < 0) + continue; + const int fa = faces[f * 3], fb = faces[f * 3 + 1], fc = faces[f * 3 + 2]; + if (fa == vo || fb == vo || fc == vo) + continue; + const Vec3d oa = pos[size_t(fa)], ob = pos[size_t(fb)], oc = pos[size_t(fc)]; + const Vec3d on = (ob - oa).cross(oc - oa); + const Vec3d na = (fa == vc) ? np : oa; + const Vec3d nb = (fb == vc) ? np : ob; + const Vec3d nc = (fc == vc) ? np : oc; + const Vec3d nn = (nb - na).cross(nc - na); + const double raw = on.dot(nn); + if (raw < 0.0) + return true; + if (raw * raw < DECIMATE_FLIP_DOT * DECIMATE_FLIP_DOT * on.squaredNorm() * nn.squaredNorm()) + return true; + } + return false; + }; + + const size_t init_faces = active_faces; + const size_t to_remove = std::max(1, init_faces > target_triangles + ? init_faces - target_triangles : init_faces); + const double harvest_ceil = harvest_tol * harvest_tol; + bool reached_target = locked_over_budget; + double last_progress = 0.0; + + while (!heap.empty()) { + if (active_faces <= target_triangles) { + if (!harvest_flat) + break; + reached_target = true; + } + + const HeapEntry top = heap.top(); + heap.pop(); + // The popped entry is the cheapest left, so exceeding the tolerance ends the run. + if (reached_target && top.cost > harvest_ceil) + break; + + const int v1 = top.v1, v2 = top.v2; + if (!active[size_t(v1)] || !active[size_t(v2)]) + continue; + if (version[size_t(v1)] != top.ver1 || version[size_t(v2)] != top.ver2) + continue; + if (shared_face_count(v1, v2) < 2) + continue; + lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call + if (has_link_violation(v1, v2, lk_epoch)) + continue; + if (check_flipped(v1, v2, top.p) || check_flipped(v2, v1, top.p)) + continue; + + // v1 survives at the new position, v2 goes. + pos[size_t(v1)] = top.p; + quadrics[size_t(v1)] += quadrics[size_t(v2)]; + ++version[size_t(v1)]; + + for (int s = vf_head[size_t(v2)]; s >= 0;) { + const size_t f = size_t(slot_face[size_t(s)]); + const int s_next = slot_next[size_t(s)]; // read before the list is modified + if (faces[f * 3] >= 0) { + for (int k = 0; k < 3; ++k) + if (faces[f * 3 + size_t(k)] == v2) { + faces[f * 3 + size_t(k)] = v1; + break; + } + const int fa = faces[f * 3], fb = faces[f * 3 + 1], fc = faces[f * 3 + 2]; + if (fa == fb || fb == fc || fa == fc) { + for (int k = 0; k < 3; ++k) + if (const int sk = face_slot[f * 3 + size_t(k)]; sk >= 0) { + unlink_slot(sk); + face_slot[f * 3 + size_t(k)] = -1; + } + faces[f * 3] = faces[f * 3 + 1] = faces[f * 3 + 2] = -1; + --active_faces; + } else + move_slot(s, v1); + } + s = s_next; + } + active[size_t(v2)] = 0; + + ++epoch; + for (int sv = vf_head[size_t(v1)]; sv >= 0; sv = slot_next[size_t(sv)]) { + const size_t f = size_t(slot_face[size_t(sv)]); + if (faces[f * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) { + const int nb = faces[f * 3 + size_t(k)]; + if (nb == v1 || nb_stamp[size_t(nb)] == epoch) + continue; + nb_stamp[size_t(nb)] = epoch; + // v1 is never locked - a locked edge never entered the heap. + if (active[size_t(nb)] && (locked_vert.empty() || !locked_vert[size_t(nb)])) + push_edge(v1, nb); + } + } + + if (on_progress) { + const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); + if (p - last_progress > 0.005) { + last_progress = p; + if (!on_progress(p)) + break; + } + } + } + + // Rebuild from the surviving faces, with per-face normals. + TriSoup &out = result.geometry; + for (size_t f = 0; f < face_count; ++f) { + if (faces[f * 3] < 0) + continue; + const Vec3f a = pos[size_t(faces[f * 3])].cast(); + const Vec3f b = pos[size_t(faces[f * 3 + 1])].cast(); + const Vec3f c = pos[size_t(faces[f * 3 + 2])].cast(); + const Vec3f nrm = (b - a).cross(c - a).normalized(); + out.pos.insert(out.pos.end(), { a, b, c }); + out.nrm.insert(out.nrm.end(), { nrm, nrm, nrm }); + } + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp new file mode 100644 index 0000000000..8876ab00df --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -0,0 +1,56 @@ +#pragma once + +// Quadric error metric decimation (Garland & Heckbert), with two additions that matter on a +// displaced mesh. +// +// Crease quadrics: an interior edge sharper than the threshold gets penalty planes at both endpoints, +// perpendicular to each adjacent face and through the edge, weighted so such edges collapse last or +// not at all. A texture's hard step keeps its geometry while the flat ground around it reduces. +// +// Flat-face harvesting: the loop keeps going past the triangle target while each collapse's error +// stays under an absolute bound, so flat faces that cost nothing to remove are not left behind. + +#include +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +// Reject a collapse deviating more than about 78 degrees from the old face normal. +static constexpr double DECIMATE_FLIP_DOT = 0.2; +// Edges sharper than 60 degrees are treated as creases. +static constexpr double DECIMATE_CREASE_COS = 0.5; +// Quadric penalty weight for a crease plane. +static constexpr double DECIMATE_CREASE_WEIGHT = 1e4; + +// Upper bound in mm on the deviation a harvested collapse may introduce; the real one is smaller, +// since the cost sums squared distances over all incident faces. +// +// Absolute, not relative to the cost at which the target was crossed. A relative band fails in the +// case with the most to shed: when the target is reached with a large flat surplus left, the crossing +// cost is essentially zero, so the band is too and nothing is harvested. +static constexpr double DECIMATE_DEFAULT_HARVEST_TOL = 0.005; + +// Returns false to cancel. +using DecimateProgressFn = std::function; + +struct DecimateResult +{ + TriSoup geometry; + // The locked faces alone met the target, so it was unreachable without touching preserved + // geometry. + bool locked_over_budget = false; +}; + +// `locked_faces`: one entry per input triangle; a vertex touching one may neither move nor be +// removed, which also pins the ring between the two regions. +DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool harvest_flat = true, + double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL, + const std::vector &locked_faces = {}, + const DecimateProgressFn &on_progress = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDisplace.cpp b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp new file mode 100644 index 0000000000..ef417a7bef --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp @@ -0,0 +1,268 @@ +#include "TextureBakeDisplace.hpp" + +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample, + const DisplaceSettings &settings, const DisplaceBounds &bounds, + const DisplaceProgressFn &on_progress) +{ + TriSoup out; + const size_t count = geometry.pos.size(); + if (count == 0 || !sample) + return geometry; + + out.pos.resize(count); + out.nrm.resize(count); + + // Everything below is keyed by this id, which is what makes one vector per position expressible. + const bool need_id_positions = settings.boundary_falloff > 0.f; + QuantizedPointMap dedup(WELD_GRID_GEOMETRY, std::min(count, size_t(1) << 22)); + std::vector vertex_id(count); + std::vector id_pos; + int next_id = 0; + for (size_t i = 0; i < count; ++i) { + const int id = dedup.get_or_set(geometry.pos[i], next_id); + if (dedup.inserted()) { + ++next_id; + if (need_id_positions) + id_pos.push_back(geometry.pos[i]); + } + vertex_id[i] = id; + } + const size_t unique_count = size_t(next_id); + + // Pass 1: area-weighted smooth normals per position, plus what masking and falloff need. + std::vector smooth_nrm(unique_count, Vec3d::Zero()); + std::vector masked_area(unique_count, 0.0), total_area(unique_count, 0.0); + const bool have_weights = !geometry.exclude_weight.empty(); + std::vector user_excluded_face(have_weights ? count / 3 : 0, 0); + std::vector excluded_pos(have_weights ? unique_count : 0, 0); + + for (size_t t = 0; t + 2 < count; t += 3) { + const Vec3d a = geometry.pos[t].cast(); + const Vec3d face_n = (geometry.pos[t + 1].cast() - a).cross(geometry.pos[t + 2].cast() - a); + const double face_area = face_n.norm(); // twice the triangle area, so weighting is natural + const double nz = face_area > 1e-12 ? face_n.z() / face_area : 0.0; + const double face_angle = std::acos(std::min(1.0, std::abs(nz))) * (180.0 / M_PI); + const bool angle_masked = + nz < 0.0 ? (settings.bottom_angle_limit > 0.f && face_angle <= settings.bottom_angle_limit) + : (settings.top_angle_limit > 0.f && face_angle <= settings.top_angle_limit); + + // Thresholded high, not at a half: merging by maximum leaves a face bordering an excluded one + // with two corners at 1.0, averaging about 0.67, which a half threshold would misread. + bool user_excluded = false; + if (have_weights) { + const float avg = (geometry.exclude_weight[t] + geometry.exclude_weight[t + 1] + + geometry.exclude_weight[t + 2]) / 3.f; + user_excluded = avg > 0.99f; + if (user_excluded) + user_excluded_face[t / 3] = 1; + } + + for (int v = 0; v < 3; ++v) { + const size_t vid = size_t(vertex_id[t + size_t(v)]); + if (user_excluded && have_weights) + excluded_pos[vid] = 1; + // Subdivision split vertices at sharp edges, so these are smooth across soft edges and + // sharp across hard ones - no faceting on round surfaces, no rounding of corners. + smooth_nrm[vid] += geometry.nrm[t + size_t(v)].cast() * face_area; + if (angle_masked) + masked_area[vid] += face_area; + total_area[vid] += face_area; + } + } + + // The pre-normalisation magnitude over the total area says how much the neighbouring faces agree: + // near 1 they do, near 0 they cancelled, meaning a knife edge with no usable surface direction. + std::vector reliability(unique_count, 0.0); + for (size_t id = 0; id < unique_count; ++id) { + const double len = smooth_nrm[id].norm(); + reliability[id] = (len > 0.0 && total_area[id] > 0.0) ? len / total_area[id] : 0.0; + smooth_nrm[id] = (len > 0.0) ? Vec3d(smooth_nrm[id] / len) : Vec3d(0.0, 0.0, 1.0); + } + + // Pass 1.5: the smoothed blend normal - see the header for why it is separate. + std::vector blend_nrm = smooth_nrm; + if (settings.blend_normal_smoothing > 0 && unique_count > 0) { + // CSR adjacency over the welded graph, deliberately a multigraph: duplicates weight a pair by + // how often it shares an edge, so a well-connected surface couples more strongly. + std::vector degree(unique_count, 0); + const auto add_degree = [&](int a, int b) { + if (a != b) { ++degree[size_t(a)]; ++degree[size_t(b)]; } + }; + for (size_t t = 0; t + 2 < count; t += 3) { + const int a = vertex_id[t], b = vertex_id[t + 1], c = vertex_id[t + 2]; + add_degree(a, b); add_degree(b, c); add_degree(c, a); + } + std::vector csr_start(unique_count + 1, 0); + for (size_t id = 0; id < unique_count; ++id) + csr_start[id + 1] = csr_start[id] + degree[id]; + std::vector neighbors(csr_start[unique_count]); + std::vector cursor(unique_count, 0); + const auto add_edge = [&](int a, int b) { + if (a == b) + return; + neighbors[csr_start[size_t(a)] + cursor[size_t(a)]++] = uint32_t(b); + neighbors[csr_start[size_t(b)] + cursor[size_t(b)]++] = uint32_t(a); + }; + for (size_t t = 0; t + 2 < count; t += 3) { + const int a = vertex_id[t], b = vertex_id[t + 1], c = vertex_id[t + 2]; + add_edge(a, b); add_edge(b, c); add_edge(c, a); + } + + std::vector cur = smooth_nrm, nxt(unique_count, Vec3d::Zero()); + for (int iter = 0; iter < settings.blend_normal_smoothing; ++iter) { + for (size_t id = 0; id < unique_count; ++id) { + const uint32_t s = csr_start[id], e = csr_start[id + 1]; + if (e == s) { + nxt[id] = cur[id]; + continue; + } + Vec3d sum = Vec3d::Zero(); + for (uint32_t k = s; k < e; ++k) + sum += cur[neighbors[k]]; + sum /= double(e - s); + const double len = sum.norm(); + // Cancelling neighbours mean a knife edge; keep what we had. + nxt[id] = (len > 1e-12) ? Vec3d(sum / len) : cur[id]; + } + cur.swap(nxt); + } + blend_nrm = std::move(cur); + } + + // A boundary position borders both masked and unmasked faces, or sits on the exclusion seam. + // Every other position gets its distance to the nearest one, ramped to 1 at the falloff distance. + std::vector falloff; + if (settings.boundary_falloff > 0.f && unique_count > 0) { + std::vector boundary; + for (size_t id = 0; id < unique_count; ++id) { + const double frac = total_area[id] > 0.0 ? masked_area[id] / total_area[id] : 0.0; + const bool on_excl = !excluded_pos.empty() && excluded_pos[id] != 0; + if (on_excl || (frac > 0.0 && frac < 1.0)) + boundary.push_back(id_pos[id]); + } + falloff.assign(unique_count, 1.0); + if (!boundary.empty()) { + // A uniform grid: the query is nearest-point only, so a tree costs more than it saves. + Vec3f lo = boundary.front(), hi = boundary.front(); + for (const Vec3f &p : boundary) { + lo = lo.cwiseMin(p); + hi = hi.cwiseMax(p); + } + const Vec3f span = (hi - lo).cwiseMax(Vec3f(1e-6f, 1e-6f, 1e-6f)); + const int res = std::clamp(int(std::ceil(std::cbrt(double(boundary.size())) * 2.0)), 4, 128); + const Vec3f cell = span / float(res); + const float cell_min = cell.minCoeff(); + const auto cell_of = [&](const Vec3f &p) { + Vec3i32 c; + for (int k = 0; k < 3; ++k) + c[k] = std::clamp(int((p[k] - lo[k]) / span[k] * float(res)), 0, res - 1); + return c; + }; + const auto cell_index = [&](int x, int y, int z) { + return size_t(z) * size_t(res) * size_t(res) + size_t(y) * size_t(res) + size_t(x); + }; + std::vector> grid(size_t(res) * size_t(res) * size_t(res)); + for (size_t i = 0; i < boundary.size(); ++i) { + const Vec3i32 c = cell_of(boundary[i]); + grid[cell_index(c.x(), c.y(), c.z())].push_back(int(i)); + } + + const double radius = double(settings.boundary_falloff); + for (size_t id = 0; id < unique_count; ++id) { + const Vec3f &p = id_pos[id]; + const Vec3i32 c = cell_of(p); + double best = std::numeric_limits::max(); + // Anything in shell r is at least (r - 1) cells away, so once the best found is within + // that bound nothing closer can be hiding further out. + for (int r = 0; r < res; ++r) { + for (int dz = -r; dz <= r; ++dz) + for (int dy = -r; dy <= r; ++dy) + for (int dx = -r; dx <= r; ++dx) { + // The shell only; its interior was covered by a smaller r. + if (r > 0 && std::abs(dx) != r && std::abs(dy) != r && std::abs(dz) != r) + continue; + const int qx = c.x() + dx, qy = c.y() + dy, qz = c.z() + dz; + if (qx < 0 || qy < 0 || qz < 0 || qx >= res || qy >= res || qz >= res) + continue; + for (const int bi : grid[cell_index(qx, qy, qz)]) + best = std::min(best, double((boundary[size_t(bi)] - p).norm())); + } + if (best <= double(r) * double(cell_min)) + break; + } + falloff[id] = (best == std::numeric_limits::max() || radius <= 0.0) + ? 1.0 + : std::clamp(best / radius, 0.0, 1.0); + } + } + } + + // Pass 2: one sample per unique position. + std::vector grey(unique_count, 0.0); + std::vector grey_set(unique_count, 0); + for (size_t i = 0; i < count; ++i) { + const size_t vid = size_t(vertex_id[i]); + if (grey_set[vid]) + continue; + grey_set[vid] = 1; + grey[vid] = double(sample(geometry.pos[i], smooth_nrm[vid].cast(), + blend_nrm[vid].cast())); + } + + // Pass 3: move every copy of a position by the identical vector. + for (size_t i = 0; i < count; ++i) { + const Vec3f &p = geometry.pos[i]; + const size_t vid = size_t(vertex_id[i]); + + // Only angle masking uses the per-position blend, so an excluded face never dims its + // neighbours through a shared vertex. + const bool face_excluded = !user_excluded_face.empty() && user_excluded_face[i / 3] != 0; + // Pinned where an included face shares a position with an excluded one, sealing the boundary. + const bool sealed_boundary = + !face_excluded && !excluded_pos.empty() && excluded_pos[vid] != 0; + const double masked_frac = total_area[vid] > 0.0 ? masked_area[vid] / total_area[vid] : 0.0; + const double centered = settings.symmetric ? (grey[vid] - 0.5) : grey[vid]; + const double ramp = falloff.empty() ? 1.0 : falloff[vid]; + const double disp = (face_excluded || sealed_boundary) + ? 0.0 + : ramp * (1.0 - masked_frac) * centered * double(settings.amplitude); + + Vec3d moved = p.cast() + smooth_nrm[vid] * disp; + + // Stop a partly masked vertex poking through the surface it borders. + if (masked_frac > 0.0) { + if (settings.bottom_angle_limit > 0.f && moved.z() < double(p.z())) moved.z() = double(p.z()); + if (settings.top_angle_limit > 0.f && moved.z() > double(p.z())) moved.z() = double(p.z()); + } + if (settings.no_downward_z && moved.z() < double(p.z())) + moved.z() = double(p.z()); + // A vertex starting on the bottom plane stays there: otherwise a downward-facing face pulls + // *up* where the sample is below mid-grey, leaving bed-contact vertices at differing heights. + if (settings.no_downward_z && double(p.z()) <= double(bounds.min.z()) + 1e-5) + moved.z() = double(p.z()); + + out.pos[i] = moved.cast(); + + if (on_progress && (i % 5000) == 0 && !on_progress(double(i) / double(count))) + return geometry; // cancelled: hand back the input untouched + } + + // Per-face, not averaged across shared positions: averaging can flip an excluded face's normal + // when its neighbours moved outward. + for (size_t t = 0; t + 2 < count; t += 3) { + const Vec3f n = (out.pos[t + 1] - out.pos[t]).cross(out.pos[t + 2] - out.pos[t]).normalized(); + out.nrm[t] = out.nrm[t + 1] = out.nrm[t + 2] = n; + } + out.exclude_weight = geometry.exclude_weight; + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDisplace.hpp b/src/libslic3r/TextureBake/TextureBakeDisplace.hpp new file mode 100644 index 0000000000..eee6ca0626 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDisplace.hpp @@ -0,0 +1,68 @@ +#pragma once + +// Displacement along surface normals. +// +// The mesh is non-indexed, so at a shared edge two triangles hold the same position with different +// face normals; displacing each copy along its own normal sends them to different points and opens a +// crack. So one smooth (area-weighted) normal per unique position drives both the sample lookup and +// the displacement direction, every copy moves by the same vector, and the result is watertight by +// construction. Displaced normals are then smooth at hard edges, but the geometry is still faceted, +// so printed edges stay sharp. + +#include +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +// Height at a point, called once per unique welded position. `smooth_normal` is the vector the +// displacement will move along; `blend_normal` is that after smoothing, for projection blend weights. +using HeightSampleFn = std::function; + +struct DisplaceSettings +{ + // Displacement height in mm, applied to the sampled value. + float amplitude = 0.4f; + + // Sample around a mid-grey rest level rather than displacing outward only. + bool symmetric = false; + + // Faces flatter than these (degrees from horizontal) are held back, leaving bed-contact and top + // surfaces alone. 0 disables that side. + float bottom_angle_limit = 5.f; + float top_angle_limit = 0.f; + + // Never move a vertex below its original Z, so no new overhang. The sideways component is kept. + bool no_downward_z = false; + + // Distance in mm over which displacement ramps up from a mask boundary. 0 leaves a hard edge. + float boundary_falloff = 0.f; + + // Laplacian iterations on the blend normal only - the displacement direction must stay the exact + // smooth normal or copies of a position move differently and the mesh cracks. Inside a blend band + // the weight gradient is largest, so a few degrees of vertex-to-vertex jitter multiplies the + // difference between two unrelated height samples into visible seam noise. A no-op on an + // already-smooth surface. + int blend_normal_smoothing = 32; +}; + +// Model extents; only the minimum Z is read, for the bottom-plane clamp. +struct DisplaceBounds +{ + Vec3f min = Vec3f::Zero(); + Vec3f max = Vec3f::Zero(); +}; + +// Returns false to cancel. +using DisplaceProgressFn = std::function; + +TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample, + const DisplaceSettings &settings, const DisplaceBounds &bounds, + const DisplaceProgressFn &on_progress = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeIndex.cpp b/src/libslic3r/TextureBake/TextureBakeIndex.cpp new file mode 100644 index 0000000000..ac7234774b --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeIndex.cpp @@ -0,0 +1,25 @@ +#include "TextureBakeIndex.hpp" + +#include + +namespace Slic3r { +namespace TextureBake { + +WeldResult weld_vertices(const std::vector &positions, double quant) +{ + WeldResult out; + QuantizedPointMap map(quant, std::min(positions.size(), size_t(1) << 22)); + out.vertex_id.resize(positions.size()); + int next_id = 0; + for (size_t i = 0; i < positions.size(); ++i) { + const int id = map.get_or_set(positions[i], next_id); + if (map.inserted()) + ++next_id; + out.vertex_id[i] = id; + } + out.unique_count = next_id; + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeIndex.hpp b/src/libslic3r/TextureBake/TextureBakeIndex.hpp new file mode 100644 index 0000000000..74ef068174 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeIndex.hpp @@ -0,0 +1,164 @@ +#pragma once + +// Vertex welding for the texture bake pipeline. The pipeline works on non-indexed triangle soup, so +// a shared point exists once per incident triangle with float noise between the copies; welding maps +// each quantised position to one integer id. +// +// The three grids below are deliberately not unified - changing one at a call site changes +// watertightness. 100 um matches the precision files are written with; 10 um keeps small fillet +// vertices distinct (they merge at 100 um, giving needle artifacts after displacement) while still +// absorbing float noise; 1 um is what collapse positioning needs. + +#include +#include +#include + +#include "../Point.hpp" + +namespace Slic3r { +namespace TextureBake { + +static constexpr double WELD_GRID_EXPORT = 1e4; // 100 um +static constexpr double WELD_GRID_GEOMETRY = 1e5; // 10 um +static constexpr double WELD_GRID_DECIMATION = 1e6; // 1 um + +// Round half toward positive infinity. Quantised coordinates hit exact halves often enough that the +// tie rule matters. +inline int64_t grid_round(double v) { return int64_t(std::floor(v + 0.5)); } + +// Open-addressing table over flat arrays: no allocation per lookup, exact integer key comparison. +// Values must be non-negative; -1 is the empty sentinel and what get() returns on a miss. +class QuantizedPointMap +{ +public: + explicit QuantizedPointMap(double quant, size_t expected = 256) : m_quant(quant) + { + size_t cap = 16; + const size_t target = std::max(16, size_t(std::ceil(double(expected) / 0.6))); + while (cap < target) + cap *= 2; + alloc(cap); + } + + size_t size() const { return m_size; } + // Whether the last get_or_set() inserted rather than found. + bool inserted() const { return m_inserted; } + + int get(float x, float y, float z) + { + return m_val[slot(grid_round(double(x) * m_quant), grid_round(double(y) * m_quant), + grid_round(double(z) * m_quant))]; + } + int get(const Vec3f &p) { return get(p.x(), p.y(), p.z()); } + + // The value already stored for this position's grid cell; if there is none, store `value` and + // return it. inserted() then says which of the two happened. + int get_or_set(float x, float y, float z, int value) + { + const int64_t qx = grid_round(double(x) * m_quant); + const int64_t qy = grid_round(double(y) * m_quant); + const int64_t qz = grid_round(double(z) * m_quant); + const size_t i = slot(qx, qy, qz); + if (m_val[i] != -1) { + m_inserted = false; + return m_val[i]; + } + m_qx[i] = qx; m_qy[i] = qy; m_qz[i] = qz; + m_val[i] = value; + m_inserted = true; + if (++m_size > size_t(double(m_cap) * 0.7)) + grow(); + return value; + } + int get_or_set(const Vec3f &p, int value) { return get_or_set(p.x(), p.y(), p.z(), value); } + + // The same table as a set of integer tuples (edge marking, midpoint cache). Quantisation is + // bypassed: routing ids through the float overloads loses precision above 2^24. + int get_key(int64_t a, int64_t b, int64_t c) { return m_val[slot(a, b, c)]; } + int get_or_set_key(int64_t a, int64_t b, int64_t c, int value) + { + const size_t i = slot(a, b, c); + if (m_val[i] != -1) { + m_inserted = false; + return m_val[i]; + } + m_qx[i] = a; m_qy[i] = b; m_qz[i] = c; + m_val[i] = value; + m_inserted = true; + if (++m_size > size_t(double(m_cap) * 0.7)) + grow(); + return value; + } + +private: + void alloc(size_t cap) + { + m_cap = cap; + m_mask = cap - 1; + m_qx.assign(cap, 0); + m_qy.assign(cap, 0); + m_qz.assign(cap, 0); + m_val.assign(cap, -1); + } + + size_t slot(int64_t qx, int64_t qy, int64_t qz) const + { + uint32_t h = uint32_t(int32_t(qx) * int32_t(0x9E3779B1)) ^ + uint32_t(int32_t(qy) * int32_t(0x85EBCA77)) ^ + uint32_t(int32_t(qz) * int32_t(0xC2B2AE3D)); + h ^= h >> 15; + size_t i = size_t(h) & m_mask; + // Equality is checked against the stored 64-bit keys, so truncating to 32 bits for the hash + // costs collisions at worst, never a wrong answer. + while (m_val[i] != -1) { + if (m_qx[i] == qx && m_qy[i] == qy && m_qz[i] == qz) + return i; + i = (i + 1) & m_mask; + } + return i; + } + + void grow() + { + std::vector oqx = std::move(m_qx), oqy = std::move(m_qy), oqz = std::move(m_qz); + std::vector oval = std::move(m_val); + const size_t ocap = m_cap; + alloc(ocap * 2); + for (size_t i = 0; i < ocap; ++i) { + if (oval[i] == -1) + continue; + const size_t s = slot(oqx[i], oqy[i], oqz[i]); + m_qx[s] = oqx[i]; m_qy[s] = oqy[i]; m_qz[s] = oqz[i]; + m_val[s] = oval[i]; + } + } + + double m_quant; + size_t m_cap = 0, m_mask = 0, m_size = 0; + bool m_inserted = false; + std::vector m_qx, m_qy, m_qz; + std::vector m_val; +}; + +// Three consecutive entries per triangle. The indexers turn this into shared vertices where a stage +// needs adjacency. +struct TriSoup +{ + std::vector pos; + std::vector nrm; // parallel to pos + std::vector exclude_weight; // parallel to pos; empty when nothing is excluded + + size_t triangle_count() const { return pos.size() / 3; } + bool empty() const { return pos.empty(); } +}; + +// Assign each vertex the sequential id of its quantised position, first occurrence winning. +struct WeldResult +{ + std::vector vertex_id; + int unique_count = 0; +}; +WeldResult weld_vertices(const std::vector &positions, double quant); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeMesh.cpp b/src/libslic3r/TextureBake/TextureBakeMesh.cpp new file mode 100644 index 0000000000..6f6d72218d --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeMesh.cpp @@ -0,0 +1,62 @@ +#include "TextureBakeMesh.hpp" + +#include + +namespace Slic3r { +namespace TextureBake { + +TriSoup to_soup(const indexed_triangle_set &its, const std::vector &face_excluded) +{ + TriSoup out; + const size_t n = its.indices.size(); + out.pos.resize(n * 3); + out.nrm.resize(n * 3); + const bool have_excl = face_excluded.size() == n; + if (have_excl) + out.exclude_weight.resize(n * 3); + + for (size_t t = 0; t < n; ++t) { + const stl_triangle_vertex_indices &tri = its.indices[t]; + const Vec3f a = its.vertices[size_t(tri[0])]; + const Vec3f b = its.vertices[size_t(tri[1])]; + const Vec3f c = its.vertices[size_t(tri[2])]; + Vec3f nrm = (b - a).cross(c - a); + const float len = nrm.norm(); + nrm = (len > 0.f) ? Vec3f(nrm / len) : Vec3f(0.f, 0.f, 1.f); + out.pos[t * 3] = a; + out.pos[t * 3 + 1] = b; + out.pos[t * 3 + 2] = c; + // Per-face on purpose: the accurate indexer derives smooth normals and splits at sharp edges + // itself, so averaged ones would pre-empt that. + out.nrm[t * 3] = out.nrm[t * 3 + 1] = out.nrm[t * 3 + 2] = nrm; + if (have_excl) { + const float w = face_excluded[t] ? 1.f : 0.f; + out.exclude_weight[t * 3] = out.exclude_weight[t * 3 + 1] = out.exclude_weight[t * 3 + 2] = w; + } + } + return out; +} + +indexed_triangle_set to_indexed_triangle_set(const TriSoup &soup) +{ + indexed_triangle_set out; + const size_t n = soup.pos.size(); + out.indices.reserve(n / 3); + QuantizedPointMap map(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + std::vector id(n); + for (size_t i = 0; i < n; ++i) { + id[i] = map.get_or_set(soup.pos[i], int(out.vertices.size())); + if (map.inserted()) + out.vertices.push_back(soup.pos[i]); + } + for (size_t t = 0; t + 2 < n; t += 3) { + // Welded-together corners carry no area. + if (id[t] == id[t + 1] || id[t + 1] == id[t + 2] || id[t] == id[t + 2]) + continue; + out.indices.emplace_back(id[t], id[t + 1], id[t + 2]); + } + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeMesh.hpp b/src/libslic3r/TextureBake/TextureBakeMesh.hpp new file mode 100644 index 0000000000..b4e3ced5fe --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeMesh.hpp @@ -0,0 +1,19 @@ +#pragma once + +// Conversion between the pipeline's triangle soup and the indexed mesh used elsewhere. The pipeline +// stays on soup because each stage welds on its own grid, and those differences are load-bearing. + +#include "TextureBakeIndex.hpp" +#include "../TriangleMesh.hpp" + +namespace Slic3r { +namespace TextureBake { + +// `face_excluded`: one entry per input triangle, becoming the soup's per-corner exclusion weight. +TriSoup to_soup(const indexed_triangle_set &its, const std::vector &face_excluded = {}); + +// Welds at the geometry grid. +indexed_triangle_set to_indexed_triangle_set(const TriSoup &soup); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp new file mode 100644 index 0000000000..a86fda0e0d --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -0,0 +1,216 @@ +#include "TextureBakePipeline.hpp" + +#include +#include + +namespace Slic3r { +namespace TextureBake { + +void clamp_below_bottom(TriSoup &geometry, float bottom_z) +{ + for (size_t t = 0; t + 2 < geometry.pos.size(); t += 3) { + bool dirty = false; + for (int k = 0; k < 3; ++k) + if (geometry.pos[t + size_t(k)].z() < bottom_z) { + geometry.pos[t + size_t(k)].z() = bottom_z; + dirty = true; + } + if (!dirty) + continue; + Vec3f n = (geometry.pos[t + 1] - geometry.pos[t]).cross(geometry.pos[t + 2] - geometry.pos[t]); + const float len = n.norm(); + n = (len > 0.f) ? Vec3f(n / len) : Vec3f(0.f, 0.f, 1.f); + geometry.nrm[t] = geometry.nrm[t + 1] = geometry.nrm[t + 2] = n; + } +} + +size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol) +{ + const size_t vert_count = geometry.pos.size(); + const size_t tri_count = vert_count / 3; + if (tri_count == 0 || tol <= 0.0) + return 0; + + // Weld at the finest grid: by this point copies of one position are bit-identical, because every + // earlier stage moved them by the same vector. + QuantizedPointMap weld(WELD_GRID_DECIMATION, std::min(vert_count, size_t(1) << 22)); + std::vector vid(vert_count); + int unique = 0; + for (size_t i = 0; i < vert_count; ++i) { + vid[i] = weld.get_or_set(geometry.pos[i], unique); + if (weld.inserted()) + ++unique; + } + // Incident corners per position, CSR style. + std::vector start(size_t(unique) + 1, 0); + for (size_t i = 0; i < vert_count; ++i) + ++start[size_t(vid[i]) + 1]; + for (size_t id = 0; id < size_t(unique); ++id) + start[id + 1] += start[id]; + std::vector inc(vert_count), cursor(size_t(unique), 0); + for (size_t i = 0; i < vert_count; ++i) + inc[start[size_t(vid[i])] + cursor[size_t(vid[i])]++] = uint32_t(i); + + const double fold_cos = std::cos(75.0 * M_PI / 180.0); + std::vector dirty_tri(tri_count, 0); + + for (size_t id = 0; id < size_t(unique); ++id) { + const float z = geometry.pos[inc[start[id]]].z(); + if (z == bottom_z || std::abs(double(z) - double(bottom_z)) > tol) + continue; + + // Simulate the move: every incident triangle must keep positive area and must not fold. + bool ok = true; + for (uint32_t k = start[id]; k < start[id + 1] && ok; ++k) { + const size_t t = size_t(inc[k]) / 3; + Vec3f p[3]; + for (int v = 0; v < 3; ++v) { + p[v] = geometry.pos[t * 3 + size_t(v)]; + if (vid[t * 3 + size_t(v)] == int(id)) + p[v].z() = bottom_z; + } + const Vec3d on = (geometry.pos[t * 3 + 1] - geometry.pos[t * 3]) + .cross(geometry.pos[t * 3 + 2] - geometry.pos[t * 3]).cast(); + const Vec3d nn = (p[1] - p[0]).cross(p[2] - p[0]).cast(); + const double o2 = on.squaredNorm(), n2 = nn.squaredNorm(); + if (n2 < 1e-20) { ok = false; break; } // would collapse to zero area + if (o2 < 1e-20) continue; // already degenerate, cannot judge a rotation + const double dot = on.dot(nn); + if (dot < 0.0 || dot * dot < fold_cos * fold_cos * o2 * n2) + ok = false; + } + if (!ok) + continue; + + for (uint32_t k = start[id]; k < start[id + 1]; ++k) { + geometry.pos[inc[k]].z() = bottom_z; + dirty_tri[size_t(inc[k]) / 3] = 1; + } + } + + size_t dirty = 0; + for (size_t t = 0; t < tri_count; ++t) { + if (!dirty_tri[t]) + continue; + ++dirty; + Vec3f n = (geometry.pos[t * 3 + 1] - geometry.pos[t * 3]) + .cross(geometry.pos[t * 3 + 2] - geometry.pos[t * 3]); + const float len = n.norm(); + n = (len > 0.f) ? Vec3f(n / len) : Vec3f(0.f, 0.f, 1.f); + geometry.nrm[t * 3] = geometry.nrm[t * 3 + 1] = geometry.nrm[t * 3 + 2] = n; + } + return dirty; +} + +PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, + const PipelineSettings &settings, const DisplaceBounds &bounds, + PipelineMode mode, const std::vector &face_excluded, + const PipelineProgressFn &on_progress) +{ + PipelineResult result; + const auto report = [&](const char *stage, double f) { + return !on_progress || on_progress(stage, f); + }; + + if (input.empty() || !sample) { + result.geometry = input; + return result; + } + + // 1. Refine to the target edge length. + SubdivideResult sub = subdivide( + input, settings.refine_length, face_excluded, /* fast */ false, settings.safety_cap, + [&](double f, size_t, double) { return report("subdivide", f); }); + result.safety_cap_hit = sub.safety_cap_hit; + if (!report("subdivide", 1.0)) { + result.canceled = true; + return result; + } + + // 2. Dissolve the slivers refinement inherited, then recover the edges that lengthened. + if (settings.regularize) { + RegularizeOptions ropts = settings.regularize_opts; + ropts.preserve_excluded = settings.preserve_untextured; + RegularizeResult reg = regularize_mesh(sub.geometry, sub.face_parent_id, + settings.refine_length, ropts); + result.collapse_count = reg.collapse_count; + if (!report("regularize", 1.0)) { + result.canceled = true; + return result; + } + if (reg.collapse_count > 0) { + // Excluded faces are carried on the soup itself, so the flag is re-derived rather than + // indexed across the collapse. + std::vector excl; + if (!reg.geometry.exclude_weight.empty()) { + excl.assign(reg.geometry.triangle_count(), 0); + for (size_t t = 0; t < excl.size(); ++t) + excl[t] = reg.geometry.exclude_weight[t * 3] > 0.99f ? 1 : 0; + } + sub = subdivide(reg.geometry, settings.refine_length * settings.regularize_second_pass_mul, + excl, false, settings.safety_cap, + [&](double f, size_t, double) { return report("re-subdivide", f); }); + result.safety_cap_hit = result.safety_cap_hit || sub.safety_cap_hit; + // The second pass renumbers faces, so the parent map has to be composed through it. + std::vector composed(sub.face_parent_id.size()); + for (size_t i = 0; i < composed.size(); ++i) { + const int mid = sub.face_parent_id[i]; + composed[i] = (mid >= 0 && size_t(mid) < reg.face_parent_id.size()) + ? reg.face_parent_id[size_t(mid)] : -1; + } + sub.face_parent_id = std::move(composed); + } else { + sub.geometry = std::move(reg.geometry); + sub.face_parent_id = std::move(reg.face_parent_id); + } + } + + // 3. Displace. + TriSoup displaced = apply_displacement(sub.geometry, sample, settings.displace, bounds, + [&](double f) { return report("displace", f); }); + if (!report("displace", 1.0)) { + result.canceled = true; + return result; + } + + // 4. Decimate - export only. A bake needs the face-parent map, which a collapse destroys. + std::vector parent = std::move(sub.face_parent_id); + if (mode == PipelineMode::Export) { + const bool needs_decimation = displaced.triangle_count() > settings.max_triangles; + if (needs_decimation || settings.harvest_flat) { + std::vector locked; + if (settings.preserve_untextured && !displaced.exclude_weight.empty()) { + locked.assign(displaced.triangle_count(), 0); + for (size_t t = 0; t < locked.size(); ++t) + locked[t] = displaced.exclude_weight[t * 3] > 0.99f ? 1 : 0; + } + DecimateResult dec = decimate(displaced, settings.max_triangles, settings.harvest_flat, + settings.harvest_tol, locked, + [&](double f) { return report("decimate", f); }); + result.locked_over_budget = dec.locked_over_budget; + displaced = std::move(dec.geometry); + parent.clear(); // no longer meaningful + } + if (!report("decimate", 1.0)) { + result.canceled = true; + return result; + } + } + + // 5. Flatten the bed-contact surface. + if (settings.displace.bottom_angle_limit > 0.f) + clamp_below_bottom(displaced, bounds.min.z()); + if (settings.bottom_snap_tol > 0.0) + snap_bottom_to_flat(displaced, bounds.min.z(), settings.bottom_snap_tol); + + // 6. Close the T-junctions decimation left behind. Only meaningful when it ran. + if (mode == PipelineMode::Export && parent.empty()) + displaced = resolve_t_junctions(displaced); + + result.geometry = std::move(displaced); + result.face_parent_id = std::move(parent); + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp new file mode 100644 index 0000000000..a9a7db1957 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -0,0 +1,98 @@ +#pragma once + +// The bake pipeline: +// +// subdivide -> [regularize -> re-subdivide] -> displace -> [decimate] +// -> bottom clamp -> bottom snap -> [resolve T-junctions] +// +// Regularization sits between two subdivisions on purpose: it dissolves the slivers refinement +// inherited, which lengthens some edges past the target, and the second pass brings those back. +// Before any subdivision it would have nothing to work on, since the slivers come from refining a +// needle; after a single pass it would leave the mesh coarser than asked for. +// +// Decimation and repair are export-only - decimation drops the output-to-input face mapping a bake +// needs to carry per-face data forward. + +#include +#include +#include + +#include "TextureBakeDecimate.hpp" +#include "TextureBakeDisplace.hpp" +#include "TextureBakeIndex.hpp" +#include "TextureBakeRegularize.hpp" +#include "TextureBakeRepair.hpp" +#include "TextureBakeSubdivide.hpp" + +namespace Slic3r { +namespace TextureBake { + +enum class PipelineMode +{ + // Keeps the face-parent mapping; skips decimation and repair. + Bake, + // The full sequence, including decimation and repair. + Export, +}; + +struct PipelineSettings +{ + // Target edge length for the refinement, in mm. + double refine_length = 1.0; + + // Sliver removal between the two subdivision passes. + bool regularize = true; + RegularizeOptions regularize_opts; + // Slightly above the first pass, so it recovers the edges regularization lengthened instead of + // re-refining what it just merged. + double regularize_second_pass_mul = 1.1; + + DisplaceSettings displace; + + // Export mode only. + size_t max_triangles = 750'000; + bool harvest_flat = true; + double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL; + // Lock the untextured region against both regularization and decimation. + bool preserve_untextured = true; + + // Snap vertices within this of the bottom plane onto it. 0 disables. + double bottom_snap_tol = 0.1; + + int safety_cap = SUBDIVIDE_SAFETY_CAP; +}; + +// Stage name and a fraction within it. Returning false cancels the run. +using PipelineProgressFn = std::function; + +struct PipelineResult +{ + TriSoup geometry; + // Output face -> input face. Empty in Export mode, where decimation invalidates it. + std::vector face_parent_id; + bool safety_cap_hit = false; + bool locked_over_budget = false; + size_t collapse_count = 0; + bool canceled = false; +}; + +PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, + const PipelineSettings &settings, const DisplaceBounds &bounds, + PipelineMode mode, const std::vector &face_excluded = {}, + const PipelineProgressFn &on_progress = {}); + +// Snap anything that ended below the model's original bottom back up to it. +void clamp_below_bottom(TriSoup &geometry, float bottom_z); + +// Flatten the bed-contact surface by snapping positions within `tol` of the bottom plane onto it. +// +// Gated, not unconditional: an unconditional band snap also flattens the undersides of texture bumps +// near the base, folding them coplanar into the bottom face. Folded faces overlap the plate, so edges +// there pick up four incident faces - non-manifold edges and phantom shells on re-import. All copies +// of a position move together, and the move is rejected if any incident triangle would go degenerate +// or rotate more than about 75 degrees. A real bed-contact sliver rotates by a fraction of a degree +// and still snaps. Returns how many triangles moved. +size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol = 0.1); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRegularize.cpp b/src/libslic3r/TextureBake/TextureBakeRegularize.cpp new file mode 100644 index 0000000000..4539e38723 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRegularize.cpp @@ -0,0 +1,380 @@ +#include "TextureBakeRegularize.hpp" + +#include +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +// Vertex-to-triangle lists as intrusive doubly linked lists of corner slots over flat arrays. Slot s +// is corner (triangle * 3 + k), owned by corners[s]. Deleted and moved corners are unlinked, so a +// collapse costs no allocation. +struct SlotLists +{ + std::vector head, next, prev; + + void init(size_t vertex_count, size_t slot_count) + { + head.assign(vertex_count, -1); + next.assign(slot_count, -1); + prev.assign(slot_count, -1); + } + void link(int s, const std::vector &corners) + { + const int v = corners[size_t(s)]; + const int h = head[size_t(v)]; + prev[size_t(s)] = -1; + next[size_t(s)] = h; + if (h != -1) + prev[size_t(h)] = s; + head[size_t(v)] = s; + } + void unlink(int s, const std::vector &corners) + { + const int p = prev[size_t(s)], n = next[size_t(s)]; + if (p != -1) next[size_t(p)] = n; + else head[size_t(corners[size_t(s)])] = n; + if (n != -1) prev[size_t(n)] = p; + } +}; + +} // namespace + +RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector &face_parent_id, + double max_edge_length, const RegularizeOptions &opts) +{ + RegularizeResult result; + const size_t tri_count = geometry.triangle_count(); + if (tri_count == 0 || max_edge_length <= 0.0) { + result.geometry = geometry; + result.face_parent_id = face_parent_id; + return result; + } + + const double base_max_len_sq = (max_edge_length * opts.slack) * (max_edge_length * opts.slack); + const double aggr_max_len_sq = + (max_edge_length * opts.aggressive_slack) * (max_edge_length * opts.aggressive_slack); + const double extreme_aspect2 = opts.extreme_sliver_aspect * opts.extreme_sliver_aspect; + const double aspect_thr2 = opts.aspect_threshold * opts.aspect_threshold; + + // Double precision: a collapse writes a midpoint back and later collapses read it, so rounding + // would accumulate. + QuantizedPointMap pos_map(WELD_GRID_GEOMETRY, std::min(tri_count * 3, size_t(1) << 22)); + std::vector vert; + std::vector corners(tri_count * 3); + vert.reserve(tri_count); + for (size_t i = 0; i < tri_count * 3; ++i) { + const Vec3f &p = geometry.pos[i]; + const int id = pos_map.get_or_set(p, int(vert.size())); + if (pos_map.inserted()) + vert.push_back(p.cast()); + corners[i] = id; + } + const size_t vert_count = vert.size(); + + std::vector tri_nrm(tri_count, Vec3d::Zero()); + std::vector tri_deleted(tri_count, 0); + result.face_parent_id = face_parent_id; + if (result.face_parent_id.size() != tri_count) + result.face_parent_id.assign(tri_count, 0); + + const auto sq_dist = [&](int a, int b) { return (vert[size_t(a)] - vert[size_t(b)]).squaredNorm(); }; + + const auto recompute_face_normal = [&](size_t t) { + const Vec3d &a = vert[size_t(corners[t * 3])]; + const Vec3d n = (vert[size_t(corners[t * 3 + 1])] - a).cross(vert[size_t(corners[t * 3 + 2])] - a); + const double len = n.norm(); + tri_nrm[t] = (len > 0.0) ? Vec3d(n / len) : Vec3d::Zero(); + }; + for (size_t t = 0; t < tri_count; ++t) + recompute_face_normal(t); + + // Never updated - the normal gate measures against these, so drift cannot compound across rounds. + const std::vector orig_nrm = tri_nrm; + + // Squared thinness, the longest edge over the shortest altitude: + // thinness = lmax / hmin = lmax^2 / (2 * area), so thinness^2 = lmax^4 / |AB x AC|^2 + // + // Not lmax/lmin, which misses what matters here: three near-collinear points can have all edges + // similar, so an edge ratio reports about 2 and the gate skips a triangle with near-zero area + // whose corners sample three unrelated texels. An equilateral scores about 1.15. + const auto tri_aspect_sq = [&](size_t t) -> double { + const Vec3d &a = vert[size_t(corners[t * 3])]; + const Vec3d ab = vert[size_t(corners[t * 3 + 1])] - a; + const Vec3d ac = vert[size_t(corners[t * 3 + 2])] - a; + const Vec3d bc = vert[size_t(corners[t * 3 + 2])] - vert[size_t(corners[t * 3 + 1])]; + const double lmax2 = std::max({ ab.squaredNorm(), ac.squaredNorm(), bc.squaredNorm() }); + const double cross2 = ab.cross(ac).squaredNorm(); + return cross2 > 0.0 ? lmax2 * lmax2 / cross2 : std::numeric_limits::infinity(); + }; + + SlotLists slots; + slots.init(vert_count, tri_count * 3); + for (size_t s = 0; s < tri_count * 3; ++s) + slots.link(int(s), corners); + + // O(1) membership without clearing a set per collapse. + std::vector vert_stamp(vert_count, 0), tri_stamp(tri_count, 0); + uint32_t stamp_gen = 0; + + // Both endpoints of a hard edge are barred from being collapse endpoints, preserving such + // corners exactly while leaving flat-face interiors free. + // + // Skipped when either triangle is an extreme sliver: a sliver's normal is dominated by where its + // far apex sits, so noise pivots it tens of degrees with no feature behind it, and freezing on + // that would lock the very chains this pass exists to dissolve. Genuine features are bordered by + // well-shaped triangles and are unaffected. + std::vector frozen_vert(vert_count, 0); + { + std::vector tri_thin2(tri_count); + for (size_t t = 0; t < tri_count; ++t) + tri_thin2[t] = tri_aspect_sq(t); + QuantizedPointMap edge_seen(1.0, std::min(tri_count * 3, size_t(1) << 22)); + for (size_t t = 0; t < tri_count; ++t) + for (int e = 0; e < 3; ++e) { + const int u = corners[t * 3 + size_t(e)]; + const int v = corners[t * 3 + size_t((e + 1) % 3)]; + const int lo = std::min(u, v), hi = std::max(u, v); + const int other = edge_seen.get_or_set_key(lo, hi, 0, int(t)); + if (edge_seen.inserted()) + continue; + if (tri_thin2[t] > extreme_aspect2 || tri_thin2[size_t(other)] > extreme_aspect2) + continue; + if (tri_nrm[t].dot(tri_nrm[size_t(other)]) < opts.sharp_edge_cos) { + frozen_vert[size_t(u)] = 1; + frozen_vert[size_t(v)] = 1; + } + } + } + + // Exclusion freeze. The weight is constant across a face's corners, so the first one answers. + if (opts.preserve_excluded && !geometry.exclude_weight.empty()) + for (size_t t = 0; t < tri_count; ++t) + if (geometry.exclude_weight[t * 3] > 0.99f) + for (int k = 0; k < 3; ++k) + frozen_vert[size_t(corners[t * 3 + size_t(k)])] = 1; + + std::vector wing_scratch, affected_scratch; + + const auto third_vertex = [&](size_t t, int u, int v) { + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + if (a != u && a != v) return a; + if (b != u && b != v) return b; + return c; + }; + const auto triangles_sharing_edge = [&](int u, int v) -> std::vector & { + wing_scratch.clear(); + for (int s = slots.head[size_t(u)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t]) + continue; + if (corners[t * 3] == v || corners[t * 3 + 1] == v || corners[t * 3 + 2] == v) + wing_scratch.push_back(int(t)); + } + return wing_scratch; + }; + + RegularizeRejectStats &stats = result.reject_stats; + + const auto try_collapse = [&](int u, int v) -> bool { + if (u == v) + return false; + if (frozen_vert[size_t(u)] || frozen_vert[size_t(v)]) { ++stats.frozen; return false; } + + // Two wings means a manifold interior edge. + std::vector &wings = triangles_sharing_edge(u, v); + if (wings.size() != 2) { ++stats.wing_count; return false; } + const size_t w0 = size_t(wings[0]), w1 = size_t(wings[1]); + const int apex1 = third_vertex(w0, u, v), apex2 = third_vertex(w1, u, v); + if (apex1 == apex2) { ++stats.folded_apex; return false; } + + // The edge cap loosens if *either* wing is extreme, since the re-subdivision recovers an + // over-long edge. The normal cap needs *both*, which is what protects fillets. + const double w1a = tri_aspect_sq(w0), w2a = tri_aspect_sq(w1); + const bool either_extreme = w1a > extreme_aspect2 || w2a > extreme_aspect2; + const bool both_extreme = w1a > extreme_aspect2 && w2a > extreme_aspect2; + const double eff_max_len_sq = either_extreme ? aggr_max_len_sq : base_max_len_sq; + const double eff_normal_cos = + both_extreme ? opts.aggressive_normal_delta_cos : opts.max_normal_delta_cos; + + // A vertex sharing a triangle with both endpoints, other than the wing apexes, would go + // non-manifold. Stamp one side's neighbours, scan the other against them. + ++stamp_gen; + for (int s = slots.head[size_t(v)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t]) + continue; + for (int k = 0; k < 3; ++k) + if (const int x = corners[t * 3 + size_t(k)]; x != v) + vert_stamp[size_t(x)] = stamp_gen; + } + for (int s = slots.head[size_t(u)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t]) + continue; + for (int k = 0; k < 3; ++k) { + const int x = corners[t * 3 + size_t(k)]; + if (x != u && x != v && x != apex1 && x != apex2 && vert_stamp[size_t(x)] == stamp_gen) { + ++stats.link_condition; + return false; + } + } + } + + const Vec3d m = (vert[size_t(u)] + vert[size_t(v)]) * 0.5; + + // Everything using either endpoint; the wings are being deleted. + ++stamp_gen; + affected_scratch.clear(); + for (const int endpoint : { u, v }) + for (int s = slots.head[size_t(endpoint)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t] || t == w0 || t == w1) + continue; + if (tri_stamp[t] != stamp_gen) { + tri_stamp[t] = stamp_gen; + affected_scratch.push_back(int(t)); + } + } + + // Validate every affected triangle before touching anything. + for (const int ti : affected_scratch) { + const size_t t = size_t(ti); + Vec3d p[3]; + for (int k = 0; k < 3; ++k) { + const int x = corners[t * 3 + size_t(k)]; + p[k] = (x == u || x == v) ? m : vert[size_t(x)]; + } + const double ab2 = (p[1] - p[0]).squaredNorm(); + const double bc2 = (p[2] - p[1]).squaredNorm(); + const double ca2 = (p[0] - p[2]).squaredNorm(); + if (ab2 > eff_max_len_sq || bc2 > eff_max_len_sq || ca2 > eff_max_len_sq) { + ++stats.edge_cap; + return false; + } + const Vec3d n = (p[1] - p[0]).cross(p[2] - p[0]); + const double nlen = n.norm(); + if (nlen <= 0.0) { ++stats.degenerate; return false; } + if ((n / nlen).dot(orig_nrm[t]) < eff_normal_cos) { ++stats.normal_change; return false; } + } + + // Apply: move u to the merged position and redirect every reference to v. + vert[size_t(u)] = m; + for (const size_t w : { w0, w1 }) { + tri_deleted[w] = 1; + for (int k = 0; k < 3; ++k) + slots.unlink(int(w * 3) + k, corners); + } + // A non-wing triangle contains v exactly once, so moving its slots suffices. + for (int s = slots.head[size_t(v)]; s != -1;) { + const int ns = slots.next[size_t(s)]; + slots.unlink(s, corners); + corners[size_t(s)] = u; + slots.link(s, corners); + recompute_face_normal(size_t(s) / 3); + s = ns; + } + for (int s = slots.head[size_t(u)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (!tri_deleted[t]) + recompute_face_normal(t); + } + return true; + }; + + for (int round = 0; round < opts.maxrounds; ++round) { + // Rebuilt each round so earlier collapses inform the priorities. + std::vector cand; + std::vector cand_aspect; + for (size_t t = 0; t < tri_count; ++t) { + if (tri_deleted[t]) + continue; + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + if (std::min({ sq_dist(a, b), sq_dist(b, c), sq_dist(c, a) }) <= 0.0) + continue; + const double aspect2 = tri_aspect_sq(t); + if (aspect2 < aspect_thr2) + continue; + cand.push_back(int(t)); + cand_aspect.push_back(aspect2); + } + // Worst first; ties keep ascending order so the pass is deterministic. + std::vector order(cand.size()); + std::iota(order.begin(), order.end(), 0); + std::stable_sort(order.begin(), order.end(), + [&](int x, int y) { return cand_aspect[size_t(x)] > cand_aspect[size_t(y)]; }); + + size_t round_collapses = 0; + for (const int oi : order) { + const size_t t = size_t(cand[size_t(oi)]); + if (tri_deleted[t]) + continue; + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + // All three edges, shortest first: a sliver straddling a seam has its shortest edge + // crossing it, which the normal gate refuses, while a long edge along one surface + // collapses safely. Trying only the shortest would leave those stuck. + struct Cand { double len2; int u, v; }; + Cand e[3] = { { sq_dist(a, b), a, b }, { sq_dist(b, c), b, c }, { sq_dist(c, a), c, a } }; + std::stable_sort(std::begin(e), std::end(e), + [](const Cand &x, const Cand &y) { return x.len2 < y.len2; }); + if (try_collapse(e[0].u, e[0].v) || try_collapse(e[1].u, e[1].v) || + try_collapse(e[2].u, e[2].v)) + ++round_collapses; + } + result.collapse_count += round_collapses; + if (round_collapses == 0) + break; + } + + // Drop deleted triangles and rebuild the soup. + const bool have_weights = !geometry.exclude_weight.empty(); + std::vector out_parent; + TriSoup &out = result.geometry; + for (size_t t = 0; t < tri_count; ++t) { + if (tri_deleted[t]) + continue; + for (int k = 0; k < 3; ++k) + out.pos.push_back(vert[size_t(corners[t * 3 + size_t(k)])].cast()); + if (have_weights) { + // Constant across a face's corners. + const float w = geometry.exclude_weight[t * 3]; + out.exclude_weight.insert(out.exclude_weight.end(), { w, w, w }); + } + out_parent.push_back(result.face_parent_id[t]); + } + result.face_parent_id = std::move(out_parent); + + // Rebuilt from the compacted geometry - the collapses moved vertices. + out.nrm.assign(out.pos.size(), Vec3f::Zero()); + { + std::vector accum(out.pos.size(), Vec3d::Zero()); + QuantizedPointMap weld(WELD_GRID_GEOMETRY, out.pos.size()); + std::vector vid(out.pos.size()); + int next = 0; + for (size_t i = 0; i < out.pos.size(); ++i) { + vid[i] = weld.get_or_set(out.pos[i], next); + if (weld.inserted()) + ++next; + } + std::vector vn(size_t(next), Vec3d::Zero()); + for (size_t t = 0; t * 3 < out.pos.size(); ++t) { + const Vec3d a = out.pos[t * 3].cast(); + const Vec3d n = (out.pos[t * 3 + 1].cast() - a).cross(out.pos[t * 3 + 2].cast() - a); + for (int k = 0; k < 3; ++k) + vn[size_t(vid[t * 3 + size_t(k)])] += n; + } + for (size_t i = 0; i < out.pos.size(); ++i) { + const Vec3d &n = vn[size_t(vid[i])]; + const double l = n.norm(); + out.nrm[i] = (l > 0.0) ? Vec3d(n / l).cast() : Vec3f(0.f, 0.f, 1.f); + } + } + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRegularize.hpp b/src/libslic3r/TextureBake/TextureBakeRegularize.hpp new file mode 100644 index 0000000000..350666783a --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRegularize.hpp @@ -0,0 +1,73 @@ +#pragma once + +// Sliver removal by short-edge collapse. +// +// Subdivision turns tessellation needles into chains of slivers that are within the edge-length +// budget but still poor triangles. A sliver's three vertices land on three unrelated texels, so the +// relief picks up noise that is an artifact of the tessellation rather than of the image. +// +// A candidate's edge is collapsed to its midpoint only if it passes three gates: no affected +// triangle may exceed the target edge times a slack factor; every affected triangle must keep its +// face normal within a bound of its *original* direction (which is what stops curved surfaces being +// flattened); and the link condition must hold, or the result would be non-manifold. Boundary and +// non-manifold edges are skipped outright. Rounds repeat until one achieves nothing. + +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +struct RegularizeOptions +{ + // Candidate threshold. Set to catch real slivers - chains measure in the hundreds - without + // sweeping up moderate fillet triangles, which sit between 2 and 5. + double aspect_threshold = 5.0; + + // The base tier is loose on purpose: non-sliver boundary collapses must keep succeeding, since + // those give a chain the room to dissolve. A tight base leaves chains worse than before. The + // aggressive tier applies when at least one wing is an extreme sliver. + double slack = 3.0; + double aggressive_slack = 8.0; + + // Thinness above which a wing counts as extreme: longest edge over shortest altitude. + double extreme_sliver_aspect = 8.0; + + // Measured against each triangle's normal from before any collapse ran, so rounds of small + // allowed drift cannot compound into corner damage. Asymmetric two-tier: the loose bound needs + // *both* wings extreme, which matches a needle chain on a curved face but not a sliver beside a + // fillet, so fillets keep the tight bound. + double max_normal_delta_cos = 0.965925826289; // cos(15 degrees) + double aggressive_normal_delta_cos = 0.906307787037; // cos(25 degrees) + + // Vertices on edges sharper than this are frozen, so hard features keep every original vertex. + double sharp_edge_cos = 0.866025403784; // cos(30 degrees) + + int maxrounds = 8; + + // Freeze excluded faces entirely, so untextured geometry is never modified. + bool preserve_excluded = false; +}; + +// Which gate blocked a collapse - the only practical way to tell why a region failed to merge. +struct RegularizeRejectStats +{ + size_t frozen = 0, wing_count = 0, link_condition = 0, edge_cap = 0, normal_change = 0, + degenerate = 0, folded_apex = 0; +}; + +struct RegularizeResult +{ + TriSoup geometry; + std::vector face_parent_id; + size_t collapse_count = 0; + RegularizeRejectStats reject_stats; +}; + +RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector &face_parent_id, + double max_edge_length, const RegularizeOptions &opts = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRepair.cpp b/src/libslic3r/TextureBake/TextureBakeRepair.cpp new file mode 100644 index 0000000000..438e0ea25d --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRepair.cpp @@ -0,0 +1,215 @@ +#include "TextureBakeRepair.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +inline uint64_t edge_key(int a, int b) +{ + const uint32_t lo = uint32_t(std::min(a, b)), hi = uint32_t(std::max(a, b)); + return (uint64_t(lo) << 32) | uint64_t(hi); +} + +// On the export grid a squared cross product is either 0 (collinear) or at least about 1e-16, the +// smallest real triangle being one grid unit per leg, so this separates the two cleanly. +constexpr double DEGENERATE_AREA_SQ = 1e-18; + +} // namespace + +EdgeDefects count_edge_defects(const TriSoup &geometry, double quant) +{ + EdgeDefects out; + const size_t n = geometry.pos.size(); + out.triangles = n / 3; + QuantizedPointMap vmap(quant, std::min(n, size_t(1) << 22)); + std::vector id(n); + int next = 0; + for (size_t i = 0; i < n; ++i) { + id[i] = vmap.get_or_set(geometry.pos[i], next); + if (vmap.inserted()) + ++next; + } + std::unordered_map counts; + for (size_t t = 0; t + 2 < n; t += 3) { + const int a = id[t], b = id[t + 1], c = id[t + 2]; + if (a == b || b == c || a == c) + continue; + const int tri[3] = { a, b, c }; + for (int e = 0; e < 3; ++e) + ++counts[edge_key(tri[e], tri[(e + 1) % 3])]; + } + for (const auto &[key, c] : counts) { + (void) key; + if (c == 1) ++out.open; + else if (c > 2) ++out.non_manifold; + } + return out; +} + +size_t count_area_slivers(const TriSoup &geometry) +{ + size_t n = 0; + for (size_t t = 0; t + 2 < geometry.pos.size(); t += 3) { + const Vec3d u = (geometry.pos[t + 1] - geometry.pos[t]).cast(); + const Vec3d v = (geometry.pos[t + 2] - geometry.pos[t]).cast(); + // The threshold a slicer applies: area below 1e-12 mm^2. + if (u.cross(v).squaredNorm() < 1e-24) + ++n; + } + return n; +} + +TriSoup resolve_t_junctions(const TriSoup &geometry, const RepairOptions &opts) +{ + const size_t n_tri = geometry.triangle_count(); + const double on_tol2 = opts.on_seg_tol * opts.on_seg_tol; + const double Q = opts.weld_quant; + + // Snapped, not just welded: keeping unrounded coordinates lets a thin triangle pass the + // degeneracy test here and then collapse to collinear once the file is written, punching the very + // hole this pass prevents. Snapping makes the check see what will be written. + QuantizedPointMap vmap(Q, std::min(n_tri * 3, size_t(1) << 22)); + std::vector vert; + std::vector vid(n_tri * 3); + for (size_t i = 0; i < n_tri * 3; ++i) { + const Vec3f &p = geometry.pos[i]; + const int id = vmap.get_or_set(p, int(vert.size())); + if (vmap.inserted()) + vert.emplace_back(double(grid_round(double(p.x()) * Q)) / Q, + double(grid_round(double(p.y()) * Q)) / Q, + double(grid_round(double(p.z()) * Q)) / Q); + vid[i] = id; + } + + // Dropped: faces whose corners welded together, and needles - distinct but collinear on this + // grid. A needle reads as watertight yet is deleted downstream, and dropping it leaves exactly + // the on-edge-vertex topology the pass below closes. + std::vector> faces; + faces.reserve(n_tri); + for (size_t t = 0; t < n_tri; ++t) { + const int a = vid[t * 3], b = vid[t * 3 + 1], c = vid[t * 3 + 2]; + if (a == b || b == c || a == c) + continue; + const Vec3d u = vert[size_t(b)] - vert[size_t(a)]; + const Vec3d w = vert[size_t(c)] - vert[size_t(a)]; + if (u.cross(w).squaredNorm() < DEGENERATE_AREA_SQ) + continue; + faces.push_back({ a, b, c }); + } + + for (int iter = 0; iter < opts.max_iters; ++iter) { + std::unordered_map e_count; + for (const auto &f : faces) + for (int e = 0; e < 3; ++e) + ++e_count[edge_key(f[size_t(e)], f[size_t((e + 1) % 3)])]; + + std::unordered_set bverts; + for (const auto &[key, c] : e_count) { + if (c != 1) + continue; + bverts.insert(int(uint32_t(key >> 32))); + bverts.insert(int(uint32_t(key & 0xFFFFFFFFu))); + } + if (bverts.empty()) + break; + const std::vector bv(bverts.begin(), bverts.end()); + + struct Split { int a, b; std::vector mids; }; + std::unordered_map splits; + for (size_t fi = 0; fi < faces.size(); ++fi) { + const auto &f = faces[fi]; + for (int e = 0; e < 3; ++e) { + const int a = f[size_t(e)], b = f[size_t((e + 1) % 3)]; + if (e_count[edge_key(a, b)] != 1) + continue; // only a boundary edge carries an unresolved T-junction + const Vec3d A = vert[size_t(a)]; + const Vec3d ev = vert[size_t(b)] - A; + const double elen2 = ev.squaredNorm(); + if (elen2 < 1e-20) + continue; + std::vector> found; + for (const int c : bv) { + if (c == a || c == b) + continue; + const Vec3d cv = vert[size_t(c)] - A; + const double tp = cv.dot(ev) / elen2; + if (tp <= 1e-4 || tp >= 1.0 - 1e-4) + continue; // strictly between the ends + if ((cv - ev * tp).squaredNorm() < on_tol2) + found.emplace_back(tp, c); + } + if (!found.empty()) { + std::sort(found.begin(), found.end(), + [](const auto &x, const auto &y) { return x.first < y.first; }); + Split sp{ a, b, {} }; + for (const auto &m : found) + sp.mids.push_back(m.second); + splits.emplace(fi, std::move(sp)); + break; // one site per face per pass; iteration handles cascades + } + } + } + if (splits.empty()) + break; + + std::vector> next; + next.reserve(faces.size() + splits.size() * 2); + for (size_t fi = 0; fi < faces.size(); ++fi) { + const auto it = splits.find(fi); + if (it == splits.end()) { + next.push_back(faces[fi]); + continue; + } + const auto &f = faces[fi]; + const auto &sp = it->second; + const int apex = (f[0] != sp.a && f[0] != sp.b) ? f[0] + : (f[1] != sp.a && f[1] != sp.b) ? f[1] + : f[2]; + // Walk the base the way the face already traverses it, so the winding survives. + bool dir_ab = false; + for (int e = 0; e < 3; ++e) + if (f[size_t(e)] == sp.a && f[size_t((e + 1) % 3)] == sp.b) { + dir_ab = true; + break; + } + std::vector seq; + if (dir_ab) { + seq.push_back(sp.a); + seq.insert(seq.end(), sp.mids.begin(), sp.mids.end()); + seq.push_back(sp.b); + } else { + seq.push_back(sp.b); + seq.insert(seq.end(), sp.mids.rbegin(), sp.mids.rend()); + seq.push_back(sp.a); + } + for (size_t s = 0; s + 1 < seq.size(); ++s) + next.push_back({ seq[s], seq[s + 1], apex }); + } + faces.swap(next); + } + + TriSoup out; + out.pos.reserve(faces.size() * 3); + out.nrm.reserve(faces.size() * 3); + for (const auto &f : faces) { + const Vec3f a = vert[size_t(f[0])].cast(); + const Vec3f b = vert[size_t(f[1])].cast(); + const Vec3f c = vert[size_t(f[2])].cast(); + Vec3f nrm = (b - a).cross(c - a); + const float len = nrm.norm(); + nrm = (len > 0.f) ? Vec3f(nrm / len) : Vec3f(0.f, 0.f, 1.f); + out.pos.insert(out.pos.end(), { a, b, c }); + out.nrm.insert(out.nrm.end(), { nrm, nrm, nrm }); + } + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRepair.hpp b/src/libslic3r/TextureBake/TextureBakeRepair.hpp new file mode 100644 index 0000000000..35e9f50b06 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRepair.hpp @@ -0,0 +1,48 @@ +#pragma once + +// T-junction resolution and edge-defect accounting. +// +// Decimation can collapse a long edge whose interior still carries neighbouring triangles' vertices. +// Those then sit *on* an edge rather than at an end: watertight vertex-for-vertex, but the edge has +// one incident face on one side, which a slicer reads as an open boundary. This splits the offending +// face into a fan so every on-edge vertex becomes a real corner. + +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +struct EdgeDefects +{ + size_t open = 0, non_manifold = 0, triangles = 0; +}; + +// Welds at the export grid first: counting on the un-snapped mesh reports defects the file does not +// have and misses ones it does. +EdgeDefects count_edge_defects(const TriSoup &geometry, double quant = WELD_GRID_EXPORT); + +// Triangles a slicer would drop as degenerate. Each one, removed, punches a hole - so a non-zero +// count means watertight only on paper. +size_t count_area_slivers(const TriSoup &geometry); + +struct RepairOptions +{ + // Coordinates are snapped onto this grid, matching the precision files are written with. + double weld_quant = WELD_GRID_EXPORT; + + // How far off an edge a vertex may sit and still count as on it. Well above the harvest + // tolerance, since harvesting leaves a region flat only to within that, making a collapsed edge a + // chord the on-edge vertices deviate from by about as much. Still far below the weld grid. + double on_seg_tol = 0.02; + + // Splitting one face can expose another behind it, so the pass cascades. + int max_iters = 16; +}; + +TriSoup resolve_t_junctions(const TriSoup &geometry, const RepairOptions &opts = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp new file mode 100644 index 0000000000..7a0082e257 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp @@ -0,0 +1,414 @@ +#include "TextureBakeSubdivide.hpp" + +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +double edge_len_sq(const VertStore &v, int a, int b) +{ + return (v.pos[size_t(a)] - v.pos[size_t(b)]).squaredNorm(); +} + +// Both indexers accumulate raw, area-weighted cross products and normalise once at the end. +void normalize_store_normals(VertStore &verts) +{ + for (Vec3d &n : verts.nrm) { + const double len = n.norm(); + n = (len > 0.0) ? Vec3d(n / len) : Vec3d(0.0, 0.0, 1.0); + } +} + +// Keyed by the raw parent-vertex pair rather than by position: two sharp-edge copies of one point +// need their own midpoints, since their normals differ even though the position does not. +int get_midpoint(VertStore &verts, QuantizedPointMap &cache, int a, int b, + QuantizedPointMap *pos_canon_map) +{ + const int lo = std::min(a, b), hi = std::max(a, b); + if (const int cached = cache.get_key(lo, hi, 0); cached != -1) + return cached; + + const Vec3d m = (verts.pos[size_t(a)] + verts.pos[size_t(b)]) * 0.5; + Vec3d n = verts.nrm[size_t(a)] + verts.nrm[size_t(b)]; + const double nl = n.norm(); + n = (nl > 0.0) ? Vec3d(n / nl) : verts.nrm[size_t(a)]; + + const int idx = verts.push(m, n); + if (!verts.wgt.empty()) + verts.wgt.push_back((verts.wgt[size_t(a)] + verts.wgt[size_t(b)]) * 0.5); + if (!verts.canon.empty() && pos_canon_map != nullptr) + verts.canon.push_back(pos_canon_map->get_or_set(float(m.x()), float(m.y()), float(m.z()), idx)); + + cache.get_or_set_key(lo, hi, 0, idx); + return idx; +} + +struct PassResult +{ + std::vector indices; + std::vector face_excluded; + std::vector face_parent_id; + bool changed = false; + bool capped = false; +}; + +// Three steps, so that no T-junction can appear: +// 1. Mark every too-long edge globally, so both triangles on a shared edge decide alike. +// 1.5 Predict the exact resulting count from the marks (0->1, 1->2, 2->3, 3->4) and abort the +// *whole* pass if it exceeds the cap - a partial pass leaves split parents beside unsplit +// neighbours, the very crack step 1 prevents. +// 2. Rebuild, allocating once at the now-known size. +PassResult subdivide_pass(VertStore &verts, const std::vector &indices, double max_edge_length, + int safety_cap, const std::vector &face_excluded, + QuantizedPointMap *pos_canon_map, const std::vector &face_parent_id) +{ + PassResult out; + const double max_sq = max_edge_length * max_edge_length; + const size_t tri_count = indices.size() / 3; + const bool have_canon = !verts.canon.empty(); + + QuantizedPointMap mid_cache(1.0, 1 << 16); + QuantizedPointMap split_edges(1.0, 1 << 16); + + // With canonical ids the key is the canonical *position* id, so split copies either side of a + // sharp edge see one another's decision; without them the vertex index serves. + const auto key_of = [&](int v) -> int64_t { return have_canon ? verts.canon[size_t(v)] : v; }; + const auto mark_edge = [&](int a, int b) { + const int64_t u = key_of(a), v = key_of(b); + if (u < v) split_edges.get_or_set_key(u, v, 0, 1); + else split_edges.get_or_set_key(v, u, 0, 1); + }; + const auto is_marked = [&](int a, int b) { + const int64_t u = key_of(a), v = key_of(b); + return (u < v ? split_edges.get_key(u, v, 0) : split_edges.get_key(v, u, 0)) != -1; + }; + + // Step 1. An excluded triangle marks none of its own edges, so its interior never refines; its + // boundary edges are still marked by an included neighbour, and it follows that split. + for (size_t t = 0; t < tri_count; ++t) { + if (!face_excluded.empty() && face_excluded[t]) + continue; + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + if (edge_len_sq(verts, a, b) > max_sq) mark_edge(a, b); + if (edge_len_sq(verts, b, c) > max_sq) mark_edge(b, c); + if (edge_len_sq(verts, c, a) > max_sq) mark_edge(c, a); + } + if (split_edges.size() == 0) { + out.indices = indices; + out.face_excluded = face_excluded; + out.face_parent_id = face_parent_id; + return out; // changed stays false: nothing left to refine + } + + // Step 1.5. + size_t predicted = 0; + for (size_t t = 0; t < tri_count; ++t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const int n = int(is_marked(a, b)) + int(is_marked(b, c)) + int(is_marked(c, a)); + predicted += (n == 0) ? 1 : size_t(n + 1); + } + if (predicted > size_t(safety_cap)) { + out.indices = indices; + out.face_excluded = face_excluded; + out.face_parent_id = face_parent_id; + out.capped = true; + return out; // coarser than asked for, but watertight + } + + // Step 2. + out.indices.resize(predicted * 3); + if (!face_excluded.empty()) + out.face_excluded.resize(predicted); + if (!face_parent_id.empty()) + out.face_parent_id.resize(predicted); + size_t wi = 0, fi = 0; + const auto emit_face_data = [&](uint8_t excl, int pid, int times) { + for (int k = 0; k < times; ++k) { + if (!out.face_excluded.empty()) out.face_excluded[fi] = excl; + if (!out.face_parent_id.empty()) out.face_parent_id[fi] = pid; + ++fi; + } + }; + const auto emit = [&](int x, int y, int z) { + out.indices[wi++] = x; out.indices[wi++] = y; out.indices[wi++] = z; + }; + + for (size_t t = 0; t < tri_count; ++t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const uint8_t excl = face_excluded.empty() ? uint8_t(0) : face_excluded[t]; + const int pid = face_parent_id.empty() ? 0 : face_parent_id[t]; + const bool s_ab = is_marked(a, b), s_bc = is_marked(b, c), s_ca = is_marked(c, a); + const int n = int(s_ab) + int(s_bc) + int(s_ca); + + if (n == 0) { + emit(a, b, c); + emit_face_data(excl, pid, 1); + } else if (n == 3) { + // a + // / \ + // mCA-mAB + // / \ / \ + // c--mBC--b + const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, m_ab, m_ca); + emit(m_ab, b, m_bc); + emit(m_ca, m_bc, c); + emit(m_ab, m_bc, m_ca); + emit_face_data(excl, pid, 4); + } else if (n == 1) { + if (s_ab) { + const int m = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + emit(a, m, c); + emit(m, b, c); + } else if (s_bc) { + const int m = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + emit(a, b, m); + emit(a, m, c); + } else { + const int m = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, b, m); + emit(m, b, c); + } + emit_face_data(excl, pid, 2); + } else { + // A corner triangle on the untouched-edge vertex, then the remaining quadrilateral split + // along the midpoint-to-midpoint diagonal, which keeps the winding consistent. + // + // A sliver parent propagates: that inner diagonal inherits half the short edge and hands + // the sliver to two children per pass. No better diagonal exists - one avoiding the + // midpoints must pass through one of them, giving a zero-area triangle. Regularization + // removes such slivers before the mesh reaches here. + if (!s_ab) { // fan from c + const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, b, m_bc); + emit(a, m_bc, m_ca); + emit(c, m_ca, m_bc); + } else if (!s_bc) { // fan from a + const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, m_ab, m_ca); + emit(m_ab, b, c); + emit(m_ab, c, m_ca); + } else { // fan from b + const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + emit(b, m_bc, m_ab); + emit(a, m_ab, m_bc); + emit(a, m_bc, c); + } + emit_face_data(excl, pid, 3); + } + } + + out.changed = true; + return out; +} + +} // namespace + +IndexedMesh to_indexed_fast(const TriSoup &geometry) +{ + // Preview path: a plain position merge - no clustering, no sharp-edge splitting, no canonical ids. + IndexedMesh out; + const size_t n = geometry.pos.size(); + QuantizedPointMap vert_map(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + out.indices.resize(n); + const bool has_w = !geometry.exclude_weight.empty(); + + for (size_t i = 0; i < n; ++i) { + const Vec3f &p = geometry.pos[i]; + const Vec3f nf = geometry.nrm.empty() ? Vec3f(0.f, 0.f, 1.f) : geometry.nrm[i]; + const int idx = vert_map.get_or_set(p, int(out.verts.count())); + if (vert_map.inserted()) { + out.verts.push(p.cast(), nf.cast()); + if (has_w) + out.verts.wgt.push_back(double(geometry.exclude_weight[i])); + } else { + out.verts.nrm[size_t(idx)] += nf.cast(); + // Merge exclusion by maximum: any excluded face marks the shared vertex. + if (has_w && double(geometry.exclude_weight[i]) > out.verts.wgt[size_t(idx)]) + out.verts.wgt[size_t(idx)] = double(geometry.exclude_weight[i]); + } + out.indices[i] = idx; + } + normalize_store_normals(out.verts); + return out; +} + +IndexedMesh to_indexed(const TriSoup &geometry) +{ + // Export path. Two vertices at one position merge only when their face normals agree to within + // SUBDIVIDE_SHARP_ANGLE_DEG, which keeps a cylinder from faceting while stopping a cube's edge + // normal from leaking into the flat face interiors as subdivision carries it inward. + IndexedMesh out; + out.has_canon = true; + const size_t n = geometry.pos.size(); + const bool has_w = !geometry.exclude_weight.empty(); + const double sharp_cos = std::cos(SUBDIVIDE_SHARP_ANGLE_DEG * M_PI / 180.0); + + // Per-face normals: unit for the angle test, raw for the area-weighted accumulation. + std::vector face_unit(n), face_raw(n); + for (size_t t = 0; t + 2 < n; t += 3) { + const Vec3d a = geometry.pos[t].cast(); + const Vec3d b = geometry.pos[t + 1].cast(); + const Vec3d c = geometry.pos[t + 2].cast(); + const Vec3d r = (b - a).cross(c - a); + const double len = r.norm(); + const Vec3d u = (len > 0.0) ? Vec3d(r / len) : Vec3d(0.0, 0.0, 1.0); + for (int v = 0; v < 3; ++v) { + face_unit[t + size_t(v)] = u; + face_raw[t + size_t(v)] = r; + } + } + + out.indices.resize(n); + out.pos_canon_map = QuantizedPointMap(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + struct Cluster { int idx; Vec3d fn_unit; }; + std::unordered_map> clusters_by_canon; + + for (size_t i = 0; i < n; ++i) { + const Vec3f &p = geometry.pos[i]; + // The first vertex at a position becomes its canonical id; later split copies share it. + const int canon_id = out.pos_canon_map.get_or_set(p, int(out.verts.count())); + const bool fresh_position = out.pos_canon_map.inserted(); + + const auto add_vertex = [&](int canon) { + const int idx = out.verts.push(p.cast(), face_raw[i]); + if (has_w) + out.verts.wgt.push_back(double(geometry.exclude_weight[i])); + out.verts.canon.push_back(canon); + return idx; + }; + + if (fresh_position) { + const int idx = add_vertex(canon_id); + clusters_by_canon[canon_id].push_back({ idx, face_unit[i] }); + out.indices[i] = idx; + continue; + } + + std::vector &clusters = clusters_by_canon[canon_id]; + bool matched = false; + for (Cluster &cl : clusters) { + if (cl.fn_unit.dot(face_unit[i]) < sharp_cos) + continue; + out.verts.nrm[size_t(cl.idx)] += face_raw[i]; + if (has_w && double(geometry.exclude_weight[i]) > out.verts.wgt[size_t(cl.idx)]) + out.verts.wgt[size_t(cl.idx)] = double(geometry.exclude_weight[i]); + // Track the running average, so gradual curvature stays in one cluster instead of + // fragmenting when a distant face exceeds the threshold against the seed's fixed normal. + cl.fn_unit += face_unit[i]; + if (const double rl = cl.fn_unit.norm(); rl > 0.0) + cl.fn_unit /= rl; + out.indices[i] = cl.idx; + matched = true; + break; + } + if (!matched) { + // A sharp-edge split: a new vertex at the same position, sharing its canonical id. + const int idx = add_vertex(canon_id); + clusters.push_back({ idx, face_unit[i] }); + out.indices[i] = idx; + } + } + + normalize_store_normals(out.verts); + return out; +} + +TriSoup to_non_indexed(const VertStore &verts, const std::vector &indices, + const std::vector &face_excluded) +{ + TriSoup out; + const size_t tri_count = indices.size() / 3; + out.pos.resize(tri_count * 3); + out.nrm.resize(tri_count * 3); + const bool want_weights = !face_excluded.empty() || !verts.wgt.empty(); + if (want_weights) + out.exclude_weight.resize(tri_count * 3); + + for (size_t t = 0; t < tri_count; ++t) { + // The per-face flag, not the interpolated weight: merging by maximum can push an *included* + // face's corners to 1 when it borders two excluded neighbours, wrongly excluding it. + const bool have_face_flag = !face_excluded.empty(); + const float face_w = have_face_flag ? (face_excluded[t] ? 1.f : 0.f) : 0.f; + for (int v = 0; v < 3; ++v) { + const size_t vidx = size_t(indices[t * 3 + size_t(v)]); + out.pos[t * 3 + size_t(v)] = verts.pos[vidx].cast(); + out.nrm[t * 3 + size_t(v)] = verts.nrm[vidx].cast(); + if (want_weights) + out.exclude_weight[t * 3 + size_t(v)] = + have_face_flag ? face_w : float(verts.wgt[vidx]); + } + } + return out; +} + +SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, + const std::vector &face_excluded, bool fast, int safety_cap, + const SubdivideProgressFn &on_progress) +{ + SubdivideResult result; + if (geometry.empty() || max_edge_length <= 0.0) { + result.geometry = geometry; + return result; + } + + IndexedMesh indexed = fast ? to_indexed_fast(geometry) : to_indexed(geometry); + QuantizedPointMap *canon_map = indexed.has_canon ? &indexed.pos_canon_map : nullptr; + + std::vector current_indices = indexed.indices; + std::vector current_excluded = face_excluded; + const size_t initial_tris = indexed.indices.size() / 3; + std::vector current_parent(initial_tris); + for (size_t i = 0; i < initial_tris; ++i) + current_parent[i] = int(i); + + for (int iter = 0; iter < SUBDIVIDE_MAX_ITERATIONS; ++iter) { + if (current_indices.size() / 3 >= size_t(safety_cap)) { + result.safety_cap_hit = true; + break; + } + + PassResult pass = subdivide_pass(indexed.verts, current_indices, max_edge_length, safety_cap, + current_excluded, canon_map, current_parent); + current_indices = std::move(pass.indices); + if (!pass.face_excluded.empty()) + current_excluded = std::move(pass.face_excluded); + if (!pass.face_parent_id.empty()) + current_parent = std::move(pass.face_parent_id); + if (pass.capped || current_indices.size() / 3 >= size_t(safety_cap)) + result.safety_cap_hit = true; + + if (on_progress) { + // Reported after the pass, so the value falls each iteration instead of lagging a step. + double max_edge_sq = 0.0; + for (size_t t = 0; t + 2 < current_indices.size(); t += 3) { + const int a = current_indices[t], b = current_indices[t + 1], c = current_indices[t + 2]; + max_edge_sq = std::max({ max_edge_sq, edge_len_sq(indexed.verts, a, b), + edge_len_sq(indexed.verts, b, c), + edge_len_sq(indexed.verts, c, a) }); + } + if (!on_progress(std::min(0.95, double(iter + 1) / SUBDIVIDE_MAX_ITERATIONS), + current_indices.size() / 3, std::sqrt(max_edge_sq))) + break; // whole passes only, so what we have is still crack-free + } + + if (!pass.changed || result.safety_cap_hit) + break; + } + + result.geometry = to_non_indexed(indexed.verts, current_indices, current_excluded); + result.face_parent_id = std::move(current_parent); + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp new file mode 100644 index 0000000000..30ad56bf00 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp @@ -0,0 +1,83 @@ +#pragma once + +// Adaptive subdivision to a target edge length, by global marked-edge (red-green) refinement rather +// than longest-edge bisection. Marking is global, so two triangles sharing an edge always agree and +// the result is crack-free by construction. A triangle is rebuilt from its marked-edge count: 0 +// keeps, 1 bisects, 2 fans into three, 3 does the regular 1->4 split. +// +// The 1->4 case is what keeps the tessellation regular - its children are similar to the parent. An +// irregular one shows up after displacement as adjacent triangles tilting alternately, i.e. noise. + +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +// Memory guard for the stages downstream. At roughly 145 bytes per triangle this is about 2.9 GB. +static constexpr int SUBDIVIDE_SAFETY_CAP = 16'000'000; + +// Vertices at one position stay separate when their faces disagree by more than this: a cube keeps +// hard edges, a cylinder keeps averaged ones. +static constexpr double SUBDIVIDE_SHARP_ANGLE_DEG = 30.0; + +// A depth bound, not a work bound: the loop stops as soon as a pass changes nothing. +static constexpr int SUBDIVIDE_MAX_ITERATIONS = 12; + +// Built by the indexers, appended to by the passes. Double precision so repeated midpointing does +// not drift. +struct VertStore +{ + std::vector pos; + std::vector nrm; + std::vector wgt; // exclusion weights; empty when the caller supplied none + std::vector canon; // canonical position ids; empty in fast mode + + size_t count() const { return pos.size(); } + int push(const Vec3d &p, const Vec3d &n) + { + const int idx = int(pos.size()); + pos.push_back(p); + nrm.push_back(n); + return idx; + } +}; + +struct IndexedMesh +{ + VertStore verts; + std::vector indices; // 3 per triangle + QuantizedPointMap pos_canon_map{ WELD_GRID_GEOMETRY, 256 }; + bool has_canon = false; +}; + +// Fraction, triangle count, longest remaining edge. Returning false cancels; what comes back is +// still watertight, because passes apply whole or not at all. +using SubdivideProgressFn = std::function; + +struct SubdivideResult +{ + TriSoup geometry; + // Output triangle -> input triangle it descends from, so per-face data survives with no remap. + std::vector face_parent_id; + bool safety_cap_hit = false; +}; + +// `face_excluded`: one entry per input triangle; non-zero means its interior is never refined. Its +// edges still split when an included neighbour marks them, so no T-junction appears at the boundary. +// `fast` selects the cheap position-only indexer for previews. +SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, + const std::vector &face_excluded = {}, bool fast = false, + int safety_cap = SUBDIVIDE_SAFETY_CAP, + const SubdivideProgressFn &on_progress = {}); + +// Displacement needs the same welding and sharp-edge clustering. +IndexedMesh to_indexed(const TriSoup &geometry); +IndexedMesh to_indexed_fast(const TriSoup &geometry); +TriSoup to_non_indexed(const VertStore &verts, const std::vector &indices, + const std::vector &face_excluded); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 74485c7919..4fcf00b462 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -15,11 +15,14 @@ #include #include +#include #include "MeshBoolean.hpp" #include "Model.hpp" #include "PNGReadWrite.hpp" #include "TriangleSelector.hpp" +#include "TextureBake/TextureBakeMesh.hpp" +#include "TextureBake/TextureBakePipeline.hpp" namespace Slic3r { @@ -1361,6 +1364,83 @@ void despeckle_triangle_colors(const indexed_triangle_set &mesh, std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress) +{ + HeightFieldSampler combined = make_combined_displacement_sampler(mesh, layers, facets_data); + if (!combined) + return mesh; // nothing decodable to displace with + + // Unpainted triangles are excluded, keeping them out of refinement and pinned thereafter. + std::vector excluded(mesh.indices.size(), 1); + { + const TriangleMesh selector_mesh(mesh); + TriangleSelector selector(selector_mesh); + bool dirty = false; + for (const TriangleSelector::TriangleSplittingData &data : facets_data) { + if (data.triangles_to_split.empty()) + continue; + selector.deserialize(data, dirty); + dirty = true; + std::vector piece_src; + const indexed_triangle_set patch = + selector.get_facets_strict(EnforcerBlockerType::ENFORCER, &piece_src); + for (const int src : piece_src) + if (src >= 0 && size_t(src) < excluded.size()) + excluded[size_t(src)] = 0; + } + } + if (std::all_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; })) + return mesh; // nothing painted + + TextureBake::PipelineSettings settings; + settings.refine_length = std::max(0.01f, options.v2_refine_mm); + settings.regularize = options.v2_regularize; + settings.max_triangles = size_t(std::max(0, options.v2_max_triangles_k)) * 1000; + settings.preserve_untextured = true; + // The sampler already returns millimetres, so the displacement stage must not scale it again. + settings.displace.amplitude = 1.f; + settings.displace.symmetric = false; + // The paint decides what moves here, so the angle limits stay off. + settings.displace.bottom_angle_limit = 0.f; + settings.displace.top_angle_limit = 0.f; + + TextureBake::DisplaceBounds bounds; + bounds.min = bounds.max = mesh.vertices.empty() ? Vec3f::Zero() : mesh.vertices.front(); + for (const Vec3f &v : mesh.vertices) { + bounds.min = bounds.min.cwiseMin(v); + bounds.max = bounds.max.cwiseMax(v); + } + + const auto sample = [&combined](const Vec3f &pos, const Vec3f &smooth_normal, const Vec3f &) { + // The smooth normal: the direction the vertex actually moves along. + return combined(pos, smooth_normal); + }; + + // 0 means no simplification, i.e. Bake mode. + const TextureBake::PipelineMode mode = settings.max_triangles > 0 ? TextureBake::PipelineMode::Export + : TextureBake::PipelineMode::Bake; + TextureBake::PipelineResult result = TextureBake::run_pipeline( + TextureBake::to_soup(mesh, excluded), sample, settings, bounds, mode, excluded, + [&progress](const char *, double f) { + return !progress || progress(std::clamp(int(f * 100.0), 0, 99)); + }); + if (result.canceled || result.geometry.empty()) + return {}; + + indexed_triangle_set out = TextureBake::to_indexed_triangle_set(result.geometry); + return out.indices.empty() ? mesh : out; +} + +} // namespace + indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, @@ -1384,6 +1464,9 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (mesh.vertices.empty() || mesh.indices.empty()) return mesh; + if (options.pipeline_v2) + return build_texture_displacement_v2(mesh, layers, facets_data, options, progress); + // Layers are combined in slot order, like stacked layers in an image editor: each one folds its // own displacement into the running total via its blend mode (see TextureBlendMode). std::vector ordered_layers; @@ -2073,26 +2156,38 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // instead of with the refined region, and what forced the tiny pass budget that stopped // refinement short. { - struct EdgeRec { int he[2]; int count; }; // he = encoded half-edge (triangle * 3 + local edge) - std::unordered_map edges; - edges.reserve(tris.size() * 2); - for (int ti = 0; ti < int(tris.size()); ++ti) - for (int e = 0; e < 3; ++e) { - EdgeRec &r = edges.try_emplace(edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]), - EdgeRec{ { -1, -1 }, 0 }) - .first->second; - if (r.count < 2) - r.he[r.count] = ti * 3 + e; - ++r.count; - } - for (int ti = 0; ti < int(tris.size()); ++ti) - for (int e = 0; e < 3; ++e) { - const EdgeRec &r = edges.at(edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3])); - if (r.count > 2) - tris[ti].nb[e] = NB_NONMANIFOLD; - else if (r.count == 2) - tris[ti].nb[e] = (r.he[0] == ti * 3 + e ? r.he[1] : r.he[0]) / 3; + // Sort the half-edges by their edge key and walk the equal runs, rather than hashing every one + // of them twice into an unordered_map. Same result, but the two expensive parts - forming the + // keys and ordering them - both parallelise, where a shared hash map cannot. The map also cost + // a second full pass of lookups purely to read back what the first pass had just inserted. + std::vector> he(tris.size() * 3); // (edge key, triangle * 3 + local edge) + tbb::parallel_for(tbb::blocked_range(0, tris.size()), + [&](const tbb::blocked_range &range) { + for (size_t ti = range.begin(); ti < range.end(); ++ti) + for (int e = 0; e < 3; ++e) + he[ti * 3 + size_t(e)] = { + edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]), int(ti) * 3 + e + }; + }); + tbb::parallel_sort(he.begin(), he.end()); + // Runs of equal key are the half-edges of one edge: one is a boundary, two are neighbours, + // more is non-manifold. Serial, but it is a single linear pass over an already ordered array. + for (size_t i = 0; i < he.size();) { + size_t j = i + 1; + while (j < he.size() && he[j].first == he[i].first) + ++j; + const size_t count = j - i; + if (count == 2) { + const int a = he[i].second, b = he[i + 1].second; + tris[size_t(a / 3)].nb[a % 3] = b / 3; + tris[size_t(b / 3)].nb[b % 3] = a / 3; + } else if (count > 2) { + for (size_t k = i; k < j; ++k) + tris[size_t(he[k].second / 3)].nb[he[k].second % 3] = NB_NONMANIFOLD; } + // count == 1 keeps the NB_BOUNDARY it was initialised with. + i = j; + } } // Feature mode: per-vertex surface normal, and the sampled displacement height at each vertex. @@ -2152,6 +2247,40 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, return vcolor[v]; }; + // Sample the input mesh's own vertices up front, in parallel, for the region that is going to be + // refined. A sampler call is a texture fetch plus the projection's trigonometry per layer, and it + // is by far the most expensive thing here - but taken one at a time from inside the refinement + // loop it is also strictly serial. Every one of these vertices is read by the very first scoring + // pass anyway, so doing them together costs nothing extra and hands the work to every core. + // + // Only the region, and only the *initial* vertices: the laziness this replaces exists so that a + // small painted patch on a big model does not pay for the whole model (see height_of()), and that + // still holds. Midpoints created later stay lazy, because they do not exist yet. + if (feature_mode || color_mode) { + std::vector wanted(verts.size(), 0); + for (const Tri &t : tris) + if (refine_region[t.src] != 0) + for (int i = 0; i < 3; ++i) + wanted[size_t(t.v[i])] = 1; + // Each index is touched by exactly one iteration, so the lazy caches can be filled without + // synchronisation - and every value is the one height_of()/color_of() would have produced. + tbb::parallel_for(tbb::blocked_range(0, verts.size()), + [&](const tbb::blocked_range &range) { + for (size_t v = range.begin(); v < range.end(); ++v) { + if (!wanted[v]) + continue; + if (feature_mode) { + vheight[v] = sampler(verts[v], vnormal[v]); + vheight_valid[v] = 1; + } + if (color_mode) { + vcolor[v] = color(verts[v], vnormal[v]); + vcolor_valid[v] = 1; + } + } + }); + } + auto elen_sq = [&](int a, int b) -> float { return (verts[a] - verts[b]).squaredNorm(); }; // The one edge of a triangle taken as its "longest": greatest squared length, exact ties broken by @@ -2388,9 +2517,22 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // re-scored on pop and dropped or re-pushed. The ordering is a budget-allocation heuristic only: // neither correctness nor conformality depends on it. std::priority_queue> queue; - for (int ti = 0; ti < int(tris.size()); ++ti) - if (const float p = priority(ti); p > 1.f) - queue.emplace(p, ti); + { + // Scoring the starting mesh means a detail_error() per triangle - four more sampler calls each + // - so it is worth spreading, even though the refinement that follows cannot be. Each entry is + // written by one iteration only, and the caches those calls fill (tri_err, tri_color_split) are + // likewise per triangle, so there is nothing shared to guard. The heap is then built from the + // finished array in index order, which is exactly the order the serial loop pushed in. + std::vector initial(tris.size(), 0.f); + tbb::parallel_for(tbb::blocked_range(0, tris.size()), + [&](const tbb::blocked_range &range) { + for (size_t ti = range.begin(); ti < range.end(); ++ti) + initial[ti] = priority(int(ti)); + }); + for (int ti = 0; ti < int(tris.size()); ++ti) + if (initial[size_t(ti)] > 1.f) + queue.emplace(initial[size_t(ti)], ti); + } // Every iteration either drops one satisfied triangle from the queue or performs exactly one // bisection, and bisections are capped by the triangle budget, so this always terminates. diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 7abf1d656c..c187c3be48 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -169,7 +169,7 @@ struct TextureDisplacementLayer std::shared_ptr> image_data; float depth_mm = 0.4f; // maximum displacement along the surface normal, in mm - float tiling_scale = 10.f; // size of one texture tile, in mm + float tiling_scale = 12.5f; // size of one texture tile, in mm float rotation_deg = 0.f; Vec2f offset = Vec2f::Zero(); bool invert = false; @@ -365,6 +365,15 @@ struct TextureDisplacementOptions // deliberately (which is a blunter version of the per-layer edge-smoothing falloff). bool smooth_skip_border = true; + // Alternative bake pipeline, for side-by-side comparison. The path above is topology-preserving + // and needs the mesh prepared first; this one refines, removes slivers, displaces and optionally + // simplifies in one run. Off by default, and it produces no colour - it rebuilds the topology, so + // the per-facet assignment has nothing stable to attach to. + bool pipeline_v2 = false; + float v2_refine_mm = 0.3f; + bool v2_regularize = false; + int v2_max_triangles_k = 750; // 0 skips simplification, which is worth comparing on its own + // Colour, all of which belongs to the stack rather than to any one layer: it is about how the // printer will realise the colours, not about which image they came from. @@ -381,7 +390,8 @@ struct TextureDisplacementOptions { int mix_mode = int(color_mix_mode); ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border, - color_mix_enabled, mix_mode, color_despeckle); + pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k, color_mix_enabled, mix_mode, + color_despeckle); color_mix_mode = ColorMixMode(mix_mode); } }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index b0bfefddbd..8de71180f0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -3317,7 +3317,9 @@ bool GLGizmoTextureDisplacement::collect_paint_region( ++vstart[size_t(its.indices[i][k]) + 1]; for (size_t v = 0; v < nvert; ++v) vstart[v + 1] += vstart[v]; - std::vector vtri(size_t(vstart[nvert]), 0); + // static_cast, not size_t(...): the latter parses as a parameter declaration (see the note + // above the identical prefix sum on `part`). + std::vector vtri(static_cast(vstart[nvert]), 0); { std::vector fill(vstart.begin(), vstart.begin() + nvert); for (size_t i = 0; i < ntri; ++i) @@ -4095,6 +4097,13 @@ void GLGizmoTextureDisplacement::bake_standard() } apply_standard_mode_presets(mv); // belt and braces: never bake with values the panel is not showing + // It refines as part of the bake, so preparing first would refine a second time at another + // target. + if (mv->texture_displacement_options.pipeline_v2) { + bake(); + return; + } + // The whole recipe in one go. Either stage having nothing to do is normal, not a failure - a mesh // that is already even needs no remesh, one that is already fine enough for the texture needs no // subdivision - so no "nothing changed" message here: it goes straight on to the displacement. @@ -5364,6 +5373,48 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); + // Next to Bake and shown in both modes: the two pipelines have to be switchable on their own. + if (mv != nullptr) { + TextureDisplacementOptions &opts = mv->texture_displacement_options; + ImGui::Separator(); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Experimental bake pipeline").c_str(), &opts.pipeline_v2); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Bake with the alternative pipeline: it refines, cleans up sliver triangles, " + "displaces and simplifies in one run, instead of moving the vertices the mesh " + "already has. Nothing needs preparing first - Subdivide and Remesh are ignored. " + "It does not produce colours yet, because it rebuilds the topology."), + m_imgui->scaled(20.f)); + if (opts.pipeline_v2) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (m_imgui->slider_float(std::string(_u8L("Edge length (mm)")) + "##v2edge", &opts.v2_refine_mm, + 0.02f, 2.f, "%.1f", ImGuiLogSlider)) { + opts.v2_refine_mm = std::clamp(opts.v2_refine_mm, 0.02f, 2.f); + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangle size the painted area is refined to before displacement. This is " + "what decides how much of the texture the mesh can carry."), + m_imgui->scaled(20.f)); + if (ImGui::SliderInt((_u8L("Triangle budget (k)") + "##v2budget").c_str(), &opts.v2_max_triangles_k, + 0, 4000)) { + opts.v2_max_triangles_k = std::clamp(opts.v2_max_triangles_k, 0, 4000); + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangles to simplify down to after displacement, in thousands. 0 turns " + "simplification off, which is worth comparing on its own."), + m_imgui->scaled(20.f)); + ImGui::PopItemWidth(); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Collapse the thin triangles refinement inherits from the model's own " + "tessellation, before displacement samples them. A sliver's three corners " + "land on three unrelated parts of the texture, which is what makes the " + "relief look jagged."), + m_imgui->scaled(20.f)); + } + } + ImGui::SameLine(); m_imgui->disabled_begin(m_bake_in_progress || m_prepare_in_progress || mv == nullptr || !mv->is_texture_displacement_painted()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 93bbc61117..05d2fa20eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -511,9 +511,9 @@ private: // The default used to be 1500 (i.e. +1.5 M triangles), which is what made Standard mode's Bake // take minutes: every stage after the subdivision - the displacement itself, the convex hull, the // GLModel upload, and the re-slice changed_object() triggers - then runs on a mesh two orders of - // magnitude denser than the input. 300k is still far finer than any FDM nozzle resolves at the + // magnitude denser than the input. 750k is still far finer than any FDM nozzle resolves at the // 0.02 mm detail tolerance Standard uses, and the slider goes to 2000 for anyone who wants more. - int m_subdivide_budget_k = 300; + int m_subdivide_budget_k = 750; void subdivide_model_adaptive(); // Fills `region` (per current-mesh triangle, a REFINE_* bitmask) from the union of every layer's // painted area plus the band straddling its edge. If `paint` is non-null, also fills the per-layer @@ -572,7 +572,6 @@ private: GLModel m_paint_overlay_glmodel; // Set on every paint event, cleared when the overlay is rebuilt in render_painter_gizmo(). Kept // separate from m_bump_preview_dirty so a stroke refreshes only the small painted patch per frame, - // not the bump mesh (which also carries every *unpainted* triangle of the volume). bool m_paint_overlay_dirty = false; void rebuild_paint_overlay(); void render_paint_overlay(); From 08fa489335976f93cb57b9910aea7ec819902062 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 3 Sep 2026 09:13:14 +0200 Subject: [PATCH 24/89] Add new bake pipeline --- src/libslic3r/CMakeLists.txt | 16 + .../TextureBake/TextureBakeDecimate.cpp | 478 ++++++++++++++++++ .../TextureBake/TextureBakeDecimate.hpp | 56 ++ .../TextureBake/TextureBakeDisplace.cpp | 268 ++++++++++ .../TextureBake/TextureBakeDisplace.hpp | 68 +++ .../TextureBake/TextureBakeIndex.cpp | 25 + .../TextureBake/TextureBakeIndex.hpp | 164 ++++++ src/libslic3r/TextureBake/TextureBakeMesh.cpp | 62 +++ src/libslic3r/TextureBake/TextureBakeMesh.hpp | 19 + .../TextureBake/TextureBakePipeline.cpp | 216 ++++++++ .../TextureBake/TextureBakePipeline.hpp | 98 ++++ .../TextureBake/TextureBakeRegularize.cpp | 380 ++++++++++++++ .../TextureBake/TextureBakeRegularize.hpp | 73 +++ .../TextureBake/TextureBakeRepair.cpp | 215 ++++++++ .../TextureBake/TextureBakeRepair.hpp | 48 ++ .../TextureBake/TextureBakeSubdivide.cpp | 414 +++++++++++++++ .../TextureBake/TextureBakeSubdivide.hpp | 83 +++ src/libslic3r/TextureDisplacement.cpp | 186 ++++++- src/libslic3r/TextureDisplacement.hpp | 14 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 53 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 5 +- 21 files changed, 2913 insertions(+), 28 deletions(-) create mode 100644 src/libslic3r/TextureBake/TextureBakeDecimate.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDecimate.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDisplace.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDisplace.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeIndex.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeIndex.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeMesh.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeMesh.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakePipeline.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakePipeline.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRegularize.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRegularize.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRepair.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRepair.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeSubdivide.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeSubdivide.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a0677e332e..56b2a1168f 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -464,6 +464,22 @@ set(lisbslic3r_sources TextConfiguration.hpp TextureDisplacement.cpp TextureDisplacement.hpp + TextureBake/TextureBakeIndex.cpp + TextureBake/TextureBakeIndex.hpp + TextureBake/TextureBakeSubdivide.cpp + TextureBake/TextureBakeSubdivide.hpp + TextureBake/TextureBakeRegularize.cpp + TextureBake/TextureBakeRegularize.hpp + TextureBake/TextureBakeDisplace.cpp + TextureBake/TextureBakeDisplace.hpp + TextureBake/TextureBakeDecimate.cpp + TextureBake/TextureBakeDecimate.hpp + TextureBake/TextureBakeRepair.cpp + TextureBake/TextureBakeRepair.hpp + TextureBake/TextureBakePipeline.cpp + TextureBake/TextureBakePipeline.hpp + TextureBake/TextureBakeMesh.cpp + TextureBake/TextureBakeMesh.hpp Thread.cpp Thread.hpp Time.cpp diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp new file mode 100644 index 0000000000..d4744f40a7 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -0,0 +1,478 @@ +#include "TextureBakeDecimate.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +// Symmetric 4x4 quadric, as its 10 upper-triangle values. +struct Quadric +{ + std::array q{}; + + void add_plane(double a, double b, double c, double d) + { + q[0] += a * a; q[1] += a * b; q[2] += a * c; q[3] += a * d; + q[4] += b * b; q[5] += b * c; q[6] += b * d; + q[7] += c * c; q[8] += c * d; + q[9] += d * d; + } + void operator+=(const Quadric &o) + { + for (int i = 0; i < 10; ++i) + q[size_t(i)] += o.q[size_t(i)]; + } + double eval(double x, double y, double z) const + { + return q[0] * x * x + 2 * q[1] * x * y + 2 * q[2] * x * z + 2 * q[3] * x + + q[4] * y * y + 2 * q[5] * y * z + 2 * q[6] * y + + q[7] * z * z + 2 * q[8] * z + q[9]; + } +}; + +double eval_sum(const std::vector &qs, int v1, int v2, const Vec3d &p) +{ + return qs[size_t(v1)].eval(p.x(), p.y(), p.z()) + qs[size_t(v2)].eval(p.x(), p.y(), p.z()); +} + +// The position minimising the summed quadric, if the system is well conditioned enough to trust. +bool solve_q(const std::vector &qs, int v1, int v2, Vec3d &out) +{ + const auto &A = qs[size_t(v1)].q; + const auto &B = qs[size_t(v2)].q; + const double a00 = A[0] + B[0], a01 = A[1] + B[1], a02 = A[2] + B[2]; + const double a11 = A[4] + B[4], a12 = A[5] + B[5], a22 = A[7] + B[7]; + const double b0 = -(A[3] + B[3]), b1 = -(A[6] + B[6]), b2 = -(A[8] + B[8]); + + const double det = a00 * (a11 * a22 - a12 * a12) - a01 * (a01 * a22 - a12 * a02) + + a02 * (a01 * a12 - a11 * a02); + const double max_el = std::max({ std::abs(a00), std::abs(a01), std::abs(a02), std::abs(a11), + std::abs(a12), std::abs(a22) }); + // Scaled with the matrix, so it means the same at any model scale. + const double threshold = max_el * max_el * max_el * 1e-10; + if (std::abs(det) < std::max(threshold, 1e-30)) + return false; + + const double inv = 1.0 / det; + out.x() = inv * (b0 * (a11 * a22 - a12 * a12) - a01 * (b1 * a22 - a12 * b2) + a02 * (b1 * a12 - a11 * b2)); + out.y() = inv * (a00 * (b1 * a22 - a12 * b2) - b0 * (a01 * a22 - a12 * a02) + a02 * (a01 * b2 - b1 * a02)); + out.z() = inv * (a00 * (a11 * b2 - b1 * a12) - a01 * (a01 * b2 - b1 * a02) + b0 * (a01 * a12 - a11 * a02)); + return true; +} + +Vec3d face_normal_unit(const std::vector &pos, int a, int b, int c) +{ + const Vec3d n = (pos[size_t(b)] - pos[size_t(a)]).cross(pos[size_t(c)] - pos[size_t(a)]); + const double len = n.norm(); + return (len > 0.0) ? Vec3d(n / len) : Vec3d::Zero(); +} + +// Versions are captured at push time; a mismatch on pop means a later collapse invalidated the entry. +// Lazy deletion, far cheaper than removing entries eagerly. +struct HeapEntry +{ + double cost; + int v1, v2; + uint32_t ver1, ver2; + Vec3d p; + bool operator>(const HeapEntry &o) const { return cost > o.cost; } +}; + +} // namespace + +DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool harvest_flat, + double harvest_tol, const std::vector &locked_faces, + const DecimateProgressFn &on_progress) +{ + DecimateResult result; + const size_t n = geometry.pos.size(); + if (n < 3) { + result.geometry = geometry; + return result; + } + + // The finest grid. Anything coarser fuses distinct fine-feature vertices on a displaced mesh, + // leaving it non-manifold before decimation starts and producing open edges afterwards. + QuantizedPointMap vert_map(WELD_GRID_DECIMATION, std::min(n, size_t(1) << 22)); + std::vector pos; + std::vector remap(n); + for (size_t i = 0; i < n; ++i) { + const int idx = vert_map.get_or_set(geometry.pos[i], int(pos.size())); + if (vert_map.inserted()) + pos.push_back(geometry.pos[i].cast()); + remap[i] = idx; + } + const size_t vert_count = pos.size(); + const size_t face_count = n / 3; + std::vector faces(face_count * 3); + for (size_t i = 0; i < n; ++i) + faces[i] = remap[i]; + + if (face_count <= target_triangles && !harvest_flat) { + result.geometry = geometry; + return result; + } + + // An edge with a locked endpoint never reaches the heap. + std::vector locked_vert; + size_t locked_face_count = 0; + if (!locked_faces.empty()) { + locked_vert.assign(vert_count, 0); + for (size_t f = 0; f < face_count && f < locked_faces.size(); ++f) { + if (!locked_faces[f]) + continue; + ++locked_face_count; + for (int k = 0; k < 3; ++k) + locked_vert[size_t(faces[f * 3 + size_t(k)])] = 1; + } + } + // With the locked faces alone at the target, chasing it would grind the free region to its guard + // limit for nothing - harvest only, and say so. + const bool locked_over_budget = + !locked_vert.empty() && face_count > target_triangles && locked_face_count >= target_triangles; + result.locked_over_budget = locked_over_budget; + if (locked_over_budget && !harvest_flat) { + result.geometry = geometry; + return result; + } + + std::vector quadrics(vert_count); + for (size_t f = 0; f < face_count; ++f) { + const int a = faces[f * 3], b = faces[f * 3 + 1], c = faces[f * 3 + 2]; + if (a < 0) + continue; + const Vec3d nrm = face_normal_unit(pos, a, b, c); + if (nrm.isZero()) + continue; + const double d = -nrm.dot(pos[size_t(a)]); + for (const int v : { a, b, c }) + quadrics[size_t(v)].add_plane(nrm.x(), nrm.y(), nrm.z(), d); + } + + // Two penalty planes per endpoint on a sharp interior edge, each perpendicular to one adjacent + // face and containing the edge, constraining the vertex to the crease line. + { + struct EdgeRec { int va, vb, f0, f1; uint8_t count; }; + std::vector edges; + QuantizedPointMap edge_idx(1.0, std::min(face_count * 3, size_t(1) << 22)); + for (size_t f = 0; f < face_count; ++f) { + if (faces[f * 3] < 0) + continue; + for (int e = 0; e < 3; ++e) { + const int va = faces[f * 3 + size_t(e)]; + const int vb = faces[f * 3 + size_t((e + 1) % 3)]; + const int lo = std::min(va, vb), hi = std::max(va, vb); + const int ei = edge_idx.get_or_set_key(lo, hi, 0, int(edges.size())); + if (edge_idx.inserted()) + edges.push_back({ lo, hi, int(f), -1, 1 }); + else if (edges[size_t(ei)].count == 1) { + edges[size_t(ei)].f1 = int(f); + edges[size_t(ei)].count = 2; + } else + // Non-manifold; never feeds a crease. + edges[size_t(ei)].count = 3; + } + } + + const double sqrt_w = std::sqrt(DECIMATE_CREASE_WEIGHT); + for (const EdgeRec &er : edges) { + if (er.count != 2) + continue; // boundary or non-manifold + const Vec3d n0 = face_normal_unit(pos, faces[size_t(er.f0) * 3], faces[size_t(er.f0) * 3 + 1], + faces[size_t(er.f0) * 3 + 2]); + const Vec3d n1 = face_normal_unit(pos, faces[size_t(er.f1) * 3], faces[size_t(er.f1) * 3 + 1], + faces[size_t(er.f1) * 3 + 2]); + if (n0.dot(n1) >= DECIMATE_CREASE_COS) + continue; // smooth enough to be no crease + + const Vec3d e = pos[size_t(er.vb)] - pos[size_t(er.va)]; + const double elen = e.norm(); + if (elen <= 0.0) + continue; + const Vec3d ed = e / elen; + for (const Vec3d &fn : { n0, n1 }) { + Vec3d pn = fn.cross(ed); + const double plen = pn.norm(); + if (plen < 1e-10) + continue; // edge parallel to the face normal + pn /= plen; + const double d = -pn.dot(pos[size_t(er.va)]); + // sqrt(w) on the inputs gives w times the accumulated products. + for (const int v : { er.va, er.vb }) + quadrics[size_t(v)].add_plane(pn.x() * sqrt_w, pn.y() * sqrt_w, pn.z() * sqrt_w, + d * sqrt_w); + } + } + } + + // Vertex-face incidence as intrusive linked lists of slots over flat arrays. + const size_t S = face_count * 3; + std::vector vf_head(vert_count, -1), slot_face(S), slot_vert(S), slot_next(S, -1), + slot_prev(S, -1), face_slot(S, -1); + for (size_t f = 0; f < face_count; ++f) + for (int k = 0; k < 3; ++k) { + const int s = int(f) * 3 + k; + const int v = faces[size_t(s)]; + slot_face[size_t(s)] = int(f); + slot_vert[size_t(s)] = v; + slot_next[size_t(s)] = vf_head[size_t(v)]; + slot_prev[size_t(s)] = -1; + if (vf_head[size_t(v)] >= 0) + slot_prev[size_t(vf_head[size_t(v)])] = s; + vf_head[size_t(v)] = s; + face_slot[size_t(s)] = s; + } + const auto unlink_slot = [&](int s) { + const int p = slot_prev[size_t(s)], nx = slot_next[size_t(s)]; + if (p >= 0) slot_next[size_t(p)] = nx; + else vf_head[size_t(slot_vert[size_t(s)])] = nx; + if (nx >= 0) slot_prev[size_t(nx)] = p; + }; + const auto move_slot = [&](int s, int nv) { + unlink_slot(s); + slot_next[size_t(s)] = vf_head[size_t(nv)]; + slot_prev[size_t(s)] = -1; + if (vf_head[size_t(nv)] >= 0) + slot_prev[size_t(vf_head[size_t(nv)])] = s; + vf_head[size_t(nv)] = s; + slot_vert[size_t(s)] = nv; + }; + + std::vector active(vert_count, 1); + std::vector version(vert_count, 0); + std::vector nb_stamp(vert_count, 0), lk_stamp(vert_count, 0); + uint32_t epoch = 1, lk_epoch = 1; + size_t active_faces = face_count; + + std::priority_queue, std::greater> heap; + + const auto push_edge = [&](int v1, int v2) { + Vec3d p; + if (!solve_q(quadrics, v1, v2, p)) { + const Vec3d mid = (pos[size_t(v1)] + pos[size_t(v2)]) * 0.5; + const double e1 = eval_sum(quadrics, v1, v2, pos[size_t(v1)]); + const double e2 = eval_sum(quadrics, v1, v2, pos[size_t(v2)]); + const double em = eval_sum(quadrics, v1, v2, mid); + const double emin = std::min({ e1, e2, em }); + const double etol = emin * 1e-2 + 1e-12; + // The midpoint when the three are near-equal, i.e. flat: it moves adjacent triangles + // least, so fewer normal flips and no stalling on coplanar geometry. + if (em <= emin + etol) p = mid; + else if (e1 <= e2) p = pos[size_t(v1)]; + else p = pos[size_t(v2)]; + } + // Where quadric costs are all near zero, shorter edges first keeps triangle quality up. + const double len2 = (pos[size_t(v2)] - pos[size_t(v1)]).squaredNorm(); + heap.push({ eval_sum(quadrics, v1, v2, p) + len2 * 1e-8, v1, v2, version[size_t(v1)], + version[size_t(v2)], p }); + }; + + { + QuantizedPointMap seed_seen(1.0, std::min(face_count * 3, size_t(1) << 22)); + for (size_t f = 0; f < face_count; ++f) { + if (faces[f * 3] < 0) + continue; + for (int e = 0; e < 3; ++e) { + const int va = faces[f * 3 + size_t(e)]; + const int vb = faces[f * 3 + size_t((e + 1) % 3)]; + if (!locked_vert.empty() && (locked_vert[size_t(va)] || locked_vert[size_t(vb)])) + continue; + seed_seen.get_or_set_key(std::min(va, vb), std::max(va, vb), 0, 1); + if (seed_seen.inserted()) + push_edge(va, vb); + } + } + } + + // 0 means a stale entry, 1 a boundary edge, 2 or more safe. + const auto shared_face_count = [&](int v1, int v2) { + int count = 0; + for (int s = vf_head[size_t(v1)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) + if (faces[size_t(f) * 3 + size_t(k)] == v2) { + if (++count >= 2) + return 2; + break; + } + } + return count; + }; + + // Safe only when the sole common neighbours of the endpoints are the apexes of the faces the edge + // already shares; any other would pile a third triangle onto an edge after the collapse. + const auto has_link_violation = [&](int v1, int v2, uint32_t ep) { + for (int s = vf_head[size_t(v1)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) + if (const int x = faces[size_t(f) * 3 + size_t(k)]; x != v1) + lk_stamp[size_t(x)] = ep; + } + int shared = 0; + for (int s = vf_head[size_t(v1)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + const int a = faces[size_t(f) * 3], b = faces[size_t(f) * 3 + 1], c = faces[size_t(f) * 3 + 2]; + if (a == v2 || b == v2 || c == v2) { + ++shared; + const int apex = (a != v1 && a != v2) ? a : (b != v1 && b != v2) ? b : c; + lk_stamp[size_t(apex)] = ep + 1; // a legal shared-face apex + } + } + if (shared > 2) + return true; // already non-manifold + for (int s = vf_head[size_t(v2)]; s >= 0; s = slot_next[size_t(s)]) { + const int f = slot_face[size_t(s)]; + if (faces[size_t(f) * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) { + const int x = faces[size_t(f) * 3 + size_t(k)]; + if (x != v2 && x != v1 && lk_stamp[size_t(x)] == ep) + return true; + } + } + return false; + }; + + // Squared-dot, so no square root or division. Faces containing the other endpoint are the ones + // being removed, so they are skipped. + const auto check_flipped = [&](int vc, int vo, const Vec3d &np) { + for (int s = vf_head[size_t(vc)]; s >= 0; s = slot_next[size_t(s)]) { + const size_t f = size_t(slot_face[size_t(s)]); + if (faces[f * 3] < 0) + continue; + const int fa = faces[f * 3], fb = faces[f * 3 + 1], fc = faces[f * 3 + 2]; + if (fa == vo || fb == vo || fc == vo) + continue; + const Vec3d oa = pos[size_t(fa)], ob = pos[size_t(fb)], oc = pos[size_t(fc)]; + const Vec3d on = (ob - oa).cross(oc - oa); + const Vec3d na = (fa == vc) ? np : oa; + const Vec3d nb = (fb == vc) ? np : ob; + const Vec3d nc = (fc == vc) ? np : oc; + const Vec3d nn = (nb - na).cross(nc - na); + const double raw = on.dot(nn); + if (raw < 0.0) + return true; + if (raw * raw < DECIMATE_FLIP_DOT * DECIMATE_FLIP_DOT * on.squaredNorm() * nn.squaredNorm()) + return true; + } + return false; + }; + + const size_t init_faces = active_faces; + const size_t to_remove = std::max(1, init_faces > target_triangles + ? init_faces - target_triangles : init_faces); + const double harvest_ceil = harvest_tol * harvest_tol; + bool reached_target = locked_over_budget; + double last_progress = 0.0; + + while (!heap.empty()) { + if (active_faces <= target_triangles) { + if (!harvest_flat) + break; + reached_target = true; + } + + const HeapEntry top = heap.top(); + heap.pop(); + // The popped entry is the cheapest left, so exceeding the tolerance ends the run. + if (reached_target && top.cost > harvest_ceil) + break; + + const int v1 = top.v1, v2 = top.v2; + if (!active[size_t(v1)] || !active[size_t(v2)]) + continue; + if (version[size_t(v1)] != top.ver1 || version[size_t(v2)] != top.ver2) + continue; + if (shared_face_count(v1, v2) < 2) + continue; + lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call + if (has_link_violation(v1, v2, lk_epoch)) + continue; + if (check_flipped(v1, v2, top.p) || check_flipped(v2, v1, top.p)) + continue; + + // v1 survives at the new position, v2 goes. + pos[size_t(v1)] = top.p; + quadrics[size_t(v1)] += quadrics[size_t(v2)]; + ++version[size_t(v1)]; + + for (int s = vf_head[size_t(v2)]; s >= 0;) { + const size_t f = size_t(slot_face[size_t(s)]); + const int s_next = slot_next[size_t(s)]; // read before the list is modified + if (faces[f * 3] >= 0) { + for (int k = 0; k < 3; ++k) + if (faces[f * 3 + size_t(k)] == v2) { + faces[f * 3 + size_t(k)] = v1; + break; + } + const int fa = faces[f * 3], fb = faces[f * 3 + 1], fc = faces[f * 3 + 2]; + if (fa == fb || fb == fc || fa == fc) { + for (int k = 0; k < 3; ++k) + if (const int sk = face_slot[f * 3 + size_t(k)]; sk >= 0) { + unlink_slot(sk); + face_slot[f * 3 + size_t(k)] = -1; + } + faces[f * 3] = faces[f * 3 + 1] = faces[f * 3 + 2] = -1; + --active_faces; + } else + move_slot(s, v1); + } + s = s_next; + } + active[size_t(v2)] = 0; + + ++epoch; + for (int sv = vf_head[size_t(v1)]; sv >= 0; sv = slot_next[size_t(sv)]) { + const size_t f = size_t(slot_face[size_t(sv)]); + if (faces[f * 3] < 0) + continue; + for (int k = 0; k < 3; ++k) { + const int nb = faces[f * 3 + size_t(k)]; + if (nb == v1 || nb_stamp[size_t(nb)] == epoch) + continue; + nb_stamp[size_t(nb)] = epoch; + // v1 is never locked - a locked edge never entered the heap. + if (active[size_t(nb)] && (locked_vert.empty() || !locked_vert[size_t(nb)])) + push_edge(v1, nb); + } + } + + if (on_progress) { + const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); + if (p - last_progress > 0.005) { + last_progress = p; + if (!on_progress(p)) + break; + } + } + } + + // Rebuild from the surviving faces, with per-face normals. + TriSoup &out = result.geometry; + for (size_t f = 0; f < face_count; ++f) { + if (faces[f * 3] < 0) + continue; + const Vec3f a = pos[size_t(faces[f * 3])].cast(); + const Vec3f b = pos[size_t(faces[f * 3 + 1])].cast(); + const Vec3f c = pos[size_t(faces[f * 3 + 2])].cast(); + const Vec3f nrm = (b - a).cross(c - a).normalized(); + out.pos.insert(out.pos.end(), { a, b, c }); + out.nrm.insert(out.nrm.end(), { nrm, nrm, nrm }); + } + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp new file mode 100644 index 0000000000..8876ab00df --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -0,0 +1,56 @@ +#pragma once + +// Quadric error metric decimation (Garland & Heckbert), with two additions that matter on a +// displaced mesh. +// +// Crease quadrics: an interior edge sharper than the threshold gets penalty planes at both endpoints, +// perpendicular to each adjacent face and through the edge, weighted so such edges collapse last or +// not at all. A texture's hard step keeps its geometry while the flat ground around it reduces. +// +// Flat-face harvesting: the loop keeps going past the triangle target while each collapse's error +// stays under an absolute bound, so flat faces that cost nothing to remove are not left behind. + +#include +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +// Reject a collapse deviating more than about 78 degrees from the old face normal. +static constexpr double DECIMATE_FLIP_DOT = 0.2; +// Edges sharper than 60 degrees are treated as creases. +static constexpr double DECIMATE_CREASE_COS = 0.5; +// Quadric penalty weight for a crease plane. +static constexpr double DECIMATE_CREASE_WEIGHT = 1e4; + +// Upper bound in mm on the deviation a harvested collapse may introduce; the real one is smaller, +// since the cost sums squared distances over all incident faces. +// +// Absolute, not relative to the cost at which the target was crossed. A relative band fails in the +// case with the most to shed: when the target is reached with a large flat surplus left, the crossing +// cost is essentially zero, so the band is too and nothing is harvested. +static constexpr double DECIMATE_DEFAULT_HARVEST_TOL = 0.005; + +// Returns false to cancel. +using DecimateProgressFn = std::function; + +struct DecimateResult +{ + TriSoup geometry; + // The locked faces alone met the target, so it was unreachable without touching preserved + // geometry. + bool locked_over_budget = false; +}; + +// `locked_faces`: one entry per input triangle; a vertex touching one may neither move nor be +// removed, which also pins the ring between the two regions. +DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool harvest_flat = true, + double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL, + const std::vector &locked_faces = {}, + const DecimateProgressFn &on_progress = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDisplace.cpp b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp new file mode 100644 index 0000000000..ef417a7bef --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp @@ -0,0 +1,268 @@ +#include "TextureBakeDisplace.hpp" + +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample, + const DisplaceSettings &settings, const DisplaceBounds &bounds, + const DisplaceProgressFn &on_progress) +{ + TriSoup out; + const size_t count = geometry.pos.size(); + if (count == 0 || !sample) + return geometry; + + out.pos.resize(count); + out.nrm.resize(count); + + // Everything below is keyed by this id, which is what makes one vector per position expressible. + const bool need_id_positions = settings.boundary_falloff > 0.f; + QuantizedPointMap dedup(WELD_GRID_GEOMETRY, std::min(count, size_t(1) << 22)); + std::vector vertex_id(count); + std::vector id_pos; + int next_id = 0; + for (size_t i = 0; i < count; ++i) { + const int id = dedup.get_or_set(geometry.pos[i], next_id); + if (dedup.inserted()) { + ++next_id; + if (need_id_positions) + id_pos.push_back(geometry.pos[i]); + } + vertex_id[i] = id; + } + const size_t unique_count = size_t(next_id); + + // Pass 1: area-weighted smooth normals per position, plus what masking and falloff need. + std::vector smooth_nrm(unique_count, Vec3d::Zero()); + std::vector masked_area(unique_count, 0.0), total_area(unique_count, 0.0); + const bool have_weights = !geometry.exclude_weight.empty(); + std::vector user_excluded_face(have_weights ? count / 3 : 0, 0); + std::vector excluded_pos(have_weights ? unique_count : 0, 0); + + for (size_t t = 0; t + 2 < count; t += 3) { + const Vec3d a = geometry.pos[t].cast(); + const Vec3d face_n = (geometry.pos[t + 1].cast() - a).cross(geometry.pos[t + 2].cast() - a); + const double face_area = face_n.norm(); // twice the triangle area, so weighting is natural + const double nz = face_area > 1e-12 ? face_n.z() / face_area : 0.0; + const double face_angle = std::acos(std::min(1.0, std::abs(nz))) * (180.0 / M_PI); + const bool angle_masked = + nz < 0.0 ? (settings.bottom_angle_limit > 0.f && face_angle <= settings.bottom_angle_limit) + : (settings.top_angle_limit > 0.f && face_angle <= settings.top_angle_limit); + + // Thresholded high, not at a half: merging by maximum leaves a face bordering an excluded one + // with two corners at 1.0, averaging about 0.67, which a half threshold would misread. + bool user_excluded = false; + if (have_weights) { + const float avg = (geometry.exclude_weight[t] + geometry.exclude_weight[t + 1] + + geometry.exclude_weight[t + 2]) / 3.f; + user_excluded = avg > 0.99f; + if (user_excluded) + user_excluded_face[t / 3] = 1; + } + + for (int v = 0; v < 3; ++v) { + const size_t vid = size_t(vertex_id[t + size_t(v)]); + if (user_excluded && have_weights) + excluded_pos[vid] = 1; + // Subdivision split vertices at sharp edges, so these are smooth across soft edges and + // sharp across hard ones - no faceting on round surfaces, no rounding of corners. + smooth_nrm[vid] += geometry.nrm[t + size_t(v)].cast() * face_area; + if (angle_masked) + masked_area[vid] += face_area; + total_area[vid] += face_area; + } + } + + // The pre-normalisation magnitude over the total area says how much the neighbouring faces agree: + // near 1 they do, near 0 they cancelled, meaning a knife edge with no usable surface direction. + std::vector reliability(unique_count, 0.0); + for (size_t id = 0; id < unique_count; ++id) { + const double len = smooth_nrm[id].norm(); + reliability[id] = (len > 0.0 && total_area[id] > 0.0) ? len / total_area[id] : 0.0; + smooth_nrm[id] = (len > 0.0) ? Vec3d(smooth_nrm[id] / len) : Vec3d(0.0, 0.0, 1.0); + } + + // Pass 1.5: the smoothed blend normal - see the header for why it is separate. + std::vector blend_nrm = smooth_nrm; + if (settings.blend_normal_smoothing > 0 && unique_count > 0) { + // CSR adjacency over the welded graph, deliberately a multigraph: duplicates weight a pair by + // how often it shares an edge, so a well-connected surface couples more strongly. + std::vector degree(unique_count, 0); + const auto add_degree = [&](int a, int b) { + if (a != b) { ++degree[size_t(a)]; ++degree[size_t(b)]; } + }; + for (size_t t = 0; t + 2 < count; t += 3) { + const int a = vertex_id[t], b = vertex_id[t + 1], c = vertex_id[t + 2]; + add_degree(a, b); add_degree(b, c); add_degree(c, a); + } + std::vector csr_start(unique_count + 1, 0); + for (size_t id = 0; id < unique_count; ++id) + csr_start[id + 1] = csr_start[id] + degree[id]; + std::vector neighbors(csr_start[unique_count]); + std::vector cursor(unique_count, 0); + const auto add_edge = [&](int a, int b) { + if (a == b) + return; + neighbors[csr_start[size_t(a)] + cursor[size_t(a)]++] = uint32_t(b); + neighbors[csr_start[size_t(b)] + cursor[size_t(b)]++] = uint32_t(a); + }; + for (size_t t = 0; t + 2 < count; t += 3) { + const int a = vertex_id[t], b = vertex_id[t + 1], c = vertex_id[t + 2]; + add_edge(a, b); add_edge(b, c); add_edge(c, a); + } + + std::vector cur = smooth_nrm, nxt(unique_count, Vec3d::Zero()); + for (int iter = 0; iter < settings.blend_normal_smoothing; ++iter) { + for (size_t id = 0; id < unique_count; ++id) { + const uint32_t s = csr_start[id], e = csr_start[id + 1]; + if (e == s) { + nxt[id] = cur[id]; + continue; + } + Vec3d sum = Vec3d::Zero(); + for (uint32_t k = s; k < e; ++k) + sum += cur[neighbors[k]]; + sum /= double(e - s); + const double len = sum.norm(); + // Cancelling neighbours mean a knife edge; keep what we had. + nxt[id] = (len > 1e-12) ? Vec3d(sum / len) : cur[id]; + } + cur.swap(nxt); + } + blend_nrm = std::move(cur); + } + + // A boundary position borders both masked and unmasked faces, or sits on the exclusion seam. + // Every other position gets its distance to the nearest one, ramped to 1 at the falloff distance. + std::vector falloff; + if (settings.boundary_falloff > 0.f && unique_count > 0) { + std::vector boundary; + for (size_t id = 0; id < unique_count; ++id) { + const double frac = total_area[id] > 0.0 ? masked_area[id] / total_area[id] : 0.0; + const bool on_excl = !excluded_pos.empty() && excluded_pos[id] != 0; + if (on_excl || (frac > 0.0 && frac < 1.0)) + boundary.push_back(id_pos[id]); + } + falloff.assign(unique_count, 1.0); + if (!boundary.empty()) { + // A uniform grid: the query is nearest-point only, so a tree costs more than it saves. + Vec3f lo = boundary.front(), hi = boundary.front(); + for (const Vec3f &p : boundary) { + lo = lo.cwiseMin(p); + hi = hi.cwiseMax(p); + } + const Vec3f span = (hi - lo).cwiseMax(Vec3f(1e-6f, 1e-6f, 1e-6f)); + const int res = std::clamp(int(std::ceil(std::cbrt(double(boundary.size())) * 2.0)), 4, 128); + const Vec3f cell = span / float(res); + const float cell_min = cell.minCoeff(); + const auto cell_of = [&](const Vec3f &p) { + Vec3i32 c; + for (int k = 0; k < 3; ++k) + c[k] = std::clamp(int((p[k] - lo[k]) / span[k] * float(res)), 0, res - 1); + return c; + }; + const auto cell_index = [&](int x, int y, int z) { + return size_t(z) * size_t(res) * size_t(res) + size_t(y) * size_t(res) + size_t(x); + }; + std::vector> grid(size_t(res) * size_t(res) * size_t(res)); + for (size_t i = 0; i < boundary.size(); ++i) { + const Vec3i32 c = cell_of(boundary[i]); + grid[cell_index(c.x(), c.y(), c.z())].push_back(int(i)); + } + + const double radius = double(settings.boundary_falloff); + for (size_t id = 0; id < unique_count; ++id) { + const Vec3f &p = id_pos[id]; + const Vec3i32 c = cell_of(p); + double best = std::numeric_limits::max(); + // Anything in shell r is at least (r - 1) cells away, so once the best found is within + // that bound nothing closer can be hiding further out. + for (int r = 0; r < res; ++r) { + for (int dz = -r; dz <= r; ++dz) + for (int dy = -r; dy <= r; ++dy) + for (int dx = -r; dx <= r; ++dx) { + // The shell only; its interior was covered by a smaller r. + if (r > 0 && std::abs(dx) != r && std::abs(dy) != r && std::abs(dz) != r) + continue; + const int qx = c.x() + dx, qy = c.y() + dy, qz = c.z() + dz; + if (qx < 0 || qy < 0 || qz < 0 || qx >= res || qy >= res || qz >= res) + continue; + for (const int bi : grid[cell_index(qx, qy, qz)]) + best = std::min(best, double((boundary[size_t(bi)] - p).norm())); + } + if (best <= double(r) * double(cell_min)) + break; + } + falloff[id] = (best == std::numeric_limits::max() || radius <= 0.0) + ? 1.0 + : std::clamp(best / radius, 0.0, 1.0); + } + } + } + + // Pass 2: one sample per unique position. + std::vector grey(unique_count, 0.0); + std::vector grey_set(unique_count, 0); + for (size_t i = 0; i < count; ++i) { + const size_t vid = size_t(vertex_id[i]); + if (grey_set[vid]) + continue; + grey_set[vid] = 1; + grey[vid] = double(sample(geometry.pos[i], smooth_nrm[vid].cast(), + blend_nrm[vid].cast())); + } + + // Pass 3: move every copy of a position by the identical vector. + for (size_t i = 0; i < count; ++i) { + const Vec3f &p = geometry.pos[i]; + const size_t vid = size_t(vertex_id[i]); + + // Only angle masking uses the per-position blend, so an excluded face never dims its + // neighbours through a shared vertex. + const bool face_excluded = !user_excluded_face.empty() && user_excluded_face[i / 3] != 0; + // Pinned where an included face shares a position with an excluded one, sealing the boundary. + const bool sealed_boundary = + !face_excluded && !excluded_pos.empty() && excluded_pos[vid] != 0; + const double masked_frac = total_area[vid] > 0.0 ? masked_area[vid] / total_area[vid] : 0.0; + const double centered = settings.symmetric ? (grey[vid] - 0.5) : grey[vid]; + const double ramp = falloff.empty() ? 1.0 : falloff[vid]; + const double disp = (face_excluded || sealed_boundary) + ? 0.0 + : ramp * (1.0 - masked_frac) * centered * double(settings.amplitude); + + Vec3d moved = p.cast() + smooth_nrm[vid] * disp; + + // Stop a partly masked vertex poking through the surface it borders. + if (masked_frac > 0.0) { + if (settings.bottom_angle_limit > 0.f && moved.z() < double(p.z())) moved.z() = double(p.z()); + if (settings.top_angle_limit > 0.f && moved.z() > double(p.z())) moved.z() = double(p.z()); + } + if (settings.no_downward_z && moved.z() < double(p.z())) + moved.z() = double(p.z()); + // A vertex starting on the bottom plane stays there: otherwise a downward-facing face pulls + // *up* where the sample is below mid-grey, leaving bed-contact vertices at differing heights. + if (settings.no_downward_z && double(p.z()) <= double(bounds.min.z()) + 1e-5) + moved.z() = double(p.z()); + + out.pos[i] = moved.cast(); + + if (on_progress && (i % 5000) == 0 && !on_progress(double(i) / double(count))) + return geometry; // cancelled: hand back the input untouched + } + + // Per-face, not averaged across shared positions: averaging can flip an excluded face's normal + // when its neighbours moved outward. + for (size_t t = 0; t + 2 < count; t += 3) { + const Vec3f n = (out.pos[t + 1] - out.pos[t]).cross(out.pos[t + 2] - out.pos[t]).normalized(); + out.nrm[t] = out.nrm[t + 1] = out.nrm[t + 2] = n; + } + out.exclude_weight = geometry.exclude_weight; + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDisplace.hpp b/src/libslic3r/TextureBake/TextureBakeDisplace.hpp new file mode 100644 index 0000000000..eee6ca0626 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDisplace.hpp @@ -0,0 +1,68 @@ +#pragma once + +// Displacement along surface normals. +// +// The mesh is non-indexed, so at a shared edge two triangles hold the same position with different +// face normals; displacing each copy along its own normal sends them to different points and opens a +// crack. So one smooth (area-weighted) normal per unique position drives both the sample lookup and +// the displacement direction, every copy moves by the same vector, and the result is watertight by +// construction. Displaced normals are then smooth at hard edges, but the geometry is still faceted, +// so printed edges stay sharp. + +#include +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +// Height at a point, called once per unique welded position. `smooth_normal` is the vector the +// displacement will move along; `blend_normal` is that after smoothing, for projection blend weights. +using HeightSampleFn = std::function; + +struct DisplaceSettings +{ + // Displacement height in mm, applied to the sampled value. + float amplitude = 0.4f; + + // Sample around a mid-grey rest level rather than displacing outward only. + bool symmetric = false; + + // Faces flatter than these (degrees from horizontal) are held back, leaving bed-contact and top + // surfaces alone. 0 disables that side. + float bottom_angle_limit = 5.f; + float top_angle_limit = 0.f; + + // Never move a vertex below its original Z, so no new overhang. The sideways component is kept. + bool no_downward_z = false; + + // Distance in mm over which displacement ramps up from a mask boundary. 0 leaves a hard edge. + float boundary_falloff = 0.f; + + // Laplacian iterations on the blend normal only - the displacement direction must stay the exact + // smooth normal or copies of a position move differently and the mesh cracks. Inside a blend band + // the weight gradient is largest, so a few degrees of vertex-to-vertex jitter multiplies the + // difference between two unrelated height samples into visible seam noise. A no-op on an + // already-smooth surface. + int blend_normal_smoothing = 32; +}; + +// Model extents; only the minimum Z is read, for the bottom-plane clamp. +struct DisplaceBounds +{ + Vec3f min = Vec3f::Zero(); + Vec3f max = Vec3f::Zero(); +}; + +// Returns false to cancel. +using DisplaceProgressFn = std::function; + +TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample, + const DisplaceSettings &settings, const DisplaceBounds &bounds, + const DisplaceProgressFn &on_progress = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeIndex.cpp b/src/libslic3r/TextureBake/TextureBakeIndex.cpp new file mode 100644 index 0000000000..ac7234774b --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeIndex.cpp @@ -0,0 +1,25 @@ +#include "TextureBakeIndex.hpp" + +#include + +namespace Slic3r { +namespace TextureBake { + +WeldResult weld_vertices(const std::vector &positions, double quant) +{ + WeldResult out; + QuantizedPointMap map(quant, std::min(positions.size(), size_t(1) << 22)); + out.vertex_id.resize(positions.size()); + int next_id = 0; + for (size_t i = 0; i < positions.size(); ++i) { + const int id = map.get_or_set(positions[i], next_id); + if (map.inserted()) + ++next_id; + out.vertex_id[i] = id; + } + out.unique_count = next_id; + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeIndex.hpp b/src/libslic3r/TextureBake/TextureBakeIndex.hpp new file mode 100644 index 0000000000..74ef068174 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeIndex.hpp @@ -0,0 +1,164 @@ +#pragma once + +// Vertex welding for the texture bake pipeline. The pipeline works on non-indexed triangle soup, so +// a shared point exists once per incident triangle with float noise between the copies; welding maps +// each quantised position to one integer id. +// +// The three grids below are deliberately not unified - changing one at a call site changes +// watertightness. 100 um matches the precision files are written with; 10 um keeps small fillet +// vertices distinct (they merge at 100 um, giving needle artifacts after displacement) while still +// absorbing float noise; 1 um is what collapse positioning needs. + +#include +#include +#include + +#include "../Point.hpp" + +namespace Slic3r { +namespace TextureBake { + +static constexpr double WELD_GRID_EXPORT = 1e4; // 100 um +static constexpr double WELD_GRID_GEOMETRY = 1e5; // 10 um +static constexpr double WELD_GRID_DECIMATION = 1e6; // 1 um + +// Round half toward positive infinity. Quantised coordinates hit exact halves often enough that the +// tie rule matters. +inline int64_t grid_round(double v) { return int64_t(std::floor(v + 0.5)); } + +// Open-addressing table over flat arrays: no allocation per lookup, exact integer key comparison. +// Values must be non-negative; -1 is the empty sentinel and what get() returns on a miss. +class QuantizedPointMap +{ +public: + explicit QuantizedPointMap(double quant, size_t expected = 256) : m_quant(quant) + { + size_t cap = 16; + const size_t target = std::max(16, size_t(std::ceil(double(expected) / 0.6))); + while (cap < target) + cap *= 2; + alloc(cap); + } + + size_t size() const { return m_size; } + // Whether the last get_or_set() inserted rather than found. + bool inserted() const { return m_inserted; } + + int get(float x, float y, float z) + { + return m_val[slot(grid_round(double(x) * m_quant), grid_round(double(y) * m_quant), + grid_round(double(z) * m_quant))]; + } + int get(const Vec3f &p) { return get(p.x(), p.y(), p.z()); } + + // The value already stored for this position's grid cell; if there is none, store `value` and + // return it. inserted() then says which of the two happened. + int get_or_set(float x, float y, float z, int value) + { + const int64_t qx = grid_round(double(x) * m_quant); + const int64_t qy = grid_round(double(y) * m_quant); + const int64_t qz = grid_round(double(z) * m_quant); + const size_t i = slot(qx, qy, qz); + if (m_val[i] != -1) { + m_inserted = false; + return m_val[i]; + } + m_qx[i] = qx; m_qy[i] = qy; m_qz[i] = qz; + m_val[i] = value; + m_inserted = true; + if (++m_size > size_t(double(m_cap) * 0.7)) + grow(); + return value; + } + int get_or_set(const Vec3f &p, int value) { return get_or_set(p.x(), p.y(), p.z(), value); } + + // The same table as a set of integer tuples (edge marking, midpoint cache). Quantisation is + // bypassed: routing ids through the float overloads loses precision above 2^24. + int get_key(int64_t a, int64_t b, int64_t c) { return m_val[slot(a, b, c)]; } + int get_or_set_key(int64_t a, int64_t b, int64_t c, int value) + { + const size_t i = slot(a, b, c); + if (m_val[i] != -1) { + m_inserted = false; + return m_val[i]; + } + m_qx[i] = a; m_qy[i] = b; m_qz[i] = c; + m_val[i] = value; + m_inserted = true; + if (++m_size > size_t(double(m_cap) * 0.7)) + grow(); + return value; + } + +private: + void alloc(size_t cap) + { + m_cap = cap; + m_mask = cap - 1; + m_qx.assign(cap, 0); + m_qy.assign(cap, 0); + m_qz.assign(cap, 0); + m_val.assign(cap, -1); + } + + size_t slot(int64_t qx, int64_t qy, int64_t qz) const + { + uint32_t h = uint32_t(int32_t(qx) * int32_t(0x9E3779B1)) ^ + uint32_t(int32_t(qy) * int32_t(0x85EBCA77)) ^ + uint32_t(int32_t(qz) * int32_t(0xC2B2AE3D)); + h ^= h >> 15; + size_t i = size_t(h) & m_mask; + // Equality is checked against the stored 64-bit keys, so truncating to 32 bits for the hash + // costs collisions at worst, never a wrong answer. + while (m_val[i] != -1) { + if (m_qx[i] == qx && m_qy[i] == qy && m_qz[i] == qz) + return i; + i = (i + 1) & m_mask; + } + return i; + } + + void grow() + { + std::vector oqx = std::move(m_qx), oqy = std::move(m_qy), oqz = std::move(m_qz); + std::vector oval = std::move(m_val); + const size_t ocap = m_cap; + alloc(ocap * 2); + for (size_t i = 0; i < ocap; ++i) { + if (oval[i] == -1) + continue; + const size_t s = slot(oqx[i], oqy[i], oqz[i]); + m_qx[s] = oqx[i]; m_qy[s] = oqy[i]; m_qz[s] = oqz[i]; + m_val[s] = oval[i]; + } + } + + double m_quant; + size_t m_cap = 0, m_mask = 0, m_size = 0; + bool m_inserted = false; + std::vector m_qx, m_qy, m_qz; + std::vector m_val; +}; + +// Three consecutive entries per triangle. The indexers turn this into shared vertices where a stage +// needs adjacency. +struct TriSoup +{ + std::vector pos; + std::vector nrm; // parallel to pos + std::vector exclude_weight; // parallel to pos; empty when nothing is excluded + + size_t triangle_count() const { return pos.size() / 3; } + bool empty() const { return pos.empty(); } +}; + +// Assign each vertex the sequential id of its quantised position, first occurrence winning. +struct WeldResult +{ + std::vector vertex_id; + int unique_count = 0; +}; +WeldResult weld_vertices(const std::vector &positions, double quant); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeMesh.cpp b/src/libslic3r/TextureBake/TextureBakeMesh.cpp new file mode 100644 index 0000000000..6f6d72218d --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeMesh.cpp @@ -0,0 +1,62 @@ +#include "TextureBakeMesh.hpp" + +#include + +namespace Slic3r { +namespace TextureBake { + +TriSoup to_soup(const indexed_triangle_set &its, const std::vector &face_excluded) +{ + TriSoup out; + const size_t n = its.indices.size(); + out.pos.resize(n * 3); + out.nrm.resize(n * 3); + const bool have_excl = face_excluded.size() == n; + if (have_excl) + out.exclude_weight.resize(n * 3); + + for (size_t t = 0; t < n; ++t) { + const stl_triangle_vertex_indices &tri = its.indices[t]; + const Vec3f a = its.vertices[size_t(tri[0])]; + const Vec3f b = its.vertices[size_t(tri[1])]; + const Vec3f c = its.vertices[size_t(tri[2])]; + Vec3f nrm = (b - a).cross(c - a); + const float len = nrm.norm(); + nrm = (len > 0.f) ? Vec3f(nrm / len) : Vec3f(0.f, 0.f, 1.f); + out.pos[t * 3] = a; + out.pos[t * 3 + 1] = b; + out.pos[t * 3 + 2] = c; + // Per-face on purpose: the accurate indexer derives smooth normals and splits at sharp edges + // itself, so averaged ones would pre-empt that. + out.nrm[t * 3] = out.nrm[t * 3 + 1] = out.nrm[t * 3 + 2] = nrm; + if (have_excl) { + const float w = face_excluded[t] ? 1.f : 0.f; + out.exclude_weight[t * 3] = out.exclude_weight[t * 3 + 1] = out.exclude_weight[t * 3 + 2] = w; + } + } + return out; +} + +indexed_triangle_set to_indexed_triangle_set(const TriSoup &soup) +{ + indexed_triangle_set out; + const size_t n = soup.pos.size(); + out.indices.reserve(n / 3); + QuantizedPointMap map(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + std::vector id(n); + for (size_t i = 0; i < n; ++i) { + id[i] = map.get_or_set(soup.pos[i], int(out.vertices.size())); + if (map.inserted()) + out.vertices.push_back(soup.pos[i]); + } + for (size_t t = 0; t + 2 < n; t += 3) { + // Welded-together corners carry no area. + if (id[t] == id[t + 1] || id[t + 1] == id[t + 2] || id[t] == id[t + 2]) + continue; + out.indices.emplace_back(id[t], id[t + 1], id[t + 2]); + } + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeMesh.hpp b/src/libslic3r/TextureBake/TextureBakeMesh.hpp new file mode 100644 index 0000000000..b4e3ced5fe --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeMesh.hpp @@ -0,0 +1,19 @@ +#pragma once + +// Conversion between the pipeline's triangle soup and the indexed mesh used elsewhere. The pipeline +// stays on soup because each stage welds on its own grid, and those differences are load-bearing. + +#include "TextureBakeIndex.hpp" +#include "../TriangleMesh.hpp" + +namespace Slic3r { +namespace TextureBake { + +// `face_excluded`: one entry per input triangle, becoming the soup's per-corner exclusion weight. +TriSoup to_soup(const indexed_triangle_set &its, const std::vector &face_excluded = {}); + +// Welds at the geometry grid. +indexed_triangle_set to_indexed_triangle_set(const TriSoup &soup); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp new file mode 100644 index 0000000000..a86fda0e0d --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -0,0 +1,216 @@ +#include "TextureBakePipeline.hpp" + +#include +#include + +namespace Slic3r { +namespace TextureBake { + +void clamp_below_bottom(TriSoup &geometry, float bottom_z) +{ + for (size_t t = 0; t + 2 < geometry.pos.size(); t += 3) { + bool dirty = false; + for (int k = 0; k < 3; ++k) + if (geometry.pos[t + size_t(k)].z() < bottom_z) { + geometry.pos[t + size_t(k)].z() = bottom_z; + dirty = true; + } + if (!dirty) + continue; + Vec3f n = (geometry.pos[t + 1] - geometry.pos[t]).cross(geometry.pos[t + 2] - geometry.pos[t]); + const float len = n.norm(); + n = (len > 0.f) ? Vec3f(n / len) : Vec3f(0.f, 0.f, 1.f); + geometry.nrm[t] = geometry.nrm[t + 1] = geometry.nrm[t + 2] = n; + } +} + +size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol) +{ + const size_t vert_count = geometry.pos.size(); + const size_t tri_count = vert_count / 3; + if (tri_count == 0 || tol <= 0.0) + return 0; + + // Weld at the finest grid: by this point copies of one position are bit-identical, because every + // earlier stage moved them by the same vector. + QuantizedPointMap weld(WELD_GRID_DECIMATION, std::min(vert_count, size_t(1) << 22)); + std::vector vid(vert_count); + int unique = 0; + for (size_t i = 0; i < vert_count; ++i) { + vid[i] = weld.get_or_set(geometry.pos[i], unique); + if (weld.inserted()) + ++unique; + } + // Incident corners per position, CSR style. + std::vector start(size_t(unique) + 1, 0); + for (size_t i = 0; i < vert_count; ++i) + ++start[size_t(vid[i]) + 1]; + for (size_t id = 0; id < size_t(unique); ++id) + start[id + 1] += start[id]; + std::vector inc(vert_count), cursor(size_t(unique), 0); + for (size_t i = 0; i < vert_count; ++i) + inc[start[size_t(vid[i])] + cursor[size_t(vid[i])]++] = uint32_t(i); + + const double fold_cos = std::cos(75.0 * M_PI / 180.0); + std::vector dirty_tri(tri_count, 0); + + for (size_t id = 0; id < size_t(unique); ++id) { + const float z = geometry.pos[inc[start[id]]].z(); + if (z == bottom_z || std::abs(double(z) - double(bottom_z)) > tol) + continue; + + // Simulate the move: every incident triangle must keep positive area and must not fold. + bool ok = true; + for (uint32_t k = start[id]; k < start[id + 1] && ok; ++k) { + const size_t t = size_t(inc[k]) / 3; + Vec3f p[3]; + for (int v = 0; v < 3; ++v) { + p[v] = geometry.pos[t * 3 + size_t(v)]; + if (vid[t * 3 + size_t(v)] == int(id)) + p[v].z() = bottom_z; + } + const Vec3d on = (geometry.pos[t * 3 + 1] - geometry.pos[t * 3]) + .cross(geometry.pos[t * 3 + 2] - geometry.pos[t * 3]).cast(); + const Vec3d nn = (p[1] - p[0]).cross(p[2] - p[0]).cast(); + const double o2 = on.squaredNorm(), n2 = nn.squaredNorm(); + if (n2 < 1e-20) { ok = false; break; } // would collapse to zero area + if (o2 < 1e-20) continue; // already degenerate, cannot judge a rotation + const double dot = on.dot(nn); + if (dot < 0.0 || dot * dot < fold_cos * fold_cos * o2 * n2) + ok = false; + } + if (!ok) + continue; + + for (uint32_t k = start[id]; k < start[id + 1]; ++k) { + geometry.pos[inc[k]].z() = bottom_z; + dirty_tri[size_t(inc[k]) / 3] = 1; + } + } + + size_t dirty = 0; + for (size_t t = 0; t < tri_count; ++t) { + if (!dirty_tri[t]) + continue; + ++dirty; + Vec3f n = (geometry.pos[t * 3 + 1] - geometry.pos[t * 3]) + .cross(geometry.pos[t * 3 + 2] - geometry.pos[t * 3]); + const float len = n.norm(); + n = (len > 0.f) ? Vec3f(n / len) : Vec3f(0.f, 0.f, 1.f); + geometry.nrm[t * 3] = geometry.nrm[t * 3 + 1] = geometry.nrm[t * 3 + 2] = n; + } + return dirty; +} + +PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, + const PipelineSettings &settings, const DisplaceBounds &bounds, + PipelineMode mode, const std::vector &face_excluded, + const PipelineProgressFn &on_progress) +{ + PipelineResult result; + const auto report = [&](const char *stage, double f) { + return !on_progress || on_progress(stage, f); + }; + + if (input.empty() || !sample) { + result.geometry = input; + return result; + } + + // 1. Refine to the target edge length. + SubdivideResult sub = subdivide( + input, settings.refine_length, face_excluded, /* fast */ false, settings.safety_cap, + [&](double f, size_t, double) { return report("subdivide", f); }); + result.safety_cap_hit = sub.safety_cap_hit; + if (!report("subdivide", 1.0)) { + result.canceled = true; + return result; + } + + // 2. Dissolve the slivers refinement inherited, then recover the edges that lengthened. + if (settings.regularize) { + RegularizeOptions ropts = settings.regularize_opts; + ropts.preserve_excluded = settings.preserve_untextured; + RegularizeResult reg = regularize_mesh(sub.geometry, sub.face_parent_id, + settings.refine_length, ropts); + result.collapse_count = reg.collapse_count; + if (!report("regularize", 1.0)) { + result.canceled = true; + return result; + } + if (reg.collapse_count > 0) { + // Excluded faces are carried on the soup itself, so the flag is re-derived rather than + // indexed across the collapse. + std::vector excl; + if (!reg.geometry.exclude_weight.empty()) { + excl.assign(reg.geometry.triangle_count(), 0); + for (size_t t = 0; t < excl.size(); ++t) + excl[t] = reg.geometry.exclude_weight[t * 3] > 0.99f ? 1 : 0; + } + sub = subdivide(reg.geometry, settings.refine_length * settings.regularize_second_pass_mul, + excl, false, settings.safety_cap, + [&](double f, size_t, double) { return report("re-subdivide", f); }); + result.safety_cap_hit = result.safety_cap_hit || sub.safety_cap_hit; + // The second pass renumbers faces, so the parent map has to be composed through it. + std::vector composed(sub.face_parent_id.size()); + for (size_t i = 0; i < composed.size(); ++i) { + const int mid = sub.face_parent_id[i]; + composed[i] = (mid >= 0 && size_t(mid) < reg.face_parent_id.size()) + ? reg.face_parent_id[size_t(mid)] : -1; + } + sub.face_parent_id = std::move(composed); + } else { + sub.geometry = std::move(reg.geometry); + sub.face_parent_id = std::move(reg.face_parent_id); + } + } + + // 3. Displace. + TriSoup displaced = apply_displacement(sub.geometry, sample, settings.displace, bounds, + [&](double f) { return report("displace", f); }); + if (!report("displace", 1.0)) { + result.canceled = true; + return result; + } + + // 4. Decimate - export only. A bake needs the face-parent map, which a collapse destroys. + std::vector parent = std::move(sub.face_parent_id); + if (mode == PipelineMode::Export) { + const bool needs_decimation = displaced.triangle_count() > settings.max_triangles; + if (needs_decimation || settings.harvest_flat) { + std::vector locked; + if (settings.preserve_untextured && !displaced.exclude_weight.empty()) { + locked.assign(displaced.triangle_count(), 0); + for (size_t t = 0; t < locked.size(); ++t) + locked[t] = displaced.exclude_weight[t * 3] > 0.99f ? 1 : 0; + } + DecimateResult dec = decimate(displaced, settings.max_triangles, settings.harvest_flat, + settings.harvest_tol, locked, + [&](double f) { return report("decimate", f); }); + result.locked_over_budget = dec.locked_over_budget; + displaced = std::move(dec.geometry); + parent.clear(); // no longer meaningful + } + if (!report("decimate", 1.0)) { + result.canceled = true; + return result; + } + } + + // 5. Flatten the bed-contact surface. + if (settings.displace.bottom_angle_limit > 0.f) + clamp_below_bottom(displaced, bounds.min.z()); + if (settings.bottom_snap_tol > 0.0) + snap_bottom_to_flat(displaced, bounds.min.z(), settings.bottom_snap_tol); + + // 6. Close the T-junctions decimation left behind. Only meaningful when it ran. + if (mode == PipelineMode::Export && parent.empty()) + displaced = resolve_t_junctions(displaced); + + result.geometry = std::move(displaced); + result.face_parent_id = std::move(parent); + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp new file mode 100644 index 0000000000..a9a7db1957 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -0,0 +1,98 @@ +#pragma once + +// The bake pipeline: +// +// subdivide -> [regularize -> re-subdivide] -> displace -> [decimate] +// -> bottom clamp -> bottom snap -> [resolve T-junctions] +// +// Regularization sits between two subdivisions on purpose: it dissolves the slivers refinement +// inherited, which lengthens some edges past the target, and the second pass brings those back. +// Before any subdivision it would have nothing to work on, since the slivers come from refining a +// needle; after a single pass it would leave the mesh coarser than asked for. +// +// Decimation and repair are export-only - decimation drops the output-to-input face mapping a bake +// needs to carry per-face data forward. + +#include +#include +#include + +#include "TextureBakeDecimate.hpp" +#include "TextureBakeDisplace.hpp" +#include "TextureBakeIndex.hpp" +#include "TextureBakeRegularize.hpp" +#include "TextureBakeRepair.hpp" +#include "TextureBakeSubdivide.hpp" + +namespace Slic3r { +namespace TextureBake { + +enum class PipelineMode +{ + // Keeps the face-parent mapping; skips decimation and repair. + Bake, + // The full sequence, including decimation and repair. + Export, +}; + +struct PipelineSettings +{ + // Target edge length for the refinement, in mm. + double refine_length = 1.0; + + // Sliver removal between the two subdivision passes. + bool regularize = true; + RegularizeOptions regularize_opts; + // Slightly above the first pass, so it recovers the edges regularization lengthened instead of + // re-refining what it just merged. + double regularize_second_pass_mul = 1.1; + + DisplaceSettings displace; + + // Export mode only. + size_t max_triangles = 750'000; + bool harvest_flat = true; + double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL; + // Lock the untextured region against both regularization and decimation. + bool preserve_untextured = true; + + // Snap vertices within this of the bottom plane onto it. 0 disables. + double bottom_snap_tol = 0.1; + + int safety_cap = SUBDIVIDE_SAFETY_CAP; +}; + +// Stage name and a fraction within it. Returning false cancels the run. +using PipelineProgressFn = std::function; + +struct PipelineResult +{ + TriSoup geometry; + // Output face -> input face. Empty in Export mode, where decimation invalidates it. + std::vector face_parent_id; + bool safety_cap_hit = false; + bool locked_over_budget = false; + size_t collapse_count = 0; + bool canceled = false; +}; + +PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, + const PipelineSettings &settings, const DisplaceBounds &bounds, + PipelineMode mode, const std::vector &face_excluded = {}, + const PipelineProgressFn &on_progress = {}); + +// Snap anything that ended below the model's original bottom back up to it. +void clamp_below_bottom(TriSoup &geometry, float bottom_z); + +// Flatten the bed-contact surface by snapping positions within `tol` of the bottom plane onto it. +// +// Gated, not unconditional: an unconditional band snap also flattens the undersides of texture bumps +// near the base, folding them coplanar into the bottom face. Folded faces overlap the plate, so edges +// there pick up four incident faces - non-manifold edges and phantom shells on re-import. All copies +// of a position move together, and the move is rejected if any incident triangle would go degenerate +// or rotate more than about 75 degrees. A real bed-contact sliver rotates by a fraction of a degree +// and still snaps. Returns how many triangles moved. +size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol = 0.1); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRegularize.cpp b/src/libslic3r/TextureBake/TextureBakeRegularize.cpp new file mode 100644 index 0000000000..4539e38723 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRegularize.cpp @@ -0,0 +1,380 @@ +#include "TextureBakeRegularize.hpp" + +#include +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +// Vertex-to-triangle lists as intrusive doubly linked lists of corner slots over flat arrays. Slot s +// is corner (triangle * 3 + k), owned by corners[s]. Deleted and moved corners are unlinked, so a +// collapse costs no allocation. +struct SlotLists +{ + std::vector head, next, prev; + + void init(size_t vertex_count, size_t slot_count) + { + head.assign(vertex_count, -1); + next.assign(slot_count, -1); + prev.assign(slot_count, -1); + } + void link(int s, const std::vector &corners) + { + const int v = corners[size_t(s)]; + const int h = head[size_t(v)]; + prev[size_t(s)] = -1; + next[size_t(s)] = h; + if (h != -1) + prev[size_t(h)] = s; + head[size_t(v)] = s; + } + void unlink(int s, const std::vector &corners) + { + const int p = prev[size_t(s)], n = next[size_t(s)]; + if (p != -1) next[size_t(p)] = n; + else head[size_t(corners[size_t(s)])] = n; + if (n != -1) prev[size_t(n)] = p; + } +}; + +} // namespace + +RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector &face_parent_id, + double max_edge_length, const RegularizeOptions &opts) +{ + RegularizeResult result; + const size_t tri_count = geometry.triangle_count(); + if (tri_count == 0 || max_edge_length <= 0.0) { + result.geometry = geometry; + result.face_parent_id = face_parent_id; + return result; + } + + const double base_max_len_sq = (max_edge_length * opts.slack) * (max_edge_length * opts.slack); + const double aggr_max_len_sq = + (max_edge_length * opts.aggressive_slack) * (max_edge_length * opts.aggressive_slack); + const double extreme_aspect2 = opts.extreme_sliver_aspect * opts.extreme_sliver_aspect; + const double aspect_thr2 = opts.aspect_threshold * opts.aspect_threshold; + + // Double precision: a collapse writes a midpoint back and later collapses read it, so rounding + // would accumulate. + QuantizedPointMap pos_map(WELD_GRID_GEOMETRY, std::min(tri_count * 3, size_t(1) << 22)); + std::vector vert; + std::vector corners(tri_count * 3); + vert.reserve(tri_count); + for (size_t i = 0; i < tri_count * 3; ++i) { + const Vec3f &p = geometry.pos[i]; + const int id = pos_map.get_or_set(p, int(vert.size())); + if (pos_map.inserted()) + vert.push_back(p.cast()); + corners[i] = id; + } + const size_t vert_count = vert.size(); + + std::vector tri_nrm(tri_count, Vec3d::Zero()); + std::vector tri_deleted(tri_count, 0); + result.face_parent_id = face_parent_id; + if (result.face_parent_id.size() != tri_count) + result.face_parent_id.assign(tri_count, 0); + + const auto sq_dist = [&](int a, int b) { return (vert[size_t(a)] - vert[size_t(b)]).squaredNorm(); }; + + const auto recompute_face_normal = [&](size_t t) { + const Vec3d &a = vert[size_t(corners[t * 3])]; + const Vec3d n = (vert[size_t(corners[t * 3 + 1])] - a).cross(vert[size_t(corners[t * 3 + 2])] - a); + const double len = n.norm(); + tri_nrm[t] = (len > 0.0) ? Vec3d(n / len) : Vec3d::Zero(); + }; + for (size_t t = 0; t < tri_count; ++t) + recompute_face_normal(t); + + // Never updated - the normal gate measures against these, so drift cannot compound across rounds. + const std::vector orig_nrm = tri_nrm; + + // Squared thinness, the longest edge over the shortest altitude: + // thinness = lmax / hmin = lmax^2 / (2 * area), so thinness^2 = lmax^4 / |AB x AC|^2 + // + // Not lmax/lmin, which misses what matters here: three near-collinear points can have all edges + // similar, so an edge ratio reports about 2 and the gate skips a triangle with near-zero area + // whose corners sample three unrelated texels. An equilateral scores about 1.15. + const auto tri_aspect_sq = [&](size_t t) -> double { + const Vec3d &a = vert[size_t(corners[t * 3])]; + const Vec3d ab = vert[size_t(corners[t * 3 + 1])] - a; + const Vec3d ac = vert[size_t(corners[t * 3 + 2])] - a; + const Vec3d bc = vert[size_t(corners[t * 3 + 2])] - vert[size_t(corners[t * 3 + 1])]; + const double lmax2 = std::max({ ab.squaredNorm(), ac.squaredNorm(), bc.squaredNorm() }); + const double cross2 = ab.cross(ac).squaredNorm(); + return cross2 > 0.0 ? lmax2 * lmax2 / cross2 : std::numeric_limits::infinity(); + }; + + SlotLists slots; + slots.init(vert_count, tri_count * 3); + for (size_t s = 0; s < tri_count * 3; ++s) + slots.link(int(s), corners); + + // O(1) membership without clearing a set per collapse. + std::vector vert_stamp(vert_count, 0), tri_stamp(tri_count, 0); + uint32_t stamp_gen = 0; + + // Both endpoints of a hard edge are barred from being collapse endpoints, preserving such + // corners exactly while leaving flat-face interiors free. + // + // Skipped when either triangle is an extreme sliver: a sliver's normal is dominated by where its + // far apex sits, so noise pivots it tens of degrees with no feature behind it, and freezing on + // that would lock the very chains this pass exists to dissolve. Genuine features are bordered by + // well-shaped triangles and are unaffected. + std::vector frozen_vert(vert_count, 0); + { + std::vector tri_thin2(tri_count); + for (size_t t = 0; t < tri_count; ++t) + tri_thin2[t] = tri_aspect_sq(t); + QuantizedPointMap edge_seen(1.0, std::min(tri_count * 3, size_t(1) << 22)); + for (size_t t = 0; t < tri_count; ++t) + for (int e = 0; e < 3; ++e) { + const int u = corners[t * 3 + size_t(e)]; + const int v = corners[t * 3 + size_t((e + 1) % 3)]; + const int lo = std::min(u, v), hi = std::max(u, v); + const int other = edge_seen.get_or_set_key(lo, hi, 0, int(t)); + if (edge_seen.inserted()) + continue; + if (tri_thin2[t] > extreme_aspect2 || tri_thin2[size_t(other)] > extreme_aspect2) + continue; + if (tri_nrm[t].dot(tri_nrm[size_t(other)]) < opts.sharp_edge_cos) { + frozen_vert[size_t(u)] = 1; + frozen_vert[size_t(v)] = 1; + } + } + } + + // Exclusion freeze. The weight is constant across a face's corners, so the first one answers. + if (opts.preserve_excluded && !geometry.exclude_weight.empty()) + for (size_t t = 0; t < tri_count; ++t) + if (geometry.exclude_weight[t * 3] > 0.99f) + for (int k = 0; k < 3; ++k) + frozen_vert[size_t(corners[t * 3 + size_t(k)])] = 1; + + std::vector wing_scratch, affected_scratch; + + const auto third_vertex = [&](size_t t, int u, int v) { + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + if (a != u && a != v) return a; + if (b != u && b != v) return b; + return c; + }; + const auto triangles_sharing_edge = [&](int u, int v) -> std::vector & { + wing_scratch.clear(); + for (int s = slots.head[size_t(u)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t]) + continue; + if (corners[t * 3] == v || corners[t * 3 + 1] == v || corners[t * 3 + 2] == v) + wing_scratch.push_back(int(t)); + } + return wing_scratch; + }; + + RegularizeRejectStats &stats = result.reject_stats; + + const auto try_collapse = [&](int u, int v) -> bool { + if (u == v) + return false; + if (frozen_vert[size_t(u)] || frozen_vert[size_t(v)]) { ++stats.frozen; return false; } + + // Two wings means a manifold interior edge. + std::vector &wings = triangles_sharing_edge(u, v); + if (wings.size() != 2) { ++stats.wing_count; return false; } + const size_t w0 = size_t(wings[0]), w1 = size_t(wings[1]); + const int apex1 = third_vertex(w0, u, v), apex2 = third_vertex(w1, u, v); + if (apex1 == apex2) { ++stats.folded_apex; return false; } + + // The edge cap loosens if *either* wing is extreme, since the re-subdivision recovers an + // over-long edge. The normal cap needs *both*, which is what protects fillets. + const double w1a = tri_aspect_sq(w0), w2a = tri_aspect_sq(w1); + const bool either_extreme = w1a > extreme_aspect2 || w2a > extreme_aspect2; + const bool both_extreme = w1a > extreme_aspect2 && w2a > extreme_aspect2; + const double eff_max_len_sq = either_extreme ? aggr_max_len_sq : base_max_len_sq; + const double eff_normal_cos = + both_extreme ? opts.aggressive_normal_delta_cos : opts.max_normal_delta_cos; + + // A vertex sharing a triangle with both endpoints, other than the wing apexes, would go + // non-manifold. Stamp one side's neighbours, scan the other against them. + ++stamp_gen; + for (int s = slots.head[size_t(v)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t]) + continue; + for (int k = 0; k < 3; ++k) + if (const int x = corners[t * 3 + size_t(k)]; x != v) + vert_stamp[size_t(x)] = stamp_gen; + } + for (int s = slots.head[size_t(u)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t]) + continue; + for (int k = 0; k < 3; ++k) { + const int x = corners[t * 3 + size_t(k)]; + if (x != u && x != v && x != apex1 && x != apex2 && vert_stamp[size_t(x)] == stamp_gen) { + ++stats.link_condition; + return false; + } + } + } + + const Vec3d m = (vert[size_t(u)] + vert[size_t(v)]) * 0.5; + + // Everything using either endpoint; the wings are being deleted. + ++stamp_gen; + affected_scratch.clear(); + for (const int endpoint : { u, v }) + for (int s = slots.head[size_t(endpoint)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (tri_deleted[t] || t == w0 || t == w1) + continue; + if (tri_stamp[t] != stamp_gen) { + tri_stamp[t] = stamp_gen; + affected_scratch.push_back(int(t)); + } + } + + // Validate every affected triangle before touching anything. + for (const int ti : affected_scratch) { + const size_t t = size_t(ti); + Vec3d p[3]; + for (int k = 0; k < 3; ++k) { + const int x = corners[t * 3 + size_t(k)]; + p[k] = (x == u || x == v) ? m : vert[size_t(x)]; + } + const double ab2 = (p[1] - p[0]).squaredNorm(); + const double bc2 = (p[2] - p[1]).squaredNorm(); + const double ca2 = (p[0] - p[2]).squaredNorm(); + if (ab2 > eff_max_len_sq || bc2 > eff_max_len_sq || ca2 > eff_max_len_sq) { + ++stats.edge_cap; + return false; + } + const Vec3d n = (p[1] - p[0]).cross(p[2] - p[0]); + const double nlen = n.norm(); + if (nlen <= 0.0) { ++stats.degenerate; return false; } + if ((n / nlen).dot(orig_nrm[t]) < eff_normal_cos) { ++stats.normal_change; return false; } + } + + // Apply: move u to the merged position and redirect every reference to v. + vert[size_t(u)] = m; + for (const size_t w : { w0, w1 }) { + tri_deleted[w] = 1; + for (int k = 0; k < 3; ++k) + slots.unlink(int(w * 3) + k, corners); + } + // A non-wing triangle contains v exactly once, so moving its slots suffices. + for (int s = slots.head[size_t(v)]; s != -1;) { + const int ns = slots.next[size_t(s)]; + slots.unlink(s, corners); + corners[size_t(s)] = u; + slots.link(s, corners); + recompute_face_normal(size_t(s) / 3); + s = ns; + } + for (int s = slots.head[size_t(u)]; s != -1; s = slots.next[size_t(s)]) { + const size_t t = size_t(s) / 3; + if (!tri_deleted[t]) + recompute_face_normal(t); + } + return true; + }; + + for (int round = 0; round < opts.maxrounds; ++round) { + // Rebuilt each round so earlier collapses inform the priorities. + std::vector cand; + std::vector cand_aspect; + for (size_t t = 0; t < tri_count; ++t) { + if (tri_deleted[t]) + continue; + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + if (std::min({ sq_dist(a, b), sq_dist(b, c), sq_dist(c, a) }) <= 0.0) + continue; + const double aspect2 = tri_aspect_sq(t); + if (aspect2 < aspect_thr2) + continue; + cand.push_back(int(t)); + cand_aspect.push_back(aspect2); + } + // Worst first; ties keep ascending order so the pass is deterministic. + std::vector order(cand.size()); + std::iota(order.begin(), order.end(), 0); + std::stable_sort(order.begin(), order.end(), + [&](int x, int y) { return cand_aspect[size_t(x)] > cand_aspect[size_t(y)]; }); + + size_t round_collapses = 0; + for (const int oi : order) { + const size_t t = size_t(cand[size_t(oi)]); + if (tri_deleted[t]) + continue; + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + // All three edges, shortest first: a sliver straddling a seam has its shortest edge + // crossing it, which the normal gate refuses, while a long edge along one surface + // collapses safely. Trying only the shortest would leave those stuck. + struct Cand { double len2; int u, v; }; + Cand e[3] = { { sq_dist(a, b), a, b }, { sq_dist(b, c), b, c }, { sq_dist(c, a), c, a } }; + std::stable_sort(std::begin(e), std::end(e), + [](const Cand &x, const Cand &y) { return x.len2 < y.len2; }); + if (try_collapse(e[0].u, e[0].v) || try_collapse(e[1].u, e[1].v) || + try_collapse(e[2].u, e[2].v)) + ++round_collapses; + } + result.collapse_count += round_collapses; + if (round_collapses == 0) + break; + } + + // Drop deleted triangles and rebuild the soup. + const bool have_weights = !geometry.exclude_weight.empty(); + std::vector out_parent; + TriSoup &out = result.geometry; + for (size_t t = 0; t < tri_count; ++t) { + if (tri_deleted[t]) + continue; + for (int k = 0; k < 3; ++k) + out.pos.push_back(vert[size_t(corners[t * 3 + size_t(k)])].cast()); + if (have_weights) { + // Constant across a face's corners. + const float w = geometry.exclude_weight[t * 3]; + out.exclude_weight.insert(out.exclude_weight.end(), { w, w, w }); + } + out_parent.push_back(result.face_parent_id[t]); + } + result.face_parent_id = std::move(out_parent); + + // Rebuilt from the compacted geometry - the collapses moved vertices. + out.nrm.assign(out.pos.size(), Vec3f::Zero()); + { + std::vector accum(out.pos.size(), Vec3d::Zero()); + QuantizedPointMap weld(WELD_GRID_GEOMETRY, out.pos.size()); + std::vector vid(out.pos.size()); + int next = 0; + for (size_t i = 0; i < out.pos.size(); ++i) { + vid[i] = weld.get_or_set(out.pos[i], next); + if (weld.inserted()) + ++next; + } + std::vector vn(size_t(next), Vec3d::Zero()); + for (size_t t = 0; t * 3 < out.pos.size(); ++t) { + const Vec3d a = out.pos[t * 3].cast(); + const Vec3d n = (out.pos[t * 3 + 1].cast() - a).cross(out.pos[t * 3 + 2].cast() - a); + for (int k = 0; k < 3; ++k) + vn[size_t(vid[t * 3 + size_t(k)])] += n; + } + for (size_t i = 0; i < out.pos.size(); ++i) { + const Vec3d &n = vn[size_t(vid[i])]; + const double l = n.norm(); + out.nrm[i] = (l > 0.0) ? Vec3d(n / l).cast() : Vec3f(0.f, 0.f, 1.f); + } + } + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRegularize.hpp b/src/libslic3r/TextureBake/TextureBakeRegularize.hpp new file mode 100644 index 0000000000..350666783a --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRegularize.hpp @@ -0,0 +1,73 @@ +#pragma once + +// Sliver removal by short-edge collapse. +// +// Subdivision turns tessellation needles into chains of slivers that are within the edge-length +// budget but still poor triangles. A sliver's three vertices land on three unrelated texels, so the +// relief picks up noise that is an artifact of the tessellation rather than of the image. +// +// A candidate's edge is collapsed to its midpoint only if it passes three gates: no affected +// triangle may exceed the target edge times a slack factor; every affected triangle must keep its +// face normal within a bound of its *original* direction (which is what stops curved surfaces being +// flattened); and the link condition must hold, or the result would be non-manifold. Boundary and +// non-manifold edges are skipped outright. Rounds repeat until one achieves nothing. + +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +struct RegularizeOptions +{ + // Candidate threshold. Set to catch real slivers - chains measure in the hundreds - without + // sweeping up moderate fillet triangles, which sit between 2 and 5. + double aspect_threshold = 5.0; + + // The base tier is loose on purpose: non-sliver boundary collapses must keep succeeding, since + // those give a chain the room to dissolve. A tight base leaves chains worse than before. The + // aggressive tier applies when at least one wing is an extreme sliver. + double slack = 3.0; + double aggressive_slack = 8.0; + + // Thinness above which a wing counts as extreme: longest edge over shortest altitude. + double extreme_sliver_aspect = 8.0; + + // Measured against each triangle's normal from before any collapse ran, so rounds of small + // allowed drift cannot compound into corner damage. Asymmetric two-tier: the loose bound needs + // *both* wings extreme, which matches a needle chain on a curved face but not a sliver beside a + // fillet, so fillets keep the tight bound. + double max_normal_delta_cos = 0.965925826289; // cos(15 degrees) + double aggressive_normal_delta_cos = 0.906307787037; // cos(25 degrees) + + // Vertices on edges sharper than this are frozen, so hard features keep every original vertex. + double sharp_edge_cos = 0.866025403784; // cos(30 degrees) + + int maxrounds = 8; + + // Freeze excluded faces entirely, so untextured geometry is never modified. + bool preserve_excluded = false; +}; + +// Which gate blocked a collapse - the only practical way to tell why a region failed to merge. +struct RegularizeRejectStats +{ + size_t frozen = 0, wing_count = 0, link_condition = 0, edge_cap = 0, normal_change = 0, + degenerate = 0, folded_apex = 0; +}; + +struct RegularizeResult +{ + TriSoup geometry; + std::vector face_parent_id; + size_t collapse_count = 0; + RegularizeRejectStats reject_stats; +}; + +RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector &face_parent_id, + double max_edge_length, const RegularizeOptions &opts = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRepair.cpp b/src/libslic3r/TextureBake/TextureBakeRepair.cpp new file mode 100644 index 0000000000..438e0ea25d --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRepair.cpp @@ -0,0 +1,215 @@ +#include "TextureBakeRepair.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +inline uint64_t edge_key(int a, int b) +{ + const uint32_t lo = uint32_t(std::min(a, b)), hi = uint32_t(std::max(a, b)); + return (uint64_t(lo) << 32) | uint64_t(hi); +} + +// On the export grid a squared cross product is either 0 (collinear) or at least about 1e-16, the +// smallest real triangle being one grid unit per leg, so this separates the two cleanly. +constexpr double DEGENERATE_AREA_SQ = 1e-18; + +} // namespace + +EdgeDefects count_edge_defects(const TriSoup &geometry, double quant) +{ + EdgeDefects out; + const size_t n = geometry.pos.size(); + out.triangles = n / 3; + QuantizedPointMap vmap(quant, std::min(n, size_t(1) << 22)); + std::vector id(n); + int next = 0; + for (size_t i = 0; i < n; ++i) { + id[i] = vmap.get_or_set(geometry.pos[i], next); + if (vmap.inserted()) + ++next; + } + std::unordered_map counts; + for (size_t t = 0; t + 2 < n; t += 3) { + const int a = id[t], b = id[t + 1], c = id[t + 2]; + if (a == b || b == c || a == c) + continue; + const int tri[3] = { a, b, c }; + for (int e = 0; e < 3; ++e) + ++counts[edge_key(tri[e], tri[(e + 1) % 3])]; + } + for (const auto &[key, c] : counts) { + (void) key; + if (c == 1) ++out.open; + else if (c > 2) ++out.non_manifold; + } + return out; +} + +size_t count_area_slivers(const TriSoup &geometry) +{ + size_t n = 0; + for (size_t t = 0; t + 2 < geometry.pos.size(); t += 3) { + const Vec3d u = (geometry.pos[t + 1] - geometry.pos[t]).cast(); + const Vec3d v = (geometry.pos[t + 2] - geometry.pos[t]).cast(); + // The threshold a slicer applies: area below 1e-12 mm^2. + if (u.cross(v).squaredNorm() < 1e-24) + ++n; + } + return n; +} + +TriSoup resolve_t_junctions(const TriSoup &geometry, const RepairOptions &opts) +{ + const size_t n_tri = geometry.triangle_count(); + const double on_tol2 = opts.on_seg_tol * opts.on_seg_tol; + const double Q = opts.weld_quant; + + // Snapped, not just welded: keeping unrounded coordinates lets a thin triangle pass the + // degeneracy test here and then collapse to collinear once the file is written, punching the very + // hole this pass prevents. Snapping makes the check see what will be written. + QuantizedPointMap vmap(Q, std::min(n_tri * 3, size_t(1) << 22)); + std::vector vert; + std::vector vid(n_tri * 3); + for (size_t i = 0; i < n_tri * 3; ++i) { + const Vec3f &p = geometry.pos[i]; + const int id = vmap.get_or_set(p, int(vert.size())); + if (vmap.inserted()) + vert.emplace_back(double(grid_round(double(p.x()) * Q)) / Q, + double(grid_round(double(p.y()) * Q)) / Q, + double(grid_round(double(p.z()) * Q)) / Q); + vid[i] = id; + } + + // Dropped: faces whose corners welded together, and needles - distinct but collinear on this + // grid. A needle reads as watertight yet is deleted downstream, and dropping it leaves exactly + // the on-edge-vertex topology the pass below closes. + std::vector> faces; + faces.reserve(n_tri); + for (size_t t = 0; t < n_tri; ++t) { + const int a = vid[t * 3], b = vid[t * 3 + 1], c = vid[t * 3 + 2]; + if (a == b || b == c || a == c) + continue; + const Vec3d u = vert[size_t(b)] - vert[size_t(a)]; + const Vec3d w = vert[size_t(c)] - vert[size_t(a)]; + if (u.cross(w).squaredNorm() < DEGENERATE_AREA_SQ) + continue; + faces.push_back({ a, b, c }); + } + + for (int iter = 0; iter < opts.max_iters; ++iter) { + std::unordered_map e_count; + for (const auto &f : faces) + for (int e = 0; e < 3; ++e) + ++e_count[edge_key(f[size_t(e)], f[size_t((e + 1) % 3)])]; + + std::unordered_set bverts; + for (const auto &[key, c] : e_count) { + if (c != 1) + continue; + bverts.insert(int(uint32_t(key >> 32))); + bverts.insert(int(uint32_t(key & 0xFFFFFFFFu))); + } + if (bverts.empty()) + break; + const std::vector bv(bverts.begin(), bverts.end()); + + struct Split { int a, b; std::vector mids; }; + std::unordered_map splits; + for (size_t fi = 0; fi < faces.size(); ++fi) { + const auto &f = faces[fi]; + for (int e = 0; e < 3; ++e) { + const int a = f[size_t(e)], b = f[size_t((e + 1) % 3)]; + if (e_count[edge_key(a, b)] != 1) + continue; // only a boundary edge carries an unresolved T-junction + const Vec3d A = vert[size_t(a)]; + const Vec3d ev = vert[size_t(b)] - A; + const double elen2 = ev.squaredNorm(); + if (elen2 < 1e-20) + continue; + std::vector> found; + for (const int c : bv) { + if (c == a || c == b) + continue; + const Vec3d cv = vert[size_t(c)] - A; + const double tp = cv.dot(ev) / elen2; + if (tp <= 1e-4 || tp >= 1.0 - 1e-4) + continue; // strictly between the ends + if ((cv - ev * tp).squaredNorm() < on_tol2) + found.emplace_back(tp, c); + } + if (!found.empty()) { + std::sort(found.begin(), found.end(), + [](const auto &x, const auto &y) { return x.first < y.first; }); + Split sp{ a, b, {} }; + for (const auto &m : found) + sp.mids.push_back(m.second); + splits.emplace(fi, std::move(sp)); + break; // one site per face per pass; iteration handles cascades + } + } + } + if (splits.empty()) + break; + + std::vector> next; + next.reserve(faces.size() + splits.size() * 2); + for (size_t fi = 0; fi < faces.size(); ++fi) { + const auto it = splits.find(fi); + if (it == splits.end()) { + next.push_back(faces[fi]); + continue; + } + const auto &f = faces[fi]; + const auto &sp = it->second; + const int apex = (f[0] != sp.a && f[0] != sp.b) ? f[0] + : (f[1] != sp.a && f[1] != sp.b) ? f[1] + : f[2]; + // Walk the base the way the face already traverses it, so the winding survives. + bool dir_ab = false; + for (int e = 0; e < 3; ++e) + if (f[size_t(e)] == sp.a && f[size_t((e + 1) % 3)] == sp.b) { + dir_ab = true; + break; + } + std::vector seq; + if (dir_ab) { + seq.push_back(sp.a); + seq.insert(seq.end(), sp.mids.begin(), sp.mids.end()); + seq.push_back(sp.b); + } else { + seq.push_back(sp.b); + seq.insert(seq.end(), sp.mids.rbegin(), sp.mids.rend()); + seq.push_back(sp.a); + } + for (size_t s = 0; s + 1 < seq.size(); ++s) + next.push_back({ seq[s], seq[s + 1], apex }); + } + faces.swap(next); + } + + TriSoup out; + out.pos.reserve(faces.size() * 3); + out.nrm.reserve(faces.size() * 3); + for (const auto &f : faces) { + const Vec3f a = vert[size_t(f[0])].cast(); + const Vec3f b = vert[size_t(f[1])].cast(); + const Vec3f c = vert[size_t(f[2])].cast(); + Vec3f nrm = (b - a).cross(c - a); + const float len = nrm.norm(); + nrm = (len > 0.f) ? Vec3f(nrm / len) : Vec3f(0.f, 0.f, 1.f); + out.pos.insert(out.pos.end(), { a, b, c }); + out.nrm.insert(out.nrm.end(), { nrm, nrm, nrm }); + } + return out; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRepair.hpp b/src/libslic3r/TextureBake/TextureBakeRepair.hpp new file mode 100644 index 0000000000..35e9f50b06 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRepair.hpp @@ -0,0 +1,48 @@ +#pragma once + +// T-junction resolution and edge-defect accounting. +// +// Decimation can collapse a long edge whose interior still carries neighbouring triangles' vertices. +// Those then sit *on* an edge rather than at an end: watertight vertex-for-vertex, but the edge has +// one incident face on one side, which a slicer reads as an open boundary. This splits the offending +// face into a fan so every on-edge vertex becomes a real corner. + +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +struct EdgeDefects +{ + size_t open = 0, non_manifold = 0, triangles = 0; +}; + +// Welds at the export grid first: counting on the un-snapped mesh reports defects the file does not +// have and misses ones it does. +EdgeDefects count_edge_defects(const TriSoup &geometry, double quant = WELD_GRID_EXPORT); + +// Triangles a slicer would drop as degenerate. Each one, removed, punches a hole - so a non-zero +// count means watertight only on paper. +size_t count_area_slivers(const TriSoup &geometry); + +struct RepairOptions +{ + // Coordinates are snapped onto this grid, matching the precision files are written with. + double weld_quant = WELD_GRID_EXPORT; + + // How far off an edge a vertex may sit and still count as on it. Well above the harvest + // tolerance, since harvesting leaves a region flat only to within that, making a collapsed edge a + // chord the on-edge vertices deviate from by about as much. Still far below the weld grid. + double on_seg_tol = 0.02; + + // Splitting one face can expose another behind it, so the pass cascades. + int max_iters = 16; +}; + +TriSoup resolve_t_junctions(const TriSoup &geometry, const RepairOptions &opts = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp new file mode 100644 index 0000000000..7a0082e257 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp @@ -0,0 +1,414 @@ +#include "TextureBakeSubdivide.hpp" + +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +double edge_len_sq(const VertStore &v, int a, int b) +{ + return (v.pos[size_t(a)] - v.pos[size_t(b)]).squaredNorm(); +} + +// Both indexers accumulate raw, area-weighted cross products and normalise once at the end. +void normalize_store_normals(VertStore &verts) +{ + for (Vec3d &n : verts.nrm) { + const double len = n.norm(); + n = (len > 0.0) ? Vec3d(n / len) : Vec3d(0.0, 0.0, 1.0); + } +} + +// Keyed by the raw parent-vertex pair rather than by position: two sharp-edge copies of one point +// need their own midpoints, since their normals differ even though the position does not. +int get_midpoint(VertStore &verts, QuantizedPointMap &cache, int a, int b, + QuantizedPointMap *pos_canon_map) +{ + const int lo = std::min(a, b), hi = std::max(a, b); + if (const int cached = cache.get_key(lo, hi, 0); cached != -1) + return cached; + + const Vec3d m = (verts.pos[size_t(a)] + verts.pos[size_t(b)]) * 0.5; + Vec3d n = verts.nrm[size_t(a)] + verts.nrm[size_t(b)]; + const double nl = n.norm(); + n = (nl > 0.0) ? Vec3d(n / nl) : verts.nrm[size_t(a)]; + + const int idx = verts.push(m, n); + if (!verts.wgt.empty()) + verts.wgt.push_back((verts.wgt[size_t(a)] + verts.wgt[size_t(b)]) * 0.5); + if (!verts.canon.empty() && pos_canon_map != nullptr) + verts.canon.push_back(pos_canon_map->get_or_set(float(m.x()), float(m.y()), float(m.z()), idx)); + + cache.get_or_set_key(lo, hi, 0, idx); + return idx; +} + +struct PassResult +{ + std::vector indices; + std::vector face_excluded; + std::vector face_parent_id; + bool changed = false; + bool capped = false; +}; + +// Three steps, so that no T-junction can appear: +// 1. Mark every too-long edge globally, so both triangles on a shared edge decide alike. +// 1.5 Predict the exact resulting count from the marks (0->1, 1->2, 2->3, 3->4) and abort the +// *whole* pass if it exceeds the cap - a partial pass leaves split parents beside unsplit +// neighbours, the very crack step 1 prevents. +// 2. Rebuild, allocating once at the now-known size. +PassResult subdivide_pass(VertStore &verts, const std::vector &indices, double max_edge_length, + int safety_cap, const std::vector &face_excluded, + QuantizedPointMap *pos_canon_map, const std::vector &face_parent_id) +{ + PassResult out; + const double max_sq = max_edge_length * max_edge_length; + const size_t tri_count = indices.size() / 3; + const bool have_canon = !verts.canon.empty(); + + QuantizedPointMap mid_cache(1.0, 1 << 16); + QuantizedPointMap split_edges(1.0, 1 << 16); + + // With canonical ids the key is the canonical *position* id, so split copies either side of a + // sharp edge see one another's decision; without them the vertex index serves. + const auto key_of = [&](int v) -> int64_t { return have_canon ? verts.canon[size_t(v)] : v; }; + const auto mark_edge = [&](int a, int b) { + const int64_t u = key_of(a), v = key_of(b); + if (u < v) split_edges.get_or_set_key(u, v, 0, 1); + else split_edges.get_or_set_key(v, u, 0, 1); + }; + const auto is_marked = [&](int a, int b) { + const int64_t u = key_of(a), v = key_of(b); + return (u < v ? split_edges.get_key(u, v, 0) : split_edges.get_key(v, u, 0)) != -1; + }; + + // Step 1. An excluded triangle marks none of its own edges, so its interior never refines; its + // boundary edges are still marked by an included neighbour, and it follows that split. + for (size_t t = 0; t < tri_count; ++t) { + if (!face_excluded.empty() && face_excluded[t]) + continue; + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + if (edge_len_sq(verts, a, b) > max_sq) mark_edge(a, b); + if (edge_len_sq(verts, b, c) > max_sq) mark_edge(b, c); + if (edge_len_sq(verts, c, a) > max_sq) mark_edge(c, a); + } + if (split_edges.size() == 0) { + out.indices = indices; + out.face_excluded = face_excluded; + out.face_parent_id = face_parent_id; + return out; // changed stays false: nothing left to refine + } + + // Step 1.5. + size_t predicted = 0; + for (size_t t = 0; t < tri_count; ++t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const int n = int(is_marked(a, b)) + int(is_marked(b, c)) + int(is_marked(c, a)); + predicted += (n == 0) ? 1 : size_t(n + 1); + } + if (predicted > size_t(safety_cap)) { + out.indices = indices; + out.face_excluded = face_excluded; + out.face_parent_id = face_parent_id; + out.capped = true; + return out; // coarser than asked for, but watertight + } + + // Step 2. + out.indices.resize(predicted * 3); + if (!face_excluded.empty()) + out.face_excluded.resize(predicted); + if (!face_parent_id.empty()) + out.face_parent_id.resize(predicted); + size_t wi = 0, fi = 0; + const auto emit_face_data = [&](uint8_t excl, int pid, int times) { + for (int k = 0; k < times; ++k) { + if (!out.face_excluded.empty()) out.face_excluded[fi] = excl; + if (!out.face_parent_id.empty()) out.face_parent_id[fi] = pid; + ++fi; + } + }; + const auto emit = [&](int x, int y, int z) { + out.indices[wi++] = x; out.indices[wi++] = y; out.indices[wi++] = z; + }; + + for (size_t t = 0; t < tri_count; ++t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const uint8_t excl = face_excluded.empty() ? uint8_t(0) : face_excluded[t]; + const int pid = face_parent_id.empty() ? 0 : face_parent_id[t]; + const bool s_ab = is_marked(a, b), s_bc = is_marked(b, c), s_ca = is_marked(c, a); + const int n = int(s_ab) + int(s_bc) + int(s_ca); + + if (n == 0) { + emit(a, b, c); + emit_face_data(excl, pid, 1); + } else if (n == 3) { + // a + // / \ + // mCA-mAB + // / \ / \ + // c--mBC--b + const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, m_ab, m_ca); + emit(m_ab, b, m_bc); + emit(m_ca, m_bc, c); + emit(m_ab, m_bc, m_ca); + emit_face_data(excl, pid, 4); + } else if (n == 1) { + if (s_ab) { + const int m = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + emit(a, m, c); + emit(m, b, c); + } else if (s_bc) { + const int m = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + emit(a, b, m); + emit(a, m, c); + } else { + const int m = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, b, m); + emit(m, b, c); + } + emit_face_data(excl, pid, 2); + } else { + // A corner triangle on the untouched-edge vertex, then the remaining quadrilateral split + // along the midpoint-to-midpoint diagonal, which keeps the winding consistent. + // + // A sliver parent propagates: that inner diagonal inherits half the short edge and hands + // the sliver to two children per pass. No better diagonal exists - one avoiding the + // midpoints must pass through one of them, giving a zero-area triangle. Regularization + // removes such slivers before the mesh reaches here. + if (!s_ab) { // fan from c + const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, b, m_bc); + emit(a, m_bc, m_ca); + emit(c, m_ca, m_bc); + } else if (!s_bc) { // fan from a + const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map); + emit(a, m_ab, m_ca); + emit(m_ab, b, c); + emit(m_ab, c, m_ca); + } else { // fan from b + const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map); + const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map); + emit(b, m_bc, m_ab); + emit(a, m_ab, m_bc); + emit(a, m_bc, c); + } + emit_face_data(excl, pid, 3); + } + } + + out.changed = true; + return out; +} + +} // namespace + +IndexedMesh to_indexed_fast(const TriSoup &geometry) +{ + // Preview path: a plain position merge - no clustering, no sharp-edge splitting, no canonical ids. + IndexedMesh out; + const size_t n = geometry.pos.size(); + QuantizedPointMap vert_map(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + out.indices.resize(n); + const bool has_w = !geometry.exclude_weight.empty(); + + for (size_t i = 0; i < n; ++i) { + const Vec3f &p = geometry.pos[i]; + const Vec3f nf = geometry.nrm.empty() ? Vec3f(0.f, 0.f, 1.f) : geometry.nrm[i]; + const int idx = vert_map.get_or_set(p, int(out.verts.count())); + if (vert_map.inserted()) { + out.verts.push(p.cast(), nf.cast()); + if (has_w) + out.verts.wgt.push_back(double(geometry.exclude_weight[i])); + } else { + out.verts.nrm[size_t(idx)] += nf.cast(); + // Merge exclusion by maximum: any excluded face marks the shared vertex. + if (has_w && double(geometry.exclude_weight[i]) > out.verts.wgt[size_t(idx)]) + out.verts.wgt[size_t(idx)] = double(geometry.exclude_weight[i]); + } + out.indices[i] = idx; + } + normalize_store_normals(out.verts); + return out; +} + +IndexedMesh to_indexed(const TriSoup &geometry) +{ + // Export path. Two vertices at one position merge only when their face normals agree to within + // SUBDIVIDE_SHARP_ANGLE_DEG, which keeps a cylinder from faceting while stopping a cube's edge + // normal from leaking into the flat face interiors as subdivision carries it inward. + IndexedMesh out; + out.has_canon = true; + const size_t n = geometry.pos.size(); + const bool has_w = !geometry.exclude_weight.empty(); + const double sharp_cos = std::cos(SUBDIVIDE_SHARP_ANGLE_DEG * M_PI / 180.0); + + // Per-face normals: unit for the angle test, raw for the area-weighted accumulation. + std::vector face_unit(n), face_raw(n); + for (size_t t = 0; t + 2 < n; t += 3) { + const Vec3d a = geometry.pos[t].cast(); + const Vec3d b = geometry.pos[t + 1].cast(); + const Vec3d c = geometry.pos[t + 2].cast(); + const Vec3d r = (b - a).cross(c - a); + const double len = r.norm(); + const Vec3d u = (len > 0.0) ? Vec3d(r / len) : Vec3d(0.0, 0.0, 1.0); + for (int v = 0; v < 3; ++v) { + face_unit[t + size_t(v)] = u; + face_raw[t + size_t(v)] = r; + } + } + + out.indices.resize(n); + out.pos_canon_map = QuantizedPointMap(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + struct Cluster { int idx; Vec3d fn_unit; }; + std::unordered_map> clusters_by_canon; + + for (size_t i = 0; i < n; ++i) { + const Vec3f &p = geometry.pos[i]; + // The first vertex at a position becomes its canonical id; later split copies share it. + const int canon_id = out.pos_canon_map.get_or_set(p, int(out.verts.count())); + const bool fresh_position = out.pos_canon_map.inserted(); + + const auto add_vertex = [&](int canon) { + const int idx = out.verts.push(p.cast(), face_raw[i]); + if (has_w) + out.verts.wgt.push_back(double(geometry.exclude_weight[i])); + out.verts.canon.push_back(canon); + return idx; + }; + + if (fresh_position) { + const int idx = add_vertex(canon_id); + clusters_by_canon[canon_id].push_back({ idx, face_unit[i] }); + out.indices[i] = idx; + continue; + } + + std::vector &clusters = clusters_by_canon[canon_id]; + bool matched = false; + for (Cluster &cl : clusters) { + if (cl.fn_unit.dot(face_unit[i]) < sharp_cos) + continue; + out.verts.nrm[size_t(cl.idx)] += face_raw[i]; + if (has_w && double(geometry.exclude_weight[i]) > out.verts.wgt[size_t(cl.idx)]) + out.verts.wgt[size_t(cl.idx)] = double(geometry.exclude_weight[i]); + // Track the running average, so gradual curvature stays in one cluster instead of + // fragmenting when a distant face exceeds the threshold against the seed's fixed normal. + cl.fn_unit += face_unit[i]; + if (const double rl = cl.fn_unit.norm(); rl > 0.0) + cl.fn_unit /= rl; + out.indices[i] = cl.idx; + matched = true; + break; + } + if (!matched) { + // A sharp-edge split: a new vertex at the same position, sharing its canonical id. + const int idx = add_vertex(canon_id); + clusters.push_back({ idx, face_unit[i] }); + out.indices[i] = idx; + } + } + + normalize_store_normals(out.verts); + return out; +} + +TriSoup to_non_indexed(const VertStore &verts, const std::vector &indices, + const std::vector &face_excluded) +{ + TriSoup out; + const size_t tri_count = indices.size() / 3; + out.pos.resize(tri_count * 3); + out.nrm.resize(tri_count * 3); + const bool want_weights = !face_excluded.empty() || !verts.wgt.empty(); + if (want_weights) + out.exclude_weight.resize(tri_count * 3); + + for (size_t t = 0; t < tri_count; ++t) { + // The per-face flag, not the interpolated weight: merging by maximum can push an *included* + // face's corners to 1 when it borders two excluded neighbours, wrongly excluding it. + const bool have_face_flag = !face_excluded.empty(); + const float face_w = have_face_flag ? (face_excluded[t] ? 1.f : 0.f) : 0.f; + for (int v = 0; v < 3; ++v) { + const size_t vidx = size_t(indices[t * 3 + size_t(v)]); + out.pos[t * 3 + size_t(v)] = verts.pos[vidx].cast(); + out.nrm[t * 3 + size_t(v)] = verts.nrm[vidx].cast(); + if (want_weights) + out.exclude_weight[t * 3 + size_t(v)] = + have_face_flag ? face_w : float(verts.wgt[vidx]); + } + } + return out; +} + +SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, + const std::vector &face_excluded, bool fast, int safety_cap, + const SubdivideProgressFn &on_progress) +{ + SubdivideResult result; + if (geometry.empty() || max_edge_length <= 0.0) { + result.geometry = geometry; + return result; + } + + IndexedMesh indexed = fast ? to_indexed_fast(geometry) : to_indexed(geometry); + QuantizedPointMap *canon_map = indexed.has_canon ? &indexed.pos_canon_map : nullptr; + + std::vector current_indices = indexed.indices; + std::vector current_excluded = face_excluded; + const size_t initial_tris = indexed.indices.size() / 3; + std::vector current_parent(initial_tris); + for (size_t i = 0; i < initial_tris; ++i) + current_parent[i] = int(i); + + for (int iter = 0; iter < SUBDIVIDE_MAX_ITERATIONS; ++iter) { + if (current_indices.size() / 3 >= size_t(safety_cap)) { + result.safety_cap_hit = true; + break; + } + + PassResult pass = subdivide_pass(indexed.verts, current_indices, max_edge_length, safety_cap, + current_excluded, canon_map, current_parent); + current_indices = std::move(pass.indices); + if (!pass.face_excluded.empty()) + current_excluded = std::move(pass.face_excluded); + if (!pass.face_parent_id.empty()) + current_parent = std::move(pass.face_parent_id); + if (pass.capped || current_indices.size() / 3 >= size_t(safety_cap)) + result.safety_cap_hit = true; + + if (on_progress) { + // Reported after the pass, so the value falls each iteration instead of lagging a step. + double max_edge_sq = 0.0; + for (size_t t = 0; t + 2 < current_indices.size(); t += 3) { + const int a = current_indices[t], b = current_indices[t + 1], c = current_indices[t + 2]; + max_edge_sq = std::max({ max_edge_sq, edge_len_sq(indexed.verts, a, b), + edge_len_sq(indexed.verts, b, c), + edge_len_sq(indexed.verts, c, a) }); + } + if (!on_progress(std::min(0.95, double(iter + 1) / SUBDIVIDE_MAX_ITERATIONS), + current_indices.size() / 3, std::sqrt(max_edge_sq))) + break; // whole passes only, so what we have is still crack-free + } + + if (!pass.changed || result.safety_cap_hit) + break; + } + + result.geometry = to_non_indexed(indexed.verts, current_indices, current_excluded); + result.face_parent_id = std::move(current_parent); + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp new file mode 100644 index 0000000000..30ad56bf00 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp @@ -0,0 +1,83 @@ +#pragma once + +// Adaptive subdivision to a target edge length, by global marked-edge (red-green) refinement rather +// than longest-edge bisection. Marking is global, so two triangles sharing an edge always agree and +// the result is crack-free by construction. A triangle is rebuilt from its marked-edge count: 0 +// keeps, 1 bisects, 2 fans into three, 3 does the regular 1->4 split. +// +// The 1->4 case is what keeps the tessellation regular - its children are similar to the parent. An +// irregular one shows up after displacement as adjacent triangles tilting alternately, i.e. noise. + +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +// Memory guard for the stages downstream. At roughly 145 bytes per triangle this is about 2.9 GB. +static constexpr int SUBDIVIDE_SAFETY_CAP = 16'000'000; + +// Vertices at one position stay separate when their faces disagree by more than this: a cube keeps +// hard edges, a cylinder keeps averaged ones. +static constexpr double SUBDIVIDE_SHARP_ANGLE_DEG = 30.0; + +// A depth bound, not a work bound: the loop stops as soon as a pass changes nothing. +static constexpr int SUBDIVIDE_MAX_ITERATIONS = 12; + +// Built by the indexers, appended to by the passes. Double precision so repeated midpointing does +// not drift. +struct VertStore +{ + std::vector pos; + std::vector nrm; + std::vector wgt; // exclusion weights; empty when the caller supplied none + std::vector canon; // canonical position ids; empty in fast mode + + size_t count() const { return pos.size(); } + int push(const Vec3d &p, const Vec3d &n) + { + const int idx = int(pos.size()); + pos.push_back(p); + nrm.push_back(n); + return idx; + } +}; + +struct IndexedMesh +{ + VertStore verts; + std::vector indices; // 3 per triangle + QuantizedPointMap pos_canon_map{ WELD_GRID_GEOMETRY, 256 }; + bool has_canon = false; +}; + +// Fraction, triangle count, longest remaining edge. Returning false cancels; what comes back is +// still watertight, because passes apply whole or not at all. +using SubdivideProgressFn = std::function; + +struct SubdivideResult +{ + TriSoup geometry; + // Output triangle -> input triangle it descends from, so per-face data survives with no remap. + std::vector face_parent_id; + bool safety_cap_hit = false; +}; + +// `face_excluded`: one entry per input triangle; non-zero means its interior is never refined. Its +// edges still split when an included neighbour marks them, so no T-junction appears at the boundary. +// `fast` selects the cheap position-only indexer for previews. +SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, + const std::vector &face_excluded = {}, bool fast = false, + int safety_cap = SUBDIVIDE_SAFETY_CAP, + const SubdivideProgressFn &on_progress = {}); + +// Displacement needs the same welding and sharp-edge clustering. +IndexedMesh to_indexed(const TriSoup &geometry); +IndexedMesh to_indexed_fast(const TriSoup &geometry); +TriSoup to_non_indexed(const VertStore &verts, const std::vector &indices, + const std::vector &face_excluded); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 74485c7919..4fcf00b462 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -15,11 +15,14 @@ #include #include +#include #include "MeshBoolean.hpp" #include "Model.hpp" #include "PNGReadWrite.hpp" #include "TriangleSelector.hpp" +#include "TextureBake/TextureBakeMesh.hpp" +#include "TextureBake/TextureBakePipeline.hpp" namespace Slic3r { @@ -1361,6 +1364,83 @@ void despeckle_triangle_colors(const indexed_triangle_set &mesh, std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress) +{ + HeightFieldSampler combined = make_combined_displacement_sampler(mesh, layers, facets_data); + if (!combined) + return mesh; // nothing decodable to displace with + + // Unpainted triangles are excluded, keeping them out of refinement and pinned thereafter. + std::vector excluded(mesh.indices.size(), 1); + { + const TriangleMesh selector_mesh(mesh); + TriangleSelector selector(selector_mesh); + bool dirty = false; + for (const TriangleSelector::TriangleSplittingData &data : facets_data) { + if (data.triangles_to_split.empty()) + continue; + selector.deserialize(data, dirty); + dirty = true; + std::vector piece_src; + const indexed_triangle_set patch = + selector.get_facets_strict(EnforcerBlockerType::ENFORCER, &piece_src); + for (const int src : piece_src) + if (src >= 0 && size_t(src) < excluded.size()) + excluded[size_t(src)] = 0; + } + } + if (std::all_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; })) + return mesh; // nothing painted + + TextureBake::PipelineSettings settings; + settings.refine_length = std::max(0.01f, options.v2_refine_mm); + settings.regularize = options.v2_regularize; + settings.max_triangles = size_t(std::max(0, options.v2_max_triangles_k)) * 1000; + settings.preserve_untextured = true; + // The sampler already returns millimetres, so the displacement stage must not scale it again. + settings.displace.amplitude = 1.f; + settings.displace.symmetric = false; + // The paint decides what moves here, so the angle limits stay off. + settings.displace.bottom_angle_limit = 0.f; + settings.displace.top_angle_limit = 0.f; + + TextureBake::DisplaceBounds bounds; + bounds.min = bounds.max = mesh.vertices.empty() ? Vec3f::Zero() : mesh.vertices.front(); + for (const Vec3f &v : mesh.vertices) { + bounds.min = bounds.min.cwiseMin(v); + bounds.max = bounds.max.cwiseMax(v); + } + + const auto sample = [&combined](const Vec3f &pos, const Vec3f &smooth_normal, const Vec3f &) { + // The smooth normal: the direction the vertex actually moves along. + return combined(pos, smooth_normal); + }; + + // 0 means no simplification, i.e. Bake mode. + const TextureBake::PipelineMode mode = settings.max_triangles > 0 ? TextureBake::PipelineMode::Export + : TextureBake::PipelineMode::Bake; + TextureBake::PipelineResult result = TextureBake::run_pipeline( + TextureBake::to_soup(mesh, excluded), sample, settings, bounds, mode, excluded, + [&progress](const char *, double f) { + return !progress || progress(std::clamp(int(f * 100.0), 0, 99)); + }); + if (result.canceled || result.geometry.empty()) + return {}; + + indexed_triangle_set out = TextureBake::to_indexed_triangle_set(result.geometry); + return out.indices.empty() ? mesh : out; +} + +} // namespace + indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, @@ -1384,6 +1464,9 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (mesh.vertices.empty() || mesh.indices.empty()) return mesh; + if (options.pipeline_v2) + return build_texture_displacement_v2(mesh, layers, facets_data, options, progress); + // Layers are combined in slot order, like stacked layers in an image editor: each one folds its // own displacement into the running total via its blend mode (see TextureBlendMode). std::vector ordered_layers; @@ -2073,26 +2156,38 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // instead of with the refined region, and what forced the tiny pass budget that stopped // refinement short. { - struct EdgeRec { int he[2]; int count; }; // he = encoded half-edge (triangle * 3 + local edge) - std::unordered_map edges; - edges.reserve(tris.size() * 2); - for (int ti = 0; ti < int(tris.size()); ++ti) - for (int e = 0; e < 3; ++e) { - EdgeRec &r = edges.try_emplace(edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]), - EdgeRec{ { -1, -1 }, 0 }) - .first->second; - if (r.count < 2) - r.he[r.count] = ti * 3 + e; - ++r.count; - } - for (int ti = 0; ti < int(tris.size()); ++ti) - for (int e = 0; e < 3; ++e) { - const EdgeRec &r = edges.at(edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3])); - if (r.count > 2) - tris[ti].nb[e] = NB_NONMANIFOLD; - else if (r.count == 2) - tris[ti].nb[e] = (r.he[0] == ti * 3 + e ? r.he[1] : r.he[0]) / 3; + // Sort the half-edges by their edge key and walk the equal runs, rather than hashing every one + // of them twice into an unordered_map. Same result, but the two expensive parts - forming the + // keys and ordering them - both parallelise, where a shared hash map cannot. The map also cost + // a second full pass of lookups purely to read back what the first pass had just inserted. + std::vector> he(tris.size() * 3); // (edge key, triangle * 3 + local edge) + tbb::parallel_for(tbb::blocked_range(0, tris.size()), + [&](const tbb::blocked_range &range) { + for (size_t ti = range.begin(); ti < range.end(); ++ti) + for (int e = 0; e < 3; ++e) + he[ti * 3 + size_t(e)] = { + edge_key(tris[ti].v[e], tris[ti].v[(e + 1) % 3]), int(ti) * 3 + e + }; + }); + tbb::parallel_sort(he.begin(), he.end()); + // Runs of equal key are the half-edges of one edge: one is a boundary, two are neighbours, + // more is non-manifold. Serial, but it is a single linear pass over an already ordered array. + for (size_t i = 0; i < he.size();) { + size_t j = i + 1; + while (j < he.size() && he[j].first == he[i].first) + ++j; + const size_t count = j - i; + if (count == 2) { + const int a = he[i].second, b = he[i + 1].second; + tris[size_t(a / 3)].nb[a % 3] = b / 3; + tris[size_t(b / 3)].nb[b % 3] = a / 3; + } else if (count > 2) { + for (size_t k = i; k < j; ++k) + tris[size_t(he[k].second / 3)].nb[he[k].second % 3] = NB_NONMANIFOLD; } + // count == 1 keeps the NB_BOUNDARY it was initialised with. + i = j; + } } // Feature mode: per-vertex surface normal, and the sampled displacement height at each vertex. @@ -2152,6 +2247,40 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, return vcolor[v]; }; + // Sample the input mesh's own vertices up front, in parallel, for the region that is going to be + // refined. A sampler call is a texture fetch plus the projection's trigonometry per layer, and it + // is by far the most expensive thing here - but taken one at a time from inside the refinement + // loop it is also strictly serial. Every one of these vertices is read by the very first scoring + // pass anyway, so doing them together costs nothing extra and hands the work to every core. + // + // Only the region, and only the *initial* vertices: the laziness this replaces exists so that a + // small painted patch on a big model does not pay for the whole model (see height_of()), and that + // still holds. Midpoints created later stay lazy, because they do not exist yet. + if (feature_mode || color_mode) { + std::vector wanted(verts.size(), 0); + for (const Tri &t : tris) + if (refine_region[t.src] != 0) + for (int i = 0; i < 3; ++i) + wanted[size_t(t.v[i])] = 1; + // Each index is touched by exactly one iteration, so the lazy caches can be filled without + // synchronisation - and every value is the one height_of()/color_of() would have produced. + tbb::parallel_for(tbb::blocked_range(0, verts.size()), + [&](const tbb::blocked_range &range) { + for (size_t v = range.begin(); v < range.end(); ++v) { + if (!wanted[v]) + continue; + if (feature_mode) { + vheight[v] = sampler(verts[v], vnormal[v]); + vheight_valid[v] = 1; + } + if (color_mode) { + vcolor[v] = color(verts[v], vnormal[v]); + vcolor_valid[v] = 1; + } + } + }); + } + auto elen_sq = [&](int a, int b) -> float { return (verts[a] - verts[b]).squaredNorm(); }; // The one edge of a triangle taken as its "longest": greatest squared length, exact ties broken by @@ -2388,9 +2517,22 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // re-scored on pop and dropped or re-pushed. The ordering is a budget-allocation heuristic only: // neither correctness nor conformality depends on it. std::priority_queue> queue; - for (int ti = 0; ti < int(tris.size()); ++ti) - if (const float p = priority(ti); p > 1.f) - queue.emplace(p, ti); + { + // Scoring the starting mesh means a detail_error() per triangle - four more sampler calls each + // - so it is worth spreading, even though the refinement that follows cannot be. Each entry is + // written by one iteration only, and the caches those calls fill (tri_err, tri_color_split) are + // likewise per triangle, so there is nothing shared to guard. The heap is then built from the + // finished array in index order, which is exactly the order the serial loop pushed in. + std::vector initial(tris.size(), 0.f); + tbb::parallel_for(tbb::blocked_range(0, tris.size()), + [&](const tbb::blocked_range &range) { + for (size_t ti = range.begin(); ti < range.end(); ++ti) + initial[ti] = priority(int(ti)); + }); + for (int ti = 0; ti < int(tris.size()); ++ti) + if (initial[size_t(ti)] > 1.f) + queue.emplace(initial[size_t(ti)], ti); + } // Every iteration either drops one satisfied triangle from the queue or performs exactly one // bisection, and bisections are capped by the triangle budget, so this always terminates. diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 7abf1d656c..c187c3be48 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -169,7 +169,7 @@ struct TextureDisplacementLayer std::shared_ptr> image_data; float depth_mm = 0.4f; // maximum displacement along the surface normal, in mm - float tiling_scale = 10.f; // size of one texture tile, in mm + float tiling_scale = 12.5f; // size of one texture tile, in mm float rotation_deg = 0.f; Vec2f offset = Vec2f::Zero(); bool invert = false; @@ -365,6 +365,15 @@ struct TextureDisplacementOptions // deliberately (which is a blunter version of the per-layer edge-smoothing falloff). bool smooth_skip_border = true; + // Alternative bake pipeline, for side-by-side comparison. The path above is topology-preserving + // and needs the mesh prepared first; this one refines, removes slivers, displaces and optionally + // simplifies in one run. Off by default, and it produces no colour - it rebuilds the topology, so + // the per-facet assignment has nothing stable to attach to. + bool pipeline_v2 = false; + float v2_refine_mm = 0.3f; + bool v2_regularize = false; + int v2_max_triangles_k = 750; // 0 skips simplification, which is worth comparing on its own + // Colour, all of which belongs to the stack rather than to any one layer: it is about how the // printer will realise the colours, not about which image they came from. @@ -381,7 +390,8 @@ struct TextureDisplacementOptions { int mix_mode = int(color_mix_mode); ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border, - color_mix_enabled, mix_mode, color_despeckle); + pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k, color_mix_enabled, mix_mode, + color_despeckle); color_mix_mode = ColorMixMode(mix_mode); } }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index b0bfefddbd..8de71180f0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -3317,7 +3317,9 @@ bool GLGizmoTextureDisplacement::collect_paint_region( ++vstart[size_t(its.indices[i][k]) + 1]; for (size_t v = 0; v < nvert; ++v) vstart[v + 1] += vstart[v]; - std::vector vtri(size_t(vstart[nvert]), 0); + // static_cast, not size_t(...): the latter parses as a parameter declaration (see the note + // above the identical prefix sum on `part`). + std::vector vtri(static_cast(vstart[nvert]), 0); { std::vector fill(vstart.begin(), vstart.begin() + nvert); for (size_t i = 0; i < ntri; ++i) @@ -4095,6 +4097,13 @@ void GLGizmoTextureDisplacement::bake_standard() } apply_standard_mode_presets(mv); // belt and braces: never bake with values the panel is not showing + // It refines as part of the bake, so preparing first would refine a second time at another + // target. + if (mv->texture_displacement_options.pipeline_v2) { + bake(); + return; + } + // The whole recipe in one go. Either stage having nothing to do is normal, not a failure - a mesh // that is already even needs no remesh, one that is already fine enough for the texture needs no // subdivision - so no "nothing changed" message here: it goes straight on to the displacement. @@ -5364,6 +5373,48 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); + // Next to Bake and shown in both modes: the two pipelines have to be switchable on their own. + if (mv != nullptr) { + TextureDisplacementOptions &opts = mv->texture_displacement_options; + ImGui::Separator(); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Experimental bake pipeline").c_str(), &opts.pipeline_v2); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Bake with the alternative pipeline: it refines, cleans up sliver triangles, " + "displaces and simplifies in one run, instead of moving the vertices the mesh " + "already has. Nothing needs preparing first - Subdivide and Remesh are ignored. " + "It does not produce colours yet, because it rebuilds the topology."), + m_imgui->scaled(20.f)); + if (opts.pipeline_v2) { + ImGui::PushItemWidth(m_imgui->scaled(8.4f)); + if (m_imgui->slider_float(std::string(_u8L("Edge length (mm)")) + "##v2edge", &opts.v2_refine_mm, + 0.02f, 2.f, "%.1f", ImGuiLogSlider)) { + opts.v2_refine_mm = std::clamp(opts.v2_refine_mm, 0.02f, 2.f); + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangle size the painted area is refined to before displacement. This is " + "what decides how much of the texture the mesh can carry."), + m_imgui->scaled(20.f)); + if (ImGui::SliderInt((_u8L("Triangle budget (k)") + "##v2budget").c_str(), &opts.v2_max_triangles_k, + 0, 4000)) { + opts.v2_max_triangles_k = std::clamp(opts.v2_max_triangles_k, 0, 4000); + m_preview_params_dirty = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Triangles to simplify down to after displacement, in thousands. 0 turns " + "simplification off, which is worth comparing on its own."), + m_imgui->scaled(20.f)); + ImGui::PopItemWidth(); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Collapse the thin triangles refinement inherits from the model's own " + "tessellation, before displacement samples them. A sliver's three corners " + "land on three unrelated parts of the texture, which is what makes the " + "relief look jagged."), + m_imgui->scaled(20.f)); + } + } + ImGui::SameLine(); m_imgui->disabled_begin(m_bake_in_progress || m_prepare_in_progress || mv == nullptr || !mv->is_texture_displacement_painted()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 93bbc61117..05d2fa20eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -511,9 +511,9 @@ private: // The default used to be 1500 (i.e. +1.5 M triangles), which is what made Standard mode's Bake // take minutes: every stage after the subdivision - the displacement itself, the convex hull, the // GLModel upload, and the re-slice changed_object() triggers - then runs on a mesh two orders of - // magnitude denser than the input. 300k is still far finer than any FDM nozzle resolves at the + // magnitude denser than the input. 750k is still far finer than any FDM nozzle resolves at the // 0.02 mm detail tolerance Standard uses, and the slider goes to 2000 for anyone who wants more. - int m_subdivide_budget_k = 300; + int m_subdivide_budget_k = 750; void subdivide_model_adaptive(); // Fills `region` (per current-mesh triangle, a REFINE_* bitmask) from the union of every layer's // painted area plus the band straddling its edge. If `paint` is non-null, also fills the per-layer @@ -572,7 +572,6 @@ private: GLModel m_paint_overlay_glmodel; // Set on every paint event, cleared when the overlay is rebuilt in render_painter_gizmo(). Kept // separate from m_bump_preview_dirty so a stroke refreshes only the small painted patch per frame, - // not the bump mesh (which also carries every *unpainted* triangle of the volume). bool m_paint_overlay_dirty = false; void rebuild_paint_overlay(); void render_paint_overlay(); From 60c03e706acd9bb7d23bba400cf9cae1348bd389 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 9 Sep 2026 08:42:37 +0200 Subject: [PATCH 25/89] add alternative baking algorithm --- src/libslic3r/MeshBoolean.cpp | 4 +- src/libslic3r/MeshBoolean.hpp | 7 +- .../TextureBake/TextureBakeDecimate.cpp | 89 ++++++-- .../TextureBake/TextureBakeDisplace.cpp | 54 +++-- .../TextureBake/TextureBakePipeline.cpp | 39 +++- .../TextureBake/TextureBakePipeline.hpp | 12 +- .../TextureBake/TextureBakeRelocate.cpp | 205 ++++++++++++++++++ .../TextureBake/TextureBakeRelocate.hpp | 72 ++++++ .../TextureBake/TextureBakeSubdivide.cpp | 47 ++-- src/libslic3r/TextureDisplacement.cpp | 5 + src/libslic3r/TextureDisplacement.hpp | 11 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 27 ++- 12 files changed, 503 insertions(+), 69 deletions(-) create mode 100644 src/libslic3r/TextureBake/TextureBakeRelocate.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeRelocate.hpp diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 5cd1968e22..4d9dda0c2e 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -261,7 +261,8 @@ indexed_triangle_set cgal_to_indexed_triangle_set(const CGALMesh &cgalmesh) // ///////////////////////////////////////////////////////////////////////////// indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, - unsigned n_iterations, double sharp_angle_deg) + unsigned n_iterations, double sharp_angle_deg, + unsigned n_relaxation_steps) { if (mesh.indices.empty() || target_edge_length <= 0.0) return mesh; @@ -305,6 +306,7 @@ indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double t CGALProc::isotropic_remeshing(faces(cgal_mesh), target_edge_length, cgal_mesh, CGALParams::number_of_iterations(n_iterations) + .number_of_relaxation_steps(n_relaxation_steps) .edge_is_constrained_map(ecm) .protect_constraints(true)); } catch (const std::exception &) { diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index efaf553420..c307d1528b 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -89,8 +89,13 @@ std::optional> parameterize_lscm(const indexed_triangle_set & // Edges whose dihedral angle exceeds `sharp_angle_deg`, and any open border, are held fixed so hard // features survive instead of being eroded by the relaxation pass; pass 0 to remesh everything. // Returns the input unchanged if remeshing fails (e.g. a non-manifold or self-intersecting input). +// `n_relaxation_steps` is the number of tangential relaxation passes run inside each iteration. That +// relaxation is what actually evens out the triangle distribution - splitting and collapsing alone +// only bring edge *lengths* near the target, leaving the vertices wherever they happened to land. CGAL +// defaults it to 1, which on a few iterations is not enough to look uniform. indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, - unsigned n_iterations = 3, double sharp_angle_deg = 40.0); + unsigned n_iterations = 3, double sharp_angle_deg = 40.0, + unsigned n_relaxation_steps = 1); } namespace mcut { diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index d4744f40a7..e591f7f191 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -6,6 +6,11 @@ #include #include +#include + +#include +#include + namespace Slic3r { namespace TextureBake { @@ -74,13 +79,19 @@ Vec3d face_normal_unit(const std::vector &pos, int a, int b, int c) } // Versions are captured at push time; a mismatch on pop means a later collapse invalidated the entry. -// Lazy deletion, far cheaper than removing entries eagerly. +// Lazy deletion, far cheaper than removing entries eagerly - but it means the heap accumulates stale +// duplicates, so it grows to several times the edge count and its size has to be reserved up front. +// Left to grow on its own it reallocates and copies the whole array repeatedly, which on a +// multi-million-entry heap costs more than every collapse put together. +// +// The collapse target is stored as float rather than double: it is a position on a mesh already held +// in float, and halving the entry cuts the memory the sift operations drag through cache. struct HeapEntry { double cost; int v1, v2; uint32_t ver1, ver2; - Vec3d p; + Vec3f p; bool operator>(const HeapEntry &o) const { return cost > o.cost; } }; @@ -143,16 +154,29 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } std::vector quadrics(vert_count); - for (size_t f = 0; f < face_count; ++f) { - const int a = faces[f * 3], b = faces[f * 3 + 1], c = faces[f * 3 + 2]; - if (a < 0) - continue; - const Vec3d nrm = face_normal_unit(pos, a, b, c); - if (nrm.isZero()) - continue; - const double d = -nrm.dot(pos[size_t(a)]); - for (const int v : { a, b, c }) - quadrics[size_t(v)].add_plane(nrm.x(), nrm.y(), nrm.z(), d); + { + // The plane per face is independent; accumulating it into the three incident vertices is not, + // so only the first half is parallel. + std::vector planes(face_count, Vec4d::Zero()); + tbb::parallel_for(tbb::blocked_range(0, face_count), + [&](const tbb::blocked_range &range) { + for (size_t f = range.begin(); f < range.end(); ++f) { + const int a = faces[f * 3], b = faces[f * 3 + 1], c = faces[f * 3 + 2]; + if (a < 0) + continue; + const Vec3d nrm = face_normal_unit(pos, a, b, c); + if (nrm.isZero()) + continue; + planes[f] = Vec4d(nrm.x(), nrm.y(), nrm.z(), -nrm.dot(pos[size_t(a)])); + } + }); + for (size_t f = 0; f < face_count; ++f) { + const Vec4d &pl = planes[f]; + if (pl.head<3>().isZero()) + continue; + for (int k = 0; k < 3; ++k) + quadrics[size_t(faces[f * 3 + size_t(k)])].add_plane(pl.x(), pl.y(), pl.z(), pl.w()); + } } // Two penalty planes per endpoint on a sharp interior edge, each perpendicular to one adjacent @@ -250,7 +274,22 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h uint32_t epoch = 1, lk_epoch = 1; size_t active_faces = face_count; - std::priority_queue, std::greater> heap; + // A plain vector driven by the heap algorithms, so the capacity can be reserved. Lazy deletion + // means roughly one entry per edge plus one per re-push after each collapse; the reserve below is + // sized from the edge count and simply grows if a mesh needs more. + std::vector heap; + heap.reserve(std::min(face_count * 3, size_t(1) << 24)); + const auto heap_push = [&](HeapEntry e) { + heap.push_back(e); + std::push_heap(heap.begin(), heap.end(), std::greater()); + }; + const auto heap_pop = [&]() { + std::pop_heap(heap.begin(), heap.end(), std::greater()); + const HeapEntry e = heap.back(); + heap.pop_back(); + return e; + }; + size_t pops = 0, stale_pops = 0; const auto push_edge = [&](int v1, int v2) { Vec3d p; @@ -269,8 +308,8 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } // Where quadric costs are all near zero, shorter edges first keeps triangle quality up. const double len2 = (pos[size_t(v2)] - pos[size_t(v1)]).squaredNorm(); - heap.push({ eval_sum(quadrics, v1, v2, p) + len2 * 1e-8, v1, v2, version[size_t(v1)], - version[size_t(v2)], p }); + heap_push({ eval_sum(quadrics, v1, v2, p) + len2 * 1e-8, v1, v2, version[size_t(v1)], + version[size_t(v2)], p.cast() }); }; { @@ -384,27 +423,32 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h reached_target = true; } - const HeapEntry top = heap.top(); - heap.pop(); + const HeapEntry top = heap_pop(); + ++pops; // The popped entry is the cheapest left, so exceeding the tolerance ends the run. if (reached_target && top.cost > harvest_ceil) break; const int v1 = top.v1, v2 = top.v2; - if (!active[size_t(v1)] || !active[size_t(v2)]) + if (!active[size_t(v1)] || !active[size_t(v2)]) { + ++stale_pops; continue; - if (version[size_t(v1)] != top.ver1 || version[size_t(v2)] != top.ver2) + } + if (version[size_t(v1)] != top.ver1 || version[size_t(v2)] != top.ver2) { + ++stale_pops; continue; + } if (shared_face_count(v1, v2) < 2) continue; lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call if (has_link_violation(v1, v2, lk_epoch)) continue; - if (check_flipped(v1, v2, top.p) || check_flipped(v2, v1, top.p)) + const Vec3d target = top.p.cast(); + if (check_flipped(v1, v2, target) || check_flipped(v2, v1, target)) continue; // v1 survives at the new position, v2 goes. - pos[size_t(v1)] = top.p; + pos[size_t(v1)] = target; quadrics[size_t(v1)] += quadrics[size_t(v2)]; ++version[size_t(v1)]; @@ -459,6 +503,9 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } } + BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: pops=" << pops << " stale=" << stale_pops + << " heap_peak=" << heap.capacity() << " faces=" << active_faces; + // Rebuild from the surviving faces, with per-face normals. TriSoup &out = result.geometry; for (size_t f = 0; f < face_count; ++f) { diff --git a/src/libslic3r/TextureBake/TextureBakeDisplace.cpp b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp index ef417a7bef..540cb7dda8 100644 --- a/src/libslic3r/TextureBake/TextureBakeDisplace.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp @@ -4,6 +4,9 @@ #include #include +#include +#include + namespace Slic3r { namespace TextureBake { @@ -204,20 +207,30 @@ TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample } } - // Pass 2: one sample per unique position. - std::vector grey(unique_count, 0.0); - std::vector grey_set(unique_count, 0); - for (size_t i = 0; i < count; ++i) { - const size_t vid = size_t(vertex_id[i]); - if (grey_set[vid]) - continue; - grey_set[vid] = 1; - grey[vid] = double(sample(geometry.pos[i], smooth_nrm[vid].cast(), - blend_nrm[vid].cast())); - } + // Pass 2: one sample per unique position. A representative corner is picked first so the sampling + // itself is a flat parallel loop - it is a texture fetch plus projection maths per layer, and by + // far the most expensive thing in this stage. + std::vector grey(unique_count, 0.0); + std::vector representative(unique_count, -1); + for (size_t i = 0; i < count; ++i) + if (representative[size_t(vertex_id[i])] < 0) + representative[size_t(vertex_id[i])] = int(i); + tbb::parallel_for(tbb::blocked_range(0, unique_count), + [&](const tbb::blocked_range &range) { + for (size_t vid = range.begin(); vid < range.end(); ++vid) { + const int rep = representative[vid]; + if (rep < 0) + continue; + grey[vid] = double(sample(geometry.pos[size_t(rep)], + smooth_nrm[vid].cast(), + blend_nrm[vid].cast())); + } + }); - // Pass 3: move every copy of a position by the identical vector. - for (size_t i = 0; i < count; ++i) { + // Pass 3: move every copy of a position by the identical vector. Each iteration writes only its + // own output slot, so the loop is independent per corner. + tbb::parallel_for(tbb::blocked_range(0, count), [&](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { const Vec3f &p = geometry.pos[i]; const size_t vid = size_t(vertex_id[i]); @@ -249,17 +262,18 @@ TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample moved.z() = double(p.z()); out.pos[i] = moved.cast(); - - if (on_progress && (i % 5000) == 0 && !on_progress(double(i) / double(count))) - return geometry; // cancelled: hand back the input untouched } + }); // Per-face, not averaged across shared positions: averaging can flip an excluded face's normal // when its neighbours moved outward. - for (size_t t = 0; t + 2 < count; t += 3) { - const Vec3f n = (out.pos[t + 1] - out.pos[t]).cross(out.pos[t + 2] - out.pos[t]).normalized(); - out.nrm[t] = out.nrm[t + 1] = out.nrm[t + 2] = n; - } + tbb::parallel_for(tbb::blocked_range(0, count / 3), [&](const tbb::blocked_range &r) { + for (size_t f = r.begin(); f < r.end(); ++f) { + const size_t t = f * 3; + const Vec3f n = (out.pos[t + 1] - out.pos[t]).cross(out.pos[t + 2] - out.pos[t]).normalized(); + out.nrm[t] = out.nrm[t + 1] = out.nrm[t + 2] = n; + } + }); out.exclude_weight = geometry.exclude_weight; return out; } diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index a86fda0e0d..510c44e844 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -1,8 +1,11 @@ #include "TextureBakePipeline.hpp" #include +#include #include +#include + namespace Slic3r { namespace TextureBake { @@ -111,6 +114,15 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, const auto report = [&](const char *stage, double f) { return !on_progress || on_progress(stage, f); }; + // Per-stage wall time. The stages differ in cost by orders of magnitude depending on the model, so + // without this it is guesswork which one to attack. + auto clock_now = [] { return std::chrono::steady_clock::now(); }; + auto t_stage = clock_now(); + const auto lap = [&](const char *stage, size_t tris) { + const double ms = std::chrono::duration(clock_now() - t_stage).count(); + BOOST_LOG_TRIVIAL(info) << "TextureBake " << stage << ": " << ms << " ms, " << tris << " tris"; + t_stage = clock_now(); + }; if (input.empty() || !sample) { result.geometry = input; @@ -122,6 +134,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, input, settings.refine_length, face_excluded, /* fast */ false, settings.safety_cap, [&](double f, size_t, double) { return report("subdivide", f); }); result.safety_cap_hit = sub.safety_cap_hit; + lap("subdivide", sub.geometry.triangle_count()); if (!report("subdivide", 1.0)) { result.canceled = true; return result; @@ -134,6 +147,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, RegularizeResult reg = regularize_mesh(sub.geometry, sub.face_parent_id, settings.refine_length, ropts); result.collapse_count = reg.collapse_count; + lap("regularize", reg.geometry.triangle_count()); if (!report("regularize", 1.0)) { result.canceled = true; return result; @@ -159,15 +173,31 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, ? reg.face_parent_id[size_t(mid)] : -1; } sub.face_parent_id = std::move(composed); + lap("re-subdivide", sub.geometry.triangle_count()); } else { sub.geometry = std::move(reg.geometry); sub.face_parent_id = std::move(reg.face_parent_id); } } - // 3. Displace. + // 3. Align the mesh to the height field's edges, then displace. + if (settings.relocate) { + std::vector locked; + if (settings.preserve_untextured && !sub.geometry.exclude_weight.empty()) { + locked.assign(sub.geometry.triangle_count(), 0); + for (size_t t = 0; t < locked.size(); ++t) + locked[t] = sub.geometry.exclude_weight[t * 3] > 0.99f ? 1 : 0; + } + RelocateResult rel = relocate_to_contours(sub.geometry, sample, settings.relocate_opts, locked); + BOOST_LOG_TRIVIAL(info) << "TextureBake relocate: moved=" << rel.moved + << " rejected=" << rel.rejected; + sub.geometry = std::move(rel.geometry); + lap("relocate", sub.geometry.triangle_count()); + } + TriSoup displaced = apply_displacement(sub.geometry, sample, settings.displace, bounds, [&](double f) { return report("displace", f); }); + lap("displace", displaced.triangle_count()); if (!report("displace", 1.0)) { result.canceled = true; return result; @@ -189,6 +219,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, [&](double f) { return report("decimate", f); }); result.locked_over_budget = dec.locked_over_budget; displaced = std::move(dec.geometry); + lap("decimate", displaced.triangle_count()); parent.clear(); // no longer meaningful } if (!report("decimate", 1.0)) { @@ -198,14 +229,16 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } // 5. Flatten the bed-contact surface. - if (settings.displace.bottom_angle_limit > 0.f) + if (settings.clamp_below_plate || settings.displace.bottom_angle_limit > 0.f) clamp_below_bottom(displaced, bounds.min.z()); if (settings.bottom_snap_tol > 0.0) snap_bottom_to_flat(displaced, bounds.min.z(), settings.bottom_snap_tol); // 6. Close the T-junctions decimation left behind. Only meaningful when it ran. - if (mode == PipelineMode::Export && parent.empty()) + if (mode == PipelineMode::Export && parent.empty()) { displaced = resolve_t_junctions(displaced); + lap("repair", displaced.triangle_count()); + } result.geometry = std::move(displaced); result.face_parent_id = std::move(parent); diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index a9a7db1957..a1cee46c1c 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -2,7 +2,7 @@ // The bake pipeline: // -// subdivide -> [regularize -> re-subdivide] -> displace -> [decimate] +// subdivide -> [regularize -> re-subdivide] -> [relocate] -> displace -> [decimate] // -> bottom clamp -> bottom snap -> [resolve T-junctions] // // Regularization sits between two subdivisions on purpose: it dissolves the slivers refinement @@ -21,6 +21,7 @@ #include "TextureBakeDisplace.hpp" #include "TextureBakeIndex.hpp" #include "TextureBakeRegularize.hpp" +#include "TextureBakeRelocate.hpp" #include "TextureBakeRepair.hpp" #include "TextureBakeSubdivide.hpp" @@ -47,6 +48,11 @@ struct PipelineSettings // re-refining what it just merged. double regularize_second_pass_mul = 1.1; + // Slide vertices onto the height map's own edges before displacing, so a step lands on a mesh + // edge instead of being quantised to wherever the grid fell. + bool relocate = false; + RelocateSettings relocate_opts; + DisplaceSettings displace; // Export mode only. @@ -56,6 +62,10 @@ struct PipelineSettings // Lock the untextured region against both regularization and decimation. bool preserve_untextured = true; + // Push anything that displaced below the plate back up to it. Downward movement is otherwise left + // alone, so relief on the underside is kept - only what would sink through the plate is stopped. + bool clamp_below_plate = false; + // Snap vertices within this of the bottom plane onto it. 0 disables. double bottom_snap_tol = 0.1; diff --git a/src/libslic3r/TextureBake/TextureBakeRelocate.cpp b/src/libslic3r/TextureBake/TextureBakeRelocate.cpp new file mode 100644 index 0000000000..5ea33b353b --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRelocate.cpp @@ -0,0 +1,205 @@ +#include "TextureBakeRelocate.hpp" + +#include +#include +#include + +#include +#include + +namespace Slic3r { +namespace TextureBake { + +namespace { + +// Any two unit vectors orthogonal to n. Which two does not matter - the gradient is expressed in this +// basis and converted straight back, so the result is basis independent. +void tangent_basis(const Vec3f &n, Vec3f &t1, Vec3f &t2) +{ + const Vec3f a = (std::abs(n.x()) < 0.9f) ? Vec3f(1.f, 0.f, 0.f) : Vec3f(0.f, 1.f, 0.f); + t1 = n.cross(a).normalized(); + t2 = n.cross(t1).normalized(); +} + +} // namespace + +RelocateResult relocate_to_contours(const TriSoup &geometry, const HeightSampleFn &sample, + const RelocateSettings &settings, const std::vector &locked) +{ + RelocateResult result; + result.geometry = geometry; + const size_t count = geometry.pos.size(); + const size_t tri_ct = count / 3; + if (count == 0 || !sample || settings.iterations <= 0) + return result; + + // Weld, so every copy of a position moves together and the mesh cannot come apart. + QuantizedPointMap weld(WELD_GRID_GEOMETRY, std::min(count, size_t(1) << 22)); + std::vector vid(count); + std::vector pos; + for (size_t i = 0; i < count; ++i) { + vid[i] = weld.get_or_set(geometry.pos[i], int(pos.size())); + if (weld.inserted()) + pos.push_back(geometry.pos[i]); + } + const size_t nv = pos.size(); + + // Incident corners per position, CSR style, plus the mean incident edge length that sets the scale + // for both the finite difference and the move limit. + std::vector start(nv + 1, 0); + for (size_t i = 0; i < count; ++i) + ++start[size_t(vid[i]) + 1]; + for (size_t v = 0; v < nv; ++v) + start[v + 1] += start[v]; + std::vector inc(count), cursor(nv, 0); + for (size_t i = 0; i < count; ++i) + inc[start[size_t(vid[i])] + cursor[size_t(vid[i])]++] = uint32_t(i); + + std::vector frozen(nv, 0); + if (!locked.empty()) + for (size_t t = 0; t < tri_ct && t < locked.size(); ++t) + if (locked[t]) + for (int k = 0; k < 3; ++k) + frozen[size_t(vid[t * 3 + size_t(k)])] = 1; + + std::vector edge_len(nv, 0.f), normal_len(nv, 0.f); + std::vector nrm(nv, Vec3f::Zero()); + const auto rebuild_frames = [&]() { + std::fill(nrm.begin(), nrm.end(), Vec3f::Zero()); + std::fill(edge_len.begin(), edge_len.end(), 0.f); + std::vector deg(nv, 0); + for (size_t t = 0; t < tri_ct; ++t) { + const int a = vid[t * 3], b = vid[t * 3 + 1], c = vid[t * 3 + 2]; + const Vec3f fn = (pos[size_t(b)] - pos[size_t(a)]).cross(pos[size_t(c)] - pos[size_t(a)]); + for (int k = 0; k < 3; ++k) { + const int u = vid[t * 3 + size_t(k)], w = vid[t * 3 + size_t((k + 1) % 3)]; + nrm[size_t(u)] += fn; + edge_len[size_t(u)] += (pos[size_t(w)] - pos[size_t(u)]).norm(); + ++deg[size_t(u)]; + } + } + for (size_t v = 0; v < nv; ++v) { + const float l = nrm[v].norm(); + nrm[v] = (l > 0.f) ? Vec3f(nrm[v] / l) : Vec3f(0.f, 0.f, 1.f); + edge_len[v] = deg[v] > 0 ? edge_len[v] / float(deg[v]) : 0.f; + } + }; + rebuild_frames(); + + // The level to snap onto, taken from the height actually present on this patch rather than assumed. + // A texture that never reaches full black or white would otherwise be measured against a range it + // does not occupy. + double h_lo = std::numeric_limits::max(), h_hi = -h_lo; + { + std::vector h0(nv, 0.f); + tbb::parallel_for(tbb::blocked_range(0, nv), [&](const tbb::blocked_range &r) { + for (size_t v = r.begin(); v < r.end(); ++v) + h0[v] = sample(pos[v], nrm[v], nrm[v]); + }); + for (const float h : h0) { + h_lo = std::min(h_lo, double(h)); + h_hi = std::max(h_hi, double(h)); + } + } + const double h_range = h_hi - h_lo; + if (!(h_range > 0.0)) + return result; // a flat height field has no contour to snap to + const double target = h_lo + h_range * settings.contour_level; + // A gradient is worth acting on when the height changes by this much across one edge length. + const double min_grad = h_range * settings.min_gradient_fraction; + + std::vector ever_moved(nv, 0); + + for (int iter = 0; iter < settings.iterations; ++iter) { + std::vector proposal(nv); + std::vector want(nv, 0); + + tbb::parallel_for(tbb::blocked_range(0, nv), [&](const tbb::blocked_range &r) { + for (size_t v = r.begin(); v < r.end(); ++v) { + if (frozen[v] || edge_len[v] <= 0.f) + continue; + const Vec3f n = nrm[v]; + Vec3f t1, t2; + tangent_basis(n, t1, t2); + const float eps = edge_len[v] * float(settings.gradient_step_fraction); + if (eps <= 0.f) + continue; + + // Central differences in the tangent plane. Sampling the field itself, not the mesh, + // so the gradient is the image's, at whatever resolution the mesh happens to have. + const double h = double(sample(pos[v], n, n)); + const double gx = (double(sample(pos[v] + t1 * eps, n, n)) - + double(sample(pos[v] - t1 * eps, n, n))) / (2.0 * double(eps)); + const double gy = (double(sample(pos[v] + t2 * eps, n, n)) - + double(sample(pos[v] - t2 * eps, n, n))) / (2.0 * double(eps)); + const double g2 = gx * gx + gy * gy; + if (g2 <= 0.0) + continue; + // Scale-free test: how much the height changes across one edge, versus the patch range. + if (std::sqrt(g2) * double(edge_len[v]) < min_grad) + continue; + + // Newton step onto the level set h = target, expressed back in 3D. + const double s = -(h - target) / g2; + Vec3f d = t1 * float(s * gx) + t2 * float(s * gy); + const float cap = edge_len[v] * float(settings.max_move_fraction); + const float len = d.norm(); + if (len <= 0.f) + continue; + if (len > cap) + d *= cap / len; + proposal[v] = pos[v] + d; + want[v] = 1; + } + }); + + // Apply one at a time: a move is only valid against the neighbourhood as it stands, and two + // adjacent vertices moving together can invert a triangle neither would have on its own. + size_t applied = 0; + for (size_t v = 0; v < nv; ++v) { + if (!want[v]) + continue; + const Vec3f old = pos[v]; + pos[v] = proposal[v]; + bool ok = true; + for (uint32_t k = start[v]; k < start[v + 1] && ok; ++k) { + const size_t t = size_t(inc[k]) / 3; + const Vec3f &a = pos[size_t(vid[t * 3])]; + const Vec3f n2 = (pos[size_t(vid[t * 3 + 1])] - a).cross(pos[size_t(vid[t * 3 + 2])] - a); + // Compared against the frame this vertex carried before the move: a triangle that + // flips or collapses means the move crossed a neighbour. + if (n2.squaredNorm() <= 0.f || n2.normalized().dot(nrm[v]) < 0.f) + ok = false; + } + if (ok) { + ++applied; + ever_moved[v] = 1; + } else { + pos[v] = old; + ++result.rejected; + } + } + if (applied == 0) + break; + rebuild_frames(); + } + + for (size_t v = 0; v < nv; ++v) + if (ever_moved[v]) + ++result.moved; + + // Write the relocated positions back to every copy, and rebuild the per-face normals. + for (size_t i = 0; i < count; ++i) + result.geometry.pos[i] = pos[size_t(vid[i])]; + for (size_t t = 0; t < tri_ct; ++t) { + Vec3f n = (result.geometry.pos[t * 3 + 1] - result.geometry.pos[t * 3]) + .cross(result.geometry.pos[t * 3 + 2] - result.geometry.pos[t * 3]); + const float len = n.norm(); + n = (len > 0.f) ? Vec3f(n / len) : Vec3f(0.f, 0.f, 1.f); + result.geometry.nrm[t * 3] = result.geometry.nrm[t * 3 + 1] = result.geometry.nrm[t * 3 + 2] = n; + } + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeRelocate.hpp b/src/libslic3r/TextureBake/TextureBakeRelocate.hpp new file mode 100644 index 0000000000..cd674d03ae --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeRelocate.hpp @@ -0,0 +1,72 @@ +#pragma once + +// Tangential relocation: slide vertices along the surface so triangle edges land on the height map's +// own edges, before any displacement happens. +// +// Displacement moves vertices along the normal only, so a step in the height map - the wall of a +// mortar groove, the rim of an embossed shape - is reproduced wherever the triangle grid happens to +// fall, as a staircase quantised to triangle boundaries. Refining further only makes the steps +// smaller; it never straightens them, because the edge in the image still does not coincide with any +// edge in the mesh. +// +// This pass fixes the cause rather than the symptom. For a vertex sitting near a step it takes a +// Newton step onto the contour: with h the sampled height and g its tangential gradient, the move +// +// d = -(h - target) * g / |g|^2 +// +// lands on the level set h = target to first order. The ring of vertices nearest each step therefore +// snaps onto it, the triangle edges between them follow the step, and the displaced result has a +// straight wall instead of a sawtooth. +// +// Vertices in flat regions have no gradient to speak of and are left alone, so the pass costs nothing +// where there is nothing to align. + +#include +#include +#include + +#include "TextureBakeDisplace.hpp" +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +struct RelocateSettings +{ + // Passes. Each is a Newton step, so a couple converge for vertices that start reasonably close; + // more mainly helps ones that begin further away. + int iterations = 3; + + // How far a vertex may move in one pass, as a fraction of the mean length of its incident edges. + // Below a half it cannot pass a neighbour, which is what keeps the triangulation valid without + // needing a full topological check. + double max_move_fraction = 0.35; + + // A vertex is only pulled when the height varies enough across its own footprint to mean + // something - as a fraction of the height range over the whole patch. Below this the gradient is + // noise, and chasing it would scramble flat regions. + double min_gradient_fraction = 0.05; + + // The level to snap onto, as a position in the sampled height range: 0.5 is midway between the + // lowest and highest point of the relief, which is where the wall of a step is steepest. + double contour_level = 0.5; + + // Sampling offset for the finite-difference gradient, as a fraction of the local edge length. + double gradient_step_fraction = 0.25; +}; + +struct RelocateResult +{ + TriSoup geometry; + size_t moved = 0; // positions that were relocated at least once + size_t rejected = 0; // moves refused because a triangle would have inverted +}; + +// `locked`, when non-empty, has one entry per input triangle; a vertex touching a locked triangle is +// never moved, so an excluded region keeps its exact vertex positions. +RelocateResult relocate_to_contours(const TriSoup &geometry, const HeightSampleFn &sample, + const RelocateSettings &settings = {}, + const std::vector &locked = {}); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp index 7a0082e257..0a5932e962 100644 --- a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp @@ -4,6 +4,10 @@ #include #include +#include +#include +#include + namespace Slic3r { namespace TextureBake { @@ -104,13 +108,18 @@ PassResult subdivide_pass(VertStore &verts, const std::vector &indices, dou return out; // changed stays false: nothing left to refine } - // Step 1.5. - size_t predicted = 0; - for (size_t t = 0; t < tri_count; ++t) { - const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; - const int n = int(is_marked(a, b)) + int(is_marked(b, c)) + int(is_marked(c, a)); - predicted += (n == 0) ? 1 : size_t(n + 1); - } + // Step 1.5. Read-only against the finished mark set, so it reduces in parallel. + const size_t predicted = tbb::parallel_reduce( + tbb::blocked_range(0, tri_count), size_t(0), + [&](const tbb::blocked_range &range, size_t acc) { + for (size_t t = range.begin(); t < range.end(); ++t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const int n = int(is_marked(a, b)) + int(is_marked(b, c)) + int(is_marked(c, a)); + acc += (n == 0) ? 1 : size_t(n + 1); + } + return acc; + }, + std::plus()); if (predicted > size_t(safety_cap)) { out.indices = indices; out.face_excluded = face_excluded; @@ -255,18 +264,20 @@ IndexedMesh to_indexed(const TriSoup &geometry) // Per-face normals: unit for the angle test, raw for the area-weighted accumulation. std::vector face_unit(n), face_raw(n); - for (size_t t = 0; t + 2 < n; t += 3) { - const Vec3d a = geometry.pos[t].cast(); - const Vec3d b = geometry.pos[t + 1].cast(); - const Vec3d c = geometry.pos[t + 2].cast(); - const Vec3d r = (b - a).cross(c - a); - const double len = r.norm(); - const Vec3d u = (len > 0.0) ? Vec3d(r / len) : Vec3d(0.0, 0.0, 1.0); - for (int v = 0; v < 3; ++v) { - face_unit[t + size_t(v)] = u; - face_raw[t + size_t(v)] = r; + tbb::parallel_for(tbb::blocked_range(0, n / 3), [&](const tbb::blocked_range &range) { + for (size_t f = range.begin(); f < range.end(); ++f) { + const size_t t = f * 3; + const Vec3d a = geometry.pos[t].cast(); + const Vec3d r = (geometry.pos[t + 1].cast() - a).cross( + geometry.pos[t + 2].cast() - a); + const double len = r.norm(); + const Vec3d u = (len > 0.0) ? Vec3d(r / len) : Vec3d(0.0, 0.0, 1.0); + for (int v = 0; v < 3; ++v) { + face_unit[t + size_t(v)] = u; + face_raw[t + size_t(v)] = r; + } } - } + }); out.indices.resize(n); out.pos_canon_map = QuantizedPointMap(WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 4fcf00b462..a129b503ff 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -1405,12 +1405,17 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set settings.regularize = options.v2_regularize; settings.max_triangles = size_t(std::max(0, options.v2_max_triangles_k)) * 1000; settings.preserve_untextured = true; + settings.clamp_below_plate = options.v2_clamp_below_plate; + settings.relocate = options.v2_relocate; // The sampler already returns millimetres, so the displacement stage must not scale it again. settings.displace.amplitude = 1.f; settings.displace.symmetric = false; // The paint decides what moves here, so the angle limits stay off. settings.displace.bottom_angle_limit = 0.f; settings.displace.top_angle_limit = 0.f; + // The sampler above takes the smooth normal, never the smoothed blend normal, so computing the + // latter would build a full adjacency graph and run its iterations for a value nothing reads. + settings.displace.blend_normal_smoothing = 0; TextureBake::DisplaceBounds bounds; bounds.min = bounds.max = mesh.vertices.empty() ? Vec3f::Zero() : mesh.vertices.front(); diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index c187c3be48..63cb443420 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -373,6 +373,13 @@ struct TextureDisplacementOptions float v2_refine_mm = 0.3f; bool v2_regularize = false; int v2_max_triangles_k = 750; // 0 skips simplification, which is worth comparing on its own + // Stop displacement pushing geometry through the build plate. Only what would end up below the + // model's own bottom is moved; downward relief above that is untouched. + bool v2_clamp_below_plate = false; + // Slide vertices onto the texture's own edges before displacing. Displacement moves vertices along + // the normal only, so without this a step in the image is reproduced wherever the triangle grid + // happens to fall, as a staircase rather than a straight wall. + bool v2_relocate = false; // Colour, all of which belongs to the stack rather than to any one layer: it is about how the // printer will realise the colours, not about which image they came from. @@ -390,8 +397,8 @@ struct TextureDisplacementOptions { int mix_mode = int(color_mix_mode); ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border, - pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k, color_mix_enabled, mix_mode, - color_despeckle); + pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k, v2_clamp_below_plate, + v2_relocate, color_mix_enabled, mix_mode, color_despeckle); color_mix_mode = ColorMixMode(mix_mode); } }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 8de71180f0..70e4b83e7f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -3828,8 +3828,13 @@ bool GLGizmoTextureDisplacement::plan_remesh(const indexed_triangle_set &src, fl target_edge_mm = float(budget_edge); } - indexed_triangle_set remeshed = - MeshBoolean::cgal::remesh_isotropic(src, double(target_edge_mm), 3, double(sharp_angle_deg)); + // Five iterations with three relaxation passes each, rather than CGAL's three-and-one. Splitting + // and collapsing bring edge lengths near the target but leave the vertices where they fell, so it + // is the relaxation count that decides how even the result looks - and three passes in total was + // nowhere near enough. Fifteen costs proportionally more, but only on an explicit Remesh. + indexed_triangle_set remeshed = MeshBoolean::cgal::remesh_isotropic( + src, double(target_edge_mm), /* iterations */ 5, double(sharp_angle_deg), + /* relaxation steps */ 3); // remesh_isotropic() signals failure by handing the input straight back, so compare against it // structurally. Vertex count alone is not enough: a remesh that only redistributes triangles at // roughly the current density legitimately lands on the same count, and treating that as failure @@ -5405,6 +5410,24 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "simplification off, which is worth comparing on its own."), m_imgui->scaled(20.f)); ImGui::PopItemWidth(); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Keep above build plate").c_str(), + &opts.v2_clamp_below_plate); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Push anything the displacement drove below the build plate back up to it. " + "Only geometry that would end up under the model's own bottom is moved - " + "relief that goes downward but stays above the plate is left as it is."), + m_imgui->scaled(20.f)); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Align mesh to texture edges").c_str(), + &opts.v2_relocate); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Slide vertices sideways onto the edges in the texture before displacing them. " + "Displacement can only move vertices up and down, so without this a sharp step " + "in the image lands wherever the triangles happen to be and comes out as a " + "staircase. Moving the vertices onto the step first gives a straight wall at the " + "same triangle count."), + m_imgui->scaled(20.f)); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize); if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Collapse the thin triangles refinement inherits from the model's own " From 8bf80a2a4675504d44b4a348ea384f17d7758f0b Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 9 Sep 2026 15:25:00 +0200 Subject: [PATCH 26/89] Improve texture displacement smoothing and UV editor framing --- src/libslic3r/TextureDisplacement.cpp | 157 +++++++++++++++--- src/libslic3r/TextureDisplacement.hpp | 18 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 89 +++++++++- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 11 ++ .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 3 +- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 3 + .../Jobs/TextureDisplacementPreviewJob.cpp | 2 +- .../Jobs/TextureDisplacementPreviewJob.hpp | 3 + src/slic3r/GUI/UVEditorCanvas.cpp | 85 +++++++--- src/slic3r/GUI/UVEditorCanvas.hpp | 6 +- tests/libslic3r/test_texture_displacement.cpp | 120 ++++++++++++- 11 files changed, 445 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 4fcf00b462..df87969581 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -123,10 +123,10 @@ DecodedTextureCache g_decoded_texture_cache; } // namespace namespace { -// A few passes of a separable box blur approximate a Gaussian, cheaply. `radius` is in pixels; 0 is -// a no-op. Wraps at the edges so a tiling height map stays seamless after smoothing. Operates on the -// grayscale byte buffer in place. -void smooth_height_pixels(std::vector &pixels, int width, int height, int radius) +// A few passes of a separable box blur approximate a Gaussian, cheaply. `radius` is in whole texels; +// 0 is a no-op. Wraps at the edges so a tiling height map stays seamless after smoothing. Operates on +// the grayscale byte buffer in place. +void smooth_height_pixels_box(std::vector &pixels, int width, int height, int radius) { if (radius <= 0 || width <= 0 || height <= 0 || pixels.size() != size_t(width) * size_t(height)) return; @@ -162,6 +162,28 @@ void smooth_height_pixels(std::vector &pixels, int width, int height, i } } } + +// The same blur with a *continuous* radius, which is what the Smoothing slider drives. +// +// A box blur can only work in whole texels, so mapping the slider straight onto a rounded radius +// made it move in visible jumps - and its very first step off zero was a full one-texel blur rather +// than a hint of one, which is what made the control feel like it switched on rather than ramped up. +// Blur at the next whole texel up and cross-fade the raw image back in by the fraction left over: +// below one texel that fade *is* the sub-texel kernel, and above it it turns each integer step into +// a continuous ramp. +void smooth_height_pixels(std::vector &pixels, int width, int height, float radius) +{ + if (radius <= 0.f || width <= 0 || height <= 0 || pixels.size() != size_t(width) * size_t(height)) + return; + + const int whole = std::max(1, int(std::ceil(radius))); + const float mix = std::clamp(radius / float(whole), 0.f, 1.f); + const std::vector raw = (mix < 0.999f) ? pixels : std::vector{}; + smooth_height_pixels_box(pixels, width, height, whole); + if (!raw.empty()) + for (size_t i = 0; i < pixels.size(); ++i) + pixels[i] = uint8_t(std::lround(float(raw[i]) + (float(pixels[i]) - float(raw[i])) * mix)); +} } // namespace DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer) @@ -231,11 +253,14 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer entries[key] = { std::weak_ptr>(layer.image_data), result }; } - // Smoothing radius scales with the texture so the effect is resolution-independent; capped so the - // blur stays affordable on large maps. + // Smoothing radius scales with the texture so the same slider value blurs the same *fraction* of + // the image whatever resolution it came in at, and stays continuous in the slider - see + // smooth_height_pixels(). The cap is a cost limit, not part of the mapping: the blur is + // O(width * height * radius) per pass, so a large map with the slider at the top would otherwise + // stall every preview rebuild. if (layer.smoothing > 0.f) { - const int max_radius = std::clamp(int(std::lround(0.02f * std::min(result.width, result.height))), 1, 32); - const int radius = std::max(1, int(std::lround(layer.smoothing * float(max_radius)))); + const float span = 0.05f * float(std::min(result.width, result.height)); + const float radius = std::clamp(layer.smoothing, 0.f, 1.f) * std::min(span, 48.f); smooth_height_pixels(result.pixels, result.width, result.height, radius); // Colour gets the same blur, per channel. It is the same knob for the same reason: detail in // the image finer than the mesh can carry is noise either way, and low-passing it here is the @@ -1372,7 +1397,8 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set const std::vector &layers, const TextureDisplacementFacetsData &facets_data, const TextureDisplacementOptions &options, - const DisplacementProgressFn &progress) + const DisplacementProgressFn &progress, + bool flip_normals) { HeightFieldSampler combined = make_combined_displacement_sampler(mesh, layers, facets_data); if (!combined) @@ -1424,11 +1450,21 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set return combined(pos, smooth_normal); }; + // The pipeline takes its displacement direction from the soup's winding, so a mirrored placement + // would drive the whole relief inwards. The paint masks were read off `mesh` above, against its + // own vertex order, so the winding can only be turned round after that - here, on the copy that + // becomes the soup - and has to be turned back on the way out, since the caller undoes the same + // mirror when it maps the result back into the volume's coordinates. + indexed_triangle_set oriented = mesh; + if (flip_normals) + for (stl_triangle_vertex_indices &t : oriented.indices) + std::swap(t[1], t[2]); + // 0 means no simplification, i.e. Bake mode. const TextureBake::PipelineMode mode = settings.max_triangles > 0 ? TextureBake::PipelineMode::Export : TextureBake::PipelineMode::Bake; TextureBake::PipelineResult result = TextureBake::run_pipeline( - TextureBake::to_soup(mesh, excluded), sample, settings, bounds, mode, excluded, + TextureBake::to_soup(oriented, excluded), sample, settings, bounds, mode, excluded, [&progress](const char *, double f) { return !progress || progress(std::clamp(int(f * 100.0), 0, 99)); }); @@ -1436,17 +1472,29 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set return {}; indexed_triangle_set out = TextureBake::to_indexed_triangle_set(result.geometry); - return out.indices.empty() ? mesh : out; + if (out.indices.empty()) + return mesh; + if (flip_normals) + for (stl_triangle_vertex_indices &t : out.indices) + std::swap(t[1], t[2]); + return out; } } // namespace -indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, - const std::vector &layers, - const TextureDisplacementFacetsData &facets_data, - const TextureDisplacementOptions &options, - const DisplacementProgressFn &progress, - const TextureColorRequest *color) +// The bake proper. Runs entirely in whatever space `base_mesh` is given in; the public entry point +// below is what puts it in world space and brings the result back. +// `flip_normals` says the mesh is wound the opposite way round from its outward direction, which is +// what a mirroring world transform leaves behind: the positions are right, but every normal derived +// from the winding points into the model. See build_texture_displacement(). +static indexed_triangle_set build_texture_displacement_in_place( + const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress, + const TextureColorRequest *color, + bool flip_normals) { // Returns true to keep going. An aborted run returns {} (see the header): an empty mesh is the // one result no caller can mistake for a finished bake and commit onto the volume. @@ -1465,7 +1513,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set return mesh; if (options.pipeline_v2) - return build_texture_displacement_v2(mesh, layers, facets_data, options, progress); + return build_texture_displacement_v2(mesh, layers, facets_data, options, progress, flip_normals); // Layers are combined in slot order, like stacked layers in an image editor: each one folds its // own displacement into the running total via its blend mode (see TextureBlendMode). @@ -1518,6 +1566,14 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set } } + // Both normal passes above read their direction out of the triangle winding, so a mirrored + // placement leaves every one of them pointing into the model - the relief would be carved rather + // than raised. Correct them once, here, where every later stage (displacement direction, the + // planar/triplanar projection axes, the cylinder axis) picks them up already right. + if (flip_normals) + for (Vec3f &n : vertex_normals) + n = -n; + if (!report(5)) return {}; @@ -1782,6 +1838,11 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (!any_displacement) return mesh; + // The model's own resting plane, taken before anything moves - see the clamp after smoothing. + float resting_z = std::numeric_limits::max(); + for (const Vec3f &v : mesh.vertices) + resting_z = std::min(resting_z, v.z()); + for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) if (displaced[vi]) mesh.vertices[vi] += vertex_normals[vi] * displacement[vi]; @@ -1807,6 +1868,18 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set }) : DisplacementProgressFn{}); } + // Nothing driven below the model's own resting plane can be printed: it is either through the + // build plate or, once the slicer drops the part back down onto it, holding the whole model up in + // the air. Push it back up to the plane. Only vertices the displacement actually moved are + // eligible - untouched geometry is already exactly where it started - and only those that ended + // up below it, so downward relief that stays clear of the plate is left alone. Runs in world + // space (see build_texture_displacement()), so this really is the plate and not some scaled + // stand-in for it. + if (resting_z < std::numeric_limits::max()) + for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) + if (displaced[vi] && mesh.vertices[vi].z() < resting_z) + mesh.vertices[vi].z() = resting_z; + if (!report(99)) return {}; if (want_color) { @@ -1835,6 +1908,51 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set return mesh; } +indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress, + const TextureColorRequest *color, + const Transform3d &volume_to_world) +{ + // An untransformed volume on an untransformed instance is by far the common case, and the round + // trip costs two matrix multiplies per vertex on a mesh that can carry millions of them - so take + // the identity out of the way rather than paying for it. + if (volume_to_world.matrix().isApprox(Transform3d::Identity().matrix())) + return build_texture_displacement_in_place(base_mesh, layers, facets_data, options, progress, color, false); + + const Transform3d to_local = volume_to_world.inverse(); + // A mirroring placement leaves the positions correct but every winding-derived normal pointing + // the wrong way. The winding itself is deliberately *not* touched here: the paint masks encode + // each split triangle against its own vertex order, so reordering a triangle's vertices would + // mirror the paint inside it. The bake is told instead, and negates the normals it computes. + const bool mirrored = volume_to_world.linear().determinant() < 0.0; + + indexed_triangle_set world = base_mesh; + for (Vec3f &v : world.vertices) + v = (volume_to_world * v.cast()).cast(); + + indexed_triangle_set out = + build_texture_displacement_in_place(world, layers, facets_data, options, progress, color, mirrored); + // A cancelled run returns {} and must stay {} - an empty mesh is the signal the caller checks + // before committing anything onto the volume. + if (out.vertices.empty()) + return out; + + for (Vec3f &v : out.vertices) + v = (to_local * v.cast()).cast(); + return out; +} + +Transform3d texture_displacement_volume_to_world(const ModelVolume &volume) +{ + const ModelObject *object = volume.get_object(); + if (object == nullptr || object->instances.empty() || object->instances.front() == nullptr) + return volume.get_matrix(); + return object->instances.front()->get_matrix() * volume.get_matrix(); +} + indexed_triangle_set build_texture_displacement(const ModelVolume &volume) { TextureDisplacementFacetsData facets_data; @@ -1842,7 +1960,8 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume) facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data, - volume.texture_displacement_options); + volume.texture_displacement_options, {}, nullptr, + texture_displacement_volume_to_world(volume)); } void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector &movable, float strength, diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index c187c3be48..f9eabb4d82 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -709,12 +709,28 @@ struct TextureColorRequest // straight to a TriangleSelector without a second mapping table. std::vector *out_triangle = nullptr; }; +// Where the volume sits on the plate: its instance transform times its own volume transform, i.e. +// mesh coordinates -> world millimetres. +// +// Every number the user sets is in real millimetres on the printed part - "Depth (mm)", "Tile size +// (mm)" - and the build plate is a world plane, so the bake runs in world space and transforms the +// result back. Doing it in the volume's own coordinates instead made a scaled instance stretch both +// the relief depth and the tiling by the scale factor, and under a non-uniform scale it also +// displaced along the wrong direction: a mesh normal maps to the world normal through the inverse +// transpose, not through the transform itself, so the relief leaned. Identity - the default - is +// exactly the old behaviour and is what an untransformed volume gives. indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, const TextureDisplacementOptions &options = {}, const DisplacementProgressFn &progress = {}, - const TextureColorRequest *color = nullptr); + const TextureColorRequest *color = nullptr, + const Transform3d &volume_to_world = Transform3d::Identity()); + +// `volume`'s mesh coordinates -> world millimetres: its first instance's transform times its own. +// The mesh is shared by every instance, so a multi-instance object can only be baked for one of +// them; the first is what the gizmo edits against. Identity when the volume has no object yet. +Transform3d texture_displacement_volume_to_world(const ModelVolume &volume); // Convenience overload for main-thread callers: extracts the mesh/layers/paint data/options from // `volume` and forwards to the overload above. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 8de71180f0..d31e9a8225 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -959,10 +959,30 @@ float GLGizmoTextureDisplacement::layer_texture_aspect(const TextureDisplacement return (tex.width > 0 && tex.height > 0) ? float(tex.width) / float(tex.height) : 1.f; } -std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const indexed_triangle_set &patch, +indexed_triangle_set GLGizmoTextureDisplacement::patch_in_world(const indexed_triangle_set &patch) const +{ + const ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return patch; + const Transform3d to_world = texture_displacement_volume_to_world(*mv); + if (to_world.matrix().isApprox(Transform3d::Identity().matrix())) + return patch; + + indexed_triangle_set world = patch; + for (Vec3f &v : world.vertices) + v = (to_world * v.cast()).cast(); + return world; +} + +std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer) const { - const float aspect = layer_texture_aspect(layer); + // The bake maps the texture in world millimetres, so "Tile size (mm)" means the same thing on a + // scaled instance as it does on an untouched one (see build_texture_displacement()). Everything + // that has to agree with the bake - the fast preview's uvs, the checker/distortion overlays - + // therefore has to project from the same world positions, not from the volume's own. + const indexed_triangle_set patch = patch_in_world(local_patch); + const float aspect = layer_texture_aspect(layer); if (layer.projection_method == TextureProjectionMethod::LSCM) { // compute_lscm_uvs() returns the unwrap's own (raw, mm) coordinates with the island placement // folded in - it does *not* apply the layer's tiling/rotation/offset. The bake applies those @@ -1654,6 +1674,7 @@ void GLGizmoTextureDisplacement::queue_preview_job() input.base_mesh = mv->mesh().its; input.layers = mv->texture_displacement_layers; input.options = mv->texture_displacement_options; + input.volume_to_world = texture_displacement_volume_to_world(*mv); for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = mv->texture_displacement_facet(i).get_data(); // Captured here rather than read in the handler: get_extruders_colors() is main-thread state and @@ -1774,7 +1795,9 @@ void GLGizmoTextureDisplacement::update_uv_editor() bool unwrap_changed = false; if (m_uv_unwrap_pending) { m_uv_unwrap_pending = false; - const indexed_triangle_set patch = extract_painted_patch(mv->mesh().its, state.facets); + // World millimetres, the space the bake unwraps in - otherwise the pane would lay the islands + // out at the volume's own scale and show the texture at a different size than it bakes at. + const indexed_triangle_set patch = patch_in_world(extract_painted_patch(mv->mesh().its, state.facets)); if (patch.indices.empty()) { m_uv_editor_state = UVEditorState{}; m_uv_editor_unwrap = PatchUnwrap{}; @@ -4418,8 +4441,6 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, _L("Distortion"), _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3; ImGui::SameLine(); - ImGui::Dummy(ImVec2(m_imgui->scaled(0.6f), 0.f)); - ImGui::SameLine(); if (icon_toggle(705, "texture_displacement_wireframe.svg", m_wireframe_overlay, _L("Wireframe"), _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true; @@ -4497,8 +4518,61 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // worth of controls the panel otherwise runs off the bottom of the screen, and there was // nothing to tell "settings that belong to this layer" apart from "settings that belong to // the tool". + // ImGui's stock scrollbar is a wide, square-cornered slab in a tinted track - against this + // flat panel it reads as a raw widget bolted onto the edge. Slim it to a rounded thumb over + // an invisible track. The narrower bar also gives back content width: a scrollbar eats it + // from the child's content region, which is what was clipping "Tile size (mm)" mid-word. + const float scrollbar_w = m_imgui->scaled(0.5f); + const ImVec4 grab = wxGetApp().dark_mode() ? ImVec4(1.f, 1.f, 1.f, 0.26f) : ImVec4(0.f, 0.f, 0.f, 0.26f); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, scrollbar_w); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding, 0.5f * scrollbar_w); + // The panel-wide 20 px window padding is meant for the panel; inside a region that is itself + // already inset and tinted it is just a second margin, and it was spending on empty gutters + // the width the layer rows needed. Vertical padding is left alone - it separates the first + // layer's header from the region's top edge. + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(m_imgui->scaled(0.5f), ImGui::GetStyle().WindowPadding.y)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, grab); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(grab.x, grab.y, grab.z, 0.45f)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(grab.x, grab.y, grab.z, 0.65f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.f, 1.f, 1.f, 0.04f)); - ImGui::BeginChild("##texture_layers", ImVec2(0.f, m_imgui->scaled(20.f)), true); + // NoScrollWithMouse: the wheel is handled below so the scroll can be eased instead of + // teleporting five text lines per notch, which on blocks this tall lost the reader's place. + ImGui::BeginChild("##texture_layers", ImVec2(0.f, m_imgui->scaled(20.f)), true, + ImGuiWindowFlags_NoScrollWithMouse); + { + ImGuiIO &io = ImGui::GetIO(); + const float scroll_now = ImGui::GetScrollY(); + const float scroll_max = ImGui::GetScrollMaxY(); + + // Anything that moved the scroll without us - dragging the grab, a keyboard/gamepad nav + // step, the content shrinking under a clamped offset - has to re-seed the target, or the + // easing below would immediately drag the view back to where it last animated to. + if (m_layer_scroll_applied < 0.f || std::abs(scroll_now - m_layer_scroll_applied) > 0.5f) + m_layer_scroll_target = scroll_now; + + if (io.MouseWheel != 0.f && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) + m_layer_scroll_target -= io.MouseWheel * ImGui::GetFontSize() * 4.f; + // Whole pixels: ImGui floors whatever SetScrollY() is given, so a fractional target could + // never be reached and the "still gliding" test below would stay true forever, repainting + // the canvas for good. + m_layer_scroll_target = std::floor(std::clamp(m_layer_scroll_target, 0.f, scroll_max)); + + const float delta = m_layer_scroll_target - scroll_now; + if (std::abs(delta) >= 1.f) { + // Exponential ease, formulated against the frame time so the glide takes the same wall + // time whether the canvas is running at 30 or 144 fps. The last sub-pixel step would be + // floored away, so land on the target outright once the remainder is that small. + const float t = 1.f - std::exp(-20.f * std::clamp(io.DeltaTime, 1.f / 240.f, 1.f / 15.f)); + const float step = (std::abs(delta * t) < 1.f) ? delta : delta * t; + const float next = std::floor(scroll_now + step); + ImGui::SetScrollY(next); + m_layer_scroll_applied = next; + m_parent.set_as_dirty(); // nothing else would redraw mid-glide once the mouse stops + } else { + m_layer_scroll_applied = scroll_now; + } + } for (size_t li = 0; li < ordered.size(); ++li) { TextureDisplacementLayer *layer = ordered[li]; @@ -5017,7 +5091,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } ImGui::EndChild(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); + ImGui::PopStyleVar(3); if (slot_to_remove >= 0) remove_texture_layer(slot_to_remove); // deferred: see slot_to_remove's declaration diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 05d2fa20eb..1b9d70c4f5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -621,6 +621,10 @@ private: // the shader projects on its own. Shared by the bump preview and the UV-check overlay. std::vector compute_layer_vertex_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) const; + // `patch` with its vertices moved into world millimetres - the space the bake maps the texture in + // (see build_texture_displacement()). Returned by value because the caller usually still needs the + // original: the patch doubles as render geometry, which is drawn through the volume's own matrix. + indexed_triangle_set patch_in_world(const indexed_triangle_set &patch) const; // UV-check overlay drawn over the painted patch to sanity-check the unwrap (#13/#14). Built by // rebuild_uvcheck_mesh(), drawn by render_uvcheck_mesh() with the "texture_displacement_uvcheck" @@ -751,6 +755,13 @@ private: // open/close within a session, so the choice sticks while working. bool m_undocked = false; + // Smooth scrolling for the layer-stack child region. ImGui jumps a fixed number of lines per + // wheel notch, which on a list of tall per-layer blocks reads as a hard jolt rather than a + // scroll. The wheel is intercepted (ImGuiWindowFlags_NoScrollWithMouse) and moves a *target* + // offset instead; the real scroll is eased toward it over the following frames. + float m_layer_scroll_target = 0.f; + float m_layer_scroll_applied = -1.f; // what the easing wrote last frame; <0 until the first one + // See the "Adjust Texture" block of private methods above. bool m_adjust_texture_mode = false; bool m_adjust_anchor_valid = false; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index eac60eb392..05df414ca5 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -61,7 +61,7 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) } return true; }, - color)); + color, m_input.volume_to_world)); // Always finish at 100: this is what closes the notification. Reported even on cancel, where // build_texture_displacement() returns an empty mesh and finalize() commits nothing. @@ -152,6 +152,7 @@ void queue_texture_displacement_bake(const ModelVolume &volume, const TextureCol input.base_mesh = volume.mesh().its; input.layers = volume.texture_displacement_layers; input.options = volume.texture_displacement_options; + input.volume_to_world = texture_displacement_volume_to_world(volume); for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp index cd3772ae47..753dd20742 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -23,6 +23,9 @@ struct TextureDisplacementBakeInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Mesh coordinates -> world millimetres. Captured here with everything else so the worker never + // reaches back into the live Model for it. See build_texture_displacement(). + Transform3d volume_to_world = Transform3d::Identity(); // Captured on the main thread. Empty unless some layer is colouring, in which case the bake also // writes the volume's mmu_segmentation_facets - the same per-triangle filament assignment the MMU // paint gizmo writes - alongside the displaced geometry. diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index adde26f884..013ea79750 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -43,7 +43,7 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) (!m_current_generation || m_current_generation->load() == m_generation); }, - color); + color, m_input.volume_to_world); } void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr &eptr) diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp index 2a67b8c7cc..312cf6593e 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -23,6 +23,9 @@ struct TextureDisplacementPreviewInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Mesh coordinates -> world millimetres, so the preview is displaced in the same space the bake + // is and the two cannot disagree. See build_texture_displacement(). + Transform3d volume_to_world = Transform3d::Identity(); // Empty unless a layer is colouring, in which case the preview reports the filament per triangle // alongside the mesh, so the Normal view shows what the bake will produce - interleaving included. TextureColorSettings color; diff --git a/src/slic3r/GUI/UVEditorCanvas.cpp b/src/slic3r/GUI/UVEditorCanvas.cpp index 25d737f2bb..7ab847023d 100644 --- a/src/slic3r/GUI/UVEditorCanvas.cpp +++ b/src/slic3r/GUI/UVEditorCanvas.cpp @@ -39,6 +39,25 @@ const ColorRGBA UV_COLOR_DIAL = { 1.f, 0.85f, 0.2f, 0.9f }; // rota constexpr float SNAP_PIXELS = 28.f; // how close a boundary vertex has to come before it sticks (#2) +// wxWidgets reports this canvas' size in *logical* points, while the GL drawable behind it is sized +// in device pixels. On the backends where those differ under HiDPI (the same pair GLCanvas3D guards +// its RetinaHelper with) a viewport built straight from GetSize() covers only the bottom-left +// 1/scale of the drawable, which is exactly where the whole editor ended up drawn, shrunken. +// Mouse coordinates arrive in logical points, so only the viewport needs converting - every other +// GetSize() use here is compared against event coordinates and must stay logical. +wxSize gl_drawable_size(const wxWindow *win, const wxSize &logical_size) +{ +#if defined(__APPLE__) || defined(__WXGTK3__) + const double scale = (win != nullptr) ? win->GetContentScaleFactor() : 1.0; + if (scale > 0.0) + return wxSize(std::max(1, int(std::lround(logical_size.GetWidth() * scale))), + std::max(1, int(std::lround(logical_size.GetHeight() * scale)))); +#else + (void) win; +#endif + return wxSize(std::max(1, logical_size.GetWidth()), std::max(1, logical_size.GetHeight())); +} + // The pixel format this canvas is created with has to match the one the app's single shared // wxGLContext was created against (that of View3D's canvas, from OpenGLManager::create_wxglcanvas()), // so this mirrors that attribute list *including its multisampling*: WGL requires the HDC passed to @@ -333,13 +352,37 @@ void UVEditorCanvas::content_bounds(Vec2f &min_uv, Vec2f &max_uv) const } } +void UVEditorCanvas::framed_bounds(Vec2f &min_uv, Vec2f &max_uv) const +{ + content_bounds(min_uv, max_uv); + // With tiling on, the backdrop is snapped out to whole tiles, so it is bigger than the raw + // bounds - and by a different amount on each side. Framing the raw bounds therefore left that + // backdrop visibly off-centre: hanging past one edge of the pane with dead space against the + // other. Frame what is drawn instead. (Tiling off draws only the first tile, which the bounds + // already contain, so there is nothing to snap.) + if (m_tile_enabled) { + min_uv = Vec2f(std::floor(min_uv.x()), std::floor(min_uv.y())); + max_uv = Vec2f(std::ceil(max_uv.x()), std::ceil(max_uv.y())); + } +} + void UVEditorCanvas::fit_view_to_content() { Vec2f min_uv, max_uv; - content_bounds(min_uv, max_uv); + framed_bounds(min_uv, max_uv); - m_pan = 0.5f * (min_uv + max_uv); - m_zoom = std::max(0.5f * (max_uv - min_uv).maxCoeff() * 1.1f, 0.05f); + const wxSize size = GetSize(); + const float aspect = float(std::max(1, size.GetWidth())) / float(std::max(1, size.GetHeight())); + const Vec2f half = 0.5f * (max_uv - min_uv); + + m_pan = 0.5f * (min_uv + max_uv); + // m_zoom is the half-extent shown across the *shorter* pane edge (see view_half_extents()), so + // each axis' required half-extent has to be converted back into that unit before the larger of + // the two is taken. Sizing off the bigger axis alone, as this did, ignores the pane's shape and + // zooms out further than either axis needs on anything but a square pane. The 1.1 leaves a + // margin so the outermost island edge is not flush against the frame. + m_zoom = std::max(1.1f * std::max(half.x() / std::max(aspect, 1.f), half.y() * std::min(aspect, 1.f)), + 0.05f); m_needs_fit = false; } @@ -1073,12 +1116,11 @@ void UVEditorCanvas::rebuild_background_quad() return; Vec2f lo, hi; - content_bounds(lo, hi); if (m_tile_enabled) { - // Snap out to whole tiles. Only cosmetic, but it keeps the backdrop's edge on a tile - // boundary instead of slicing a brick in half at an arbitrary place. - lo = Vec2f(std::floor(lo.x()), std::floor(lo.y())); - hi = Vec2f(std::ceil(hi.x()), std::ceil(hi.y())); + // Whole tiles, so the backdrop's edge lands on a tile boundary instead of slicing a brick in + // half. framed_bounds() applies exactly this, and the view is framed on its result - the two + // must not drift apart or the backdrop stops being centred in the pane. + framed_bounds(lo, hi); } else { // Tiling off: the sampler reads 0 outside the first tile and nothing else exists, so the // backdrop is exactly that one tile (GL_CLAMP_TO_BORDER in render() gives it the same @@ -1165,8 +1207,13 @@ void UVEditorCanvas::render() rebuild_grid(); rebuild_rotation_dial(); - const wxSize size = GetSize(); - glsafe(::glViewport(0, 0, size.GetWidth(), size.GetHeight())); + const wxSize size = GetSize(); // logical points; the on-screen handle sizes below use it + const wxSize viewport = gl_drawable_size(this, size); + glsafe(::glViewport(0, 0, viewport.GetWidth(), viewport.GetHeight())); + // Line widths below are authored in logical points (they are chosen against the same scale the + // hit-test thresholds use), so they take the same logical -> device conversion as the viewport. + const float px_scale = float(viewport.GetWidth()) / float(std::max(1, size.GetWidth())); + const auto line_width = [px_scale](float w) { set_line_width(w * px_scale); }; glsafe(::glClearColor(UV_COLOR_BG.r(), UV_COLOR_BG.g(), UV_COLOR_BG.b(), 1.f)); glsafe(::glClear(GL_COLOR_BUFFER_BIT)); glsafe(::glDisable(GL_DEPTH_TEST)); @@ -1224,7 +1271,7 @@ void UVEditorCanvas::render() } } - set_line_width(1.f); + line_width(1.f); draw(m_grid_glmodel, UV_COLOR_GRID, identity); // The texture's first tile. Always drawn, even with nothing painted, so the pane always has a @@ -1244,7 +1291,7 @@ void UVEditorCanvas::render() tile.add_line(i, (i + 1) % 4); m_tile_outline_glmodel.init_from(std::move(tile)); } - set_line_width(2.f); + line_width(2.f); draw(m_tile_outline_glmodel, UV_COLOR_TILE_OUTLINE, identity); const auto island_matrix = [this, &identity](int c) { @@ -1262,7 +1309,7 @@ void UVEditorCanvas::render() draw(m_island_fill[size_t(c)], is_selected(c) ? UV_COLOR_SEL_FILL : fill, island_matrix(c)); } - set_line_width(1.f); + line_width(1.f); for (int c = 0; c < int(m_island_wireframe.size()); ++c) draw(m_island_wireframe[size_t(c)], is_selected(c) ? UV_COLOR_SEL_WIRE : UV_COLOR_WIRE, island_matrix(c)); @@ -1271,16 +1318,16 @@ void UVEditorCanvas::render() // pane exists to answer. The selected one gets a bold edge (#7). for (int c = 0; c < int(m_island_boundary.size()); ++c) { const bool selected = is_selected(c); - set_line_width(selected ? 3.f : 1.5f); + line_width(selected ? 3.f : 1.5f); draw(m_island_boundary[size_t(c)], selected ? UV_COLOR_SEL_BOUNDARY : UV_COLOR_BOUNDARY, island_matrix(c)); } - set_line_width(1.f); + line_width(1.f); // The rotation protractor, on top of everything while a rotation gesture is live (#11). if (m_dial_glmodel.is_initialized()) { - set_line_width(2.f); + line_width(2.f); draw(m_dial_glmodel, UV_COLOR_DIAL, identity); - set_line_width(1.f); + line_width(1.f); } // Vertex/Edge mode handles: a small square drawn over the picked vertex (or each endpoint of the @@ -1357,10 +1404,10 @@ void UVEditorCanvas::render() m_cursor_sign_glmodel.reset(); if (!sign.is_empty()) m_cursor_sign_glmodel.init_from(std::move(sign)); - set_line_width(3.f); + line_width(3.f); draw(m_cursor_sign_glmodel, removing ? ColorRGBA(1.f, 0.30f, 0.25f, 0.95f) : ColorRGBA(0.35f, 0.90f, 0.45f, 0.95f), identity); - set_line_width(1.f); + line_width(1.f); } // The GL context is shared with the 3D view; leave the bits we touched as we found them. diff --git a/src/slic3r/GUI/UVEditorCanvas.hpp b/src/slic3r/GUI/UVEditorCanvas.hpp index 40397470fd..1fbed837d5 100644 --- a/src/slic3r/GUI/UVEditorCanvas.hpp +++ b/src/slic3r/GUI/UVEditorCanvas.hpp @@ -155,7 +155,11 @@ private: // The UV region worth looking at: every island, plus always at least the texture's first tile, so // there is something sensibly framed even before anything is painted. void content_bounds(Vec2f &min_uv, Vec2f &max_uv) const; - // Frames content_bounds(). Bound to Home, and run once each time an unwrap first appears. + // What is actually *drawn*, which is content_bounds() snapped out to whole tiles whenever the + // backdrop tiles (see rebuild_background_quad()). Both the framing and the backdrop go through + // this so they cannot disagree. + void framed_bounds(Vec2f &min_uv, Vec2f &max_uv) const; + // Frames framed_bounds(). Bound to Home, and run once each time an unwrap first appears. void fit_view_to_content(); // Half-extents of the visible UV region. Split out because both rendering and every mouse diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 6d6fda7ca1..edaa5f9d9f 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -61,6 +61,19 @@ static std::shared_ptr> make_checkerboard_png(size_t return std::make_shared>(std::move(bytes)); } +// The bake never drives relief below the model's own resting plane (see build_texture_displacement()), +// so on a fully painted closed solid the vertices already sitting on that plane - a cube's four bottom +// corners, whose normals point downwards - are clamped in Z and do not move by the full depth. The +// tests below are about the displacement maths, so they check the vertices the clamp cannot touch; +// the clamp itself has its own test. +static bool above_resting_plane(const indexed_triangle_set &mesh, size_t vi) +{ + float bottom = std::numeric_limits::max(); + for (const Vec3f &v : mesh.vertices) + bottom = std::min(bottom, v.z()); + return mesh.vertices[vi].z() > bottom + 1e-4f; +} + TEST_CASE("TextureDisplacement: decode_height_texture round-trips an 8-bit grayscale PNG", "[TextureDisplacement]") { TextureDisplacementLayer layer; @@ -110,6 +123,8 @@ TEST_CASE("TextureDisplacement: fully painting a mesh displaces every vertex alo REQUIRE(result.vertices.size() == cube.vertices.size()); for (size_t i = 0; i < cube.vertices.size(); ++i) { + if (!above_resting_plane(cube, i)) + continue; const float moved = (result.vertices[i] - cube.vertices[i]).norm(); CHECK_THAT(moved, WithinAbs(layer.depth_mm, 1e-3f)); } @@ -154,7 +169,8 @@ TEST_CASE("TextureDisplacement: a second layer over the same area is applied too REQUIRE(result.vertices.size() == cube.vertices.size()); REQUIRE(result.indices.size() == cube.indices.size()); for (size_t i = 0; i < cube.vertices.size(); ++i) - CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(1.5f, 1e-3f)); // 1.0 + 0.5, not just 1.0 + if (above_resting_plane(cube, i)) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(1.5f, 1e-3f)); // 1.0 + 0.5, not just 1.0 } TEST_CASE("TextureDisplacement: blend modes combine a layer with the ones below it", "[TextureDisplacement]") @@ -189,7 +205,8 @@ TEST_CASE("TextureDisplacement: blend modes combine a layer with the ones below REQUIRE(result.vertices.size() == cube.vertices.size()); for (size_t i = 0; i < cube.vertices.size(); ++i) - CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(std::get<1>(expected), 1e-3f)); + if (above_resting_plane(cube, i)) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(std::get<1>(expected), 1e-3f)); } TEST_CASE("TextureDisplacement: the lowest layer ignores its blend mode", "[TextureDisplacement]") @@ -211,7 +228,104 @@ TEST_CASE("TextureDisplacement: the lowest layer ignores its blend mode", "[Text const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets); for (size_t i = 0; i < cube.vertices.size(); ++i) - CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(2.0f, 1e-3f)); + if (above_resting_plane(cube, i)) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(2.0f, 1e-3f)); +} + +TEST_CASE("TextureDisplacement: relief is never driven below the model's resting plane", "[TextureDisplacement]") +{ + // A fully painted cube displaces outward everywhere, which on the bottom face means straight + // down - through the build plate. That geometry cannot be printed, so it is clamped back up. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(cube); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 2.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); // full depth everywhere + + float bottom = std::numeric_limits::max(); + for (const Vec3f &v : cube.vertices) + bottom = std::min(bottom, v.z()); + + const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets); + + REQUIRE(result.vertices.size() == cube.vertices.size()); + for (const Vec3f &v : result.vertices) + CHECK(v.z() >= bottom - 1e-4f); + + // ...and the clamp is confined to Z: a bottom corner still moves outwards in X and Y by the same + // amount it would have, rather than being pinned wholesale. + bool any_bottom_moved_sideways = false; + for (size_t i = 0; i < cube.vertices.size(); ++i) + if (!above_resting_plane(cube, i) && + (result.vertices[i].head<2>() - cube.vertices[i].head<2>()).norm() > 1e-3f) + any_bottom_moved_sideways = true; + CHECK(any_bottom_moved_sideways); +} + +TEST_CASE("TextureDisplacement: depth is measured in world millimetres, not the volume's own", "[TextureDisplacement]") +{ + // The same painted patch, baked once untransformed and once through a 3x scale. "Depth (mm)" is + // a millimetre on the printed part, so the *world* relief must come out the same height either + // way - which means the vertices of the scaled volume move by a third as much in its own + // coordinates. Baking both in volume space instead gave a 3x deeper relief on the scaled one. + indexed_triangle_set fan; + fan.vertices = { {0.f, 0.f, 1.f}, {1.f, 0.f, 1.f}, {0.f, 1.f, 1.f}, {-1.f, 0.f, 1.f}, {0.f, -1.f, 1.f} }; + fan.indices = { {0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1} }; + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(fan); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 1.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); + + const indexed_triangle_set plain = build_texture_displacement(fan, {layer}, facets); + Transform3d scale3 = Transform3d::Identity(); + scale3.scale(Vec3d(3.0, 3.0, 3.0)); + const indexed_triangle_set scaled = build_texture_displacement(fan, {layer}, facets, {}, {}, nullptr, scale3); + + REQUIRE(plain.vertices.size() == fan.vertices.size()); + REQUIRE(scaled.vertices.size() == fan.vertices.size()); + for (size_t i = 0; i < fan.vertices.size(); ++i) { + CHECK_THAT(plain.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f, 1e-3f)); + // A third of the movement locally is the same movement once the 3x scale is applied. + CHECK_THAT(scaled.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f / 3.0f, 1e-3f)); + } +} + +TEST_CASE("TextureDisplacement: a mirrored placement still raises the relief outwards", "[TextureDisplacement]") +{ + // Mirroring reverses the winding, and every normal in the bake is derived from the winding - so + // without correcting for it the whole relief is carved into the surface instead of raised off it. + indexed_triangle_set fan; + fan.vertices = { {0.f, 0.f, 1.f}, {1.f, 0.f, 1.f}, {0.f, 1.f, 1.f}, {-1.f, 0.f, 1.f}, {0.f, -1.f, 1.f} }; + fan.indices = { {0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1} }; + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(fan); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 1.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); + + // Mirrored in X: the patch's outward direction in world space is still +Z, so in the volume's own + // coordinates the vertices must still move +Z. + Transform3d mirror_x = Transform3d::Identity(); + mirror_x.scale(Vec3d(-1.0, 1.0, 1.0)); + const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets, {}, {}, nullptr, mirror_x); + + REQUIRE(result.vertices.size() == fan.vertices.size()); + for (size_t i = 0; i < fan.vertices.size(); ++i) + CHECK_THAT(result.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f, 1e-3f)); } TEST_CASE("TextureDisplacement: the patch border is displaced by default and pinned on request", "[TextureDisplacement]") From 45d10341c1681046fcadbb732204f5ce60f37501 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH 27/89] TextureBake: edge flips along the height field, stage recorder, faster displace --- src/libslic3r/CMakeLists.txt | 6 + .../TextureBake/TextureBakeDebug.cpp | 219 ++++++++++++++++++ .../TextureBake/TextureBakeDebug.hpp | 123 ++++++++++ .../TextureBake/TextureBakeDecimate.cpp | 57 +++-- .../TextureBake/TextureBakeDisplace.cpp | 29 +-- src/libslic3r/TextureBake/TextureBakeFlip.cpp | 212 +++++++++++++++++ src/libslic3r/TextureBake/TextureBakeFlip.hpp | 57 +++++ .../TextureBake/TextureBakeIndex.hpp | 88 ++++--- .../TextureBake/TextureBakePipeline.cpp | 72 ++++-- .../TextureBake/TextureBakePipeline.hpp | 18 +- .../TextureBake/TextureBakeRegularize.cpp | 59 +++-- .../TextureBake/TextureBakeSubdivide.cpp | 74 +++--- 12 files changed, 871 insertions(+), 143 deletions(-) create mode 100644 src/libslic3r/TextureBake/TextureBakeDebug.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeDebug.hpp create mode 100644 src/libslic3r/TextureBake/TextureBakeFlip.cpp create mode 100644 src/libslic3r/TextureBake/TextureBakeFlip.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 56b2a1168f..7442f122da 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -470,6 +470,12 @@ set(lisbslic3r_sources TextureBake/TextureBakeSubdivide.hpp TextureBake/TextureBakeRegularize.cpp TextureBake/TextureBakeRegularize.hpp + TextureBake/TextureBakeRelocate.cpp + TextureBake/TextureBakeRelocate.hpp + TextureBake/TextureBakeFlip.cpp + TextureBake/TextureBakeFlip.hpp + TextureBake/TextureBakeDebug.cpp + TextureBake/TextureBakeDebug.hpp TextureBake/TextureBakeDisplace.cpp TextureBake/TextureBakeDisplace.hpp TextureBake/TextureBakeDecimate.cpp diff --git a/src/libslic3r/TextureBake/TextureBakeDebug.cpp b/src/libslic3r/TextureBake/TextureBakeDebug.cpp new file mode 100644 index 0000000000..9465080e21 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDebug.cpp @@ -0,0 +1,219 @@ +#include "TextureBakeDebug.hpp" + +#include +#include +#include + +#include +#include + +namespace Slic3r { + +namespace { + +// Half-edge key, low index first so both sides of an edge form the same one. +inline uint64_t edge_key(int a, int b) +{ + const uint32_t lo = uint32_t(std::min(a, b)), hi = uint32_t(std::max(a, b)); + return (uint64_t(lo) << 32) | uint64_t(hi); +} + +} // namespace + +void bake_stage_topology(const BakeStageMesh &mesh, size_t &open_edges, size_t &non_manifold_edges, + size_t °enerate) +{ + open_edges = non_manifold_edges = degenerate = 0; + + // Sorted half-edges rather than a hash map: same answer, but it is one allocation and a sort + // instead of three million node allocations, which on a stage this size is the whole cost. + std::vector keys; + keys.reserve(mesh.indices.size() * 3); + for (const Vec3i32 &t : mesh.indices) { + if (t[0] == t[1] || t[1] == t[2] || t[0] == t[2]) { + ++degenerate; + continue; // a collapsed face has no edges worth counting + } + const Vec3f &a = mesh.vertices[size_t(t[0])]; + if ((mesh.vertices[size_t(t[1])] - a).cross(mesh.vertices[size_t(t[2])] - a).squaredNorm() <= 0.f) + ++degenerate; // zero area but three distinct corners: still counted as an edge carrier + for (int e = 0; e < 3; ++e) + keys.push_back(edge_key(t[e], t[(e + 1) % 3])); + } + std::sort(keys.begin(), keys.end()); + for (size_t i = 0; i < keys.size();) { + size_t j = i + 1; + while (j < keys.size() && keys[j] == keys[i]) + ++j; + const size_t incident = j - i; + if (incident == 1) + ++open_edges; + else if (incident > 2) + ++non_manifold_edges; + i = j; + } +} + +void BakeStageRecorder::finish(BakeStageSnapshot &s) +{ + s.triangles = s.mesh.indices.size(); + s.vertices = s.mesh.vertices.size(); + if (m_check_topology) + bake_stage_topology(s.mesh, s.open_edges, s.non_manifold_edges, s.degenerate); + s.topology_checked = m_check_topology; + if (s.triangles > m_mesh_cap) { + s.mesh_dropped = true; + s.mesh.vertices.clear(); + s.mesh.vertices.shrink_to_fit(); + s.mesh.indices.clear(); + s.mesh.indices.shrink_to_fit(); + } + m_stages.push_back(std::move(s)); +} + +void BakeStageRecorder::capture(const char *name, const TextureBake::TriSoup &soup, double ms, + const std::string &detail) +{ + if (!m_enabled) + return; + + BakeStageSnapshot s; + s.name = name; + s.detail = detail; + s.ms = ms; + + // The pipeline works on non-indexed soup, so welding here is what turns it back into something + // renderable. The geometry grid, matching to_indexed_triangle_set(), so the debug view shows the + // same sharing the bake's own output would have. + const size_t n = soup.pos.size(); + TextureBake::QuantizedPointMap map(TextureBake::WELD_GRID_GEOMETRY, std::min(n, size_t(1) << 22)); + std::vector id(n); + s.mesh.vertices.reserve(n / 3); + for (size_t i = 0; i < n; ++i) { + id[i] = map.get_or_set(soup.pos[i], int(s.mesh.vertices.size())); + if (map.inserted()) + s.mesh.vertices.push_back(soup.pos[i]); + } + s.mesh.indices.reserve(n / 3); + for (size_t t = 0; t + 2 < n; t += 3) { + // Corners that welded together carry no area; the bake's own conversion drops them too, so + // dropping them here keeps the stage count honest against what would be committed. + if (id[t] == id[t + 1] || id[t + 1] == id[t + 2] || id[t] == id[t + 2]) + continue; + s.mesh.indices.emplace_back(id[t], id[t + 1], id[t + 2]); + } + finish(s); +} + +void BakeStageRecorder::capture(const char *name, const std::vector &vertices, + const std::vector &indices, double ms, + const std::string &detail) +{ + if (!m_enabled) + return; + BakeStageSnapshot s; + s.name = name; + s.detail = detail; + s.ms = ms; + s.mesh.vertices = vertices; + s.mesh.indices = indices; + finish(s); +} + +void BakeStageRecorder::capture_note(const char *name, double ms, const std::string &detail) +{ + if (!m_enabled) + return; + BakeStageSnapshot s; + s.name = name; + s.detail = detail; + s.ms = ms; + s.topology_checked = false; + m_stages.push_back(std::move(s)); +} + +void BakeStageRecorder::rebase(size_t from, const Transform3d *to_local, bool flip_winding) +{ + for (size_t i = from; i < m_stages.size(); ++i) { + BakeStageMesh &m = m_stages[i].mesh; + if (to_local != nullptr) + for (Vec3f &v : m.vertices) + v = (*to_local * v.cast()).cast(); + if (flip_winding) + for (Vec3i32 &t : m.indices) + std::swap(t[1], t[2]); + } +} + +double BakeStageRecorder::total_ms() const +{ + double sum = 0.0; + for (const BakeStageSnapshot &s : m_stages) + sum += s.ms; + return sum; +} + +size_t dump_bake_stages(const std::vector &stages, const std::string &dir) +{ + boost::system::error_code ec; + boost::filesystem::create_directories(dir, ec); + if (ec) { + BOOST_LOG_TRIVIAL(error) << "BakeStageRecorder: cannot create " << dir << ": " << ec.message(); + return 0; + } + + size_t written = 0; + for (size_t i = 0; i < stages.size(); ++i) { + const BakeStageSnapshot &s = stages[i]; + if (s.mesh.empty()) + continue; + + // Stage names carry spaces and punctuation; keep the filename to what every shell and viewer + // handles without quoting. + std::string safe; + for (const char c : s.name) + safe += (std::isalnum(static_cast(c)) != 0) ? c : '_'; + + char path[1024]; + std::snprintf(path, sizeof(path), "%s/%02zu_%s.obj", dir.c_str(), i, safe.c_str()); + std::FILE *f = std::fopen(path, "wb"); + if (f == nullptr) { + BOOST_LOG_TRIVIAL(error) << "BakeStageRecorder: cannot write " << path; + continue; + } + std::fprintf(f, "# texture bake stage %zu: %s\n", i, s.name.c_str()); + if (!s.detail.empty()) + std::fprintf(f, "# %s\n", s.detail.c_str()); + std::fprintf(f, "# %zu triangles, %.2f ms\n", s.triangles, s.ms); + for (const Vec3f &v : s.mesh.vertices) + std::fprintf(f, "v %.6f %.6f %.6f\n", double(v.x()), double(v.y()), double(v.z())); + for (const Vec3i32 &t : s.mesh.indices) // OBJ indices are 1-based + std::fprintf(f, "f %d %d %d\n", t[0] + 1, t[1] + 1, t[2] + 1); + std::fclose(f); + ++written; + } + + char summary[1024]; + std::snprintf(summary, sizeof(summary), "%s/stages.txt", dir.c_str()); + if (std::FILE *f = std::fopen(summary, "wb"); f != nullptr) { + std::fprintf(f, "%-3s %-24s %10s %12s %10s %8s %8s %8s %s\n", "#", "stage", "ms", "triangles", + "vertices", "open", "nonman", "degen", "detail"); + double total = 0.0; + for (size_t i = 0; i < stages.size(); ++i) { + const BakeStageSnapshot &s = stages[i]; + total += s.ms; + std::fprintf(f, "%-3zu %-24s %10.2f %12zu %10zu ", i, s.name.c_str(), s.ms, s.triangles, + s.vertices); + if (s.topology_checked) + std::fprintf(f, "%8zu %8zu %8zu", s.open_edges, s.non_manifold_edges, s.degenerate); + else + std::fprintf(f, "%8s %8s %8s", "-", "-", "-"); + std::fprintf(f, " %s%s\n", s.detail.c_str(), s.mesh_dropped ? " [mesh over cap, not written]" : ""); + } + std::fprintf(f, "\ntotal %.2f ms across %zu stages\n", total, stages.size()); + std::fclose(f); + } + return written; +} + +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDebug.hpp b/src/libslic3r/TextureBake/TextureBakeDebug.hpp new file mode 100644 index 0000000000..5c6d987e44 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeDebug.hpp @@ -0,0 +1,123 @@ +#pragma once + +// Step-by-step capture of a bake. +// +// A bake is a chain of stages that each rewrite the whole mesh, so when the result looks wrong the +// only useful question is which stage made it wrong. This records the geometry, the wall time and the +// topology after every stage, which is what the gizmo's debug view steps through and what the +// benchmark's --dump-stages writes out. +// +// Deliberately independent of TriangleMesh: a stage is held as a plain vertex/index pair, which is +// layout-compatible with indexed_triangle_set's own members (stl_vertex is Vec3f, +// stl_triangle_vertex_indices is Vec3i32), so the GUI assigns rather than converts and the standalone +// benchmark does not have to link admesh to use this. +// +// Recording is off unless enable(true) was called, and every capture site is a null-pointer check, so +// a normal bake pays nothing for this being here. + +#include +#include +#include +#include + +#include "TextureBakeIndex.hpp" + +namespace Slic3r { + +struct BakeStageMesh +{ + std::vector vertices; + std::vector indices; + + bool empty() const { return indices.empty(); } + size_t triangle_count() const { return indices.size(); } +}; + +struct BakeStageSnapshot +{ + std::string name; // "remesh", "subdivide", ... + std::string detail; // whatever the stage has to say: collapse counts, rejected moves, ... + BakeStageMesh mesh; // empty when the stage was over the memory cap - see mesh_dropped + + double ms = 0.0; + size_t triangles = 0; + size_t vertices = 0; + + // Filled only when the recorder was asked to check topology: it is a sort over every half-edge, + // which on a multi-million triangle stage costs more than the stage being measured. + size_t open_edges = 0; + size_t non_manifold_edges = 0; + size_t degenerate = 0; + bool topology_checked = false; + + // The geometry was dropped to stay inside the memory cap; every count above is still real. + bool mesh_dropped = false; +}; + +// Edge and area defects of a captured stage. Split out so a caller can run it on its own. +void bake_stage_topology(const BakeStageMesh &mesh, size_t &open_edges, size_t &non_manifold_edges, + size_t °enerate); + +// Writes `

/NN_name.obj` for every stage that still holds geometry, plus a `stages.txt` summary. +// Returns how many meshes were written. Existing files with the same names are overwritten. +// +// A free function rather than a recorder method because by the time anyone wants the files the +// recorder is usually gone and only the stages survive - that is how the gizmo holds them. +size_t dump_bake_stages(const std::vector &stages, const std::string &dir); + +class BakeStageRecorder +{ +public: + // Nothing is recorded until this is on. + void enable(bool on) { m_enabled = on; } + bool enabled() const { return m_enabled; } + + // The edge scan is optional because it is O(n log n) over every half-edge, and a debug run that + // only wants to see the geometry should not pay for it on every stage. + void set_check_topology(bool on) { m_check_topology = on; } + bool check_topology() const { return m_check_topology; } + + // Stages above this keep their counts but not their geometry. A debug run holds every stage at + // once, and a 4 M triangle stage is about 150 MB on its own, so without a cap stepping through a + // fine bake would need more memory than the bake did. + void set_mesh_cap(size_t triangles) { m_mesh_cap = triangles; } + size_t mesh_cap() const { return m_mesh_cap; } + + // `ms` is passed in rather than measured here: the caller is already timing the stage, and the + // capture itself (a weld, a copy, possibly an edge scan) must not land inside that measurement. + void capture(const char *name, const TextureBake::TriSoup &soup, double ms, + const std::string &detail = {}); + void capture(const char *name, const std::vector &vertices, + const std::vector &indices, double ms, const std::string &detail = {}); + // For a stage that changed nothing a caller can still show, e.g. a skipped remesh. + void capture_note(const char *name, double ms, const std::string &detail); + + // Index of the next stage to be recorded. Paired with rebase() to fix up a range afterwards. + size_t mark() const { return m_stages.size(); } + + // Brings stages [from, end) into the caller's own space and winding. The bake runs in world + // millimetres and, for a mirrored placement, against a reversed winding; the debug view draws in + // the volume's local frame, so a captured range has to be brought back the same way the bake's + // own result is. `to_local` may be null for no transform. + void rebase(size_t from, const Transform3d *to_local, bool flip_winding); + + const std::vector &stages() const { return m_stages; } + std::vector take() { return std::move(m_stages); } + void clear() { m_stages.clear(); } + bool empty() const { return m_stages.empty(); } + + // Total recorded wall time, which is the bake's own time minus whatever it does outside a stage. + double total_ms() const; + + size_t dump_obj(const std::string &dir) const { return dump_bake_stages(m_stages, dir); } + +private: + void finish(BakeStageSnapshot &s); + + std::vector m_stages; + bool m_enabled = false; + bool m_check_topology = true; + size_t m_mesh_cap = 4'000'000; +}; + +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index e591f7f191..f08331c8f0 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -80,18 +80,17 @@ Vec3d face_normal_unit(const std::vector &pos, int a, int b, int c) // Versions are captured at push time; a mismatch on pop means a later collapse invalidated the entry. // Lazy deletion, far cheaper than removing entries eagerly - but it means the heap accumulates stale -// duplicates, so it grows to several times the edge count and its size has to be reserved up front. -// Left to grow on its own it reallocates and copies the whole array repeatedly, which on a -// multi-million-entry heap costs more than every collapse put together. +// duplicates, so its size has to be reserved up front and it is compacted once the dead entries +// dominate (see maybe_compact below). // -// The collapse target is stored as float rather than double: it is a position on a mesh already held -// in float, and halving the entry cuts the memory the sift operations drag through cache. +// The collapse target is not stored. A matching version stamp means neither endpoint's quadric nor its +// position has changed since the push, so the target recomputes to exactly the same value on pop - and +// the entry drops from 40 bytes to 24. Sifting is most of this stage's time, and it is memory traffic. struct HeapEntry { double cost; int v1, v2; uint32_t ver1, ver2; - Vec3f p; bool operator>(const HeapEntry &o) const { return cost > o.cost; } }; @@ -289,9 +288,31 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h heap.pop_back(); return e; }; - size_t pops = 0, stale_pops = 0; + size_t pops = 0, stale_pops = 0, compactions = 0; - const auto push_edge = [&](int v1, int v2) { + // An entry is stale once either endpoint has been removed or moved by a later collapse. + const auto is_stale = [&](const HeapEntry &e) { + return !active[size_t(e.v1)] || !active[size_t(e.v2)] || version[size_t(e.v1)] != e.ver1 || + version[size_t(e.v2)] != e.ver2; + }; + // Measured on a 2.4 M -> 750 k run, 82% of pops were stale: every collapse re-pushes the survivor's + // edges and orphans the old ones, so the heap grows to several times the live edge set and every + // sift walks that much further through memory. Dropping the dead entries and re-heapifying once + // they dominate costs one linear pass, amortised against the growth that triggered it. + // + // Keyed to the live face count rather than to the heap's own size: lazy popping keeps the heap from + // ever doubling, but the live edge set (about 1.5 per face) shrinks as decimation proceeds, so by the + // end the heap is several times what is still collapsible. Compact once it passes twice that. + const auto maybe_compact = [&]() { + if (heap.size() < std::max(size_t(1) << 16, active_faces * 3)) + return; + heap.erase(std::remove_if(heap.begin(), heap.end(), is_stale), heap.end()); + std::make_heap(heap.begin(), heap.end(), std::greater()); + ++compactions; + }; + + // Where an edge collapses to. Also re-run on pop instead of stored - see HeapEntry. + const auto collapse_target = [&](int v1, int v2) -> Vec3d { Vec3d p; if (!solve_q(quadrics, v1, v2, p)) { const Vec3d mid = (pos[size_t(v1)] + pos[size_t(v2)]) * 0.5; @@ -306,10 +327,16 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h else if (e1 <= e2) p = pos[size_t(v1)]; else p = pos[size_t(v2)]; } + return p; + }; + const auto push_edge = [&](int v1, int v2) { + const Vec3d p = collapse_target(v1, v2); + // The cost is evaluated at the exact target and the collapse moves to its float rounding - + // the same split as when the rounded target was stored in the entry, so no ordering changes. // Where quadric costs are all near zero, shorter edges first keeps triangle quality up. const double len2 = (pos[size_t(v2)] - pos[size_t(v1)]).squaredNorm(); heap_push({ eval_sum(quadrics, v1, v2, p) + len2 * 1e-8, v1, v2, version[size_t(v1)], - version[size_t(v2)], p.cast() }); + version[size_t(v2)] }); }; { @@ -430,11 +457,7 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h break; const int v1 = top.v1, v2 = top.v2; - if (!active[size_t(v1)] || !active[size_t(v2)]) { - ++stale_pops; - continue; - } - if (version[size_t(v1)] != top.ver1 || version[size_t(v2)] != top.ver2) { + if (is_stale(top)) { ++stale_pops; continue; } @@ -443,7 +466,7 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call if (has_link_violation(v1, v2, lk_epoch)) continue; - const Vec3d target = top.p.cast(); + const Vec3d target = collapse_target(v1, v2).cast().cast(); if (check_flipped(v1, v2, target) || check_flipped(v2, v1, target)) continue; @@ -492,6 +515,7 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h push_edge(v1, nb); } } + maybe_compact(); if (on_progress) { const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); @@ -504,7 +528,8 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: pops=" << pops << " stale=" << stale_pops - << " heap_peak=" << heap.capacity() << " faces=" << active_faces; + << " compactions=" << compactions << " heap_peak=" << heap.capacity() + << " faces=" << active_faces; // Rebuild from the surviving faces, with per-face normals. TriSoup &out = result.geometry; diff --git a/src/libslic3r/TextureBake/TextureBakeDisplace.cpp b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp index 540cb7dda8..6e306b1c43 100644 --- a/src/libslic3r/TextureBake/TextureBakeDisplace.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDisplace.cpp @@ -120,20 +120,23 @@ TriSoup apply_displacement(const TriSoup &geometry, const HeightSampleFn &sample std::vector cur = smooth_nrm, nxt(unique_count, Vec3d::Zero()); for (int iter = 0; iter < settings.blend_normal_smoothing; ++iter) { - for (size_t id = 0; id < unique_count; ++id) { - const uint32_t s = csr_start[id], e = csr_start[id + 1]; - if (e == s) { - nxt[id] = cur[id]; - continue; + // Jacobi, so every vertex reads the previous iteration and the rows are independent. + tbb::parallel_for(tbb::blocked_range(0, unique_count, 4096), [&](const tbb::blocked_range &r) { + for (size_t id = r.begin(); id < r.end(); ++id) { + const uint32_t s = csr_start[id], e = csr_start[id + 1]; + if (e == s) { + nxt[id] = cur[id]; + continue; + } + Vec3d sum = Vec3d::Zero(); + for (uint32_t k = s; k < e; ++k) + sum += cur[neighbors[k]]; + sum /= double(e - s); + const double len = sum.norm(); + // Cancelling neighbours mean a knife edge; keep what we had. + nxt[id] = (len > 1e-12) ? Vec3d(sum / len) : cur[id]; } - Vec3d sum = Vec3d::Zero(); - for (uint32_t k = s; k < e; ++k) - sum += cur[neighbors[k]]; - sum /= double(e - s); - const double len = sum.norm(); - // Cancelling neighbours mean a knife edge; keep what we had. - nxt[id] = (len > 1e-12) ? Vec3d(sum / len) : cur[id]; - } + }); cur.swap(nxt); } blend_nrm = std::move(cur); diff --git a/src/libslic3r/TextureBake/TextureBakeFlip.cpp b/src/libslic3r/TextureBake/TextureBakeFlip.cpp new file mode 100644 index 0000000000..f4d9997f71 --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeFlip.cpp @@ -0,0 +1,212 @@ +#include "TextureBakeFlip.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +namespace Slic3r { +namespace TextureBake { + +FlipResult flip_edges_to_height(const TriSoup &geometry, const std::vector &face_parent_id, + const HeightSampleFn &sample, const FlipSettings &settings, + const std::vector &locked) +{ + FlipResult result; + result.geometry = geometry; + result.face_parent_id = face_parent_id; + const size_t count = geometry.pos.size(); + const size_t tri_ct = count / 3; + if (count == 0 || !sample || settings.passes <= 0) + return result; + + // Weld, so a flip rewrites triangles in terms of shared vertices rather than positions. + QuantizedPointMap weld(WELD_GRID_GEOMETRY, std::min(count, size_t(1) << 22)); + std::vector vid(count); + std::vector pos; + std::vector weight; // exclude weight per unique vertex, the max over its copies + const bool has_weight = !geometry.exclude_weight.empty(); + for (size_t i = 0; i < count; ++i) { + vid[i] = weld.get_or_set(geometry.pos[i], int(pos.size())); + if (weld.inserted()) { + pos.push_back(geometry.pos[i]); + weight.push_back(has_weight ? geometry.exclude_weight[i] : 0.f); + } else if (has_weight) { + weight[size_t(vid[i])] = std::max(weight[size_t(vid[i])], geometry.exclude_weight[i]); + } + } + const size_t nv = pos.size(); + + // Triangles as vertex ids; a locked or excluded triangle never takes part. + std::vector> tri(tri_ct); + std::vector fixed(tri_ct, 0); + for (size_t t = 0; t < tri_ct; ++t) { + tri[t] = { vid[t * 3], vid[t * 3 + 1], vid[t * 3 + 2] }; + if (!locked.empty() && t < locked.size() && locked[t]) + fixed[t] = 1; + if (has_weight && geometry.exclude_weight[t * 3] > 0.99f) + fixed[t] = 1; + } + + // Height per unique vertex along its area-weighted normal, the direction displacement will use. + std::vector nrm(nv, Vec3f::Zero()); + std::vector face_n(tri_ct); + const auto rebuild_normals = [&]() { + std::fill(nrm.begin(), nrm.end(), Vec3f::Zero()); + for (size_t t = 0; t < tri_ct; ++t) { + const Vec3f fn = (pos[size_t(tri[t][1])] - pos[size_t(tri[t][0])]).cross(pos[size_t(tri[t][2])] - pos[size_t(tri[t][0])]); + face_n[t] = fn; + for (int k = 0; k < 3; ++k) + nrm[size_t(tri[t][size_t(k)])] += fn; + } + for (Vec3f &n : nrm) { + const float l = n.norm(); + n = (l > 0.f) ? Vec3f(n / l) : Vec3f(0.f, 0.f, 1.f); + } + }; + rebuild_normals(); + std::vector h(nv, 0.f); + tbb::parallel_for(tbb::blocked_range(0, nv), [&](const tbb::blocked_range &r) { + for (size_t v = r.begin(); v < r.end(); ++v) + h[v] = sample(pos[v], nrm[v], nrm[v]); + }); + float h_lo = std::numeric_limits::max(), h_hi = -h_lo; + for (const float x : h) { h_lo = std::min(h_lo, x); h_hi = std::max(h_hi, x); } + const float range = h_hi - h_lo; + if (!(range > 0.f)) + return result; // flat: every diagonal is as good as the other + const float min_gain = float(settings.min_gain_fraction) * range; + const float planar = float(settings.min_planar_cos); + + struct Candidate + { + uint32_t t1, t2; // the two triangles + uint8_t k1, k2; // corner index in each where the shared edge starts (t1: a->c, t2: c->a) + float gain; + }; + + for (int pass = 0; pass < settings.passes; ++pass) { + // Half-edges keyed by their undirected edge, sorted so the two halves of an interior edge land + // next to each other; a run of exactly two with opposite directions is a manifold interior + // edge. Sorting beats hashing here by an order of magnitude on a few million triangles. + struct Half { uint64_t key; uint32_t corner; }; + std::vector half; + half.reserve(count); + for (size_t t = 0; t < tri_ct; ++t) + for (int k = 0; k < 3; ++k) { + const int from = tri[t][size_t(k)], to = tri[t][size_t((k + 1) % 3)]; + if (from == to) continue; + const uint32_t lo = uint32_t(std::min(from, to)), hi = uint32_t(std::max(from, to)); + half.push_back({ (uint64_t(lo) << 32) | hi, uint32_t(t * 3 + size_t(k)) }); + } + tbb::parallel_sort(half.begin(), half.end(), [](const Half &x, const Half &y) { + return x.key != y.key ? x.key < y.key : x.corner < y.corner; + }); + std::vector> edges; // (corner in t1, corner in t2), t1 < t2 + edges.reserve(half.size() / 2); + for (size_t i = 0; i < half.size();) { + size_t j = i + 1; + while (j < half.size() && half[j].key == half[i].key) ++j; + if (j - i == 2) { + // Opposite directions: the lower vertex id is `from` in exactly one of the two. + const uint32_t c1 = half[i].corner, c2 = half[i + 1].corner; + const int f1 = tri[c1 / 3][size_t(c1 % 3)], f2 = tri[c2 / 3][size_t(c2 % 3)]; + if (f1 != f2) + edges.emplace_back(c1, c2); + } + i = j; + } + std::vector cands(edges.size()); + std::vector valid(edges.size(), 0); + tbb::parallel_for(tbb::blocked_range(0, edges.size()), [&](const tbb::blocked_range &r) { + for (size_t i = r.begin(); i < r.end(); ++i) { + const uint32_t c1 = uint32_t(edges[i].first), c2 = uint32_t(edges[i].second); + const uint32_t t1 = c1 / 3, t2 = c2 / 3; + const int k1 = int(c1 % 3), k2 = int(c2 % 3); + if (fixed[t1] || fixed[t2]) continue; + if (!face_parent_id.empty() && face_parent_id[t1] != face_parent_id[t2]) continue; + // a->c is the shared edge in t1, with b opposite; t2 runs c->a with d opposite. + const int a = tri[t1][size_t(k1)], c = tri[t1][size_t((k1 + 1) % 3)], b = tri[t1][size_t((k1 + 2) % 3)]; + const int d = tri[t2][size_t((k2 + 2) % 3)]; + if (b == d) continue; + // Level quads have nothing to gain; the test is on the corners, before any sampling. + const float hmin = std::min({ h[size_t(a)], h[size_t(b)], h[size_t(c)], h[size_t(d)] }); + const float hmax = std::max({ h[size_t(a)], h[size_t(b)], h[size_t(c)], h[size_t(d)] }); + if (hmax - hmin < min_gain) continue; + // Coplanar enough to have a real alternative, and convex so the alternative is valid: + // the new triangles (a, d, b) and (d, c, b) must both face the way the quad does, with + // a decent share of its area. + const Vec3f n1 = face_n[t1], n2 = face_n[t2]; + const float l1 = n1.norm(), l2 = n2.norm(); + if (l1 <= 0.f || l2 <= 0.f || n1.dot(n2) < planar * l1 * l2) continue; + const Vec3f quad_n = (n1 + n2).normalized(); + const Vec3f &pa = pos[size_t(a)], &pb = pos[size_t(b)], &pc = pos[size_t(c)], &pd = pos[size_t(d)]; + const Vec3f m1 = (pd - pa).cross(pb - pa), m2 = (pc - pd).cross(pb - pd); + const float area_old = l1 + l2, area_new = m1.dot(quad_n) + m2.dot(quad_n); + const float min_part = 0.05f * area_old; + if (m1.dot(quad_n) < min_part || m2.dot(quad_n) < min_part) continue; + if (std::abs(area_new - area_old) > 0.02f * area_old) continue; // not the same quad: folded + // The diagonals' midpoint errors. + const auto mid_err = [&](int u, int w) { + const Vec3f p = 0.5f * (pos[size_t(u)] + pos[size_t(w)]); + Vec3f n = nrm[size_t(u)] + nrm[size_t(w)]; + const float l = n.norm(); + n = (l > 0.f) ? Vec3f(n / l) : quad_n; + return std::abs(sample(p, n, n) - 0.5f * (h[size_t(u)] + h[size_t(w)])); + }; + const float gain = mid_err(a, c) - mid_err(b, d); + if (gain < min_gain) continue; + cands[i] = { t1, t2, uint8_t(k1), uint8_t(k2), gain }; + valid[i] = 1; + } + }); + std::vector chosen; + for (size_t i = 0; i < cands.size(); ++i) + if (valid[i]) chosen.push_back(cands[i]); + std::sort(chosen.begin(), chosen.end(), [](const Candidate &x, const Candidate &y) { + return x.gain != y.gain ? x.gain > y.gain : (x.t1 != y.t1 ? x.t1 < y.t1 : x.t2 < y.t2); + }); + // Best first, and a triangle changes at most once per pass. + std::vector touched(tri_ct, 0); + size_t applied = 0; + for (const Candidate &cd : chosen) { + if (touched[cd.t1] || touched[cd.t2]) continue; + const int a = tri[cd.t1][cd.k1], c = tri[cd.t1][size_t((cd.k1 + 1) % 3)], b = tri[cd.t1][size_t((cd.k1 + 2) % 3)]; + const int d = tri[cd.t2][size_t((cd.k2 + 2) % 3)]; + tri[cd.t1] = { a, d, b }; + tri[cd.t2] = { d, c, b }; + touched[cd.t1] = touched[cd.t2] = 1; + ++applied; + } + result.flipped += applied; + if (applied == 0) + break; + rebuild_normals(); // face normals feed the planarity test of the next pass + } + + if (result.flipped == 0) + return result; + + // Back to the soup: positions, weights and per-face normals from the (possibly rewritten) triangles. + for (size_t t = 0; t < tri_ct; ++t) { + for (int k = 0; k < 3; ++k) { + const size_t i = t * 3 + size_t(k); + const int v = tri[t][size_t(k)]; + result.geometry.pos[i] = pos[size_t(v)]; + if (has_weight) + result.geometry.exclude_weight[i] = weight[size_t(v)]; + } + Vec3f n = (result.geometry.pos[t * 3 + 1] - result.geometry.pos[t * 3]).cross(result.geometry.pos[t * 3 + 2] - result.geometry.pos[t * 3]); + const float len = n.norm(); + n = (len > 0.f) ? Vec3f(n / len) : Vec3f(0.f, 0.f, 1.f); + result.geometry.nrm[t * 3] = result.geometry.nrm[t * 3 + 1] = result.geometry.nrm[t * 3 + 2] = n; + } + return result; +} + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeFlip.hpp b/src/libslic3r/TextureBake/TextureBakeFlip.hpp new file mode 100644 index 0000000000..02701bbced --- /dev/null +++ b/src/libslic3r/TextureBake/TextureBakeFlip.hpp @@ -0,0 +1,57 @@ +#pragma once + +// Data-dependent edge flipping: choose each quad's diagonal to follow the height field, before any +// displacement happens. +// +// Refinement produces a regular grid, and a step in the height map that crosses that grid at an +// angle lands on alternating corners: one triangle of a quad gets a raised corner, the next does not, +// and the displaced wall comes out as a sawtooth the size of the grid. Finer triangles make the teeth +// smaller, never straight. The cause is the diagonal, not the density: with the diagonal running +// along the step both triangles of the quad sit cleanly on one side or the other, and the wall is a +// straight line between them. +// +// So, for every interior edge, compare the two diagonals of the quad it spans by how well each +// interpolates the field at its own midpoint - the sampled height there against the mean of its two +// endpoints - and keep the better one. A quad whose four corners are level is skipped outright, so +// flat regions cost nothing; a non-planar quad (a model crease) is never touched, nor is one whose +// triangles belong to different source faces or straddle the painted boundary. + +#include +#include + +#include "TextureBakeDisplace.hpp" +#include "TextureBakeIndex.hpp" + +namespace Slic3r { +namespace TextureBake { + +struct FlipSettings +{ + // Passes over all edges. Flips interact through shared triangles, so a pass applies non-conflicting + // ones and the next pass picks up the rest; two or three settle a grid. + int passes = 3; + + // A flip has to reduce the midpoint error by at least this fraction of the height range, so noise + // on a rough surface does not toggle diagonals for nothing. + double min_gain_fraction = 0.02; + + // The two triangles have to be this coplanar (cosine of their normals' angle) for the quad to have + // a meaningful alternative diagonal at all: across a real crease there is none. + double min_planar_cos = 0.985; // ~10 degrees +}; + +struct FlipResult +{ + TriSoup geometry; + std::vector face_parent_id; + size_t flipped = 0; +}; + +// `face_parent_id` may be empty; when given it is carried through unchanged (a flip never crosses a +// parent boundary). `locked` flags triangles that must not change (per triangle, may be empty). +FlipResult flip_edges_to_height(const TriSoup &geometry, const std::vector &face_parent_id, + const HeightSampleFn &sample, const FlipSettings &settings, + const std::vector &locked); + +} // namespace TextureBake +} // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakeIndex.hpp b/src/libslic3r/TextureBake/TextureBakeIndex.hpp index 74ef068174..2b1dd72395 100644 --- a/src/libslic3r/TextureBake/TextureBakeIndex.hpp +++ b/src/libslic3r/TextureBake/TextureBakeIndex.hpp @@ -9,6 +9,7 @@ // vertices distinct (they merge at 100 um, giving needle artifacts after displacement) while still // absorbing float noise; 1 um is what collapse positioning needs. +#include #include #include #include @@ -26,8 +27,12 @@ static constexpr double WELD_GRID_DECIMATION = 1e6; // 1 um // tie rule matters. inline int64_t grid_round(double v) { return int64_t(std::floor(v + 0.5)); } -// Open-addressing table over flat arrays: no allocation per lookup, exact integer key comparison. +// Open-addressing table, linear probing: no allocation per lookup, exact integer key comparison. // Values must be non-negative; -1 is the empty sentinel and what get() returns on a miss. +// +// Key and value live together in one 32-byte cell. They used to be four parallel arrays, which made a +// single probe touch four cache lines - and probing this table was 13% of a whole bake, because every +// stage welds the full soup through it. class QuantizedPointMap { public: @@ -46,8 +51,8 @@ public: int get(float x, float y, float z) { - return m_val[slot(grid_round(double(x) * m_quant), grid_round(double(y) * m_quant), - grid_round(double(z) * m_quant))]; + return m_cells[slot(grid_round(double(x) * m_quant), grid_round(double(y) * m_quant), + grid_round(double(z) * m_quant))].val; } int get(const Vec3f &p) { return get(p.x(), p.y(), p.z()); } @@ -55,35 +60,24 @@ public: // return it. inserted() then says which of the two happened. int get_or_set(float x, float y, float z, int value) { - const int64_t qx = grid_round(double(x) * m_quant); - const int64_t qy = grid_round(double(y) * m_quant); - const int64_t qz = grid_round(double(z) * m_quant); - const size_t i = slot(qx, qy, qz); - if (m_val[i] != -1) { - m_inserted = false; - return m_val[i]; - } - m_qx[i] = qx; m_qy[i] = qy; m_qz[i] = qz; - m_val[i] = value; - m_inserted = true; - if (++m_size > size_t(double(m_cap) * 0.7)) - grow(); - return value; + return get_or_set_key(grid_round(double(x) * m_quant), grid_round(double(y) * m_quant), + grid_round(double(z) * m_quant), value); } int get_or_set(const Vec3f &p, int value) { return get_or_set(p.x(), p.y(), p.z(), value); } // The same table as a set of integer tuples (edge marking, midpoint cache). Quantisation is // bypassed: routing ids through the float overloads loses precision above 2^24. - int get_key(int64_t a, int64_t b, int64_t c) { return m_val[slot(a, b, c)]; } + int get_key(int64_t a, int64_t b, int64_t c) { return m_cells[slot(a, b, c)].val; } int get_or_set_key(int64_t a, int64_t b, int64_t c, int value) { const size_t i = slot(a, b, c); - if (m_val[i] != -1) { + Cell &cell = m_cells[i]; + if (cell.val != -1) { m_inserted = false; - return m_val[i]; + return cell.val; } - m_qx[i] = a; m_qy[i] = b; m_qz[i] = c; - m_val[i] = value; + cell.qx = a; cell.qy = b; cell.qz = c; + cell.val = value; m_inserted = true; if (++m_size > size_t(double(m_cap) * 0.7)) grow(); @@ -91,27 +85,34 @@ public: } private: + struct Cell + { + int64_t qx = 0, qy = 0, qz = 0; + int32_t val = -1; + }; + void alloc(size_t cap) { m_cap = cap; m_mask = cap - 1; - m_qx.assign(cap, 0); - m_qy.assign(cap, 0); - m_qz.assign(cap, 0); - m_val.assign(cap, -1); + m_cells.assign(cap, Cell{}); } size_t slot(int64_t qx, int64_t qy, int64_t qz) const { - uint32_t h = uint32_t(int32_t(qx) * int32_t(0x9E3779B1)) ^ - uint32_t(int32_t(qy) * int32_t(0x85EBCA77)) ^ - uint32_t(int32_t(qz) * int32_t(0xC2B2AE3D)); + // Unsigned multiplies: the signed versions overflowed on nearly every key, which is undefined + // behaviour. The resulting bits are identical on every target OrcaSlicer builds for. + // + // A stronger 64-bit finalizer was tried and measured no faster - the probing that shows up in a + // profile is subdivide's parallel mark count, spread over every core, not long probe chains. + uint32_t h = (uint32_t(qx) * 0x9E3779B1u) ^ (uint32_t(qy) * 0x85EBCA77u) ^ (uint32_t(qz) * 0xC2B2AE3Du); h ^= h >> 15; size_t i = size_t(h) & m_mask; // Equality is checked against the stored 64-bit keys, so truncating to 32 bits for the hash // costs collisions at worst, never a wrong answer. - while (m_val[i] != -1) { - if (m_qx[i] == qx && m_qy[i] == qy && m_qz[i] == qz) + while (m_cells[i].val != -1) { + const Cell &c = m_cells[i]; + if (c.qx == qx && c.qy == qy && c.qz == qz) return i; i = (i + 1) & m_mask; } @@ -120,24 +121,17 @@ private: void grow() { - std::vector oqx = std::move(m_qx), oqy = std::move(m_qy), oqz = std::move(m_qz); - std::vector oval = std::move(m_val); - const size_t ocap = m_cap; - alloc(ocap * 2); - for (size_t i = 0; i < ocap; ++i) { - if (oval[i] == -1) - continue; - const size_t s = slot(oqx[i], oqy[i], oqz[i]); - m_qx[s] = oqx[i]; m_qy[s] = oqy[i]; m_qz[s] = oqz[i]; - m_val[s] = oval[i]; - } + std::vector old = std::move(m_cells); + alloc(m_cap * 2); + for (const Cell &c : old) + if (c.val != -1) + m_cells[slot(c.qx, c.qy, c.qz)] = c; } - double m_quant; - size_t m_cap = 0, m_mask = 0, m_size = 0; - bool m_inserted = false; - std::vector m_qx, m_qy, m_qz; - std::vector m_val; + double m_quant; + size_t m_cap = 0, m_mask = 0, m_size = 0; + bool m_inserted = false; + std::vector m_cells; }; // Three consecutive entries per triangle. The indexers turn this into shared vertices where a stage diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 510c44e844..37b58a6f3b 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -1,8 +1,11 @@ #include "TextureBakePipeline.hpp" +#include "TextureBakeDebug.hpp" + #include #include #include +#include #include @@ -108,7 +111,7 @@ size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol) PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, const PipelineSettings &settings, const DisplaceBounds &bounds, PipelineMode mode, const std::vector &face_excluded, - const PipelineProgressFn &on_progress) + const PipelineProgressFn &on_progress, BakeStageRecorder *debug) { PipelineResult result; const auto report = [&](const char *stage, double f) { @@ -118,9 +121,16 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, // without this it is guesswork which one to attack. auto clock_now = [] { return std::chrono::steady_clock::now(); }; auto t_stage = clock_now(); - const auto lap = [&](const char *stage, size_t tris) { + // One call site for both the log line and the debug capture, so a stage cannot appear in one and + // be missing from the other. The capture happens after the elapsed time is read: welding the soup + // and scanning its edges costs more than some of the stages do, and must not land inside the + // measurement it is reporting. + const auto lap = [&](const char *stage, const TriSoup &geometry, const std::string &detail = {}) { const double ms = std::chrono::duration(clock_now() - t_stage).count(); - BOOST_LOG_TRIVIAL(info) << "TextureBake " << stage << ": " << ms << " ms, " << tris << " tris"; + BOOST_LOG_TRIVIAL(info) << "TextureBake " << stage << ": " << ms << " ms, " + << geometry.triangle_count() << " tris"; + if (debug != nullptr) + debug->capture(stage, geometry, ms, detail); t_stage = clock_now(); }; @@ -128,13 +138,16 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, result.geometry = input; return result; } + if (debug != nullptr) + debug->capture("input", input, 0.0, "as handed to the pipeline"); + t_stage = clock_now(); // the capture above is not part of the first stage // 1. Refine to the target edge length. SubdivideResult sub = subdivide( input, settings.refine_length, face_excluded, /* fast */ false, settings.safety_cap, [&](double f, size_t, double) { return report("subdivide", f); }); result.safety_cap_hit = sub.safety_cap_hit; - lap("subdivide", sub.geometry.triangle_count()); + lap("subdivide", sub.geometry); if (!report("subdivide", 1.0)) { result.canceled = true; return result; @@ -147,7 +160,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, RegularizeResult reg = regularize_mesh(sub.geometry, sub.face_parent_id, settings.refine_length, ropts); result.collapse_count = reg.collapse_count; - lap("regularize", reg.geometry.triangle_count()); + lap("regularize", reg.geometry, std::to_string(reg.collapse_count) + " collapses"); if (!report("regularize", 1.0)) { result.canceled = true; return result; @@ -173,7 +186,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, ? reg.face_parent_id[size_t(mid)] : -1; } sub.face_parent_id = std::move(composed); - lap("re-subdivide", sub.geometry.triangle_count()); + lap("re-subdivide", sub.geometry); } else { sub.geometry = std::move(reg.geometry); sub.face_parent_id = std::move(reg.face_parent_id); @@ -192,12 +205,31 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, BOOST_LOG_TRIVIAL(info) << "TextureBake relocate: moved=" << rel.moved << " rejected=" << rel.rejected; sub.geometry = std::move(rel.geometry); - lap("relocate", sub.geometry.triangle_count()); + lap("relocate", sub.geometry, + "moved " + std::to_string(rel.moved) + ", rejected " + std::to_string(rel.rejected)); + } + + // 3b. Diagonals along the height field's steps, so they displace into straight walls. + if (settings.flip_edges) { + std::vector locked; + if (settings.preserve_untextured && !sub.geometry.exclude_weight.empty()) { + locked.assign(sub.geometry.triangle_count(), 0); + for (size_t t = 0; t < locked.size(); ++t) + locked[t] = sub.geometry.exclude_weight[t * 3] > 0.99f ? 1 : 0; + } + FlipResult fl = flip_edges_to_height(sub.geometry, sub.face_parent_id, sample, settings.flip_opts, locked); + sub.geometry = std::move(fl.geometry); + sub.face_parent_id = std::move(fl.face_parent_id); + lap("align edges", sub.geometry, std::to_string(fl.flipped) + " flips"); + if (!report("align edges", 1.0)) { + result.canceled = true; + return result; + } } TriSoup displaced = apply_displacement(sub.geometry, sample, settings.displace, bounds, [&](double f) { return report("displace", f); }); - lap("displace", displaced.triangle_count()); + lap("displace", displaced); if (!report("displace", 1.0)) { result.canceled = true; return result; @@ -206,8 +238,12 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, // 4. Decimate - export only. A bake needs the face-parent map, which a collapse destroys. std::vector parent = std::move(sub.face_parent_id); if (mode == PipelineMode::Export) { + // Only when the mesh is actually over budget. Harvesting flat faces on a mesh that already fits + // cost several times the decimation itself and degraded the relief it was handed; it now only + // runs as part of a decimation that has to happen anyway. The repair pass below keys off the + // same decision (it runs only when decimation did), so an under-budget bake skips both. const bool needs_decimation = displaced.triangle_count() > settings.max_triangles; - if (needs_decimation || settings.harvest_flat) { + if (needs_decimation) { std::vector locked; if (settings.preserve_untextured && !displaced.exclude_weight.empty()) { locked.assign(displaced.triangle_count(), 0); @@ -219,7 +255,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, [&](double f) { return report("decimate", f); }); result.locked_over_budget = dec.locked_over_budget; displaced = std::move(dec.geometry); - lap("decimate", displaced.triangle_count()); + lap("decimate", displaced, "over budget, simplified"); parent.clear(); // no longer meaningful } if (!report("decimate", 1.0)) { @@ -229,15 +265,21 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } // 5. Flatten the bed-contact surface. - if (settings.clamp_below_plate || settings.displace.bottom_angle_limit > 0.f) - clamp_below_bottom(displaced, bounds.min.z()); - if (settings.bottom_snap_tol > 0.0) - snap_bottom_to_flat(displaced, bounds.min.z(), settings.bottom_snap_tol); + { + const bool clamped = settings.clamp_below_plate || settings.displace.bottom_angle_limit > 0.f; + if (clamped) + clamp_below_bottom(displaced, bounds.min.z()); + size_t snapped = 0; + if (settings.bottom_snap_tol > 0.0) + snapped = snap_bottom_to_flat(displaced, bounds.min.z(), settings.bottom_snap_tol); + if (clamped || settings.bottom_snap_tol > 0.0) + lap("bottom clamp + snap", displaced, std::to_string(snapped) + " triangles snapped flat"); + } // 6. Close the T-junctions decimation left behind. Only meaningful when it ran. if (mode == PipelineMode::Export && parent.empty()) { displaced = resolve_t_junctions(displaced); - lap("repair", displaced.triangle_count()); + lap("repair", displaced); } result.geometry = std::move(displaced); diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index a1cee46c1c..aac6710cf4 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -21,11 +21,17 @@ #include "TextureBakeDisplace.hpp" #include "TextureBakeIndex.hpp" #include "TextureBakeRegularize.hpp" +#include "TextureBakeFlip.hpp" #include "TextureBakeRelocate.hpp" #include "TextureBakeRepair.hpp" #include "TextureBakeSubdivide.hpp" namespace Slic3r { + +// Optional step-by-step capture; see TextureBakeDebug.hpp. A pointer, and forward declared, so the +// pipeline header stays free of the mesh types the recorder converts into. +class BakeStageRecorder; + namespace TextureBake { enum class PipelineMode @@ -53,10 +59,17 @@ struct PipelineSettings bool relocate = false; RelocateSettings relocate_opts; + // Choose each quad's diagonal to follow the height field before displacing, so a step that crosses + // the grid at an angle comes out as a straight wall instead of a sawtooth. See TextureBakeFlip.hpp. + bool flip_edges = true; + FlipSettings flip_opts; + DisplaceSettings displace; // Export mode only. size_t max_triangles = 750'000; + // Keep removing zero-cost flat faces past the target. Only applies when decimation runs, i.e. when + // the displaced mesh is over max_triangles - an under-budget mesh is never decimated. bool harvest_flat = true; double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL; // Lock the untextured region against both regularization and decimation. @@ -86,10 +99,13 @@ struct PipelineResult bool canceled = false; }; +// `debug`, when given and enabled, receives the mesh after every stage that ran - which is the only +// way to tell which stage a bad result came from, since each one rewrites the whole mesh. PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, const PipelineSettings &settings, const DisplaceBounds &bounds, PipelineMode mode, const std::vector &face_excluded = {}, - const PipelineProgressFn &on_progress = {}); + const PipelineProgressFn &on_progress = {}, + BakeStageRecorder *debug = nullptr); // Snap anything that ended below the model's original bottom back up to it. void clamp_below_bottom(TriSoup &geometry, float bottom_z); diff --git a/src/libslic3r/TextureBake/TextureBakeRegularize.cpp b/src/libslic3r/TextureBake/TextureBakeRegularize.cpp index 4539e38723..3028d8bee9 100644 --- a/src/libslic3r/TextureBake/TextureBakeRegularize.cpp +++ b/src/libslic3r/TextureBake/TextureBakeRegularize.cpp @@ -3,7 +3,9 @@ #include #include #include -#include + +#include +#include namespace Slic3r { namespace TextureBake { @@ -286,31 +288,38 @@ RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector return true; }; + // Per-triangle thinness for a round's candidate scan; -1 marks "not a candidate". Scored in + // parallel, since the scan only reads the mesh, then gathered serially in index order. + std::vector round_aspect(tri_count); for (int round = 0; round < opts.maxrounds; ++round) { // Rebuilt each round so earlier collapses inform the priorities. - std::vector cand; - std::vector cand_aspect; - for (size_t t = 0; t < tri_count; ++t) { - if (tri_deleted[t]) - continue; - const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; - if (std::min({ sq_dist(a, b), sq_dist(b, c), sq_dist(c, a) }) <= 0.0) - continue; - const double aspect2 = tri_aspect_sq(t); - if (aspect2 < aspect_thr2) - continue; - cand.push_back(int(t)); - cand_aspect.push_back(aspect2); - } - // Worst first; ties keep ascending order so the pass is deterministic. - std::vector order(cand.size()); - std::iota(order.begin(), order.end(), 0); - std::stable_sort(order.begin(), order.end(), - [&](int x, int y) { return cand_aspect[size_t(x)] > cand_aspect[size_t(y)]; }); + tbb::parallel_for(tbb::blocked_range(0, tri_count), [&](const tbb::blocked_range &r) { + for (size_t t = r.begin(); t < r.end(); ++t) { + round_aspect[t] = -1.0; + if (tri_deleted[t]) + continue; + const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; + if (std::min({ sq_dist(a, b), sq_dist(b, c), sq_dist(c, a) }) <= 0.0) + continue; + const double aspect2 = tri_aspect_sq(t); + if (aspect2 >= aspect_thr2) + round_aspect[t] = aspect2; + } + }); + // Worst first; ties keep ascending triangle order so the pass is deterministic. Sorting the + // (thinness, triangle) pairs themselves gives exactly the order the index sort with its + // indirect comparator did, without that comparator's extra lookup on every comparison. + std::vector> cand; + for (size_t t = 0; t < tri_count; ++t) + if (round_aspect[t] >= 0.0) + cand.emplace_back(round_aspect[t], int(t)); + std::sort(cand.begin(), cand.end(), [](const std::pair &x, const std::pair &y) { + return x.first != y.first ? x.first > y.first : x.second < y.second; + }); size_t round_collapses = 0; - for (const int oi : order) { - const size_t t = size_t(cand[size_t(oi)]); + for (const std::pair &entry : cand) { + const size_t t = size_t(entry.second); if (tri_deleted[t]) continue; const int a = corners[t * 3], b = corners[t * 3 + 1], c = corners[t * 3 + 2]; @@ -332,8 +341,13 @@ RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector // Drop deleted triangles and rebuild the soup. const bool have_weights = !geometry.exclude_weight.empty(); + const size_t survivors = tri_count - size_t(std::count(tri_deleted.begin(), tri_deleted.end(), uint8_t(1))); std::vector out_parent; TriSoup &out = result.geometry; + out_parent.reserve(survivors); + out.pos.reserve(survivors * 3); + if (have_weights) + out.exclude_weight.reserve(survivors * 3); for (size_t t = 0; t < tri_count; ++t) { if (tri_deleted[t]) continue; @@ -351,7 +365,6 @@ RegularizeResult regularize_mesh(const TriSoup &geometry, const std::vector // Rebuilt from the compacted geometry - the collapses moved vertices. out.nrm.assign(out.pos.size(), Vec3f::Zero()); { - std::vector accum(out.pos.size(), Vec3d::Zero()); QuantizedPointMap weld(WELD_GRID_GEOMETRY, out.pos.size()); std::vector vid(out.pos.size()); int next = 0; diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp index 0a5932e962..8e2a8c82d8 100644 --- a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp @@ -75,8 +75,11 @@ PassResult subdivide_pass(VertStore &verts, const std::vector &indices, dou const size_t tri_count = indices.size() / 3; const bool have_canon = !verts.canon.empty(); - QuantizedPointMap mid_cache(1.0, 1 << 16); - QuantizedPointMap split_edges(1.0, 1 << 16); + // Sized from the pass rather than grown from 64 k: a fine pass marks on the order of one edge per + // triangle, and growing to that by doubling rehashes the whole table a dozen times. + const size_t expect = std::max(size_t(1) << 16, tri_count); + QuantizedPointMap mid_cache(1.0, expect); + QuantizedPointMap split_edges(1.0, expect); // With canonical ids the key is the canonical *position* id, so split copies either side of a // sharp edge see one another's decision; without them the vertex index serves. @@ -108,13 +111,19 @@ PassResult subdivide_pass(VertStore &verts, const std::vector &indices, dou return out; // changed stays false: nothing left to refine } - // Step 1.5. Read-only against the finished mark set, so it reduces in parallel. - const size_t predicted = tbb::parallel_reduce( + // Step 1.5. Read-only against the finished mark set, so it runs in parallel - and it keeps each + // triangle's three marks (bit 0 = ab, 1 = bc, 2 = ca), so the serial rebuild below reads a byte + // instead of probing the hash map three more times per triangle. + std::vector tri_marks(tri_count); + const size_t predicted = tbb::parallel_reduce( tbb::blocked_range(0, tri_count), size_t(0), [&](const tbb::blocked_range &range, size_t acc) { for (size_t t = range.begin(); t < range.end(); ++t) { - const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; - const int n = int(is_marked(a, b)) + int(is_marked(b, c)) + int(is_marked(c, a)); + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const uint8_t m = uint8_t(is_marked(a, b)) | uint8_t(is_marked(b, c) << 1) | + uint8_t(is_marked(c, a) << 2); + tri_marks[t] = m; + const int n = (m & 1) + ((m >> 1) & 1) + ((m >> 2) & 1); acc += (n == 0) ? 1 : size_t(n + 1); } return acc; @@ -150,7 +159,7 @@ PassResult subdivide_pass(VertStore &verts, const std::vector &indices, dou const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; const uint8_t excl = face_excluded.empty() ? uint8_t(0) : face_excluded[t]; const int pid = face_parent_id.empty() ? 0 : face_parent_id[t]; - const bool s_ab = is_marked(a, b), s_bc = is_marked(b, c), s_ca = is_marked(c, a); + const bool s_ab = (tri_marks[t] & 1) != 0, s_bc = (tri_marks[t] & 2) != 0, s_ca = (tri_marks[t] & 4) != 0; const int n = int(s_ab) + int(s_bc) + int(s_ca); if (n == 0) { @@ -345,20 +354,23 @@ TriSoup to_non_indexed(const VertStore &verts, const std::vector &indices, if (want_weights) out.exclude_weight.resize(tri_count * 3); - for (size_t t = 0; t < tri_count; ++t) { - // The per-face flag, not the interpolated weight: merging by maximum can push an *included* - // face's corners to 1 when it borders two excluded neighbours, wrongly excluding it. - const bool have_face_flag = !face_excluded.empty(); - const float face_w = have_face_flag ? (face_excluded[t] ? 1.f : 0.f) : 0.f; - for (int v = 0; v < 3; ++v) { - const size_t vidx = size_t(indices[t * 3 + size_t(v)]); - out.pos[t * 3 + size_t(v)] = verts.pos[vidx].cast(); - out.nrm[t * 3 + size_t(v)] = verts.nrm[vidx].cast(); - if (want_weights) - out.exclude_weight[t * 3 + size_t(v)] = - have_face_flag ? face_w : float(verts.wgt[vidx]); + // Each triangle writes only its own three slots, so this is a plain parallel gather. + tbb::parallel_for(tbb::blocked_range(0, tri_count), [&](const tbb::blocked_range &r) { + for (size_t t = r.begin(); t < r.end(); ++t) { + // The per-face flag, not the interpolated weight: merging by maximum can push an *included* + // face's corners to 1 when it borders two excluded neighbours, wrongly excluding it. + const bool have_face_flag = !face_excluded.empty(); + const float face_w = have_face_flag ? (face_excluded[t] ? 1.f : 0.f) : 0.f; + for (int v = 0; v < 3; ++v) { + const size_t vidx = size_t(indices[t * 3 + size_t(v)]); + out.pos[t * 3 + size_t(v)] = verts.pos[vidx].cast(); + out.nrm[t * 3 + size_t(v)] = verts.nrm[vidx].cast(); + if (want_weights) + out.exclude_weight[t * 3 + size_t(v)] = + have_face_flag ? face_w : float(verts.wgt[vidx]); + } } - } + }); return out; } @@ -375,9 +387,9 @@ SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, IndexedMesh indexed = fast ? to_indexed_fast(geometry) : to_indexed(geometry); QuantizedPointMap *canon_map = indexed.has_canon ? &indexed.pos_canon_map : nullptr; - std::vector current_indices = indexed.indices; - std::vector current_excluded = face_excluded; const size_t initial_tris = indexed.indices.size() / 3; + std::vector current_indices = std::move(indexed.indices); // nothing reads it again + std::vector current_excluded = face_excluded; std::vector current_parent(initial_tris); for (size_t i = 0; i < initial_tris; ++i) current_parent[i] = int(i); @@ -400,13 +412,19 @@ SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, if (on_progress) { // Reported after the pass, so the value falls each iteration instead of lagging a step. - double max_edge_sq = 0.0; - for (size_t t = 0; t + 2 < current_indices.size(); t += 3) { - const int a = current_indices[t], b = current_indices[t + 1], c = current_indices[t + 2]; - max_edge_sq = std::max({ max_edge_sq, edge_len_sq(indexed.verts, a, b), - edge_len_sq(indexed.verts, b, c), + // A max is order-independent, so the scan reduces in parallel to the same value. + const double max_edge_sq = tbb::parallel_reduce( + tbb::blocked_range(0, current_indices.size() / 3), 0.0, + [&](const tbb::blocked_range &r, double acc) { + for (size_t f = r.begin(); f < r.end(); ++f) { + const int a = current_indices[f * 3], b = current_indices[f * 3 + 1], + c = current_indices[f * 3 + 2]; + acc = std::max({ acc, edge_len_sq(indexed.verts, a, b), edge_len_sq(indexed.verts, b, c), edge_len_sq(indexed.verts, c, a) }); - } + } + return acc; + }, + [](double x, double y) { return std::max(x, y); }); if (!on_progress(std::min(0.95, double(iter + 1) / SUBDIVIDE_MAX_ITERATIONS), current_indices.size() / 3, std::sqrt(max_edge_sq))) break; // whole passes only, so what we have is still crack-free From f802208d4c252d6b7f6fee36b5eebffcf7d74db6 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH 28/89] Texture displacement: step cutter, v2 colours, auto resolution, anchored bake frame --- src/libslic3r/TextureDisplacement.cpp | 2422 +++++++++++++++++++++++-- src/libslic3r/TextureDisplacement.hpp | 186 +- 2 files changed, 2398 insertions(+), 210 deletions(-) diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index a129b503ff..cfc75c286f 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -1,7 +1,11 @@ #include "TextureDisplacement.hpp" #include +#include +#include +#include #include +#include #include #include #include @@ -10,17 +14,25 @@ #include #include #include +#include +#include #include #include +#include + #include #include #include +#include + +#include "AABBTreeIndirect.hpp" #include "MeshBoolean.hpp" #include "Model.hpp" #include "PNGReadWrite.hpp" #include "TriangleSelector.hpp" +#include "TextureBake/TextureBakeDebug.hpp" #include "TextureBake/TextureBakeMesh.hpp" #include "TextureBake/TextureBakePipeline.hpp" @@ -120,48 +132,106 @@ struct DecodedTextureCache std::unordered_map>, DecodedHeightTexture>> entries; }; DecodedTextureCache g_decoded_texture_cache; + +// The smoothed copy is cached too, one per image: the thumbnail, the bump-preview height texture, the +// colour texture, the projector texture, the UV editor's background and the preview job all ask for +// the same (image, smoothing) pair in the same frame while the Smoothing slider moves, and each of them +// blurring its own copy is what froze the UI. Keyed like the raw cache; a different smoothing value +// simply replaces the entry. +struct SmoothedTextureCache +{ + struct Entry + { + std::weak_ptr> source; + float smoothing = 0.f; + DecodedHeightTexture texture; + }; + std::mutex mutex; + std::unordered_map entries; +}; +SmoothedTextureCache g_smoothed_texture_cache; } // namespace namespace { -// A few passes of a separable box blur approximate a Gaussian, cheaply. `radius` is in pixels; 0 is -// a no-op. Wraps at the edges so a tiling height map stays seamless after smoothing. Operates on the -// grayscale byte buffer in place. -void smooth_height_pixels(std::vector &pixels, int width, int height, int radius) +// A few passes of a separable box blur approximate a Gaussian, cheaply. `radius` is in whole texels; +// 0 is a no-op. Wraps at the edges so a tiling height map stays seamless after smoothing. Operates on +// the grayscale byte buffer in place. +// +// Each pass is a sliding window - one add and one subtract per texel - so the cost is the image size, +// not the image size times the radius. The Smoothing slider drives this on every frame it moves, for +// every consumer of the layer, and the radius goes up to 48 texels: the per-window loop this replaces +// stalled the UI for seconds on a large map. Rows (and column blocks) run in parallel. The rounding is +// the old code's exactly, so a blur gives the same bytes as before. +void smooth_height_pixels_box(std::vector &pixels, int width, int height, int radius) { if (radius <= 0 || width <= 0 || height <= 0 || pixels.size() != size_t(width) * size_t(height)) return; const int window = 2 * radius + 1; - const float inv = 1.f / float(window); + const float inv = 1.f / float(window); + const auto wrap = [](int i, int n) { return (i % n + n) % n; }; std::vector tmp(pixels.size()); for (int pass = 0; pass < 2; ++pass) { // two passes -> smoother than a single box - // Horizontal. - for (int y = 0; y < height; ++y) { - const size_t row = size_t(y) * width; - for (int x = 0; x < width; ++x) { - float sum = 0.f; - for (int k = -radius; k <= radius; ++k) { - int sx = x + k; - sx = (sx % width + width) % width; // wrap - sum += float(pixels[row + size_t(sx)]); + // Horizontal: a running sum per row. + tbb::parallel_for(tbb::blocked_range(0, height), [&](const tbb::blocked_range &r) { + for (int y = r.begin(); y < r.end(); ++y) { + const uint8_t *src = pixels.data() + size_t(y) * size_t(width); + uint8_t *dst = tmp.data() + size_t(y) * size_t(width); + uint32_t sum = 0; + for (int k = -radius; k <= radius; ++k) + sum += src[wrap(k, width)]; + for (int x = 0; x < width; ++x) { + dst[x] = uint8_t(std::lround(float(sum) * inv)); + sum += src[wrap(x + radius + 1, width)]; + sum -= src[wrap(x - radius, width)]; } - tmp[row + size_t(x)] = uint8_t(std::lround(sum * inv)); } - } - // Vertical. - for (int x = 0; x < width; ++x) + }); + // Vertical: one running sum per column, advanced row by row so the reads stay row-major. + tbb::parallel_for(tbb::blocked_range(0, width, 256), [&](const tbb::blocked_range &r) { + std::vector sum(size_t(r.size()), 0); + for (int k = -radius; k <= radius; ++k) { + const uint8_t *row = tmp.data() + size_t(wrap(k, height)) * size_t(width); + for (int x = r.begin(); x < r.end(); ++x) + sum[size_t(x - r.begin())] += row[x]; + } for (int y = 0; y < height; ++y) { - float sum = 0.f; - for (int k = -radius; k <= radius; ++k) { - int sy = y + k; - sy = (sy % height + height) % height; // wrap - sum += float(tmp[size_t(sy) * width + size_t(x)]); + uint8_t *dst = pixels.data() + size_t(y) * size_t(width); + const uint8_t *add = tmp.data() + size_t(wrap(y + radius + 1, height)) * size_t(width); + const uint8_t *sub = tmp.data() + size_t(wrap(y - radius, height)) * size_t(width); + for (int x = r.begin(); x < r.end(); ++x) { + uint32_t &sx = sum[size_t(x - r.begin())]; + dst[x] = uint8_t(std::lround(float(sx) * inv)); + sx += add[x]; + sx -= sub[x]; } - pixels[size_t(y) * width + size_t(x)] = uint8_t(std::lround(sum * inv)); } + }); } } + +// The same blur with a *continuous* radius, which is what the Smoothing slider drives. +// +// A box blur can only work in whole texels, so mapping the slider straight onto a rounded radius +// made it move in visible jumps - and its very first step off zero was a full one-texel blur rather +// than a hint of one, which is what made the control feel like it switched on rather than ramped up. +// Blur at the next whole texel up and cross-fade the raw image back in by the fraction left over: +// below one texel that fade *is* the sub-texel kernel, and above it it turns each integer step into +// a continuous ramp. +void smooth_height_pixels(std::vector &pixels, int width, int height, float radius) +{ + if (radius <= 0.f || width <= 0 || height <= 0 || pixels.size() != size_t(width) * size_t(height)) + return; + + const int whole = std::max(1, int(std::ceil(radius))); + const float mix = std::clamp(radius / float(whole), 0.f, 1.f); + const std::vector raw = (mix < 0.999f) ? pixels : std::vector{}; + smooth_height_pixels_box(pixels, width, height, whole); + if (!raw.empty()) + for (size_t i = 0; i < pixels.size(); ++i) + pixels[i] = uint8_t(std::lround(float(raw[i]) + (float(pixels[i]) - float(raw[i])) * mix)); +} } // namespace DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer) @@ -231,11 +301,21 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer entries[key] = { std::weak_ptr>(layer.image_data), result }; } - // Smoothing radius scales with the texture so the effect is resolution-independent; capped so the - // blur stays affordable on large maps. + // Smoothing radius scales with the texture so the same slider value blurs the same *fraction* of + // the image whatever resolution it came in at, and stays continuous in the slider - see + // smooth_height_pixels(). The cap is a cost limit, not part of the mapping: the blur is + // O(width * height * radius) per pass, so a large map with the slider at the top would otherwise + // stall every preview rebuild. if (layer.smoothing > 0.f) { - const int max_radius = std::clamp(int(std::lround(0.02f * std::min(result.width, result.height))), 1, 32); - const int radius = std::max(1, int(std::lround(layer.smoothing * float(max_radius)))); + { + std::lock_guard lock(g_smoothed_texture_cache.mutex); + auto it = g_smoothed_texture_cache.entries.find(key); + if (it != g_smoothed_texture_cache.entries.end() && it->second.smoothing == layer.smoothing && + it->second.source.lock() == layer.image_data) + return it->second.texture; + } + const float span = 0.05f * float(std::min(result.width, result.height)); + const float radius = std::clamp(layer.smoothing, 0.f, 1.f) * std::min(span, 48.f); smooth_height_pixels(result.pixels, result.width, result.height, radius); // Colour gets the same blur, per channel. It is the same knob for the same reason: detail in // the image finer than the mesh can carry is noise either way, and low-passing it here is the @@ -251,10 +331,131 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer result.rgb[i * 3 + size_t(c)] = channel[i]; } } + std::lock_guard lock(g_smoothed_texture_cache.mutex); + auto &entries = g_smoothed_texture_cache.entries; + for (auto it = entries.begin(); it != entries.end();) + it = it->second.source.expired() ? entries.erase(it) : std::next(it); + entries[key] = { std::weak_ptr>(layer.image_data), layer.smoothing, result }; } return result; } +namespace { +struct TextureDetailCache +{ + std::mutex mutex; + std::unordered_map>, TextureDetail>> entries; +}; +TextureDetailCache g_texture_detail_cache; +} // namespace + +TextureDetail analyze_texture_detail(const TextureDisplacementLayer &layer) +{ + TextureDetail out; + if (layer.empty()) + return out; + const void *key = layer.image_data.get(); + { + std::lock_guard lock(g_texture_detail_cache.mutex); + auto it = g_texture_detail_cache.entries.find(key); + if (it != g_texture_detail_cache.entries.end() && it->second.first.lock() == layer.image_data) + return it->second.second; + } + // On the image as imported: the Smoothing slider must not move the recommendation around. + TextureDisplacementLayer raw = layer; + raw.smoothing = 0.f; + const DecodedHeightTexture tex = decode_height_texture(raw); + const int w = tex.width, h = tex.height; + if (w >= 3 && h >= 3) { + double sum = 0.0; + size_t sharp = 0, n = 0; + for (int y = 1; y < h - 1; ++y) { + const uint8_t *row = tex.pixels.data() + size_t(y) * size_t(w); + for (int x = 1; x < w - 1; ++x) { + const float dx = 0.5f * (float(row[x + 1]) - float(row[x - 1])); + const float dy = 0.5f * (float(row[x + w]) - float(row[x - w])); + const float mag = std::sqrt(dx * dx + dy * dy); + sum += mag; + sharp += mag > 30.f; + ++n; + } + } + out.mean_gradient = float(sum / double(n)); + out.sharp_fraction = float(sharp) / float(n); + if (out.sharp_fraction > 0.15f || out.mean_gradient > 50.f) out.pixels_per_edge = 1.f; + else if (out.sharp_fraction > 0.05f || out.mean_gradient > 20.f) out.pixels_per_edge = 1.5f; + else if (out.mean_gradient > 8.f) out.pixels_per_edge = 2.5f; + else out.pixels_per_edge = 4.f; + } + std::lock_guard lock(g_texture_detail_cache.mutex); + auto &entries = g_texture_detail_cache.entries; + for (auto it = entries.begin(); it != entries.end();) + it = it->second.first.expired() ? entries.erase(it) : std::next(it); + entries[key] = { std::weak_ptr>(layer.image_data), out }; + return out; +} + +V2Resolution recommend_v2_resolution(const indexed_triangle_set &mesh, + const std::vector &layers, + const Transform3d &volume_to_world) +{ + // BumpMesh's smart resolution, the numbers included: equilateral-cover triangle density, a 16 M + // triangle refinement cap taken at 75 %, a 0.5 mm reference relief for the budget. + constexpr double TRIS_PER_AREA = 2.309, CAP_TRIANGLES = 16e6 * 0.75; + constexpr double EDGE_MIN = 0.05, EDGE_MAX = 5.0; + constexpr double BUDGET_MIN = 10e3, BUDGET_MAX = 2000e3, REF_DEPTH = 0.5, MIN_DEPTH = 0.1; + + V2Resolution out; + // The finest layer decides: the smallest detail edge (texel x pixels per edge) across the layers. + double detail_edge = std::numeric_limits::max(), depth = 0.0; + for (const TextureDisplacementLayer &layer : layers) { + if (layer.empty() || layer.tiling_scale <= 0.f) + continue; + const DecodedHeightTexture &tex = decode_height_texture(layer); + if (tex.width <= 0) + continue; + const TextureDetail detail = analyze_texture_detail(layer); + const double texel = double(layer.tiling_scale) / double(tex.width); + const double edge = texel * double(detail.pixels_per_edge); + if (edge < detail_edge) { + detail_edge = edge; + out.texel_mm = float(texel); + out.pixels_per_edge = detail.pixels_per_edge; + depth = std::abs(double(layer.depth_mm)); + } + } + if (out.texel_mm <= 0.f || mesh.vertices.empty()) + return out; + + // Surface area and diagonal in world mm: the tile is in world mm and the pipeline refines there. + double area = 0.0; + Vec3d bmin = Vec3d::Constant(std::numeric_limits::max()), bmax = -bmin; + std::vector world(mesh.vertices.size()); + for (size_t i = 0; i < world.size(); ++i) { + world[i] = volume_to_world * mesh.vertices[i].cast(); + bmin = bmin.cwiseMin(world[i]); + bmax = bmax.cwiseMax(world[i]); + } + for (const stl_triangle_vertex_indices &t : mesh.indices) + area += 0.5 * (world[size_t(t[1])] - world[size_t(t[0])]).cross(world[size_t(t[2])] - world[size_t(t[0])]).norm(); + const double diag = (bmax - bmin).norm(); + + const double budget_edge = std::sqrt(TRIS_PER_AREA * area / CAP_TRIANGLES); + double edge = std::max(detail_edge, budget_edge); + out.budget_bound = budget_edge > detail_edge; + const double hi = std::max(EDGE_MIN, std::min(EDGE_MAX, diag / 50.0)); + edge = std::clamp(edge, EDGE_MIN, hi); + edge = std::max(EDGE_MIN, std::ceil(edge * 100.0) / 100.0); // up, so the cap holds + out.edge_mm = float(edge); + + const double depth_scale = std::sqrt(REF_DEPTH / std::max(depth, MIN_DEPTH)); + const double target_edge = double(out.pixels_per_edge) * double(out.texel_mm) * depth_scale; + const double raw = TRIS_PER_AREA * area / (target_edge * target_edge); + const double stepped = std::round(raw / 10e3) * 10e3; + out.budget_k = int(std::clamp(stepped, BUDGET_MIN, BUDGET_MAX) / 1000.0); + return out; +} + Vec2f project_planar(const Vec3f &position, const Vec3f &normal) { // Planar-project onto the two axes orthogonal to the dominant component of `normal`. Called @@ -375,15 +576,114 @@ Vec3f face_normal(const indexed_triangle_set &mesh, const stl_triangle_vertex_in return (len > 1e-12f) ? Vec3f(n / len) : Vec3f::UnitZ(); } -// Groups triangles into charts: two triangles sharing an edge join the same chart only if the angle -// between their normals is below `seam_angle_deg`. Everything sharper than that is a seam, and the -// unwrap gets cut there instead of being forced to flatten across it. +// Whether a chart can be laid flat as one island: a topological disk (V - E + F = 1 with a single boundary loop, +// which LSCM requires) whose faces all point within ~100 degrees of their average - past that even a disk folds +// over itself when flattened. Degenerate faces carry no direction and are left out of the normal test. +bool chart_is_flattenable(const indexed_triangle_set &mesh, const std::vector &normals, const std::vector &faces) +{ + constexpr float MIN_NORMAL_COS = -0.17f; // cos(100 deg) + + std::unordered_map edge_use; + std::unordered_map local_vertex; + edge_use.reserve(faces.size() * 2); + local_vertex.reserve(faces.size()); + Vec3f normal_sum = Vec3f::Zero(); + for (const int f : faces) { + const stl_triangle_vertex_indices &tri = mesh.indices[size_t(f)]; + for (int i = 0; i < 3; ++i) { + ++edge_use[undirected_edge_key(tri[i], tri[(i + 1) % 3])]; + local_vertex.emplace(tri[i], int(local_vertex.size())); + } + normal_sum += normals[size_t(f)]; + } + if (int(local_vertex.size()) - int(edge_use.size()) + int(faces.size()) != 1) + return false; + + UnionFind loops(local_vertex.size()); + int boundary_seed = -1; + for (const auto &[key, uses] : edge_use) { + if (uses > 2) + return false; // non-manifold + if (uses == 1) { + const int a = local_vertex[int(key >> 32)], b = local_vertex[int(uint32_t(key))]; + loops.unite(a, b); + boundary_seed = a; + } + } + if (boundary_seed < 0) + return false; // closed + const int loop = loops.find(boundary_seed); + for (const auto &[key, uses] : edge_use) + if (uses == 1 && loops.find(local_vertex[int(key >> 32)]) != loop) + return false; // a second boundary loop: a ring, or a disk with a hole + + const float len = normal_sum.norm(); + if (len < 1e-6f) + return false; + const Vec3f mean = normal_sum / len; + for (const int f : faces) { + const stl_triangle_vertex_indices &tri = mesh.indices[size_t(f)]; + const bool degenerate = (mesh.vertices[tri[1]] - mesh.vertices[tri[0]]).cross(mesh.vertices[tri[2]] - mesh.vertices[tri[0]]) + .squaredNorm() < 1e-20f; + if (!degenerate && normals[size_t(f)].dot(mean) < MIN_NORMAL_COS) + return false; + } + return true; +} + +// Which side of a cut each face of an unflattenable chart goes to (true / false, parallel to `faces`). The cut +// separates the faces' normals along their widest spread, so a tube splits lengthwise into two half-tubes and a +// closed sphere into two hemispheres. Where the normals barely vary (a flat ring) it splits the face centroids +// along their longest axis instead. +std::vector split_chart_sides(const indexed_triangle_set &mesh, const std::vector &normals, const std::vector &faces) +{ + const auto principal_axis = [](const std::vector &samples, Vec3f &mean, Vec3f &axis) { + mean = Vec3f::Zero(); + for (const Vec3f &s : samples) + mean += s; + mean /= float(samples.size()); + Eigen::Matrix3f covariance = Eigen::Matrix3f::Zero(); + for (const Vec3f &s : samples) { + const Vec3f d = s - mean; + covariance += d * d.transpose(); + } + const Eigen::SelfAdjointEigenSolver solver(covariance); + axis = solver.eigenvectors().col(2); // eigenvalues come sorted ascending + return solver.eigenvalues()(2) / float(samples.size()); + }; + + std::vector samples(faces.size()); + for (size_t i = 0; i < faces.size(); ++i) + samples[i] = normals[size_t(faces[i])]; + Vec3f mean, axis; + if (principal_axis(samples, mean, axis) < 1e-3f) { + for (size_t i = 0; i < faces.size(); ++i) { + const stl_triangle_vertex_indices &tri = mesh.indices[size_t(faces[i])]; + samples[i] = (mesh.vertices[tri[0]] + mesh.vertices[tri[1]] + mesh.vertices[tri[2]]) / 3.f; + } + principal_axis(samples, mean, axis); + } + std::vector side(faces.size()); + for (size_t i = 0; i < faces.size(); ++i) + side[i] = (samples[i] - mean).dot(axis) > 0.f; + return side; +} + +// Groups triangles into charts. First two triangles sharing an edge join the same chart only if the angle between +// their normals is below `seam_angle_deg` (and the edge is not a marked seam): everything sharper is a seam. The +// test uses each face's own normal - averaging in its neighbours, as this once did, cuts low-poly flat faces apart, +// because the two triangles of one cube face average in different neighbouring faces. +// +// Then every chart that cannot be laid flat as one island (see chart_is_flattenable()) is cut in two and each +// connected piece checked again: a cylinder's smooth side is one chart by angle but a ring, which no flattening +// can open without a cut, and a sphere is closed. std::vector segment_into_charts(const indexed_triangle_set &mesh, const std::vector &normals, float seam_angle_deg, const std::unordered_set &seam_keys, int &chart_count) { - const int n_faces = int(mesh.indices.size()); - const float cos_threshold = std::cos(std::clamp(seam_angle_deg, 0.f, 180.f) * float(M_PI) / 180.f); + constexpr int MAX_SPLIT_DEPTH = 8; + const int n_faces = int(mesh.indices.size()); + const float cos_threshold = std::cos(std::clamp(seam_angle_deg, 0.f, 180.f) * float(M_PI) / 180.f); // Each shared edge, with the (up to two) faces on it. std::unordered_map> edge_faces; @@ -398,47 +698,102 @@ std::vector segment_into_charts(const indexed_triangle_set &mesh, const std } } - // The chart-join test compares a *neighbourhood-averaged* normal per face - the face plus its - // edge-adjacent neighbours (~5 samples) - rather than the single face normal. On a finely - // tessellated curved surface this stops one noisy triangle from spuriously cutting (or a lone - // near-flat sliver from wrongly merging) a chart, while a genuine sharp crease, where the whole - // neighbourhood on each side agrees, still cuts. This is the "use 5 points, not one" refinement. - std::vector smoothed(mesh.indices.size()); - for (int f = 0; f < n_faces; ++f) { - Vec3f acc = normals[size_t(f)]; - const stl_triangle_vertex_indices &tri = mesh.indices[size_t(f)]; - for (int i = 0; i < 3; ++i) { - const auto it = edge_faces.find(undirected_edge_key(tri[i], tri[(i + 1) % 3])); - if (it == edge_faces.end()) - continue; - const int nb = (it->second.first == f) ? it->second.second : it->second.first; - if (nb >= 0) - acc += normals[size_t(nb)]; - } - smoothed[size_t(f)] = (acc.norm() > 1e-8f) ? Vec3f(acc.normalized()) : normals[size_t(f)]; - } - - UnionFind uf(mesh.indices.size()); + // Faces joined across each edge that does not cut. A face has three edges, so at most three such neighbours. + std::vector> adjacent(static_cast(n_faces), { -1, -1, -1 }); + const auto link = [&adjacent](int f, int nb) { + for (int &slot : adjacent[size_t(f)]) + if (slot < 0) { + slot = nb; + return; + } + }; for (const auto &[key, fp] : edge_faces) { - if (fp.second < 0) + if (fp.second < 0 || fp.first == fp.second) continue; // a boundary edge of the patch, nothing on the far side to join // A manually/auto marked seam always cuts, whatever the dihedral angle - that is exactly // what lets "mark seam" / "cut island" split a chart that is otherwise flat enough to merge. if (!seam_keys.empty() && seam_keys.count(key)) continue; - if (smoothed[size_t(fp.first)].dot(smoothed[size_t(fp.second)]) >= cos_threshold) - uf.unite(fp.first, fp.second); + if (normals[size_t(fp.first)].dot(normals[size_t(fp.second)]) >= cos_threshold) { + link(fp.first, fp.second); + link(fp.second, fp.first); + } } - std::vector chart_of(mesh.indices.size(), -1); - std::unordered_map root_to_chart; - chart_count = 0; - for (int f = 0; f < n_faces; ++f) { - const auto [it, inserted] = root_to_chart.emplace(uf.find(f), chart_count); - if (inserted) - ++chart_count; - chart_of[size_t(f)] = it->second; + // Connected pieces of `faces` over `adjacent`, staying within each face's current `group`. + std::vector group(static_cast(n_faces), 0), visited(static_cast(n_faces), -1); + int visit_pass = 0; + const auto connected_pieces = [&](const std::vector &faces, std::vector> &out) { + const int pass = visit_pass++; + std::vector stack; + for (const int seed : faces) { + if (visited[size_t(seed)] == pass) + continue; + visited[size_t(seed)] = pass; + std::vector piece{ seed }; + stack.assign(1, seed); + while (!stack.empty()) { + const int f = stack.back(); + stack.pop_back(); + for (const int nb : adjacent[size_t(f)]) + if (nb >= 0 && visited[size_t(nb)] != pass && group[size_t(nb)] == group[size_t(f)]) { + visited[size_t(nb)] = pass; + piece.push_back(nb); + stack.push_back(nb); + } + } + out.push_back(std::move(piece)); + } + }; + + std::vector all_faces(static_cast(n_faces)); + std::iota(all_faces.begin(), all_faces.end(), 0); + std::vector> pieces; + connected_pieces(all_faces, pieces); + std::vector, int>> pending; // a piece, and how many cuts produced it + for (std::vector &piece : pieces) + pending.emplace_back(std::move(piece), 0); + + std::vector> charts; + int next_group = 1; + while (!pending.empty()) { + std::vector faces = std::move(pending.back().first); + const int depth = pending.back().second; + pending.pop_back(); + if (faces.size() < 2 || depth >= MAX_SPLIT_DEPTH || chart_is_flattenable(mesh, normals, faces)) { + charts.push_back(std::move(faces)); + continue; + } + const std::vector side = split_chart_sides(mesh, normals, faces); + std::vector halves[2]; + for (size_t i = 0; i < faces.size(); ++i) + halves[side[i] ? 1 : 0].push_back(faces[i]); + if (halves[0].empty() || halves[1].empty()) { + charts.push_back(std::move(faces)); + continue; + } + for (std::vector &half : halves) { + for (const int f : half) + group[size_t(f)] = next_group; + ++next_group; + pieces.clear(); + connected_pieces(half, pieces); + for (std::vector &piece : pieces) + pending.emplace_back(std::move(piece), depth + 1); + } } + + // Chart ids in first-encountered-triangle order, which TextureIsland indexing documents. + std::vector> first_face(charts.size()); + for (size_t c = 0; c < charts.size(); ++c) + first_face[c] = { *std::min_element(charts[c].begin(), charts[c].end()), c }; + std::sort(first_face.begin(), first_face.end()); + + std::vector chart_of(static_cast(n_faces), -1); + for (size_t id = 0; id < first_face.size(); ++id) + for (const int f : charts[first_face[id].second]) + chart_of[size_t(f)] = int(id); + chart_count = int(charts.size()); return chart_of; } @@ -833,22 +1188,107 @@ bool join_chart_placement(const PatchUnwrap &unwrap, const std::vector; + +// Whether two triangles overlap by more than `eps` (separating axis test). Triangles that merely share an edge or +// a corner, as neighbours in a net do, do not. +bool triangles_overlap(const Tri2 &a, const Tri2 &b, float eps) +{ + for (const Tri2 *t : { &a, &b }) + for (int i = 0; i < 3; ++i) { + const Vec2f edge = (*t)[(i + 1) % 3] - (*t)[i]; + const float len = edge.norm(); + if (len < 1e-12f) + continue; + const Vec2f axis(-edge.y() / len, edge.x() / len); + float a_min = std::numeric_limits::max(), a_max = std::numeric_limits::lowest(); + float b_min = a_min, b_max = a_max; + for (int k = 0; k < 3; ++k) { + const float pa = axis.dot(a[k]), pb = axis.dot(b[k]); + a_min = std::min(a_min, pa); + a_max = std::max(a_max, pa); + b_min = std::min(b_min, pb); + b_max = std::max(b_max, pb); + } + if (a_max <= b_min + eps || b_max <= a_min + eps) + return false; + } + return true; +} + +// The triangles already placed in one net, bucketed in a uniform grid so a candidate chart is only tested against +// its neighbourhood. Triangles spanning many cells go into a list that is tested against everything instead. +struct NetGrid +{ + static constexpr int BIG_SPAN = 16; + float cell; + float eps; + std::unordered_map> cells; + std::vector big; + + static uint64_t key(int x, int y) { return (uint64_t(uint32_t(x)) << 32) | uint32_t(y); } + bool range(const Tri2 &t, int &x0, int &y0, int &x1, int &y1) const + { + const Vec2f lo = t[0].cwiseMin(t[1]).cwiseMin(t[2]), hi = t[0].cwiseMax(t[1]).cwiseMax(t[2]); + x0 = int(std::floor(lo.x() / cell)); + y0 = int(std::floor(lo.y() / cell)); + x1 = int(std::floor(hi.x() / cell)); + y1 = int(std::floor(hi.y() / cell)); + return x1 - x0 <= BIG_SPAN && y1 - y0 <= BIG_SPAN; + } + bool overlaps(const Tri2 &t) const + { + for (const Tri2 &b : big) + if (triangles_overlap(t, b, eps)) + return true; + int x0, y0, x1, y1; + if (!range(t, x0, y0, x1, y1)) { + for (const auto &[k, tris] : cells) + for (const Tri2 &b : tris) + if (triangles_overlap(t, b, eps)) + return true; + return false; + } + for (int x = x0; x <= x1; ++x) + for (int y = y0; y <= y1; ++y) + if (const auto it = cells.find(key(x, y)); it != cells.end()) + for (const Tri2 &b : it->second) + if (triangles_overlap(t, b, eps)) + return true; + return false; + } + void insert(const Tri2 &t) + { + int x0, y0, x1, y1; + if (!range(t, x0, y0, x1, y1)) { + big.push_back(t); + return; + } + for (int x = x0; x <= x1; ++x) + for (int y = y0; y <= y1; ++y) + cells[key(x, y)].push_back(t); + } +}; +} // namespace + std::vector compute_connected_net(const PatchUnwrap &unwrap) { - std::vector islands(size_t(std::max(unwrap.chart_count, 0))); - if (unwrap.chart_count <= 1) + const int n = std::max(unwrap.chart_count, 0); + std::vector islands(static_cast(n)); + if (n <= 1) return islands; // Chart adjacency, with one representative shared edge per adjacent pair. const auto edges = build_shared_edges(unwrap); struct PairEdge { ChartEdge a, b; }; std::map, PairEdge> pair_edge; - std::vector> adj(size_t(unwrap.chart_count)); + std::vector> adj(static_cast(n)); for (const auto &[base_edge, list] : edges) { for (size_t i = 0; i < list.size(); ++i) for (size_t j = i + 1; j < list.size(); ++j) { const int c1 = list[i].chart, c2 = list[j].chart; - if (c1 == c2 || c1 < 0 || c2 < 0 || c1 >= unwrap.chart_count || c2 >= unwrap.chart_count) + if (c1 == c2 || c1 < 0 || c2 < 0 || c1 >= n || c2 >= n) continue; const std::pair pk{ std::min(c1, c2), std::max(c1, c2) }; if (pair_edge.count(pk)) @@ -859,63 +1299,146 @@ std::vector compute_connected_net(const PatchUnwrap &unwrap) } } - std::vector placed(size_t(unwrap.chart_count), false); - std::vector pmin(size_t(unwrap.chart_count)), pmax(size_t(unwrap.chart_count)); - const auto chart_bbox = [&](int c, Vec2f &lo, Vec2f &hi) { - lo = Vec2f(std::numeric_limits::max(), std::numeric_limits::max()); - hi = Vec2f(std::numeric_limits::lowest(), std::numeric_limits::lowest()); - for (size_t i = 0; i < unwrap.uvs.size(); ++i) - if (unwrap.vertex_chart[i] == c) { - const Vec2f p = apply_island_transform(unwrap.uvs[i], c, unwrap, islands); - lo = lo.cwiseMin(p); - hi = hi.cwiseMax(p); - } - }; - const auto overlaps_placed = [&](int c, const Vec2f &lo, const Vec2f &hi) { - // A small inset, so charts that merely share an edge (touching bboxes) aren't judged to overlap. - const Vec2f eps = 0.02f * (hi - lo).cwiseAbs(); - for (int o = 0; o < unwrap.chart_count; ++o) - if (placed[size_t(o)] && o != c && - lo.x() + eps.x() < pmax[size_t(o)].x() && hi.x() - eps.x() > pmin[size_t(o)].x() && - lo.y() + eps.y() < pmax[size_t(o)].y() && hi.y() - eps.y() > pmin[size_t(o)].y()) - return true; - return false; + // Per chart: its vertices, its triangles and its flattened area. + std::vector> chart_verts(static_cast(n)), chart_tris(static_cast(n)); + std::vector chart_area(static_cast(n), 0.f); + for (size_t i = 0; i < unwrap.uvs.size(); ++i) + if (const int c = unwrap.vertex_chart[i]; c >= 0 && c < n) + chart_verts[size_t(c)].push_back(int(i)); + float extent_sum = 0.f, extent = 0.f; + for (size_t t = 0; t < unwrap.indices.size(); ++t) { + const stl_triangle_vertex_indices &tri = unwrap.indices[t]; + const int c = unwrap.vertex_chart[size_t(tri[0])]; + if (c < 0 || c >= n) + continue; + chart_tris[size_t(c)].push_back(int(t)); + const Vec2f &p0 = unwrap.uvs[size_t(tri[0])], &p1 = unwrap.uvs[size_t(tri[1])], &p2 = unwrap.uvs[size_t(tri[2])]; + const Vec2f e0 = p1 - p0, e1 = p2 - p0; + chart_area[size_t(c)] += 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x()); + const Vec2f size = p0.cwiseMax(p1).cwiseMax(p2) - p0.cwiseMin(p1).cwiseMin(p2); + extent_sum += std::max(size.x(), size.y()); + extent = std::max({ extent, p0.cwiseAbs().maxCoeff(), p1.cwiseAbs().maxCoeff(), p2.cwiseAbs().maxCoeff() }); + } + const float cell = std::max(extent_sum / float(std::max(unwrap.indices.size(), 1)), 1e-6f); + // Touching neighbours may overlap by rounding: a fraction of a typical triangle, but at least what float + // rounding of a placement at this distance from the origin can produce. + const float eps = std::max(0.02f * cell, 4e-6f * extent); + + const auto placed = [&](const Eigen::Matrix &m, int t) { + const stl_triangle_vertex_indices &tri = unwrap.indices[size_t(t)]; + Tri2 out; + for (int k = 0; k < 3; ++k) + out[size_t(k)] = m.block<2, 2>(0, 0) * unwrap.uvs[size_t(tri[k])] + m.col(2); + return out; }; - // BFS from chart 0 (kept at its packed position), unfolding each newly reached chart onto its parent. - std::queue q; - placed[0] = true; - chart_bbox(0, pmin[0], pmax[0]); - q.push(0); - while (!q.empty()) { - const int p = q.front(); - q.pop(); - for (const int c : adj[size_t(p)]) { - if (placed[size_t(c)]) - continue; - const std::pair pk{ std::min(p, c), std::max(p, c) }; - const auto it = pair_edge.find(pk); - if (it == pair_edge.end()) - continue; - const ChartEdge &pe = (p < c) ? it->second.a : it->second.b; - const ChartEdge &ce = (p < c) ? it->second.b : it->second.a; - const Vec2f pp0 = apply_island_transform(unwrap.uvs[size_t(pe.uv_lo)], p, unwrap, islands); - const Vec2f pp1 = apply_island_transform(unwrap.uvs[size_t(pe.uv_hi)], p, unwrap, islands); - islands[size_t(c)] = solve_edge_alignment(unwrap.uvs[size_t(ce.uv_lo)], unwrap.uvs[size_t(ce.uv_hi)], pp0, - pp1, unwrap.chart_centroid[size_t(c)]); + // Grow a net from the largest chart not yet in one, unfolding each neighbour onto the chart it was reached from + // (bigger neighbours first, so slivers don't claim the good spots) unless its triangles would overlap the net. + // A chart that doesn't fit stays out and roots a net of its own later, so nothing is left in a random spot. + std::vector by_area(static_cast(n)); + std::iota(by_area.begin(), by_area.end(), 0); + std::stable_sort(by_area.begin(), by_area.end(), [&chart_area](int a, int b) { return chart_area[size_t(a)] > chart_area[size_t(b)]; }); - Vec2f lo, hi; - chart_bbox(c, lo, hi); - if (overlaps_placed(c, lo, hi)) { - islands[size_t(c)] = TextureIsland{}; // would collide: leave it where the packing put it - continue; // and don't unfold its subtree off a packed chart + std::vector net_of(static_cast(n), -1); + int net_count = 0; + for (const int root : by_area) { + if (net_of[size_t(root)] >= 0 || chart_tris[size_t(root)].empty()) + continue; + const int net = net_count++; + NetGrid grid{ cell, eps, {}, {} }; + net_of[size_t(root)] = net; + { + const Eigen::Matrix m = island_transform_matrix(root, unwrap, islands); + for (const int t : chart_tris[size_t(root)]) + grid.insert(placed(m, t)); + } + std::queue q; + q.push(root); + while (!q.empty()) { + const int p = q.front(); + q.pop(); + std::vector neighbours = adj[size_t(p)]; + std::stable_sort(neighbours.begin(), neighbours.end(), + [&chart_area](int a, int b) { return chart_area[size_t(a)] > chart_area[size_t(b)]; }); + for (const int c : neighbours) { + if (net_of[size_t(c)] >= 0 || chart_tris[size_t(c)].empty()) + continue; + const auto it = pair_edge.find({ std::min(p, c), std::max(p, c) }); + if (it == pair_edge.end()) + continue; + const ChartEdge &pe = (p < c) ? it->second.a : it->second.b; + const ChartEdge &ce = (p < c) ? it->second.b : it->second.a; + const Vec2f pp0 = apply_island_transform(unwrap.uvs[size_t(pe.uv_lo)], p, unwrap, islands); + const Vec2f pp1 = apply_island_transform(unwrap.uvs[size_t(pe.uv_hi)], p, unwrap, islands); + islands[size_t(c)] = solve_edge_alignment(unwrap.uvs[size_t(ce.uv_lo)], unwrap.uvs[size_t(ce.uv_hi)], pp0, + pp1, unwrap.chart_centroid[size_t(c)]); + + const Eigen::Matrix m = island_transform_matrix(c, unwrap, islands); + std::vector tris; + tris.reserve(chart_tris[size_t(c)].size()); + bool fits = true; + for (const int t : chart_tris[size_t(c)]) { + tris.push_back(placed(m, t)); + if (grid.overlaps(tris.back())) { + fits = false; + break; + } + } + if (!fits) { + islands[size_t(c)] = TextureIsland{}; + continue; + } + for (const Tri2 &t : tris) + grid.insert(t); + net_of[size_t(c)] = net; + q.push(c); } - placed[size_t(c)] = true; - pmin[size_t(c)] = lo; - pmax[size_t(c)] = hi; - q.push(c); } } + + // Shelf-pack the nets side by side, tallest first, the way compute_patch_unwrap() packs charts. + std::vector lo(static_cast(net_count), Vec2f::Constant(std::numeric_limits::max())); + std::vector hi(static_cast(net_count), Vec2f::Constant(std::numeric_limits::lowest())); + for (int c = 0; c < n; ++c) { + const int net = net_of[size_t(c)]; + if (net < 0) + continue; + const Eigen::Matrix m = island_transform_matrix(c, unwrap, islands); + for (const int v : chart_verts[size_t(c)]) { + const Vec2f p = m.block<2, 2>(0, 0) * unwrap.uvs[size_t(v)] + m.col(2); + lo[size_t(net)] = lo[size_t(net)].cwiseMin(p); + hi[size_t(net)] = hi[size_t(net)].cwiseMax(p); + } + } + float total_area = 0.f, widest = 0.f; + for (int k = 0; k < net_count; ++k) { + const Vec2f size = hi[size_t(k)] - lo[size_t(k)]; + total_area += size.x() * size.y(); + widest = std::max(widest, size.x()); + } + const float shelf_width = std::max(widest, std::sqrt(std::max(total_area, 0.f)) * 1.4f); + const float margin = std::max(shelf_width * 0.02f, 1e-4f); + std::vector order(static_cast(net_count)); + std::iota(order.begin(), order.end(), 0); + std::stable_sort(order.begin(), order.end(), [&](int a, int b) { + return hi[size_t(a)].y() - lo[size_t(a)].y() > hi[size_t(b)].y() - lo[size_t(b)].y(); + }); + std::vector shift(static_cast(net_count), Vec2f::Zero()); + float cursor_x = 0.f, cursor_y = 0.f, row_height = 0.f; + for (const int k : order) { + const Vec2f size = hi[size_t(k)] - lo[size_t(k)]; + if (cursor_x > 0.f && cursor_x + size.x() > shelf_width) { + cursor_x = 0.f; + cursor_y += row_height + margin; + row_height = 0.f; + } + shift[size_t(k)] = Vec2f(cursor_x, cursor_y) - lo[size_t(k)]; + cursor_x += size.x() + margin; + row_height = std::max(row_height, size.y()); + } + for (int c = 0; c < n; ++c) + if (net_of[size_t(c)] >= 0) + islands[size_t(c)].offset += shift[size_t(net_of[size_t(c)])]; return islands; } @@ -931,6 +1454,30 @@ void average_island_scales(std::vector &islands) island.scale = mean; } +std::vector apply_lscm_uv_overrides(PatchUnwrap &unwrap, const std::vector> &overrides) +{ + std::vector edited(unwrap.uvs.size(), false); + if (overrides.empty()) + return edited; + // Mesh-vertex keys first, so an edit on one copy of the same vertex takes precedence over them. + std::map by_mesh_vertex; + for (const auto &[key, uv] : overrides) + if (key >= 0) + by_mesh_vertex[key] = uv; + if (!by_mesh_vertex.empty()) + for (size_t i = 0; i < unwrap.uvs.size(); ++i) + if (const auto it = by_mesh_vertex.find(unwrap.source_vertex[i]); it != by_mesh_vertex.end()) { + unwrap.uvs[i] = it->second; + edited[i] = true; + } + for (const auto &[key, uv] : overrides) + if (const int i = -key - 1; key < 0 && size_t(i) < unwrap.uvs.size()) { + unwrap.uvs[size_t(i)] = uv; + edited[size_t(i)] = true; + } + return edited; +} + std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) { // Padding disabled (0), matching the UV editor: the packed islands the editor shows and the ones the @@ -940,33 +1487,68 @@ std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const Tex if (unwrap.empty()) return {}; - // Manual per-vertex UV edits (UV editor Vertex/Edge modes) override the automatic raw unwrap - // coordinate for a mesh vertex, before the island transform - so the edit rides along with any - // island move/rotate exactly like the rest of the island. See TextureDisplacementLayer:: - // lscm_uv_overrides. Small (hand edits), so a plain map is ample. - std::map overrides; - for (const auto &[mv, uv] : layer.lscm_uv_overrides) - overrides[mv] = uv; + // Manual UV edits (UV editor Vertex/Edge modes) replace the automatic raw unwrap coordinate, before the island + // transform - so the edit rides along with any island move/rotate exactly like the rest of the island. + PatchUnwrap edited_unwrap = unwrap; + const std::vector edited = apply_lscm_uv_overrides(edited_unwrap, layer.lscm_uv_overrides); // One UV per patch vertex: a seam vertex has several (one per chart it touches) and has to // settle on one, since it can only be displaced to a single position. See compute_lscm_uvs()'s - // header comment - the surface stays watertight regardless. + // header comment - the surface stays watertight regardless. A copy that was edited by hand wins, + // so the edit is what bakes; otherwise the first copy does. std::vector per_vertex(patch.vertices.size(), Vec2f::Zero()); std::vector assigned(patch.vertices.size(), false); - for (size_t i = 0; i < unwrap.uvs.size(); ++i) { - const int pv = unwrap.source_vertex[i]; - if (pv >= 0 && !assigned[size_t(pv)]) { - Vec2f raw = unwrap.uvs[i]; - const auto it = overrides.find(pv); - if (it != overrides.end()) - raw = it->second; - per_vertex[size_t(pv)] = apply_island_transform(raw, unwrap.vertex_chart[i], unwrap, layer.islands); + for (const bool edited_pass : { true, false }) + for (size_t i = 0; i < edited_unwrap.uvs.size(); ++i) { + const int pv = edited_unwrap.source_vertex[i]; + if (pv < 0 || assigned[size_t(pv)] || edited[i] != edited_pass) + continue; + per_vertex[size_t(pv)] = apply_island_transform(edited_unwrap.uvs[i], edited_unwrap.vertex_chart[i], unwrap, layer.islands); assigned[size_t(pv)] = true; } - } return per_vertex; } +namespace { +// apply_uv_transform()'s per-layer constants, worked out once. Triplanar sampling runs the transform +// three times per point, and recomputing the rotation's cos/sin and the tiling reciprocal on every one +// of them was most of the per-sample arithmetic. +struct UVTransform +{ + float scale, cs, sn, aspect; + Vec2f offset; + + UVTransform(const TextureDisplacementLayer &layer, float aspect_) + { + scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f; + const float rad = layer.rotation_deg * float(M_PI) / 180.f; + cs = std::cos(rad); + sn = std::sin(rad); + aspect = aspect_; + offset = layer.offset; + } + Vec2f operator()(const Vec2f &planar) const + { + const Vec2f scaled = planar * scale; + Vec2f rotated(scaled.x() * cs - scaled.y() * sn, scaled.x() * sn + scaled.y() * cs); + // See apply_uv_transform() for why the aspect correction follows the rotation. + if (aspect > 0.f && aspect != 1.f) + rotated.y() *= aspect; + return rotated + offset; + } +}; + +// |n|^TRIPLANAR_BLEND_SHARPNESS per component. The exponent is a compile-time 4, so two squarings do +// what three std::pow calls per sample did. +static_assert(TRIPLANAR_BLEND_SHARPNESS == 4.f, "triplanar_weights() hard-codes the fourth power"); +inline Vec3f triplanar_weights(const Vec3f &normal) +{ + Vec3f w = normal.cwiseAbs(); + w = w.cwiseProduct(w); + return w.cwiseProduct(w); +} +} // namespace + Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer, float aspect) { const float scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f; @@ -1040,9 +1622,10 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp // width / height of the height map, so a non-square image keeps its proportions (see // apply_uv_transform()). Every projection except the projective "from view" one funnels through // here, so this one line is what makes them all aspect-correct. - const float aspect = (texture.height > 0) ? float(texture.width) / float(texture.height) : 1.f; + const float aspect = (texture.height > 0) ? float(texture.width) / float(texture.height) : 1.f; + const UVTransform xf(layer, aspect); auto sample_at = [&](const Vec2f &planar) { - return texture.sample(apply_uv_transform(planar, layer, aspect), layer.tile_enabled, layer.tile_method); + return texture.sample(xf(planar), layer.tile_enabled, layer.tile_method); }; // Precomputed per-patch LSCM solve wins over the layer's own method (see the header): the @@ -1080,9 +1663,7 @@ float sample_layer_height(const DecodedHeightTexture &texture, const TextureDisp // hard switch is what produced a visible seam wherever the dominant axis flips (see // TextureProjectionMethod::Triplanar); a weighted blend is continuous across that transition // by construction, since the weight of the axis being left behind falls smoothly to zero. - Vec3f w = normal.cwiseAbs(); - w = Vec3f(std::pow(w.x(), TRIPLANAR_BLEND_SHARPNESS), std::pow(w.y(), TRIPLANAR_BLEND_SHARPNESS), - std::pow(w.z(), TRIPLANAR_BLEND_SHARPNESS)); + Vec3f w = triplanar_weights(normal); const float w_sum = w.x() + w.y() + w.z(); if (w_sum < 1e-8f) // Degenerate normal: no axis is meaningfully dominant, so no blend is meaningful either. @@ -1107,17 +1688,17 @@ bool sample_layer_color(const DecodedHeightTexture &texture, const TextureDispla // the two differ in what "nothing here" means. Height returns 0, which is a perfectly good height // (no displacement); colour has no such neutral value - black is a colour - so every path that // returns 0 there has to report false here instead, and the caller leaves the triangle uncoloured. - const float aspect = (texture.height > 0) ? float(texture.width) / float(texture.height) : 1.f; + const float aspect = (texture.height > 0) ? float(texture.width) / float(texture.height) : 1.f; + const UVTransform xf(layer, aspect); auto sample_at = [&](const Vec2f &planar) { - return texture.sample_color(apply_uv_transform(planar, layer, aspect), layer.tile_enabled, - layer.tile_method); + return texture.sample_color(xf(planar), layer.tile_enabled, layer.tile_method); }; // Outside a non-tiled placement there is no texture at all - the same hard edge sample() gives the // height. Checked explicitly because sample_color() reports it as black, which is a real colour. auto covered = [&](const Vec2f &planar) { if (layer.tile_enabled) return true; - const Vec2f uv = apply_uv_transform(planar, layer, aspect); + const Vec2f uv = xf(planar); return uv.x() >= 0.f && uv.x() < 1.f && uv.y() >= 0.f && uv.y() < 1.f; }; @@ -1168,9 +1749,7 @@ bool sample_layer_color(const DecodedHeightTexture &texture, const TextureDispla // Blended tri-planar, weighted exactly as the height is, so colour and relief stay registered // across the cross-fade band at a 90-degree edge. - Vec3f w = normal.cwiseAbs(); - w = Vec3f(std::pow(w.x(), TRIPLANAR_BLEND_SHARPNESS), std::pow(w.y(), TRIPLANAR_BLEND_SHARPNESS), - std::pow(w.z(), TRIPLANAR_BLEND_SHARPNESS)); + Vec3f w = triplanar_weights(normal); const float w_sum = w.x() + w.y() + w.z(); if (w_sum < 1e-8f) { const Vec2f p(position.x(), position.y()); @@ -1372,7 +1951,10 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set const std::vector &layers, const TextureDisplacementFacetsData &facets_data, const TextureDisplacementOptions &options, - const DisplacementProgressFn &progress) + const DisplacementProgressFn &progress, + const TextureColorRequest *color, + bool flip_normals, + BakeStageRecorder *debug) { HeightFieldSampler combined = make_combined_displacement_sampler(mesh, layers, facets_data); if (!combined) @@ -1400,22 +1982,36 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set if (std::all_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; })) return mesh; // nothing painted + // "Auto" resolution and budget (0 and -1) resolve here, from the texture and the model - the mesh + // is already in world mm at this point, so no transform is needed. + const bool auto_edge = options.v2_refine_mm <= 0.f, auto_budget = options.v2_max_triangles_k < 0; + const V2Resolution rec = (auto_edge || auto_budget) ? recommend_v2_resolution(mesh, layers) : V2Resolution{}; TextureBake::PipelineSettings settings; - settings.refine_length = std::max(0.01f, options.v2_refine_mm); + settings.refine_length = auto_edge ? std::max(0.05, double(rec.edge_mm)) : std::max(0.01f, options.v2_refine_mm); settings.regularize = options.v2_regularize; - settings.max_triangles = size_t(std::max(0, options.v2_max_triangles_k)) * 1000; + settings.max_triangles = size_t(auto_budget ? std::max(0, rec.budget_k) : options.v2_max_triangles_k) * 1000; + BOOST_LOG_TRIVIAL(info) << "TextureBake resolution: " << settings.refine_length << " mm" + << (auto_edge ? " (auto)" : "") << ", budget " << settings.max_triangles / 1000 << " k" + << (auto_budget ? " (auto)" : ""); settings.preserve_untextured = true; - settings.clamp_below_plate = options.v2_clamp_below_plate; + // Always on: relief driven under the plate is unprintable whichever pipeline produced it, so this + // is no longer a choice the user has to make. Only geometry that ends up below the model's own + // bottom is moved - downward relief that stays clear of the plate is untouched. + settings.clamp_below_plate = true; settings.relocate = options.v2_relocate; + settings.flip_edges = options.v2_flip_edges; // The sampler already returns millimetres, so the displacement stage must not scale it again. settings.displace.amplitude = 1.f; settings.displace.symmetric = false; // The paint decides what moves here, so the angle limits stay off. settings.displace.bottom_angle_limit = 0.f; settings.displace.top_angle_limit = 0.f; - // The sampler above takes the smooth normal, never the smoothed blend normal, so computing the - // latter would build a full adjacency graph and run its iterations for a value nothing reads. - settings.displace.blend_normal_smoothing = 0; + // The sampler's normal only picks the projection blend (triplanar weights); the displacement + // direction is the pipeline's own smooth normal. Handing it the Laplacian-smoothed blend normal + // spreads a crease's 50/50 blend over a band of rows instead of one: on a cube edge the single row + // of vertices that samples both faces' patterns half and half otherwise comes out as a row of + // notches, since it matches neither face. + settings.displace.blend_normal_smoothing = 32; TextureBake::DisplaceBounds bounds; bounds.min = bounds.max = mesh.vertices.empty() ? Vec3f::Zero() : mesh.vertices.front(); @@ -1424,39 +2020,130 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set bounds.max = bounds.max.cwiseMax(v); } - const auto sample = [&combined](const Vec3f &pos, const Vec3f &smooth_normal, const Vec3f &) { - // The smooth normal: the direction the vertex actually moves along. - return combined(pos, smooth_normal); + const auto sample = [&combined](const Vec3f &pos, const Vec3f &, const Vec3f &blend_normal) { + // The blend normal chooses the projection; the move itself is along the smooth normal, which + // the pipeline applies on its own. + return combined(pos, blend_normal); }; + // The pipeline takes its displacement direction from the soup's winding, so a mirrored placement + // would drive the whole relief inwards. The paint masks were read off `mesh` above, against its + // own vertex order, so the winding can only be turned round after that - here, on the copy that + // becomes the soup - and has to be turned back on the way out, since the caller undoes the same + // mirror when it maps the result back into the volume's coordinates. + indexed_triangle_set oriented = mesh; + if (flip_normals) + for (stl_triangle_vertex_indices &t : oriented.indices) + std::swap(t[1], t[2]); + // 0 means no simplification, i.e. Bake mode. const TextureBake::PipelineMode mode = settings.max_triangles > 0 ? TextureBake::PipelineMode::Export : TextureBake::PipelineMode::Bake; - TextureBake::PipelineResult result = TextureBake::run_pipeline( - TextureBake::to_soup(mesh, excluded), sample, settings, bounds, mode, excluded, + // The pipeline works on `oriented`, whose winding was reversed above for a mirrored placement, so + // the stages it records are wound the same way. Note where they start and turn the whole range + // back afterwards, exactly as the result itself is turned back below. + const size_t debug_mark = (debug != nullptr) ? debug->mark() : 0; + TextureBake::PipelineResult result = TextureBake::run_pipeline( + TextureBake::to_soup(oriented, excluded), sample, settings, bounds, mode, excluded, [&progress](const char *, double f) { return !progress || progress(std::clamp(int(f * 100.0), 0, 99)); - }); + }, + debug); + if (debug != nullptr && flip_normals) + debug->rebase(debug_mark, nullptr, /* flip_winding */ true); if (result.canceled || result.geometry.empty()) return {}; indexed_triangle_set out = TextureBake::to_indexed_triangle_set(result.geometry); - return out.indices.empty() ? mesh : out; + if (out.indices.empty()) + return mesh; + if (flip_normals) + for (stl_triangle_vertex_indices &t : out.indices) + std::swap(t[1], t[2]); + + // Colour, per output triangle. The topology is new, so unlike the classic path there is no base + // triangle to inherit a colour from: each output triangle samples the colour stack at its own + // centroid, and takes colour only where the base surface under it is painted - found by the nearest + // base triangle, which is never more than the relief depth away. Then the same despeckle and + // filament resolution as the classic path. + if (color != nullptr && color->out_triangle != nullptr && bool(color->quantize)) { + std::vector out_color(out.indices.size(), 0); + const ColorFieldSampler sampler = make_combined_color_sampler(mesh, layers, facets_data, color->quantize); + if (sampler) { + const bool all_painted = std::none_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; }); + AABBTreeIndirect::Tree3f tree; + if (!all_painted) + tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.vertices, mesh.indices); + std::vector palette(out.indices.size(), -1); + tbb::parallel_for(tbb::blocked_range(0, out.indices.size()), [&](const tbb::blocked_range &r) { + for (size_t i = r.begin(); i < r.end(); ++i) { + const stl_triangle_vertex_indices &t = out.indices[i]; + const Vec3f &a = out.vertices[size_t(t[0])], &b = out.vertices[size_t(t[1])], &c = out.vertices[size_t(t[2])]; + const Vec3f centroid = (a + b + c) / 3.f; + if (!all_painted) { + size_t hit = 0; + Vec3f hit_point; + AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.vertices, mesh.indices, tree, + centroid, hit, hit_point); + if (hit >= excluded.size() || excluded[hit] != 0) + continue; + } + Vec3f n = (b - a).cross(c - a); + const float l = n.norm(); + n = (l > 0.f) ? Vec3f(n / l) : Vec3f::UnitZ(); + palette[i] = sampler(centroid, n); + } + }); + despeckle_triangle_colors(out, palette, color->despeckle_passes); + for (size_t i = 0; i < out.indices.size(); ++i) { + if (palette[i] < 0) + continue; + const stl_triangle_vertex_indices &t = out.indices[i]; + const Vec3f centroid = (out.vertices[size_t(t[0])] + out.vertices[size_t(t[1])] + out.vertices[size_t(t[2])]) / 3.f; + const int filament = color->resolve ? color->resolve(palette[i], centroid) : palette[i]; + if (filament >= 0) + out_color[i] = uint8_t(std::min(filament + 1, 255)); + } + } + *color->out_triangle = std::move(out_color); + } + return out; } } // namespace -indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, - const std::vector &layers, - const TextureDisplacementFacetsData &facets_data, - const TextureDisplacementOptions &options, - const DisplacementProgressFn &progress, - const TextureColorRequest *color) +// The bake proper. Runs entirely in whatever space `base_mesh` is given in; the public entry point +// below is what puts it in world space and brings the result back. +// `flip_normals` says the mesh is wound the opposite way round from its outward direction, which is +// what a mirroring world transform leaves behind: the positions are right, but every normal derived +// from the winding points into the model. See build_texture_displacement(). +static indexed_triangle_set build_texture_displacement_in_place( + const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress, + const TextureColorRequest *color, + bool flip_normals, + BakeStageRecorder *debug) { // Returns true to keep going. An aborted run returns {} (see the header): an empty mesh is the // one result no caller can mistake for a finished bake and commit onto the volume. const auto report = [&progress](int percent) { return !progress || progress(percent); }; + // Stage capture for the debug view. A stage is timed from the end of the previous one, so the + // capture itself - a copy plus an edge scan - sits outside every measurement it reports. + auto stage_clock = std::chrono::steady_clock::now(); + const auto capture = [&](const char *name, const indexed_triangle_set &m, + const std::string &detail = {}) { + if (debug == nullptr) + return; + const double ms = + std::chrono::duration(std::chrono::steady_clock::now() - stage_clock).count(); + debug->capture(name, m.vertices, m.indices, ms, detail); + stage_clock = std::chrono::steady_clock::now(); + }; + indexed_triangle_set mesh = base_mesh; // TriangleSelector's vertex array starts with the mesh's own vertices (any extra ones, created // where a brush stroke split a triangle, are appended after them), and get_facets_strict() @@ -1469,8 +2156,11 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (mesh.vertices.empty() || mesh.indices.empty()) return mesh; + capture("input", mesh, "as handed to the bake"); + if (options.pipeline_v2) - return build_texture_displacement_v2(mesh, layers, facets_data, options, progress); + return build_texture_displacement_v2(mesh, layers, facets_data, options, progress, color, flip_normals, + debug); // Layers are combined in slot order, like stacked layers in an image editor: each one folds its // own displacement into the running total via its blend mode (see TextureBlendMode). @@ -1523,6 +2213,14 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set } } + // Both normal passes above read their direction out of the triangle winding, so a mirrored + // placement leaves every one of them pointing into the model - the relief would be carved rather + // than raised. Correct them once, here, where every later stage (displacement direction, the + // planar/triplanar projection axes, the cylinder axis) picks them up already right. + if (flip_normals) + for (Vec3f &n : vertex_normals) + n = -n; + if (!report(5)) return {}; @@ -1787,9 +2485,19 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set if (!any_displacement) return mesh; + // The model's own resting plane, taken before anything moves - see the clamp after smoothing. + float resting_z = std::numeric_limits::max(); + for (const Vec3f &v : mesh.vertices) + resting_z = std::min(resting_z, v.z()); + + size_t moved_count = 0; for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) - if (displaced[vi]) + if (displaced[vi]) { mesh.vertices[vi] += vertex_normals[vi] * displacement[vi]; + ++moved_count; + } + capture("displace", mesh, std::to_string(moved_count) + " of " + + std::to_string(mesh.vertices.size()) + " vertices moved"); // Post-process relaxation of what the height maps left behind, restricted to the vertices that // actually moved - the untouched part of the model keeps its exact geometry, and the ring of @@ -1810,8 +2518,28 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set progress ? DisplacementProgressFn([&report, it = options.smooth_iterations](int pass) { return report(70 + (29 * (pass + 1)) / std::max(it, 1)); }) : DisplacementProgressFn{}); + capture("smooth", mesh, + std::to_string(options.smooth_iterations) + " iterations at strength " + + std::to_string(options.smooth_strength)); } + // Nothing driven below the model's own resting plane can be printed: it is either through the + // build plate or, once the slicer drops the part back down onto it, holding the whole model up in + // the air. Push it back up to the plane. Only vertices the displacement actually moved are + // eligible - untouched geometry is already exactly where it started - and only those that ended + // up below it, so downward relief that stays clear of the plate is left alone. Runs in world + // space (see build_texture_displacement()), so this really is the plate and not some scaled + // stand-in for it. + size_t clamped_count = 0; + if (resting_z < std::numeric_limits::max()) + for (size_t vi = 0; vi < mesh.vertices.size(); ++vi) + if (displaced[vi] && mesh.vertices[vi].z() < resting_z) { + mesh.vertices[vi].z() = resting_z; + ++clamped_count; + } + capture("clamp to plate", mesh, std::to_string(clamped_count) + " vertices raised to z = " + + std::to_string(resting_z)); + if (!report(99)) return {}; if (want_color) { @@ -1840,6 +2568,70 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set return mesh; } +indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, + const std::vector &layers, + const TextureDisplacementFacetsData &facets_data, + const TextureDisplacementOptions &options, + const DisplacementProgressFn &progress, + const TextureColorRequest *color, + const Transform3d &volume_to_world, + BakeStageRecorder *debug) +{ + // An untransformed volume on an untransformed instance is by far the common case, and the round + // trip costs two matrix multiplies per vertex on a mesh that can carry millions of them - so take + // the identity out of the way rather than paying for it. + // The texture is projected in the bake frame - world orientation and scale, anchored at the + // volume's origin (see texture_displacement_bake_frame()). + const Transform3d frame = texture_displacement_bake_frame(volume_to_world); + if (frame.matrix().isApprox(Transform3d::Identity().matrix())) + return build_texture_displacement_in_place(base_mesh, layers, facets_data, options, progress, color, + false, debug); + + const Transform3d to_local = frame.inverse(); + // A mirroring placement leaves the positions correct but every winding-derived normal pointing + // the wrong way. The winding itself is deliberately *not* touched here: the paint masks encode + // each split triangle against its own vertex order, so reordering a triangle's vertices would + // mirror the paint inside it. The bake is told instead, and negates the normals it computes. + const bool mirrored = frame.linear().determinant() < 0.0; + + indexed_triangle_set world = base_mesh; + for (Vec3f &v : world.vertices) + v = (frame * v.cast()).cast(); + + const size_t debug_mark = (debug != nullptr) ? debug->mark() : 0; + indexed_triangle_set out = build_texture_displacement_in_place(world, layers, facets_data, options, + progress, color, mirrored, debug); + // Everything the bake recorded is in world millimetres, like `out` itself. The debug view draws in + // the volume's local frame, so the stages are brought back the same way the result is. + if (debug != nullptr) + debug->rebase(debug_mark, &to_local, /* flip_winding */ false); + // A cancelled run returns {} and must stay {} - an empty mesh is the signal the caller checks + // before committing anything onto the volume. + if (out.vertices.empty()) + return out; + + for (Vec3f &v : out.vertices) + v = (to_local * v.cast()).cast(); + return out; +} + +Transform3d texture_displacement_bake_frame(const Transform3d &volume_to_world) +{ + // World orientation and scale, but the origin moved to where the volume's own origin sits: the + // texture then rides with the model when it is moved about the plate (and a single, untiled stamp + // starts on the model rather than at the plate's corner), while a tile is still `tiling_scale` + // printed millimetres whatever the instance's scale. + return Eigen::Translation3d(-volume_to_world.translation()) * volume_to_world; +} + +Transform3d texture_displacement_volume_to_world(const ModelVolume &volume) +{ + const ModelObject *object = volume.get_object(); + if (object == nullptr || object->instances.empty() || object->instances.front() == nullptr) + return volume.get_matrix(); + return object->instances.front()->get_matrix() * volume.get_matrix(); +} + indexed_triangle_set build_texture_displacement(const ModelVolume &volume) { TextureDisplacementFacetsData facets_data; @@ -1847,7 +2639,8 @@ indexed_triangle_set build_texture_displacement(const ModelVolume &volume) facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); return build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets_data, - volume.texture_displacement_options); + volume.texture_displacement_options, {}, nullptr, + texture_displacement_volume_to_world(volume)); } void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector &movable, float strength, @@ -2099,7 +2892,8 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, float chord_tolerance_mm, float min_edge_length_mm, float border_edge_length_mm, const DisplacementProgressFn &progress, - const ColorFieldSampler &color, float color_edge_length_mm) + const ColorFieldSampler &color, float color_edge_length_mm, + bool split_multi_crossings) { // Neighbour slots that are not a triangle index. constexpr int NB_BOUNDARY = -1; // open edge: terminal on its own, bisected from this side alone @@ -2136,6 +2930,9 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // driven by the length baseline alone. const bool feature_mode = bool(sampler) && chord_tolerance_mm > 0.f; const bool color_mode = bool(color) && color_edge_length_mm > 0.f; + const bool multi_mode = feature_mode && split_multi_crossings; + float step_iso_mm = 0.f; // the mid-level a step crosses; set once the region is sampled + float step_dev_mm = std::numeric_limits::max(); // deviations above this are steps const float color_sq = color_edge_length_mm > 0.f ? color_edge_length_mm * color_edge_length_mm : 0.f; const float min_floor_sq = min_edge_length_mm > 0.f ? min_edge_length_mm * min_edge_length_mm : 0.f; const float target_sq = target_edge_length_mm > 0.f ? target_edge_length_mm * target_edge_length_mm : 0.f; @@ -2284,6 +3081,14 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, } } }); + if (multi_mode) { + // The level a step crosses: midway through the relief actually present on the region. + float lo = std::numeric_limits::max(), hi = -lo; + for (size_t v = 0; v < verts.size(); ++v) + if (wanted[v]) { lo = std::min(lo, vheight[v]); hi = std::max(hi, vheight[v]); } + step_iso_mm = (lo < hi) ? 0.5f * (lo + hi) : 0.f; + step_dev_mm = (lo < hi) ? 0.4f * (hi - lo) : std::numeric_limits::max(); + } } auto elen_sq = [&](int a, int b) -> float { return (verts[a] - verts[b]).squaredNorm(); }; @@ -2349,6 +3154,12 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, return split; }; + // The spacing at which the chord test samples a triangle. Features narrower than the resolution + // floor cannot be resolved by refinement anyway, so sampling any finer than that only costs time; + // sampling any coarser is what let thin features fall between the samples. Below the floor's own + // scale it is held at 0.05 mm, since finer is invisible on an FDM part. + const float sample_spacing = std::max(min_edge_length_mm, 0.05f); + auto detail_error = [&](int ti) -> float { if (tri_err[ti] >= 0.f) return tri_err[ti]; @@ -2356,16 +3167,73 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const Vec3f pa = verts[t.v[0]], pb = verts[t.v[1]], pc = verts[t.v[2]]; const Vec3f na = vnormal[t.v[0]], nb = vnormal[t.v[1]], nc = vnormal[t.v[2]]; const float ha = height_of(t.v[0]), hb = height_of(t.v[1]), hc = height_of(t.v[2]); - static const float BARY[4][3] = { { 0.5f, 0.5f, 0.f }, { 0.f, 0.5f, 0.5f }, - { 0.5f, 0.f, 0.5f }, { 1.f / 3, 1.f / 3, 1.f / 3 } }; - float maxerr = 0.f; - for (const auto &w : BARY) { - Vec3f n = w[0] * na + w[1] * nb + w[2] * nc; - const float nl = n.norm(); - n = (nl > 1e-12f) ? Vec3f(n / nl) : na; - const float actual = sampler(w[0] * pa + w[1] * pb + w[2] * pc, n); - maxerr = std::max(maxerr, std::abs(actual - (w[0] * ha + w[1] * hb + w[2] * hc))); + + // A barycentric lattice over the whole triangle, dense enough that no feature wider than the + // sample spacing can hide between two samples. Four fixed samples (the three edge midpoints + // and the centroid) were the previous test, and on a 1 mm starting triangle they left 0.5 mm + // gaps - wider than a grid line or a knurl ridge - so whole features were never seen, the + // triangle scored zero, and it was never refined. The lattice is bounded at 8 subdivisions + // (45 points) so a large triangle does not cost thousands of samples; anything it misses at + // that size is caught once its children are small enough for the lattice to reach it. + const float longest = std::sqrt(std::max({ (pb - pa).squaredNorm(), (pc - pb).squaredNorm(), (pa - pc).squaredNorm() })); + const int n = std::clamp(int(std::ceil(longest / sample_spacing)), 2, 8); + float maxerr = 0.f; + // Whether the mid-level contour crosses an edge with a jump that is a step's worth, walked at + // the sample spacing (the lattice below is too coarse on a long edge: a 1 mm edge gets 8 + // samples, and a 0.15 mm grid line slips between them). Such a triangle is the cutter's. + bool sharp_cross = false; + if (multi_mode) { + const Vec3f *P[3] = { &pa, &pb, &pc }; + const Vec3f *Nn[3] = { &na, &nb, &nc }; + const float H[3] = { ha, hb, hc }; + for (int e = 0; e < 3; ++e) { + const Vec3f &p0 = *P[e], &p1 = *P[(e + 1) % 3], &n0 = *Nn[e], &n1 = *Nn[(e + 1) % 3]; + const int m = std::clamp(int(std::ceil((p1 - p0).norm() / sample_spacing)), 2, 32); + float last = H[e]; + for (int i = 1; i <= m; ++i) { + float h; + if (i == m) h = H[(e + 1) % 3]; + else { + const float t = float(i) / float(m); + Vec3f nn = n0 + (n1 - n0) * t; + const float nl = nn.norm(); + nn = (nl > 1e-12f) ? Vec3f(nn / nl) : n0; + h = sampler(p0 + (p1 - p0) * t, nn); + } + if ((h > step_iso_mm) != (last > step_iso_mm) && std::abs(h - last) > step_dev_mm) { + sharp_cross = true; + break; + } + last = h; + } + if (sharp_cross) + break; + } } + for (int i = 0; i <= n; ++i) + for (int j = 0; j <= n - i; ++j) { + const int k = n - i - j; + const float wa = float(i) / float(n), wb = float(j) / float(n), wc = float(k) / float(n); + float actual; + if (i == n) actual = ha; + else if (j == n) actual = hb; + else if (k == n) actual = hc; + else { + Vec3f nn = wa * na + wb * nb + wc * nc; + const float nl = nn.norm(); + nn = (nl > 1e-12f) ? Vec3f(nn / nl) : na; + actual = sampler(wa * pa + wb * pb + wc * pc, nn); + // Corners lie on the plane by construction; only the rest carry chord error. + maxerr = std::max(maxerr, std::abs(actual - (wa * ha + wb * hb + wc * hc))); + } + } + // A sharp step crossing the triangle is the cutter's job. Refinement cannot bring such a + // triangle under tolerance - a discontinuity has no chord - and would only carpet the step + // down to the floor, so its chord error is not counted. A feature lying entirely inside the + // triangle crosses no edge and still counts, which is what makes it surface until it does + // cross one. + if (multi_mode && sharp_cross) + maxerr = 0.f; tri_err[ti] = maxerr; return maxerr; }; @@ -2387,8 +3255,9 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, float p = 0.f; if (flags & REFINE_PAINTED) { p = (target_sq > 0.f) ? ll / target_sq : 0.f; - if (feature_mode) + if (feature_mode) { p = std::max(p, detail_error(ti) / chord_tolerance_mm); + } // Colour is per facet, so a colour boundary can only be drawn where there are edges along // it. Length target, floored by min_edge_length_mm above, exactly like the border band. if (color_mode && straddles_color(ti)) @@ -2404,6 +3273,18 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, return p; }; + // What ordering the heap by. priority() alone saturates: a triangle straddling a hard step in the + // texture scores relief / tolerance however small it gets, so its children always come back to the + // top and the whole budget is spent carpeting step edges down to the floor while a large triangle + // with a moderate error a few millimetres away is never reached. Weighting by area makes the key + // the surface error a split actually removes, which is what a fixed budget should be spent on - + // and once the budget is gone, what remains is spread evenly instead of piled on one feature. + auto tri_area = [&](int ti) -> float { + const Tri &t = tris[ti]; + return 0.5f * (verts[t.v[1]] - verts[t.v[0]]).cross(verts[t.v[2]] - verts[t.v[0]]).norm(); + }; + auto heap_key = [&](int ti, float p) -> float { return p * tri_area(ti); }; + auto set_nb = [&](int ti, int u, int v, int val) { if (ti < 0) return; @@ -2536,7 +3417,7 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, }); for (int ti = 0; ti < int(tris.size()); ++ti) if (initial[size_t(ti)] > 1.f) - queue.emplace(initial[size_t(ti)], ti); + queue.emplace(heap_key(ti, initial[size_t(ti)]), ti); } // Every iteration either drops one satisfied triangle from the queue or performs exactly one @@ -2584,15 +3465,1184 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, for (const int t : touched) if (const float p = priority(t); p > 1.f) - queue.emplace(p, t); + queue.emplace(heap_key(t, p), t); // The bisection may have been a step on the way to ti rather than ti itself, in which case ti // is not in `touched` and has to go back on the heap to be walked again. if (cur != ti) if (const float p = priority(ti); p > 1.f) - queue.emplace(p, ti); + queue.emplace(heap_key(ti, p), ti); } return emit(); } +namespace { + +// Ear clipping of a simple polygon given counter-clockwise in 2D; `pts` are indexed by `poly`. Small +// polygons only (a triangle clipped by a few contour polylines), so the plain O(n^2) form is enough. +// A vertex that lies on a candidate ear's boundary does not block it, and when no ear can be found +// (numerically degenerate input) the flattest vertex is dropped rather than fanning blindly, which +// on a concave region would put triangles outside it; the zero-area triangle keeps the edges paired. +void ear_clip(const std::vector &pts, std::vector poly, const std::function &emit) +{ + auto cross2 = [](const Vec2f &o, const Vec2f &a, const Vec2f &b) { + return (a.x() - o.x()) * (b.y() - o.y()) - (a.y() - o.y()) * (b.x() - o.x()); + }; + // A corner flatter than this is collinear: several vertices along one edge of the triangle. Such + // a corner is never an ear - the zero-area triangle would lie on the edge, where the neighbour, + // seeing the same points from its side, may emit the same one. + float flat = 0.f; + { + Vec2f lo = pts[size_t(poly[0])], hi = lo; + for (int id : poly) { lo = lo.cwiseMin(pts[size_t(id)]); hi = hi.cwiseMax(pts[size_t(id)]); } + flat = 1e-5f * (hi - lo).squaredNorm(); + } + while (poly.size() > 3) { + const size_t n = poly.size(); + bool found = false; + for (size_t i = 0; i < n && !found; ++i) { + const int ia = poly[(i + n - 1) % n], ib = poly[i], ic = poly[(i + 1) % n]; + const Vec2f &a = pts[size_t(ia)], &b = pts[size_t(ib)], &c = pts[size_t(ic)]; + const float area2 = cross2(a, b, c); + if (area2 <= flat) + continue; // reflex or flat corner + const float eps = 1e-6f * area2; + bool clean = true; + size_t on_base = 0; + for (size_t j = 0; j < n && clean; ++j) { + const int ij = poly[j]; + if (ij == ia || ij == ib || ij == ic) + continue; + const Vec2f &p = pts[size_t(ij)]; + clean = !(cross2(a, b, p) > eps && cross2(b, c, p) > eps && cross2(c, a, p) > eps); + // On the ear's base, between its ends: a run of vertices along the base, as when + // several sit on one edge of the triangle. + if (std::abs(cross2(c, a, p)) <= flat && (p - c).dot(a - c) > 0.f && (p - a).dot(c - a) > 0.f) + ++on_base; + } + if (!clean) + continue; + if (on_base > 0) { + // The base carries other vertices. When those are all that is left of the polygon, + // in order from c round to a, fan them from b and finish; otherwise the ear is not + // clear of the rest of the polygon. + if (on_base + 3 != n) + continue; + for (size_t j = 0; j + 1 < n - 1; ++j) { + const int u = poly[(i + 1 + j) % n], v = poly[(i + 2 + j) % n]; + emit(ib, u, v); + } + poly.clear(); + return; + } + emit(ia, ib, ic); + poly.erase(poly.begin() + long(i)); + found = true; + } + if (!found) { + // Clip the smallest convex corner anyway, or failing that the flattest: a thin triangle + // keeps every edge accounted for, and a convex one at least stays on the polygon's side. + size_t pick = 0; + float best = std::numeric_limits::max(); + bool convex_found = false; + for (size_t i = 0; i < n; ++i) { + const Vec2f &a = pts[size_t(poly[(i + n - 1) % n])], &b = pts[size_t(poly[i])], &c = pts[size_t(poly[(i + 1) % n])]; + const float m = cross2(a, b, c); + if (m > flat && (!convex_found || m < best)) { best = m; pick = i; convex_found = true; } + else if (!convex_found && std::abs(m) < best) { best = std::abs(m); pick = i; } + } + emit(poly[(pick + n - 1) % n], poly[pick], poly[(pick + 1) % n]); + poly.erase(poly.begin() + long(pick)); + } + } + if (poly.size() == 3) + emit(poly[0], poly[1], poly[2]); +} + +// Douglas-Peucker on an open polyline; endpoints always survive. +void simplify_polyline(const std::vector &in, float tol, std::vector &out) +{ + out.clear(); + if (in.size() < 3) { out = in; return; } + std::vector keep(in.size(), 0); + keep.front() = keep.back() = 1; + std::vector> stack = { { 0, in.size() - 1 } }; + while (!stack.empty()) { + const auto [i0, i1] = stack.back(); + stack.pop_back(); + if (i1 <= i0 + 1) + continue; + const Vec2f d = in[i1] - in[i0]; + const float len = d.norm(); + float best = -1.f; + size_t bi = i0; + for (size_t i = i0 + 1; i < i1; ++i) { + const Vec2f r = in[i] - in[i0]; + const float dist = (len > 1e-12f) ? std::abs(d.x() * r.y() - d.y() * r.x()) / len : r.norm(); + if (dist > best) { best = dist; bi = i; } + } + if (best > tol) { + keep[bi] = 1; + stack.push_back({ i0, bi }); + stack.push_back({ bi, i1 }); + } + } + for (size_t i = 0; i < in.size(); ++i) + if (keep[i]) out.push_back(in[i]); +} + +} // namespace + +namespace { + +// The cutter's view of one crossing of the mid-level contour with a mesh edge. +struct Crossing +{ + Vec3f p, n; + float t; // parameter from the edge's lower vertex id toward the higher + int a = -1, b = -1; // the edge, lower vertex id first + float sin_min = 1.f; // sine of the smallest angle a contour makes with the edge + float dt_max = std::numeric_limits::max(); // room along the edge before a copy could + // pass the copies of the contour's next vertex + bool used = false; // some cut triangle's contour ends here + bool no_seam = false; // a contour joins it straight to its neighbour on the edge: nothing between + bool sharp = false; + float thick = 0.f; // narrower side of the feature at the crossing, along the gradient + int single = -1, lo = -1, hi = -1; + int near_a = -1, near_b = -1; // the copy on a's side and on b's, when doubled +}; +struct Edge +{ + std::vector xs; // sorted by t +}; + +inline uint64_t step_edge_key(int a, int b) +{ + if (a > b) std::swap(a, b); + return (uint64_t(uint32_t(a)) << 32) | uint32_t(b); +} + +// What cut_mesh_at_steps() learns about the mesh before it cuts anything: the vertex heights (and the +// nudged vertex positions), the crossings on every painted edge, and the verdict `ok` - whether this +// is a step texture worth cutting at all. The verdict is what the prepare path probes on the coarse +// mesh before it chooses how to refine, so refinement and cut agree on which textures are stepped. +struct StepScan +{ + bool ok = false; + std::vector pos, vnormal; + std::vector vh; + float iso = 0.f, range = 0.f; + std::unordered_map edges; + + std::pair sample_between(int a, int b, float t) const + { + const Vec3f p = pos[size_t(a)] + (pos[size_t(b)] - pos[size_t(a)]) * t; + Vec3f n = vnormal[size_t(a)] + (vnormal[size_t(b)] - vnormal[size_t(a)]) * t; + const float l = n.norm(); + n = (l > 1e-12f) ? Vec3f(n / l) : vnormal[size_t(a)]; + return std::make_pair(p, n); + } + bool side_of(float h) const { return h > iso; } +}; + +StepScan scan_steps(const indexed_triangle_set &mesh, const std::vector ®ion, + const HeightFieldSampler &sampler, float step_width_mm, float seam_gap_mm, + float min_feature_mm, bool nudge) +{ + StepScan scan; + const size_t nv = mesh.vertices.size(), nt = mesh.indices.size(); + if (!sampler || region.size() != nt || step_width_mm <= 0.f) + return scan; + + // 1. Heights at the vertices of the cuttable triangles, along the same normals the bake will use. + // Vertex positions are worked on in `pos`: a vertex that happens to sit within a texel of a step + // is nudged off it below, and everything from the edge march to the output uses the nudged place. + std::vector &pos = scan.pos; + std::vector &vnormal = scan.vnormal; + pos = mesh.vertices; + vnormal = texture_displacement_vertex_normals(mesh); + std::vector wanted(nv, 0); + for (size_t t = 0; t < nt; ++t) + if (region[t] != 0) + for (int k = 0; k < 3; ++k) + wanted[size_t(mesh.indices[t][k])] = 1; + std::vector &vh = scan.vh; + vh.assign(nv, 0.f); + tbb::parallel_for(tbb::blocked_range(0, nv), [&](const tbb::blocked_range &r) { + for (size_t v = r.begin(); v < r.end(); ++v) + if (wanted[v]) + vh[v] = sampler(pos[v], vnormal[v]); + }); + float hmin = std::numeric_limits::max(), hmax = -hmin; + for (size_t v = 0; v < nv; ++v) + if (wanted[v]) { hmin = std::min(hmin, vh[v]); hmax = std::max(hmax, vh[v]); } + const float range = scan.range = hmax - hmin; + if (!(range > 1e-6f)) + return scan; + const float iso = scan.iso = 0.5f * (hmin + hmax); + // Only a texture that is mostly steps is cut: one whose heights sit at two levels with little in + // between (a grid, a knurl, a logo). On a smooth or noisy relief the mid-level contour runs through + // every triangle without being a step anywhere, and cutting along it would only multiply the + // triangles; the chord-based refinement is the right tool there. Judged from the vertex heights, + // which sample the whole painted surface. + { + size_t near_level = 0, total = 0; + for (size_t v = 0; v < nv; ++v) + if (wanted[v]) { ++total; near_level += std::abs(vh[v] - iso) > 0.35f * range; } + if (total == 0 || double(near_level) < 0.6 * double(total)) + return scan; + } + + // 1b. A vertex inside the blend of a sharp step - the texture's bilinear ramp is a texel wide - would + // be displaced to a height between the two sides, and every triangle at it would ramp. Such a vertex + // is nudged along the surface away from the step, down its own side's slope, until it samples a + // pure value; a step's width or so. Only where the surface is flat around it: a vertex on a crease + // of the model (a box edge) stays where it is. Skipped by a probe, which only wants the verdict. + if (nudge) { + std::vector creased(nv, 0); + std::vector first_normal(nv, Vec3f::Zero()); + std::vector shortest(nv, std::numeric_limits::max()); // shortest edge at the vertex + for (size_t t = 0; t < nt; ++t) { + const auto &tri = mesh.indices[t]; + const Vec3f &a = pos[size_t(tri[0])]; + Vec3f fn = (pos[size_t(tri[1])] - a).cross(pos[size_t(tri[2])] - a); + const float fl = fn.norm(); + if (fl < 1e-12f) continue; + fn /= fl; + for (int k = 0; k < 3; ++k) { + Vec3f &f0 = first_normal[size_t(tri[k])]; + if (f0.isZero()) f0 = fn; + else if (f0.dot(fn) < 0.985f) creased[size_t(tri[k])] = 1; // ~10 degrees + const float el = (pos[size_t(tri[(k + 1) % 3])] - pos[size_t(tri[k])]).norm(); + shortest[size_t(tri[k])] = std::min(shortest[size_t(tri[k])], el); + shortest[size_t(tri[(k + 1) % 3])] = std::min(shortest[size_t(tri[(k + 1) % 3])], el); + } + } + tbb::parallel_for(tbb::blocked_range(0, nv), [&](const tbb::blocked_range &r) { + for (size_t v = r.begin(); v < r.end(); ++v) { + if (!wanted[v] || creased[v] || std::abs(vh[v] - iso) > 0.47f * range) + continue; + const Vec3f &n = vnormal[v]; + const Vec3f ax = (std::abs(n.x()) < 0.9f) ? Vec3f::UnitX() : Vec3f::UnitY(); + const Vec3f t1 = n.cross(ax).normalized(), t2 = n.cross(t1).normalized(); + const float d = step_width_mm; + const float gx = sampler(pos[v] + t1 * d, n) - sampler(pos[v] - t1 * d, n); + const float gy = sampler(pos[v] + t2 * d, n) - sampler(pos[v] - t2 * d, n); + Vec3f g = t1 * gx + t2 * gy; + const float gl = g.norm(); + if (gl < 0.25f * range) + continue; // not at a step: a slope, which the chord test handles + g /= gl; + // Down the slope for a low vertex, up it for a high one, in steps of half a texel, and + // never by more than a fraction of the shortest edge at the vertex: on a finely refined + // mesh a texel-sized move would fold the triangles around it. + const Vec3f dir = (vh[v] > iso) ? g : Vec3f(-g); + Vec3f best = pos[v]; + float bh = vh[v]; + const float limit = std::min(2.f * step_width_mm, 0.3f * shortest[v]); + for (int i = 1; i <= 8; ++i) { + const float dist = 0.25f * step_width_mm * float(i); + if (dist > limit) + break; + const Vec3f q = pos[v] + dir * dist; + const float h = sampler(q, n); + if ((h > iso) != (vh[v] > iso)) + break; // crossed the step: the nudge would change the vertex's side + best = q; bh = h; + if (std::abs(h - iso) > 0.48f * range) + break; + } + if (std::abs(bh - iso) > std::abs(vh[v] - iso)) { pos[v] = best; vh[v] = bh; } + } + }); + } + + const auto sample_between = [&scan](int a, int b, float t) { return scan.sample_between(a, b, t); }; + const auto side_of = [&scan](float h) { return scan.side_of(h); }; + + // 2. Crossings on the edges, found on the field itself - on a 1 mm edge over a texture with 30 um + // texels, interpolating the endpoint heights could put a step anywhere along the edge. Both + // triangles on an edge see the same crossings, which is what keeps the cut conformal. + auto &edges = scan.edges; + edges.reserve(nt); + + const auto find_crossings = [&](int a, int b, Edge &edge) { + if (a > b) std::swap(a, b); + const float len = (pos[size_t(b)] - pos[size_t(a)]).norm(); + const int steps = std::clamp(int(std::ceil(len / (0.5f * step_width_mm))), 4, 128); + float t0 = 0.f, h0 = vh[size_t(a)] - iso; + for (int i = 1; i <= steps; ++i) { + const float ti = float(i) / float(steps); + const auto [pp, nn] = sample_between(a, b, ti); + const float hi_ = sampler(pp, nn) - iso; + if (h0 * hi_ < 0.f || (hi_ == 0.f && h0 != 0.f)) { + float lo_t = t0, hi_t = ti, lo_h = h0; + for (int k = 0; k < 10; ++k) { + const float tm = 0.5f * (lo_t + hi_t); + const auto [pm, nm] = sample_between(a, b, tm); + if (lo_h * (sampler(pm, nm) - iso) <= 0.f) hi_t = tm; else { lo_t = tm; } + } + Crossing c; + c.a = a; c.b = b; + // Never exactly on a vertex: that would make a zero-area triangle of the split. + c.t = std::clamp(0.5f * (lo_t + hi_t), 1e-3f, 1.f - 1e-3f); + std::tie(c.p, c.n) = sample_between(a, b, c.t); + // Sharp means the field changes by at least half its range across `step_width_mm` in + // the steepest tangent direction - a step, not a slope that merely passes mid-level. + const Vec3f ax = (std::abs(c.n.x()) < 0.9f) ? Vec3f::UnitX() : Vec3f::UnitY(); + const Vec3f t1v = c.n.cross(ax).normalized(), t2v = c.n.cross(t1v).normalized(); + const float d = 0.5f * step_width_mm; + const float gx = sampler(c.p + t1v * d, c.n) - sampler(c.p - t1v * d, c.n); + const float gy = sampler(c.p + t2v * d, c.n) - sampler(c.p - t2v * d, c.n); + const float gl = std::sqrt(gx * gx + gy * gy); + c.sharp = gl >= 0.5f * range; + // How wide the feature is on either side of the step, along the gradient: the + // distance until the field returns to mid-level, in steps of half the step width, up + // to `thick_max`. A texture whose features are only a few texels wide has no pure + // interior for a seam copy to land in and is left to refinement (see the gate below). + { + const float thick_max = 4.f * step_width_mm; + c.thick = thick_max; + if (gl > 1e-12f) { + const Vec3f g = (t1v * gx + t2v * gy) / gl; + for (int sgn = -1; sgn <= 1; sgn += 2) { + const bool high = sgn > 0; // uphill along the gradient + for (int i = 1; i <= 8; ++i) { + const float dist = 0.5f * step_width_mm * float(i); + if (dist >= c.thick) + break; + if (side_of(sampler(c.p + g * (float(sgn) * dist), c.n)) != high) { + c.thick = std::min(c.thick, dist); + break; + } + } + } + } + } + edge.xs.push_back(c); + } + t0 = ti; h0 = hi_; + } + // A pair of crossings closer than the smallest feature the mesh is meant to carry is a feature + // too thin to print, and one closer than the seam gap has no room for two seams: dropping both + // leaves the surface flat there, at the side around it. + const float thinnest = std::max(min_feature_mm, seam_gap_mm); + for (size_t i = 0; i + 1 < edge.xs.size();) { + if ((edge.xs[i + 1].t - edge.xs[i].t) * len < thinnest) + edge.xs.erase(edge.xs.begin() + long(i), edge.xs.begin() + long(i) + 2); + else + ++i; + } + // Parity: the endpoints' sides say whether the count must be odd or even; the march starts and + // ends on the vertex heights themselves, so a mismatch is a numerical accident. Drop the lot + // then - a triangle split on a wrong count is worse than a ramp there. + const bool odd_expected = side_of(vh[size_t(a)]) != side_of(vh[size_t(b)]); + if ((edge.xs.size() % 2 == 1) != odd_expected) + edge.xs.clear(); + }; + + // Edges to examine, in a fixed order so the crossings can be found in parallel. + std::vector edge_keys; + for (size_t t = 0; t < nt; ++t) { + if (region[t] == 0) + continue; + const auto &tri = mesh.indices[t]; + for (int e = 0; e < 3; ++e) + edge_keys.push_back(step_edge_key(tri[e], tri[(e + 1) % 3])); + } + std::sort(edge_keys.begin(), edge_keys.end()); + edge_keys.erase(std::unique(edge_keys.begin(), edge_keys.end()), edge_keys.end()); + std::vector found(edge_keys.size()); + tbb::parallel_for(tbb::blocked_range(0, edge_keys.size()), [&](const tbb::blocked_range &r) { + for (size_t i = r.begin(); i < r.end(); ++i) + find_crossings(int(edge_keys[i] >> 32), int(uint32_t(edge_keys[i])), found[i]); + }); + size_t crossings = 0, sharp = 0; + double thick_sum = 0.; + for (size_t i = 0; i < edge_keys.size(); ++i) { + for (const Crossing &c : found[i].xs) { ++crossings; sharp += c.sharp; thick_sum += c.thick; } + edges.emplace(edge_keys[i], std::move(found[i])); + } + // The mid-level contour has to be a step nearly everywhere it is crossed, or this is not a step + // texture: a noisy relief that happens to sit at two levels is crossed all over, mostly gently. + if (crossings == 0 || double(sharp) < 0.8 * double(crossings)) + return scan; + // And the features have to be wider than the step itself. A texture whose blobs are only a few + // texels across (noise, a fine grain) is bimodal and sharp at every crossing, yet has no pure + // interior for the seam copies to land in, and its contour runs through every triangle: cutting it + // doubles the triangle count for walls the same size as the interpolation blur. Judged from the + // mean feature thickness at the crossings, measured in step widths (2.25 = four to five texels). + if (thick_sum < 2.25 * double(step_width_mm) * double(crossings)) + return scan; + + scan.ok = true; + return scan; + +} + +} // namespace + +bool texture_has_steps_to_cut(const indexed_triangle_set &mesh, const std::vector ®ion, + const HeightFieldSampler &sampler, float step_width_mm, float seam_gap_mm, + float min_feature_mm) +{ + return scan_steps(mesh, region, sampler, step_width_mm, seam_gap_mm, min_feature_mm, /*nudge*/ false).ok; +} + +indexed_triangle_set cut_mesh_at_steps(const indexed_triangle_set &mesh, const std::vector ®ion, + const HeightFieldSampler &sampler, float step_width_mm, + float seam_gap_mm, float min_feature_mm, std::vector *out_source, + size_t *out_cut_count) +{ + const size_t nv = mesh.vertices.size(), nt = mesh.indices.size(); + if (out_cut_count) + *out_cut_count = 0; + const auto passthrough = [&]() { + if (out_source) { + out_source->resize(nt); + std::iota(out_source->begin(), out_source->end(), 0); + } + return mesh; + }; + if (!sampler || region.size() != nt || step_width_mm <= 0.f) + return passthrough(); + + StepScan scan = scan_steps(mesh, region, sampler, step_width_mm, seam_gap_mm, min_feature_mm, /*nudge*/ true); + if (!scan.ok) + return passthrough(); + std::vector &pos = scan.pos; + const std::vector &vnormal = scan.vnormal; + std::vector &vh = scan.vh; + const float iso = scan.iso, range = scan.range; + auto &edges = scan.edges; + const auto sample_between = [&scan](int a, int b, float t) { return scan.sample_between(a, b, t); }; + const auto side_of = [&scan](float h) { return scan.side_of(h); }; + const auto edge_key = [](int a, int b) { return step_edge_key(a, b); }; + (void) vnormal; (void) range; (void) iso; + + // 3. Per triangle: the perimeter as a loop of corners and crossings, and the contour inside the + // triangle traced from a local raster of the field (marching squares at half the step width), as + // polylines from one crossing to another. The trace is what makes a corner of the pattern come out + // as a corner instead of a chord clipping it, and what pairs the crossings up - no guessing. A + // polyline is kept only when its two ends land on distinct crossings the edge march found; + // otherwise the triangle is split at its crossings without a seam (the neighbours' cuts still meet + // no T-junction) and the surface ramps there. Unpainted neighbours of a crossed edge are split the + // same way. + struct Chain + { + int i = -1, j = -1; // crossing indices in the loop, from i to j + std::vector pts; // interior vertices, from i toward j + std::vector lo, hi; // output copies of the interior vertices (same when single) + float clearance = std::numeric_limits::max(); // distance to the nearest other contour + }; + struct Loop + { + std::vector ids; // perimeter entries: corner vertex ids (>= 0) or -(1 + crossing index) + std::vector xs; // crossings in perimeter order + std::vector xpos; // their positions in ids + std::vector chains; + std::vector partner; // per crossing: the crossing its chain leads to + std::vector chain_of; + std::vector no_seam; // crossings joined to their neighbour on the edge with nothing between + Vec3f N = Vec3f::Zero(); + bool ok = false; + }; + const auto walk_edge = [&](int a, int b, std::vector &out) { + auto it = edges.find(edge_key(a, b)); + if (it == edges.end()) return; + Edge &edge = it->second; + if (a < b) for (auto &c : edge.xs) out.push_back(&c); + else for (auto ci = edge.xs.rbegin(); ci != edge.xs.rend(); ++ci) out.push_back(&*ci); + }; + std::vector loops(nt); + for (size_t t = 0; t < nt; ++t) { + const auto &tri = mesh.indices[t]; + Loop &L = loops[t]; + for (int e = 0; e < 3; ++e) { + L.ids.push_back(tri[e]); + std::vector on_edge; + walk_edge(tri[e], tri[(e + 1) % 3], on_edge); + for (Crossing *c : on_edge) { + L.xpos.push_back(int(L.ids.size())); + L.ids.push_back(-(1 + int(L.xs.size()))); + L.xs.push_back(c); + } + } + const Vec3f &a0 = pos[size_t(tri[0])]; + L.N = (pos[size_t(tri[1])] - a0).cross(pos[size_t(tri[2])] - a0); + const float nl = L.N.norm(); + if (nl > 1e-12f) L.N /= nl; + } + + const float cell = 0.5f * step_width_mm; + const auto trace = [&](size_t t) { + Loop &L = loops[t]; + const int n = int(L.xs.size()); + const auto &tri = mesh.indices[t]; + if (n == 0 || region[t] == 0) + return; + if (n % 2 != 0 || L.N.squaredNorm() < 0.5f) + return; + // Local frame on the triangle's plane; the loop runs counter-clockwise in it. + const Vec3f &A = pos[size_t(tri[0])], &B = pos[size_t(tri[1])], &C = pos[size_t(tri[2])]; + const Vec3f U = (B - A).normalized(), V = L.N.cross(U); + const auto to2 = [&](const Vec3f &p) { return Vec2f((p - A).dot(U), (p - A).dot(V)); }; + const auto to3 = [&](const Vec2f &q) { return Vec3f(A + U * q.x() + V * q.y()); }; + const Vec2f a2 = to2(A), b2 = to2(B), c2 = to2(C); + const float det = (b2.x() - a2.x()) * (c2.y() - a2.y()) - (c2.x() - a2.x()) * (b2.y() - a2.y()); + if (std::abs(det) < 1e-12f) + return; + + // A barycentric lattice over the triangle, dense enough that its boundary rows are at least as + // fine as the edge march. Its boundary points take their side from the march - the lower + // corner's side, flipped at every crossing passed - so the contour traced through the lattice + // ends exactly at the crossings the neighbours share; no clipping, no matching. A conflict + // between that and the far corner's own side (an edge whose crossings the march dropped for + // parity) leaves the triangle uncut. + const float longest = std::sqrt(std::max({ (b2 - a2).squaredNorm(), (c2 - b2).squaredNorm(), (a2 - c2).squaredNorm() })); + const int N = std::clamp(int(std::ceil(longest / cell)), 4, 96); + const float h = longest / float(N); + const int W = N + 1; + // Lattice point (j, k): A + (B - A) j/N + (C - A) k/N, j + k <= N. Edge 0 (A->B) is k == 0, + // edge 1 (B->C) is j + k == N, edge 2 (C->A) is j == 0. + const auto at2 = [&](int j, int k) { return Vec2f(a2 + (b2 - a2) * (float(j) / float(N)) + (c2 - a2) * (float(k) / float(N))); }; + std::vector f(size_t(W) * size_t(W), 0.f); + std::vector sign(size_t(W) * size_t(W), 0); + const auto id = [&](int j, int k) { return size_t(k) * size_t(W) + size_t(j); }; + for (int k = 0; k <= N; ++k) + for (int j = 0; j + k <= N; ++j) { + const float wb = float(j) / float(N), wc = float(k) / float(N), wa = 1.f - wb - wc; + Vec3f nn = wa * vnormal[size_t(tri[0])] + wb * vnormal[size_t(tri[1])] + wc * vnormal[size_t(tri[2])]; + const float nl = nn.norm(); + nn = (nl > 1e-12f) ? Vec3f(nn / nl) : L.N; + const float v = sampler(A + (B - A) * wb + (C - A) * wc, nn) - iso; + f[id(j, k)] = v; + sign[id(j, k)] = v > 0.f ? 1 : -1; + } + // Boundary sides from the march. Each edge walks from its first corner in loop order; the + // crossings on it are in that order in L.xs. Position along edge e of lattice point m/N. + const auto edge_point = [&](int e, int m) -> std::pair { // (j, k) + return e == 0 ? std::make_pair(m, 0) : e == 1 ? std::make_pair(N - m, m) : std::make_pair(0, N - m); + }; + // The crossings of edge e, with their parameter from the edge's first corner in loop order. + std::vector>> edge_x(3); + { + int e = -1, k = 0; + for (int idv : L.ids) { + if (idv >= 0) { ++e; continue; } + const Crossing &c = *L.xs[size_t(k)]; + // c.t runs from the lower vertex id; the loop walks tri[e] -> tri[(e + 1) % 3]. + const float u = (tri[e] < tri[(e + 1) % 3]) ? c.t : 1.f - c.t; + edge_x[size_t(e)].push_back({ u, k }); + ++k; + } + } + for (int e = 0; e < 3; ++e) { + int8_t s = side_of(vh[size_t(tri[e])]) ? 1 : -1; + size_t next = 0; + for (int m = 0; m <= N; ++m) { + const float u = float(m) / float(N); + while (next < edge_x[size_t(e)].size() && edge_x[size_t(e)][next].first < u) { s = -s; ++next; } + const auto [j, k] = edge_point(e, m); + sign[id(j, k)] = s; + // Keep the value on the march's side of zero, so interpolation toward the interior + // does not put a crossing where the march has none - and clear of zero, so a contour + // that does pass between this point and the next row is not put on the boundary. + if ((f[id(j, k)] > 0.f) != (s > 0)) + f[id(j, k)] = float(s) * std::max(std::abs(f[id(j, k)]), 0.05f * range); + } + if ((s > 0) != side_of(vh[size_t(tri[(e + 1) % 3])])) + return; // parity conflict on this edge + } + + // Marching triangles. A contour point lives on a lattice edge, keyed by its lower point and + // direction: 0 (j,k)-(j+1,k), 1 (j,k)-(j,k+1), 2 (j+1,k)-(j,k+1). A boundary lattice edge with + // a sign change carries exactly the march crossing in its interval, and is that crossing. + std::vector pts; + std::vector pt_x; // crossing index, or -1 for an interior point + std::unordered_map point_of; + std::vector> link; + bool bad = false; + const auto point_on = [&](int j, int k, int dir) -> int { + const uint32_t key = (uint32_t(j) << 16) | (uint32_t(k) << 2) | uint32_t(dir); + auto it = point_of.find(key); + if (it != point_of.end()) + return it->second; + // Endpoints (j0, k0) and (jb, kb). + const int j0 = dir == 2 ? j + 1 : j, k0 = k; + const int jb = dir == 0 ? j + 1 : j, kb = dir == 0 ? k : k + 1; + int xk = -1; + Vec2f p; + int e = -1; + float u0 = 0.f, u1 = 0.f; + if (k0 == 0 && kb == 0) { e = 0; u0 = float(j0) / float(N); u1 = float(jb) / float(N); } + else if (j0 + k0 == N && jb + kb == N) { e = 1; u0 = float(k0) / float(N); u1 = float(kb) / float(N); } + else if (j0 == 0 && jb == 0) { e = 2; u0 = float(N - k0) / float(N); u1 = float(N - kb) / float(N); } + if (e >= 0) { + const float lo = std::min(u0, u1), hi = std::max(u0, u1); + for (const auto &[u, k_] : edge_x[size_t(e)]) + if (u >= lo && u < hi) { if (xk >= 0) bad = true; xk = k_; } + if (xk < 0) { bad = true; p = 0.5f * (at2(j0, k0) + at2(jb, kb)); } + else p = to2(L.xs[size_t(xk)]->p); + } else { + const float fa = f[id(j0, k0)], fb = f[id(jb, kb)]; + const float s = (fa != fb) ? std::clamp(fa / (fa - fb), 0.f, 1.f) : 0.5f; + p = at2(j0, k0) + (at2(jb, kb) - at2(j0, k0)) * s; + } + const int pid = int(pts.size()); + pts.push_back(p); + pt_x.push_back(xk); + link.push_back({ -1, -1 }); + point_of.emplace(key, pid); + return pid; + }; + const auto connect = [&](int p, int q) { + for (int *slot : { &link[size_t(p)][0], &link[size_t(p)][1] }) + if (*slot < 0) { *slot = q; break; } + for (int *slot : { &link[size_t(q)][0], &link[size_t(q)][1] }) + if (*slot < 0) { *slot = p; break; } + }; + // Up triangle (j,k),(j+1,k),(j,k+1): edges dir 0 at (j,k), dir 1 at (j,k), dir 2 at (j,k). + // Down triangle (j+1,k),(j+1,k+1),(j,k+1): dir 1 at (j+1,k), dir 0 at (j,k+1), dir 2 at (j,k). + for (int k = 0; k < N; ++k) + for (int j = 0; j + k < N; ++j) { + { + const int8_t s0_ = sign[id(j, k)], s1_ = sign[id(j + 1, k)], s2_ = sign[id(j, k + 1)]; + if (!(s0_ == s1_ && s1_ == s2_)) { + std::vector ends; + if (s0_ != s1_) ends.push_back(point_on(j, k, 0)); + if (s0_ != s2_) ends.push_back(point_on(j, k, 1)); + if (s1_ != s2_) ends.push_back(point_on(j, k, 2)); + if (ends.size() == 2) connect(ends[0], ends[1]); + } + } + if (j + k + 1 < N) { + const int8_t s0_ = sign[id(j + 1, k)], s1_ = sign[id(j + 1, k + 1)], s2_ = sign[id(j, k + 1)]; + if (!(s0_ == s1_ && s1_ == s2_)) { + std::vector ends; + if (s0_ != s1_) ends.push_back(point_on(j + 1, k, 1)); + if (s1_ != s2_) ends.push_back(point_on(j, k + 1, 0)); + if (s0_ != s2_) ends.push_back(point_on(j, k, 2)); + if (ends.size() == 2) connect(ends[0], ends[1]); + } + } + } + if (bad) + return; + + // Chains from every crossing to the crossing it leads to. Closed loops never touch the + // boundary and are left alone - refinement resolves a feature that sits entirely inside a + // triangle. + std::vector matched(static_cast(n), -1); + std::vector chains; + std::vector> raws; // the traced polyline of each chain, crossing to crossing + std::vector seen(pts.size(), 0); + for (size_t s = 0; s < pts.size(); ++s) { + if (seen[s] || pt_x[s] < 0) + continue; + std::vector full; + int prev = -1, cur = int(s); + while (cur >= 0 && !seen[size_t(cur)]) { + seen[size_t(cur)] = 1; + full.push_back(pts[size_t(cur)]); + if (pt_x[size_t(cur)] >= 0 && cur != int(s)) + break; + const int nx0 = link[size_t(cur)][0], nx1 = link[size_t(cur)][1]; + const int next = (nx0 != prev) ? nx0 : nx1; + prev = cur; cur = next; + } + if (cur < 0 || pt_x[size_t(cur)] < 0 || cur == int(s)) + return; // a contour that starts at a crossing and ends nowhere: the lattice is inconsistent + Chain ch; + ch.i = pt_x[s]; + ch.j = pt_x[size_t(cur)]; + if (matched[size_t(ch.i)] >= 0 || matched[size_t(ch.j)] >= 0 || ch.i == ch.j) + return; + matched[size_t(ch.i)] = ch.j; + matched[size_t(ch.j)] = ch.i; + chains.push_back(std::move(ch)); + raws.push_back(std::move(full)); + } + // How close each contour comes to another: two sides of a thin feature. Simplifying either by + // the usual tolerance, or offsetting its copies by the usual gap, could then push it across + // the other, so both are scaled to the clearance. Segment-to-segment over every pair. + { + const auto seg_dist = [](const Vec2f &a, const Vec2f &b, const Vec2f &c, const Vec2f &d) { + const auto pt_seg = [](const Vec2f &q, const Vec2f &u, const Vec2f &v) { + const Vec2f uv = v - u; + const float t = std::clamp((q - u).dot(uv) / std::max(uv.squaredNorm(), 1e-12f), 0.f, 1.f); + return (q - (u + uv * t)).norm(); + }; + return std::min({ pt_seg(a, c, d), pt_seg(b, c, d), pt_seg(c, a, b), pt_seg(d, a, b) }); + }; + for (size_t x = 0; x < raws.size(); ++x) + for (size_t y = x + 1; y < raws.size(); ++y) { + float best = std::min(chains[x].clearance, chains[y].clearance); + for (size_t i = 0; i + 1 < raws[x].size(); ++i) + for (size_t j = 0; j + 1 < raws[y].size(); ++j) + best = std::min(best, seg_dist(raws[x][i], raws[x][i + 1], raws[y][j], raws[y][j + 1])); + chains[x].clearance = std::min(chains[x].clearance, best); + chains[y].clearance = std::min(chains[y].clearance, best); + } + } + for (size_t c = 0; c < chains.size(); ++c) { + Chain &ch = chains[c]; + const std::vector &full = raws[c]; + const float tol = std::min(h, 0.25f * ch.clearance); + const float gap = std::min(seam_gap_mm, 0.5f * ch.clearance); + std::vector simple; + simplify_polyline(full, tol, simple); + // A vertex within the seam gap of the previous one, or of the far end, leaves its copies no + // room to clear the blend of the step, and a corner that close to an edge is within the + // seam's own width anyway: it is merged into its neighbour. + { + std::vector spaced; + spaced.push_back(simple.front()); + for (size_t m = 1; m + 1 < simple.size(); ++m) + if ((simple[m] - spaced.back()).norm() >= gap && (simple[m] - simple.back()).norm() >= gap) + spaced.push_back(simple[m]); + spaced.push_back(simple.back()); + simple = std::move(spaced); + } + const int m_loop = int(L.ids.size()); + const bool adjacent = (L.xpos[size_t(ch.j)] - L.xpos[size_t(ch.i)] + m_loop) % m_loop == 1 || + (L.xpos[size_t(ch.i)] - L.xpos[size_t(ch.j)] + m_loop) % m_loop == 1; + if (simple.size() == 2 && full.size() > 2 && adjacent) { + // Never down to a bare chord between two crossings next to each other on one edge: the + // shallow pocket between them would be joined along the edge itself, and the regions on + // either side would overlap. The raw point farthest from the chord stays. + const Vec2f d = full.back() - full.front(); + const float dl = std::max(d.norm(), 1e-9f); + size_t bi = full.size() / 2; + float bd = 0.1f * tol; // below this the contour is straight: take its middle point + for (size_t m = 1; m + 1 < full.size(); ++m) { + const Vec2f r = full[m] - full.front(); + const float dist = std::abs(d.x() * r.y() - d.y() * r.x()) / dl; + if (dist > bd) { bd = dist; bi = m; } + } + simple.insert(simple.begin() + 1, full[bi]); + } + // Hairpins - the contour doubling back on itself with less than the seam gap between the + // two sides, the tip of a grain line thinner than the gap - would give a region polygon + // whose two sides cross. The spike vertex goes, and the feature is flattened there, as a + // thin feature on an edge is. + for (bool again = true; again;) { + again = false; + for (size_t m = 1; m + 1 < simple.size(); ++m) { + const Vec2f d1 = simple[m] - simple[m - 1], d2 = simple[m + 1] - simple[m]; + const float l1 = d1.norm(), l2 = d2.norm(); + if (l1 < 1e-9f || l2 < 1e-9f) { simple.erase(simple.begin() + long(m)); again = true; break; } + if (d1.dot(d2) / (l1 * l2) > -0.5f) + continue; // turns less than 120 degrees: a corner, not a hairpin + // Distance between the two sides: the shorter side's far end to the other side. + const Vec2f &tip = simple[m]; + const auto dist_to = [&](const Vec2f &q, const Vec2f &a, const Vec2f &b) { + const Vec2f ab = b - a; + const float t = std::clamp((q - a).dot(ab) / std::max(ab.squaredNorm(), 1e-12f), 0.f, 1.f); + return (q - (a + ab * t)).norm(); + }; + const float between = (l1 < l2) ? dist_to(simple[m - 1], tip, simple[m + 1]) : dist_to(simple[m + 1], simple[m - 1], tip); + if (between < seam_gap_mm) { + simple.erase(simple.begin() + long(m)); + again = true; + break; + } + } + } + for (size_t m = 1; m + 1 < simple.size(); ++m) + ch.pts.push_back(to3(simple[m])); + if (ch.pts.empty() && adjacent) { + // Two crossings next to each other on one edge and the contour, once simplified, going + // straight from one to the other: an empty pocket. There is nothing to wall off - a wall + // along the edge would be a zero-area strip, and the neighbour would lay its own on top + // - so neither crossing gets a seam. Recorded here and applied once the tracing is done. + L.no_seam.push_back(ch.i); + L.no_seam.push_back(ch.j); + } + } + for (int k = 0; k < n; ++k) + if (matched[size_t(k)] < 0) + return; + L.chains = std::move(chains); + L.partner = std::move(matched); + L.chain_of.assign(static_cast(n), -1); + for (size_t c = 0; c < L.chains.size(); ++c) { + L.chain_of[size_t(L.chains[c].i)] = int(c); + L.chain_of[size_t(L.chains[c].j)] = int(c); + } + L.ok = true; + }; + tbb::parallel_for(tbb::blocked_range(0, nt), [&](const tbb::blocked_range &r) { + for (size_t t = r.begin(); t < r.end(); ++t) + trace(t); + }); + size_t cut_count = 0; + for (size_t t = 0; t < nt; ++t) { + cut_count += loops[t].ok ? 1 : 0; + if (loops[t].ok) + for (int k : loops[t].no_seam) + loops[t].xs[size_t(k)]->no_seam = true; + } + + // 4. Which crossings a contour reaches, and how obliquely: the copies of a doubled crossing are moved + // apart along the edge, so the shallower the contour meets the edge the further apart they need to + // be to sit the same distance clear of it. + for (size_t t = 0; t < nt; ++t) { + Loop &L = loops[t]; + if (!L.ok) + continue; + for (auto &ch : L.chains) { + Crossing &p = *L.xs[size_t(ch.i)], &q = *L.xs[size_t(ch.j)]; + p.used = q.used = true; + const Vec3f p_next = ch.pts.empty() ? q.p : ch.pts.front(); + const Vec3f q_prev = ch.pts.empty() ? p.p : ch.pts.back(); + for (auto [c, toward] : { std::make_pair(&p, p_next), std::make_pair(&q, q_prev) }) { + Vec3f d = toward - c->p; + const float dl = d.norm(); + if (dl < 1e-12f) continue; + const Vec3f ed = (pos[size_t(c->b)] - pos[size_t(c->a)]).normalized(); + c->sin_min = std::min(c->sin_min, ed.cross(d / dl).norm()); + // Only the part of a move along the edge that runs along the contour brings the copy + // toward the next vertex's copies. With no interior vertex the chord's two ends share + // it; otherwise the interior copies, placed first, say how much room there is. + if (ch.pts.empty()) + c->dt_max = std::min(c->dt_max, 0.45f * dl / std::max(std::abs(ed.dot(d / dl)), 1e-3f)); + } + } + } + + // 5. Output vertices: the originals, then one or two per crossing and per interior contour vertex. + // A sharp crossing that a contour reaches is doubled, into two vertices on the edge either side of + // it, each on its own side of the contour; a triangle on that edge that is not cut itself simply + // carries both on its perimeter, one after the other, so the cut neighbour's wall still meets a + // closed surface. Keeping the copies on the edge is what keeps every triangle's split planar and + // inside the triangle. A crossing that is not sharp, or that no contour reaches, stays one vertex + // and the surface ramps there. + indexed_triangle_set out; + out.vertices = pos; + const float half_gap = 0.5f * seam_gap_mm; + // Interior contour vertices: doubled when the contour's ends are, offset across the contour along + // the bisector of the two segments meeting there, toward the high side; mitre limited, and held + // inside the triangle - a copy pushed across an edge would fold the region over the neighbour. + for (size_t t = 0; t < nt; ++t) { + Loop &L = loops[t]; + if (!L.ok) + continue; + const auto &tri = mesh.indices[t]; + const Vec3f &A = pos[size_t(tri[0])], &B = pos[size_t(tri[1])], &C = pos[size_t(tri[2])]; + const Vec3f U = (B - A).normalized(), V = L.N.cross(U); + const Vec2f a2(0.f, 0.f), b2((B - A).dot(U), (B - A).dot(V)), c2((C - A).dot(U), (C - A).dot(V)); + const float det = (b2.x() - a2.x()) * (c2.y() - a2.y()) - (c2.x() - a2.x()) * (b2.y() - a2.y()); + const auto bary2 = [&](const Vec3f &p) { + const Vec2f q((p - A).dot(U), (p - A).dot(V)); + const float wb = ((q.x() - a2.x()) * (c2.y() - a2.y()) - (c2.x() - a2.x()) * (q.y() - a2.y())) / det; + const float wc = ((b2.x() - a2.x()) * (q.y() - a2.y()) - (q.x() - a2.x()) * (b2.y() - a2.y())) / det; + return Vec3f(1.f - wb - wc, wb, wc); + }; + // The largest fraction of the offset that keeps the copy inside, with a little to spare. + const auto inside_fraction = [&](const Vec3f &p, const Vec3f &off) { + const Vec3f w0 = bary2(p), w1 = bary2(p + off); + float lambda = 1.f; + for (int i = 0; i < 3; ++i) + if (w1[i] < 0.f && w0[i] > w1[i]) + lambda = std::min(lambda, w0[i] / (w0[i] - w1[i])); + return 0.9f * std::max(lambda, 0.f); + }; + const bool s0 = side_of(vh[size_t(mesh.indices[t][0])]); + for (auto &ch : L.chains) { + const Crossing &p = *L.xs[size_t(ch.i)], &q = *L.xs[size_t(ch.j)]; + const bool doubled = p.sharp && p.used && !p.no_seam && q.sharp && q.used && !q.no_seam; + // The arc right after crossing ch.i lies on one side; which way that is from the contour + // says where "high" is. The arc's side: the loop's first corner's, flipped once per + // crossing passed, so after crossing index k it is !s0 for even k and s0 for odd. + const bool arc_high = (ch.i % 2 == 0) ? !s0 : s0; + const int after_pos = L.xpos[size_t(ch.i)] + 1; + const int entry = L.ids[size_t(after_pos) % L.ids.size()]; + const Vec3f ref = entry >= 0 ? pos[size_t(entry)] : L.xs[size_t(-entry - 1)]->p; + const size_t m = ch.pts.size(); + ch.lo.resize(m); ch.hi.resize(m); + // The left of the walk from i to j is one side of the contour all along it; whether that + // side is high is settled once, at the first segment, where the arc is a known reference. + // The direction the contour leaves the crossing in, taken over a usable length: the first + // interior vertex may sit right next to it. + Vec3f d_first = q.p - p.p; + for (size_t k = 0; k < m; ++k) + if ((ch.pts[k] - p.p).norm() > 0.25f * seam_gap_mm) { d_first = ch.pts[k] - p.p; break; } + d_first.normalize(); + const bool left_high = (L.N.cross(d_first).dot(ref - p.p) > 0.f) == arc_high; + for (size_t k = 0; k < m; ++k) { + if (!doubled) { + ch.lo[k] = ch.hi[k] = int(out.vertices.size()); + out.vertices.push_back(ch.pts[k]); + continue; + } + const Vec3f prev = (k == 0) ? p.p : ch.pts[k - 1]; + const Vec3f next = (k + 1 == m) ? q.p : ch.pts[k + 1]; + Vec3f d1 = (ch.pts[k] - prev).normalized(), d2 = (next - ch.pts[k]).normalized(); + Vec3f n1 = L.N.cross(d1), n2 = L.N.cross(d2); + Vec3f bis = n1 + n2; + float bl = bis.norm(); + Vec3f off; + if (bl < 1e-6f) off = n1; + else { + bis /= bl; + // Mitre: the offset polyline stays `half_gap` from both segments, up to twice that. + const float cos_half = std::max(bis.dot(n1), 0.5f); + off = bis / cos_half; + } + if (!left_high) off = -off; + // No further along either segment than half its length, so copies never cross their + // neighbours'; across the segments the offset is free. + const float ol = off.norm(); + const float reach = std::min({ half_gap, 0.45f * ch.clearance, + 0.45f * (ch.pts[k] - prev).norm() / std::max(std::abs(off.dot(d1)) / ol, 1e-3f), + 0.45f * (next - ch.pts[k]).norm() / std::max(std::abs(off.dot(d2)) / ol, 1e-3f) }); + const Vec3f off_lo = -off * (reach * inside_fraction(ch.pts[k], -off * reach)); + const Vec3f off_hi = off * (reach * inside_fraction(ch.pts[k], off * reach)); + // As for the crossings: each copy at the first of a few positions outward that samples + // pure, or the vertex stays single. + const auto place = [&](const Vec3f &o, bool high) -> int { + for (int step = 1; step <= 4; ++step) { + const Vec3f q = ch.pts[k] + o * (0.5f * float(step)); + if (step > 2 && inside_fraction(ch.pts[k], q - ch.pts[k]) < 0.999f) break; + const float h = sampler(q, L.N) - iso; + if ((h > 0.f) == high && std::abs(h) >= 0.3f * range) { + out.vertices.push_back(q); + return int(out.vertices.size()) - 1; + } + } + return -1; + }; + const int il = place(off_lo, false), ih = place(off_hi, true); + if (il < 0 || ih < 0) { + out.vertices.resize(out.vertices.size() - (il >= 0 ? 1 : 0) - (ih >= 0 ? 1 : 0)); + ch.lo[k] = ch.hi[k] = int(out.vertices.size()); + out.vertices.push_back(ch.pts[k]); + continue; + } + ch.lo[k] = il; ch.hi[k] = ih; + } + // Room for the end crossings' copies along the edge: the nearer of the first interior + // vertex's copies, measured along the contour, less a margin, over the edge's share of + // that direction. + if (m > 0) + for (auto [c, k, other] : { std::make_tuple(&p, size_t(0), q.p), std::make_tuple(&q, m - 1, p.p) }) { + (void) other; + Crossing &cr = const_cast(*c); + const Vec3f d = ch.pts[k] - cr.p; + const float dl = d.norm(); + if (dl < 1e-9f) continue; + const Vec3f dn = d / dl; + const float u_lo = (out.vertices[size_t(ch.lo[k])] - cr.p).dot(dn); + const float u_hi = (out.vertices[size_t(ch.hi[k])] - cr.p).dot(dn); + const Vec3f ed = (pos[size_t(cr.b)] - pos[size_t(cr.a)]).normalized(); + cr.dt_max = std::min(cr.dt_max, 0.9f * std::max(std::min(u_lo, u_hi), 0.f) / std::max(std::abs(ed.dot(dn)), 1e-3f)); + } + } + } + + for (auto &kv : edges) { + Edge &edge = kv.second; + for (size_t i = 0; i < edge.xs.size(); ++i) { + Crossing &c = edge.xs[i]; + if (!(c.sharp && c.used) || c.no_seam) { + c.single = int(out.vertices.size()); + out.vertices.push_back(c.p); + continue; + } + const float len = (pos[size_t(c.b)] - pos[size_t(c.a)]).norm(); + // Room along the edge: a third of the way to the previous and the next crossing or vertex, + // and less than half the shortest contour segment leaving the crossing, so the copies of + // the contour's next vertex cannot cross these. + const float t_prev = (i == 0) ? 0.f : edge.xs[i - 1].t; + const float t_next = (i + 1 == edge.xs.size()) ? 1.f : edge.xs[i + 1].t; + float dt = half_gap / std::max(c.sin_min, 0.15f) / std::max(len, 1e-6f); + dt = std::min({ dt, (c.t - t_prev) / 3.f, (t_next - c.t) / 3.f, c.dt_max / std::max(len, 1e-6f) }); + if (!(dt > 1e-6f)) { + c.single = int(out.vertices.size()); + out.vertices.push_back(c.p); + continue; + } + // The side of the edge before this crossing: the lower vertex's, flipped once per crossing + // passed on the way. + const bool before_high = side_of(vh[size_t(c.a)]) != (i % 2 == 1); + // Each copy has to sample a pure value of its own side - inside the step's blend it would be + // displaced to a height between the sides, and one such vertex tilts every triangle at it. + // The copy is placed at the first of a few positions along the edge, from dt outward, that + // samples pure; when none does the crossing stays one vertex and the surface ramps across + // that edge only. + // The search never goes past what the neighbours allow: less than half way to the next + // crossing along the edge (so two crossings' copies never meet), most of the way to the + // edge's vertex, and not past dt_max toward the contour's next vertex. + const float lo_share = (i == 0) ? 0.9f : 0.49f, hi_share = (i + 1 == edge.xs.size()) ? 0.9f : 0.49f; + const float t_lo_lim = c.t - std::min(lo_share * (c.t - t_prev), c.dt_max / std::max(len, 1e-6f)); + const float t_hi_lim = c.t + std::min(hi_share * (t_next - c.t), c.dt_max / std::max(len, 1e-6f)); + const auto place = [&](float sgn, bool high) -> int { + for (int step = 1; step <= 6; ++step) { + const float tt = c.t + sgn * dt * (0.5f * float(step)); + if (tt < t_lo_lim || tt > t_hi_lim) break; + const auto [q, qn] = sample_between(c.a, c.b, tt); + const float h = sampler(q, qn) - iso; + if ((h > 0.f) == high && std::abs(h) >= 0.3f * range) { + out.vertices.push_back(q); + return int(out.vertices.size()) - 1; + } + } + return -1; + }; + const int na = place(-1.f, before_high), nb = place(+1.f, !before_high); + if (na < 0 || nb < 0) { + out.vertices.resize(out.vertices.size() - (na >= 0 ? 1 : 0) - (nb >= 0 ? 1 : 0)); + c.single = int(out.vertices.size()); + out.vertices.push_back(c.p); + continue; + } + c.near_a = na; c.near_b = nb; + c.lo = before_high ? c.near_b : c.near_a; + c.hi = before_high ? c.near_a : c.near_b; + } + } + // 6. Output triangles: each region of a cut triangle ear-clipped on its own side's copies, a wall + // strip per contour wound with the surface, everything else passed through or plainly split. + out.indices.reserve(nt * 2); + std::vector source; + source.reserve(nt * 2); + const auto emit = [&](int a, int b, int c, int src) { out.indices.emplace_back(a, b, c); source.push_back(src); }; + const auto copy_for = [&](const Crossing &c, bool high) { return c.single >= 0 ? c.single : (high ? c.hi : c.lo); }; + // Triangulates a triangle that carries extra vertices on its edges without a zero-area sliver: the + // first extra vertex found is joined to the opposite corner, which splits the triangle in two that + // carry fewer extras each, and so on. A fan from a corner cannot do this - the extras on that + // corner's own edges are collinear with it. Each side is the corner followed by the extras on the + // way to the next corner. + const std::function, 3>, int)> split_tri = + [&](std::array, 3> side, int src) { + for (int e = 0; e < 3; ++e) + if (side[size_t(e)].size() > 1) { + const auto &S = side[size_t(e)]; + const int a = S[0], x = S[1], c = side[size_t((e + 2) % 3)][0]; + std::vector rest(S.begin() + 1, S.end()); // x and the extras after it + split_tri({ std::vector{ a }, std::vector{ x }, side[size_t((e + 2) % 3)] }, src); + split_tri({ std::move(rest), side[size_t((e + 1) % 3)], std::vector{ c } }, src); + return; + } + emit(side[0][0], side[1][0], side[2][0], src); + }; + // A wall strip between the low and high copies of a contour, from crossing p to crossing q. + const auto emit_wall = [&](const Crossing &p, const Chain &ch, const Crossing &q, const Vec3f &N, int src) { + std::vector lo, hi; + lo.push_back(copy_for(p, false)); hi.push_back(copy_for(p, true)); + for (size_t k = 0; k < ch.pts.size(); ++k) { lo.push_back(ch.lo[k]); hi.push_back(ch.hi[k]); } + lo.push_back(copy_for(q, false)); hi.push_back(copy_for(q, true)); + auto tri_up = [&](int i, int j, int k) { + if (i == j || j == k || k == i) return; + const Vec3f &A = out.vertices[size_t(i)], &B = out.vertices[size_t(j)], &C = out.vertices[size_t(k)]; + if ((B - A).cross(C - A).dot(N) >= 0.f) emit(i, j, k, src); else emit(i, k, j, src); + }; + for (size_t k = 0; k + 1 < lo.size(); ++k) { + tri_up(lo[k], lo[k + 1], hi[k + 1]); + tri_up(lo[k], hi[k + 1], hi[k]); + } + }; + + for (size_t t = 0; t < nt; ++t) { + const auto &tri = mesh.indices[t]; + const Loop &L = loops[t]; + const int n = int(L.xs.size()); + if (n == 0) { emit(tri[0], tri[1], tri[2], int(t)); continue; } + const size_t m = L.ids.size(); + const bool s0 = side_of(vh[size_t(tri[0])]); + + if (L.ok) { + // Every crossing starts one region: the run of the loop after it, on the side the field has + // there, following any other contour it meets back to the loop, until it returns to the + // start. Each region is tracked per (crossing, side) so the one between two contours is + // emitted once, not once per contour. The region's vertices are collected in the triangle's + // plane for ear clipping, since a traced contour can make it concave. + const Vec3f &A = pos[size_t(tri[0])], &B = pos[size_t(tri[1])]; + const Vec3f U = (B - A).normalized(), V = L.N.cross(U); + std::vector pts2; + std::vector ids2; + const auto push = [&](int id) { + const Vec3f &p = out.vertices[size_t(id)]; + pts2.emplace_back((p - A).dot(U), (p - A).dot(V)); + ids2.push_back(id); + }; + // Interior vertices of chain c, walking from crossing `from`, on side S. + const auto push_chain = [&](int c, int from, bool S) { + const Chain &ch = L.chains[size_t(c)]; + const auto &cp = S ? ch.hi : ch.lo; + if (from == ch.i) for (size_t k = 0; k < cp.size(); ++k) push(cp[k]); + else for (size_t k = cp.size(); k-- > 0;) push(cp[k]); + }; + const auto side_after = [&](int k) { return (k % 2 == 0) ? !s0 : s0; }; + std::vector> consumed(static_cast(n), { false, false }); + for (int k = 0; k < n; ++k) { + const bool S = side_after(k); + if (consumed[size_t(k)][S]) + continue; + pts2.clear(); ids2.clear(); + push(copy_for(*L.xs[size_t(k)], S)); + consumed[size_t(k)][S] = true; + size_t i = size_t(L.xpos[size_t(k)] + 1) % m; + for (size_t guard = 0; guard < 2 * m; ++guard) { + const int id = L.ids[i]; + if (id >= 0) { push(id); i = (i + 1) % m; continue; } + const int c = -id - 1; + consumed[size_t(c)][S] = true; + push(copy_for(*L.xs[size_t(c)], S)); + if (c == L.partner[size_t(k)]) + break; + // Follow the contour from c to its partner, then continue the loop after it. + const int pc = L.partner[size_t(c)]; + push_chain(L.chain_of[size_t(c)], c, S); + consumed[size_t(pc)][S] = true; + push(copy_for(*L.xs[size_t(pc)], S)); + if (pc == L.partner[size_t(k)]) + break; + i = size_t(L.xpos[size_t(pc)] + 1) % m; + } + // Close the polygon along the region's own contour back to k, in reverse. + push_chain(L.chain_of[size_t(k)], L.partner[size_t(k)], S); + std::vector poly(ids2.size()); + std::iota(poly.begin(), poly.end(), 0); + ear_clip(pts2, poly, [&](int a, int b, int c) { emit(ids2[size_t(a)], ids2[size_t(b)], ids2[size_t(c)], int(t)); }); + } + for (const auto &ch : L.chains) + emit_wall(*L.xs[size_t(ch.i)], ch, *L.xs[size_t(ch.j)], L.N, int(t)); + continue; + } + // Not cut: split at the crossings so the neighbours' cuts meet no T-junction. A doubled crossing + // contributes both copies, the one nearer the vertex the walk comes from first. + std::array, 3> side; + int e = -1; + for (size_t i = 0; i < m; ++i) { + if (L.ids[i] >= 0) { ++e; side[size_t(e)].push_back(L.ids[i]); continue; } + const Crossing &c = *L.xs[size_t(-L.ids[i] - 1)]; + if (c.single >= 0) { side[size_t(e)].push_back(c.single); continue; } + const bool from_a = tri[e] == c.a; + side[size_t(e)].push_back(from_a ? c.near_a : c.near_b); + side[size_t(e)].push_back(from_a ? c.near_b : c.near_a); + } + split_tri(std::move(side), int(t)); + } + // Safety net: a texture whose features are of the seam's own scale can fold copies over each + // other in spite of everything above. When more than a trace of the output faces the wrong way, + // the cut is not trusted and the mesh goes out as it came in. + { + size_t inverted = 0; + for (size_t i = 0; i < out.indices.size(); ++i) { + const auto &f = out.indices[i]; + const auto &g = mesh.indices[size_t(source[i])]; + const Vec3f n = (out.vertices[size_t(f[1])] - out.vertices[size_t(f[0])]).cross(out.vertices[size_t(f[2])] - out.vertices[size_t(f[0])]); + const Vec3f ns = (mesh.vertices[size_t(g[1])] - mesh.vertices[size_t(g[0])]).cross(mesh.vertices[size_t(g[2])] - mesh.vertices[size_t(g[0])]); + inverted += n.dot(ns) < 0.f && n.norm() > 1e-7f; + } + if (double(inverted) > 0.002 * double(out.indices.size())) + return passthrough(); + } + if (out_source) *out_source = std::move(source); + if (out_cut_count) *out_cut_count = cut_count; + return out; +} + } // namespace Slic3r diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 63cb443420..ea208fbcb3 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -20,6 +20,11 @@ namespace Slic3r { +// Optional step-by-step capture of a bake; see TextureBake/TextureBakeDebug.hpp. Forward declared and +// taken by pointer so this header, which most of the texture feature includes, does not grow a +// dependency for something only the debug view and the benchmarks use. +class BakeStageRecorder; + class ModelVolume; // Bits of subdivide_mesh_adaptive()'s per-triangle `refine_region` mask. See that function. @@ -259,14 +264,14 @@ struct TextureDisplacementLayer // chart ids, so this is meaningful only against the unwrap it was made on. std::vector island_groups; - // Only used by TextureProjectionMethod::LSCM: manual per-vertex UV edits made in the UV editor's - // Vertex/Edge select modes. Each pair is (mesh vertex index, its overriding raw-unwrap coordinate in - // mm) - the *raw* unwrap position, i.e. before the island transform, so the edited vertex still - // moves and rotates with its island. In compute_lscm_uvs() this replaces the automatic unwrap - // coordinate for that vertex; in the editor it edits the displayed geometry directly. Keyed in mesh- - // vertex space like lscm_seam_edges (dropped on a topology change). The raw coordinate is only - // meaningful against the current unwrap, so a re-unwrap clears these. A mesh vertex shared by several - // charts (a seam vertex) settles on one, matching compute_lscm_uvs()'s single-UV-per-vertex rule. + // Only used by TextureProjectionMethod::LSCM: manual UV edits made in the UV editor's Vertex/Edge + // select modes. Each pair is (key, its overriding raw-unwrap coordinate in mm) - the *raw* unwrap + // position, i.e. before the island transform, so the edited vertex still moves and rotates with its + // island. The key names what is edited (see apply_lscm_uv_overrides()): + // - negative: one unwrapped vertex of the current unwrap, as lscm_uv_override_key(index). This is what + // the editor stores, so a seam vertex dragged in one island leaves its copies in the neighbouring + // islands where they are. Like `islands`, meaningful only against the unwrap it was made on. + // - zero or positive: a mesh vertex, every unwrapped copy of it (how edits were stored before). std::vector> lscm_uv_overrides; // How this layer folds into the displacement accumulated by the layers below it. Ignored for @@ -365,21 +370,27 @@ struct TextureDisplacementOptions // deliberately (which is a blunter version of the per-layer edge-smoothing falloff). bool smooth_skip_border = true; - // Alternative bake pipeline, for side-by-side comparison. The path above is topology-preserving - // and needs the mesh prepared first; this one refines, removes slivers, displaces and optionally - // simplifies in one run. Off by default, and it produces no colour - it rebuilds the topology, so - // the per-facet assignment has nothing stable to attach to. - bool pipeline_v2 = false; - float v2_refine_mm = 0.3f; + // Which bake pipeline. On (the default): refine, remove slivers, displace and simplify in one run, + // nothing to prepare first. Off, the classic path: the mesh is prepared first (remesh, adaptive + // subdivision, step cut) and then displaced vertex by vertex, keeping the topology - which is what + // colour needs, since the per-facet assignment has nothing stable to attach to once the topology is + // rebuilt. Projects saved with the classic path keep it: the flag is stored per volume. + bool pipeline_v2 = true; + // Refinement edge length, mm. 0 (the default) means automatic: chosen from the texture's texel + // size and sharpness and the model's size, see recommend_v2_resolution(). A saved project with an + // explicit value keeps it. + float v2_refine_mm = 0.f; bool v2_regularize = false; - int v2_max_triangles_k = 750; // 0 skips simplification, which is worth comparing on its own - // Stop displacement pushing geometry through the build plate. Only what would end up below the - // model's own bottom is moved; downward relief above that is untouched. - bool v2_clamp_below_plate = false; + // Simplification target in thousands of triangles. -1 (the default) means automatic, from the + // same recommendation; 0 skips simplification, which is worth comparing on its own. + int v2_max_triangles_k = -1; // Slide vertices onto the texture's own edges before displacing. Displacement moves vertices along // the normal only, so without this a step in the image is reproduced wherever the triangle grid // happens to fall, as a staircase rather than a straight wall. bool v2_relocate = false; + // Data-dependent edge flips before displacement (see TextureBakeFlip.hpp). On by default and not a + // user setting; deliberately left out of serialize() so project files are unaffected. + bool v2_flip_edges = true; // Colour, all of which belongs to the stack rather than to any one layer: it is about how the // printer will realise the colours, not about which image they came from. @@ -397,12 +408,44 @@ struct TextureDisplacementOptions { int mix_mode = int(color_mix_mode); ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border, - pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k, v2_clamp_below_plate, + pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k, v2_relocate, color_mix_enabled, mix_mode, color_despeckle); color_mix_mode = ColorMixMode(mix_mode); } }; +// How much detail a height texture carries, as BumpMesh's smart resolution measures it: central +// differences of the grey image, the mean gradient and the share of texels steeper than 30 grey +// levels, mapped to how many texels one mesh edge may span (1 for a hard-edged image, 4 for a smooth +// one). Cached per image, like the decode. +struct TextureDetail +{ + float mean_gradient = 0.f; + float sharp_fraction = 0.f; + float pixels_per_edge = 4.f; +}; +TextureDetail analyze_texture_detail(const TextureDisplacementLayer &layer); + +// The default pipeline's automatic resolution: the refinement edge and the simplification budget the +// texture and the model call for, when the options leave them at "auto". +// - edge = texel size (tile / image width, in world mm, over the finest layer) x pixels per edge, +// but no finer than keeps the refinement under a 12 M triangle cap for this surface area, clamped +// to [0.05 mm, min(5 mm, diagonal / 50)] and rounded up to 0.01 mm; +// - budget = the triangle count an edge of that texel size needs over the surface, scaled by the +// relief depth (a gentle relief needs fewer), stepped to 10 k and clamped to [10 k, 2000 k]. +// `edge_mm` is 0 when no layer has a usable texture. +struct V2Resolution +{ + float edge_mm = 0.f; + int budget_k = 0; + float texel_mm = 0.f; + float pixels_per_edge = 0.f; + bool budget_bound = false; // the edge came from the triangle cap, not from the texture +}; +V2Resolution recommend_v2_resolution(const indexed_triangle_set &mesh, + const std::vector &layers, + const Transform3d &volume_to_world = Transform3d::Identity()); + // Decoded height (and, for a colour source image, colour) samples, independent of any GUI/OpenGL // texture object so they can be evaluated from a background bake Job as well as from GUI-side // preview code. @@ -601,11 +644,12 @@ Vec2f apply_island_transform(const Vec2f &uv, int chart, const PatchUnwrap &unwr // its vertex buffer at all. Eigen::Matrix island_transform_matrix(int chart, const PatchUnwrap &unwrap, const std::vector &islands); -// Lays the unwrap's charts out as a connected net: charts that share a mesh edge are unfolded so +// Lays the unwrap's charts out as connected nets: charts that share a mesh edge are unfolded so // their shared edge coincides (a cube -> its six faces joined along a spanning tree of edges, the rest // left as free borders). Charts stay separate islands, so their borders still show and any of them can -// still be moved by hand afterwards. A chart whose unfold would overlap one already placed is left -// where the packing put it. Returns one placement per chart. See the gizmo's auto-connect option. +// still be moved by hand afterwards. Each net grows from the largest chart not yet placed; a chart whose +// triangles would overlap the net stays out of it and starts a net of its own. The nets are then packed +// side by side. Returns one placement per chart. See the gizmo's auto-connect option. std::vector compute_connected_net(const PatchUnwrap &unwrap); // The placement that unfolds `child` onto `parent` along their shared mesh edge, honouring `parent`'s @@ -642,6 +686,13 @@ PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_a // both the layer's seam angle and its hand-placed islands. std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer); +// The TextureDisplacementLayer::lscm_uv_overrides key for one unwrapped vertex (an index into PatchUnwrap::uvs). +inline int lscm_uv_override_key(int unwrapped_vertex) { return -(unwrapped_vertex + 1); } + +// Writes the overrides into `unwrap.uvs`: mesh-vertex keys onto every copy of their vertex, then unwrapped-vertex +// keys onto their one copy. Returns, per unwrapped vertex, whether an override set it. +std::vector apply_lscm_uv_overrides(PatchUnwrap &unwrap, const std::vector> &overrides); + // One paint mask (as stored by ModelVolume::texture_displacement_facets) per possible layer slot. using TextureDisplacementFacetsData = std::array; @@ -716,12 +767,34 @@ struct TextureColorRequest // straight to a TriangleSelector without a second mapping table. std::vector *out_triangle = nullptr; }; +// Where the volume sits on the plate: its instance transform times its own volume transform, i.e. +// mesh coordinates -> world millimetres. +// +// Every number the user sets is in real millimetres on the printed part - "Depth (mm)", "Tile size +// (mm)" - and the build plate is a world plane, so the bake runs in world space and transforms the +// result back. Doing it in the volume's own coordinates instead made a scaled instance stretch both +// the relief depth and the tiling by the scale factor, and under a non-uniform scale it also +// displaced along the wrong direction: a mesh normal maps to the world normal through the inverse +// transpose, not through the transform itself, so the relief leaned. Identity - the default - is +// exactly the old behaviour and is what an untransformed volume gives. indexed_triangle_set build_texture_displacement(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, const TextureDisplacementOptions &options = {}, const DisplacementProgressFn &progress = {}, - const TextureColorRequest *color = nullptr); + const TextureColorRequest *color = nullptr, + const Transform3d &volume_to_world = Transform3d::Identity(), + // When given and enabled, receives the mesh after each + // stage, already brought back into `base_mesh`'s frame. + BakeStageRecorder *debug = nullptr); + +// `volume`'s mesh coordinates -> world millimetres: its first instance's transform times its own. +// The mesh is shared by every instance, so a multi-instance object can only be baked for one of +// them; the first is what the gizmo edits against. Identity when the volume has no object yet. +Transform3d texture_displacement_volume_to_world(const ModelVolume &volume); +// The frame the bake and the previews project the texture in: `volume_to_world` with its translation +// removed, i.e. world orientation and scale about the volume's own origin. See build_texture_displacement(). +Transform3d texture_displacement_bake_frame(const Transform3d &volume_to_world); // Convenience overload for main-thread callers: extracts the mesh/layers/paint data/options from // `volume` and forwards to the overload above. @@ -881,7 +954,69 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, float border_edge_length_mm = 0.f, const DisplacementProgressFn &progress = nullptr, const ColorFieldSampler &color = nullptr, - float color_edge_length_mm = 0.f); + float color_edge_length_mm = 0.f, + // Step mode, for a mesh that cut_mesh_at_steps() will cut + // next: a triangle one of whose edges crosses a *sharp* + // step of the height field (a jump of 40 % of the relief + // between two samples at the sample spacing, across the + // mid-level) gets a chord error of zero, since the step is + // the cutter's to reproduce and refining it only carpets + // the edge of the pattern. Features that no edge crosses + // yet still refine until one does. No effect outside + // feature mode. + bool split_multi_crossings = false); + +// Cuts `mesh` along the height field's mid-level contour wherever the field steps sharply across it, +// and doubles the seam, so that displacing the result produces a vertical wall at the step instead of +// a ramp across whichever triangle the step happened to fall in. +// +// This is what a binary height map - a grid, stripes, a knurl, wood grain as black-and-white bands - +// needs, and what refinement alone cannot give it. Refining a triangle that straddles a step never +// brings the chord error under any tolerance: the surface has a discontinuity, and a finer triangle only +// makes the ramp narrower. Every refinement level then leaves a band of its own size along every step, +// and the triangle budget ends up spent on carpeting the edges of the pattern, one to two orders of +// magnitude more triangles than the pattern needs, while the ramps are still visible. +// +// What it does, in order: +// - Samples the field at the vertices along the bake's normals and takes the mid-level as the contour. +// A vertex that sits inside a step's blend is nudged along the surface, down its own side's slope, +// until it samples a pure value, so no vertex bakes to a half height. +// - Marches every painted edge for crossings of the mid-level (sampled at half `step_width_mm`, then +// bisected). Both triangles at an edge see the same crossings, which keeps the result conformal. A +// crossing is *sharp* when the field changes by half its range within `step_width_mm`. +// - Traces the contour inside each triangle on a local lattice (marching triangles) and keeps the +// polylines that join one crossing to another; a closed loop inside a triangle is left to +// refinement. Each polyline is simplified and kept clear of its neighbours. +// - Doubles the seam: every polyline vertex and every crossing gets a copy on each side, moved +// `seam_gap_mm` apart across the contour to where each samples a pure value of its own side. The +// regions between the seams are ear-clipped; the strip between the two copies is triangulated flat +// and becomes the wall once the high side is displaced. A triangle whose contour could not be traced +// is split at its crossings without a seam, so the neighbours still meet it without a T-junction. +// +// Nothing is cut unless the texture is a step texture: most vertex heights sit at one of two levels, +// most crossings are sharp, and the features are wider than a couple of step widths (a noisy or a +// smooth relief is passed through unchanged, as is a mesh with nothing painted). If the cut would leave +// more than a trace of inverted triangles the input is returned unchanged as well. +// +// `region` flags the triangles that may be cut (the painted ones, same encoding as +// subdivide_mesh_adaptive(), any non-zero value). An edge may carry any number of crossings; two closer +// than `max(min_feature_mm, seam_gap_mm)` are a feature too thin to carry a seam and are dropped as a +// pair, leaving the surface flat there. `out_source` receives the input triangle each output triangle +// descends from, so paint carries over; `out_cut_count` the number of input triangles that were cut +// (0 means the mesh came back unchanged). +indexed_triangle_set cut_mesh_at_steps(const indexed_triangle_set &mesh, const std::vector ®ion, + const HeightFieldSampler &sampler, float step_width_mm, + float seam_gap_mm, float min_feature_mm = 0.f, + std::vector *out_source = nullptr, size_t *out_cut_count = nullptr); + +// The cutter's verdict alone: whether cut_mesh_at_steps() with the same arguments would cut anything, +// judged the same way (two levels, sharp crossings, features wider than the step) but without the +// vertex nudge or any tracing, so it is cheap enough to ask on the coarse mesh. The prepare path asks +// it *before* refining: a subdivision run in step mode leaves the steps alone for the cutter, which is +// only right if the cutter is then going to cut them. +bool texture_has_steps_to_cut(const indexed_triangle_set &mesh, const std::vector ®ion, + const HeightFieldSampler &sampler, float step_width_mm, float seam_gap_mm, + float min_feature_mm = 0.f); // The recipe for getting a mesh ready to receive displacement: even out the triangle density, then // refine it where the texture bends. Either stage is skipped when its target is <= 0. Pure data, and @@ -906,6 +1041,9 @@ struct TextureDisplacementPrepareParams // Separate from the height criteria because colour lands per facet: a flat surface carrying a // sharp colour edge needs triangles along that edge even though its height is perfectly smooth. float subdiv_color_edge_mm = 0.f; + // Cut the mesh along sharp steps in the texture after refining, so they bake as walls rather than + // ramps. See cut_mesh_at_steps(). Off leaves the old ramp behaviour. + bool cut_steps = false; }; // What a preparation run produced. An empty `mesh` means there was nothing to do and the caller must From bc280522451de30f55abd70859937ce0435fe362 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH 29/89] Bump and uvcheck shaders: project in the bake's world frame --- .../shaders/110/texture_displacement_bump.fs | 19 ++++++++------- .../110/texture_displacement_uvcheck.fs | 7 ++++-- .../shaders/140/texture_displacement_bump.fs | 23 +++++++++++-------- .../140/texture_displacement_uvcheck.fs | 7 ++++-- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index 4686668467..5dff96d479 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -46,7 +46,8 @@ uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; uniform float midlevel; // the height that means "don't move"; needed by the parallax step -uniform vec3 eye_model_pos; // camera position in this volume's local space, for the view ray +uniform vec3 eye_model_pos; // camera position in the texture frame (world minus tex_anchor) +uniform vec3 tex_anchor; // the volume's origin in world space: the texture frame's origin uniform bool use_vertex_uv; // 2x3 affine (lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the dragged island's uv; see the // 140 variant. Identity when nothing is dragged. @@ -134,7 +135,9 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // World millimetres throughout, like the bake - see the 140 variant. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; // the frame the texture is projected in, as the bake does if (volume_mirrored) triangle_normal = -triangle_normal; @@ -155,8 +158,8 @@ void main() have_uv = true; float h = texture2D(height_tex, uv).r; float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 sigmaS = dFdx(model_pos.xyz); - vec3 sigmaT = dFdy(model_pos.xyz); + vec3 sigmaS = dFdx(world_pos.xyz); + vec3 sigmaT = dFdy(world_pos.xyz); vec3 R1 = cross(sigmaT, triangle_normal); vec3 R2 = cross(triangle_normal, sigmaS); float det = dot(sigmaS, R1); @@ -171,9 +174,9 @@ void main() // Parallax occlusion mapping: march the view ray through the height shell and shade at the // first point where it drops below the displaced surface (see header). float amp = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 view_dir = normalize(eye_model_pos - model_pos.xyz); + vec3 view_dir = normalize(eye_model_pos - tex_pos); float v_dot_n = dot(view_dir, triangle_normal); - vec2 uv = project_uv(model_pos.xyz, triangle_normal); + vec2 uv = project_uv(tex_pos, triangle_normal); // The shell the displaced surface lives inside, as signed heights along the normal. Taken from // both ends of h in [0, 1] so it stays correct for an inverted layer or a raised midlevel, @@ -192,11 +195,11 @@ void main() // by construction, and step inward; the crossing is what this pixel actually sees. float s = h_hi / v_dot_n; float ds = (h_hi - h_lo) / (v_dot_n * float(PARALLAX_STEPS)); - vec2 prev_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 prev_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float prev_gap = h_hi - amp * (H_AT(prev_uv) - midlevel); // >= 0 by construction for (int i = 0; i < PARALLAX_STEPS; ++i) { s -= ds; - vec2 cur_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 cur_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float gap = s * v_dot_n - amp * (H_AT(cur_uv) - midlevel); if (gap <= 0.0) { // Crossed between the last two samples - interpolating the hit is what stops it diff --git a/resources/shaders/110/texture_displacement_uvcheck.fs b/resources/shaders/110/texture_displacement_uvcheck.fs index fbfa6a0517..2679bc57e7 100644 --- a/resources/shaders/110/texture_displacement_uvcheck.fs +++ b/resources/shaders/110/texture_displacement_uvcheck.fs @@ -16,6 +16,7 @@ uniform bool volume_mirrored; uniform int mode; uniform float checker_freq; uniform float tiling_scale; +uniform vec3 tex_anchor; // the volume's origin in world space uniform float rotation_rad; uniform vec2 uv_offset; uniform bool use_vertex_uv; @@ -49,7 +50,9 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // World space anchored at the volume's origin, like the bake and the bump preview. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; if (volume_mirrored) triangle_normal = -triangle_normal; @@ -57,7 +60,7 @@ void main() if (mode == 1) { base = heatmap(distortion); } else { - vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal); + vec2 uv = use_vertex_uv ? vertex_uv : project_uv(tex_pos, triangle_normal); vec2 c = floor(uv * checker_freq); float check = mod(c.x + c.y, 2.0); base = (check < 0.5) ? vec3(0.22, 0.23, 0.26) : vec3(0.82, 0.83, 0.86); diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 4f8e3c8186..8127ac35a1 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -105,7 +105,8 @@ uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; uniform float midlevel; // the height that means "don't move"; needed by the parallax step -uniform vec3 eye_model_pos; // camera position in this volume's local space, for the view ray +uniform vec3 eye_model_pos; // camera position in the texture frame (world minus tex_anchor) +uniform vec3 tex_anchor; // the volume's origin in world space: the texture frame's origin uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM) // A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of // the island currently being dragged in the UV editor (island_active > 0.5). Identity when nothing is @@ -123,7 +124,7 @@ in vec2 vertex_uv; out vec4 out_color; -// The two model-space axes the triplanar planar coordinate is read off, per dominant normal +// The two world-space axes the triplanar planar coordinate is read off, per dominant normal // component - same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y // along b. void projection_axes(vec3 n, out vec3 t, out vec3 b) @@ -200,7 +201,11 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // Everything below runs in world millimetres, like the bake: a tile is tiling_scale mm on the + // printed part whatever the instance's scale or rotation, so the preview has to project from the + // world position and perturb the world normal. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; // the frame the texture is projected in, as the bake does if (volume_mirrored) triangle_normal = -triangle_normal; @@ -231,8 +236,8 @@ void main() have_uv = true; float h = texture(height_tex, uv).r; float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 sigmaS = dFdx(model_pos.xyz); - vec3 sigmaT = dFdy(model_pos.xyz); + vec3 sigmaS = dFdx(world_pos.xyz); + vec3 sigmaT = dFdy(world_pos.xyz); vec3 R1 = cross(sigmaT, triangle_normal); vec3 R2 = cross(triangle_normal, sigmaS); float det = dot(sigmaS, R1); @@ -250,9 +255,9 @@ void main() // Parallax occlusion mapping: march the view ray through the height shell and shade at the // first point where it drops below the displaced surface (see header). float amp = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 view_dir = normalize(eye_model_pos - model_pos.xyz); + vec3 view_dir = normalize(eye_model_pos - tex_pos); float v_dot_n = dot(view_dir, triangle_normal); - vec2 uv = project_uv(model_pos.xyz, triangle_normal); + vec2 uv = project_uv(tex_pos, triangle_normal); // The shell the displaced surface lives inside, as signed heights along the normal. Taken from // both ends of h in [0, 1] so it stays correct for an inverted layer or a raised midlevel, @@ -271,11 +276,11 @@ void main() // by construction, and step inward; the crossing is what this pixel actually sees. float s = h_hi / v_dot_n; float ds = (h_hi - h_lo) / (v_dot_n * float(PARALLAX_STEPS)); - vec2 prev_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 prev_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float prev_gap = h_hi - amp * (H_AT(prev_uv) - midlevel); // >= 0 by construction for (int i = 0; i < PARALLAX_STEPS; ++i) { s -= ds; - vec2 cur_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 cur_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float gap = s * v_dot_n - amp * (H_AT(cur_uv) - midlevel); if (gap <= 0.0) { // Crossed between the last two samples - interpolating the hit is what stops it diff --git a/resources/shaders/140/texture_displacement_uvcheck.fs b/resources/shaders/140/texture_displacement_uvcheck.fs index 8b430db52d..038c915f3f 100644 --- a/resources/shaders/140/texture_displacement_uvcheck.fs +++ b/resources/shaders/140/texture_displacement_uvcheck.fs @@ -25,6 +25,7 @@ uniform bool volume_mirrored; uniform int mode; // 0 checker, 1 distortion uniform float checker_freq; // checker squares per uv unit (one uv unit == one texture tile) uniform float tiling_scale; +uniform vec3 tex_anchor; // the volume's origin in world space uniform float rotation_rad; uniform vec2 uv_offset; uniform bool use_vertex_uv; @@ -61,7 +62,9 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // World space anchored at the volume's origin, like the bake and the bump preview. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; if (volume_mirrored) triangle_normal = -triangle_normal; @@ -69,7 +72,7 @@ void main() if (mode == 1) { base = heatmap(distortion); } else { - vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal); + vec2 uv = use_vertex_uv ? vertex_uv : project_uv(tex_pos, triangle_normal); vec2 c = floor(uv * checker_freq); float check = mod(c.x + c.y, 2.0); // Two distinct greys, plus a faint tint on one set so orientation is readable at a glance. From 78f873a27ff8ee05f968a24f772c393cf7c0c358 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH 30/89] Texture gizmo: one-run pipeline by default, auto resolution, colour fixes, debug view --- src/slic3r/CMakeLists.txt | 2 + .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 3566 +++++++++++------ .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 120 +- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 3 +- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 3 + .../GUI/Jobs/TextureDisplacementDebugJob.cpp | 113 + .../GUI/Jobs/TextureDisplacementDebugJob.hpp | 82 + .../Jobs/TextureDisplacementPreviewJob.cpp | 2 +- .../Jobs/TextureDisplacementPreviewJob.hpp | 3 + src/slic3r/GUI/Plater.cpp | 6 + src/slic3r/GUI/TextureLibrary.cpp | 105 + src/slic3r/GUI/TextureLibrary.hpp | 16 +- src/slic3r/GUI/UVEditorCanvas.cpp | 1168 ++++-- src/slic3r/GUI/UVEditorCanvas.hpp | 135 +- 14 files changed, 3787 insertions(+), 1537 deletions(-) create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.cpp create mode 100644 src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7a2c963e2f..b949b8eff5 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -331,6 +331,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/TextureDisplacementBakeJob.hpp GUI/Jobs/TextureDisplacementPrepareJob.cpp GUI/Jobs/TextureDisplacementPrepareJob.hpp + GUI/Jobs/TextureDisplacementDebugJob.cpp + GUI/Jobs/TextureDisplacementDebugJob.hpp GUI/Jobs/TextureDisplacementPreviewJob.cpp GUI/Jobs/TextureDisplacementPreviewJob.hpp GUI/Jobs/ThreadSafeQueue.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 70e4b83e7f..bd396d9484 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -1,5 +1,6 @@ #include "GLGizmoTextureDisplacement.hpp" +#include #include #include "libslic3r/AABBTreeIndirect.hpp" @@ -28,6 +29,7 @@ #include "slic3r/GUI/UVEditorCanvas.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementPrepareJob.hpp" +#include "slic3r/GUI/Jobs/TextureDisplacementDebugJob.hpp" #include "slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "GLGizmoUtils.hpp" @@ -223,6 +225,39 @@ constexpr int PALETTE_LUT_EDGE = 24; // Ceiling on the printable palette, which bounds that fill cost (and the shader's uniform array). constexpr int PALETTE_MAX_ENTRIES = 64; +// sRGB (0..1) <-> CIELAB, D65. Exactly what the bump shader's srgb_to_lab() computes, so the CPU +// quantizer, the mixed-palette entries and the per-fragment preview all match in the same space. +// Not slic3r/Utils/ColorSpaceConvert: its RGB2Lab wants 0..1 but its Lab2RGB hands back linear +// values on a 0..100 scale, and the earlier code fed the former 0..255 and divided the latter by 255 - +// self-consistent enough for the bake's nearest-entry search to rank sensibly, but the preview, which +// compares real Lab against those values, picked the darkest filament everywhere. +static Vec3f srgb_to_lab(const Vec3f &c) +{ + const auto lin = [](float v) { return v > 0.04045f ? std::pow((v + 0.055f) / 1.055f, 2.4f) : v / 12.92f; }; + const float r = lin(c.x()), g = lin(c.y()), b = lin(c.z()); + const float x = (0.4124f * r + 0.3576f * g + 0.1805f * b) / 0.95047f; + const float y = (0.2126f * r + 0.7152f * g + 0.0722f * b); + const float z = (0.0193f * r + 0.1192f * g + 0.9505f * b) / 1.08883f; + const auto f = [](float t) { return t > 0.008856f ? std::cbrt(t) : 7.787f * t + 16.f / 116.f; }; + const float fx = f(x), fy = f(y), fz = f(z); + return Vec3f(116.f * fy - 16.f, 500.f * (fx - fy), 200.f * (fy - fz)); +} + +static Vec3f lab_to_srgb(const Vec3f &lab) +{ + const float fy = (lab.x() + 16.f) / 116.f, fx = lab.y() / 500.f + fy, fz = fy - lab.z() / 200.f; + const auto finv = [](float t) { return t > 0.206893f ? t * t * t : (t - 16.f / 116.f) / 7.787f; }; + const float x = finv(fx) * 0.95047f, y = finv(fy), z = finv(fz) * 1.08883f; + const float r = 3.2406f * x - 1.5372f * y - 0.4986f * z; + const float g = -0.9689f * x + 1.8758f * y + 0.0415f * z; + const float b = 0.0557f * x - 0.2040f * y + 1.0570f * z; + const auto gam = [](float v) { + v = std::clamp(v, 0.f, 1.f); + return v > 0.0031308f ? 1.055f * std::pow(v, 1.f / 2.4f) - 0.055f : 12.92f * v; + }; + return Vec3f(gam(r), gam(g), gam(b)); +} + std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &decoded, int max_px = THUMBNAIL_MAX_PX) { if (decoded.empty()) @@ -233,22 +268,27 @@ std::unique_ptr upload_height_thumbnail(const DecodedHeightTexture &d const int w = std::max(1, decoded.width / scale); const int h = std::max(1, decoded.height / scale); + // A colour texture is shown in colour, so it can be told apart from its grey neighbours in the picker; its + // height is the luminance of those same colours, so nothing about the relief is hidden by that. + const bool color = decoded.has_color(); std::vector rgba(size_t(w) * size_t(h) * 4); for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) { // Average the source block this destination pixel covers. const int x0 = x * decoded.width / w, x1 = std::max(x0 + 1, (x + 1) * decoded.width / w); const int y0 = y * decoded.height / h, y1 = std::max(y0 + 1, (y + 1) * decoded.height / h); - unsigned int sum = 0, n = 0; + unsigned int sum[3] = { 0, 0, 0 }; + unsigned int n = 0; for (int sy = y0; sy < y1 && sy < decoded.height; ++sy) - for (int sx = x0; sx < x1 && sx < decoded.width; ++sx, ++n) - sum += decoded.pixels[size_t(sy) * size_t(decoded.width) + size_t(sx)]; + for (int sx = x0; sx < x1 && sx < decoded.width; ++sx, ++n) { + const size_t si = size_t(sy) * size_t(decoded.width) + size_t(sx); + for (int c = 0; c < 3; ++c) + sum[c] += color ? decoded.rgb[si * 3 + size_t(c)] : decoded.pixels[si]; + } - const unsigned char gray = (n > 0) ? static_cast(sum / n) : 0; - const size_t di = (size_t(y) * size_t(w) + size_t(x)) * 4; - rgba[di + 0] = gray; - rgba[di + 1] = gray; - rgba[di + 2] = gray; + const size_t di = (size_t(y) * size_t(w) + size_t(x)) * 4; + for (int c = 0; c < 3; ++c) + rgba[di + size_t(c)] = (n > 0) ? static_cast(sum[c] / n) : 0; rgba[di + 3] = 255; } @@ -333,10 +373,8 @@ bool GLGizmoTextureDisplacement::on_init() m_desc["cursor_size"] = _L("Brush size"); m_desc["circle"] = _L("Circle"); m_desc["sphere"] = _L("Sphere"); - m_desc["add_texture"] = _L("Add layer"); m_desc["remove_layer"] = _L("Remove"); m_desc["bake"] = _L("Bake"); - m_desc["remove_all"] = _L("Erase all"); return true; } @@ -401,7 +439,9 @@ PainterGizmoType GLGizmoTextureDisplacement::get_painter_type() const wxString GLGizmoTextureDisplacement::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const { - return shift_down ? _L("Erase texture displacement paint") : _L("Paint texture displacement"); + // Right button does the opposite of the toggle; Shift always erases. + const bool erasing = shift_down || (m_erase_mode != (button_down == GLGizmoPainterBase::Button::Right)); + return erasing ? _L("Erase texture displacement paint") : _L("Paint texture displacement"); } void GLGizmoTextureDisplacement::render_painter_gizmo() @@ -959,10 +999,31 @@ float GLGizmoTextureDisplacement::layer_texture_aspect(const TextureDisplacement return (tex.width > 0 && tex.height > 0) ? float(tex.width) / float(tex.height) : 1.f; } -std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const indexed_triangle_set &patch, +indexed_triangle_set GLGizmoTextureDisplacement::patch_in_world(const indexed_triangle_set &patch) const +{ + const ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return patch; + // The bake's own frame, so the projection this feeds agrees with what bakes. + const Transform3d to_world = texture_displacement_bake_frame(texture_displacement_volume_to_world(*mv)); + if (to_world.matrix().isApprox(Transform3d::Identity().matrix())) + return patch; + + indexed_triangle_set world = patch; + for (Vec3f &v : world.vertices) + v = (to_world * v.cast()).cast(); + return world; +} + +std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer) const { - const float aspect = layer_texture_aspect(layer); + // The bake maps the texture in world millimetres, so "Tile size (mm)" means the same thing on a + // scaled instance as it does on an untouched one (see build_texture_displacement()). Everything + // that has to agree with the bake - the fast preview's uvs, the checker/distortion overlays - + // therefore has to project from the same world positions, not from the volume's own. + const indexed_triangle_set patch = patch_in_world(local_patch); + const float aspect = layer_texture_aspect(layer); if (layer.projection_method == TextureProjectionMethod::LSCM) { // compute_lscm_uvs() returns the unwrap's own (raw, mm) coordinates with the island placement // folded in - it does *not* apply the layer's tiling/rotation/offset. The bake applies those @@ -1209,8 +1270,9 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() const ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); shader->set_uniform("clipping_plane", clp_data.clp_dataf); shader->set_uniform("z_range", clp_data.z_range); - const Matrix3d view_normal_matrix = - camera.get_view_matrix().matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + // The shader works on world-space normals (it projects the texture in world millimetres, as the + // bake does), so the normal matrix is the view rotation alone - no model part. + const Matrix3d view_normal_matrix = camera.get_view_matrix().matrix().block(0, 0, 3, 3); shader->set_uniform("view_normal_matrix", view_normal_matrix); shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0); glsafe(::glActiveTexture(GL_TEXTURE0)); @@ -1254,7 +1316,11 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() // surface: it does not slide as the camera orbits and does not deepen with depth_mm, which is // exactly when the fast preview stops looking like geometry. shader->set_uniform("midlevel", layer->midlevel); - shader->set_uniform("eye_model_pos", Vec3f((trafo_matrix.inverse() * camera.get_position()).cast())); + // The texture frame is world space anchored at the volume's origin (texture_displacement_bake_frame()): + // the shader subtracts the anchor from the world position, and the eye is handed over the same way. + const Vec3d tex_anchor = trafo_matrix.translation(); + shader->set_uniform("tex_anchor", Vec3f(tex_anchor.cast())); + shader->set_uniform("eye_model_pos", Vec3f((camera.get_position() - tex_anchor).cast())); // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than // projecting; see rebuild_bump_preview_mesh(). shader->set_uniform("use_vertex_uv", m_bump_preview_uses_vertex_uv); @@ -1270,10 +1336,8 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() shader->set_uniform("has_color_tex", color_tex != nullptr); for (int i = 0; i < palette_count; ++i) { const Vec3f &rgb = m_bump_preview_palette[size_t(i)].rgb; - float l, a, b; - RGB2Lab(rgb.x() * 255.f, rgb.y() * 255.f, rgb.z() * 255.f, &l, &a, &b); shader->set_uniform(("palette_rgb[" + std::to_string(i) + "]").c_str(), rgb); - shader->set_uniform(("palette_lab[" + std::to_string(i) + "]").c_str(), Vec3f(l, a, b)); + shader->set_uniform(("palette_lab[" + std::to_string(i) + "]").c_str(), srgb_to_lab(rgb)); } if (color_tex != nullptr) { shader->set_uniform("color_tex", 1); @@ -1484,10 +1548,12 @@ void GLGizmoTextureDisplacement::render_uvcheck_mesh() const ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); shader->set_uniform("clipping_plane", clp_data.clp_dataf); shader->set_uniform("z_range", clp_data.z_range); - const Matrix3d view_normal_matrix = - camera.get_view_matrix().matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + // The shader works on world-space normals (it projects the texture in world millimetres, as the + // bake does), so the normal matrix is the view rotation alone - no model part. + const Matrix3d view_normal_matrix = camera.get_view_matrix().matrix().block(0, 0, 3, 3); shader->set_uniform("view_normal_matrix", view_normal_matrix); shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0); + shader->set_uniform("tex_anchor", Vec3f(trafo_matrix.translation().cast())); // see the bump shader shader->set_uniform("mode", m_uv_check_mode == UVCheckMode::Distortion ? 1 : 0); shader->set_uniform("checker_freq", 4.f); // squares per texture tile shader->set_uniform("tiling_scale", layer->tiling_scale); @@ -1608,6 +1674,12 @@ void GLGizmoTextureDisplacement::rebuild_preview() if (m_subdivide_editing && m_subdivide_adaptive) rebuild_subdivide_preview(); + // The debug view owns m_preview_glmodel while it is up. A stage is a snapshot of a run that has + // already finished, so letting the live preview overwrite it would replace the thing being + // inspected with something else; leaving the view is explicit (its Close button). + if (m_debug_stage >= 0) + return; + const ModelVolume *mv = texture_volume(); if (mv == nullptr || !mv->is_texture_displacement_painted()) { m_preview_glmodel.reset(); @@ -1654,6 +1726,11 @@ void GLGizmoTextureDisplacement::queue_preview_job() input.base_mesh = mv->mesh().its; input.layers = mv->texture_displacement_layers; input.options = mv->texture_displacement_options; + // The preview is looked at, not printed or sliced: skip the simplification and the repair that + // follows it, which are most of the one-run pipeline's time. The relief is the same. + if (input.options.pipeline_v2) + input.options.v2_max_triangles_k = 0; + input.volume_to_world = texture_displacement_volume_to_world(*mv); for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = mv->texture_displacement_facet(i).get_data(); // Captured here rather than read in the handler: get_extruders_colors() is main-thread state and @@ -1727,6 +1804,8 @@ void GLGizmoTextureDisplacement::update_uv_editor() UVEditorCanvas *uv_canvas = plater->get_uv_editor_canvas(); if (uv_canvas == nullptr) return; + // Wired on every call rather than with the first unwrap: Unwrap itself is one of the pane's commands. + uv_canvas->set_command_callback([this](UVEditorCanvas::Command cmd, float value) { on_uv_command(int(cmd), value); }); const ModelVolume *mv = texture_volume(); TextureDisplacementLayer *layer = active_layer(); @@ -1736,6 +1815,8 @@ void GLGizmoTextureDisplacement::update_uv_editor() // it was hidden, via the state comparison below). if (!m_show_uv_editor || mv == nullptr || layer == nullptr || layer->projection_method != TextureProjectionMethod::LSCM) { + m_uv_editor_bg = UVBackground::None; // the next time it is shown, upload the background afresh + push_uv_pane_state(); plater->show_uv_editor(false); return; } @@ -1774,30 +1855,24 @@ void GLGizmoTextureDisplacement::update_uv_editor() bool unwrap_changed = false; if (m_uv_unwrap_pending) { m_uv_unwrap_pending = false; - const indexed_triangle_set patch = extract_painted_patch(mv->mesh().its, state.facets); + // World millimetres, the space the bake unwraps in - otherwise the pane would lay the islands + // out at the volume's own scale and show the texture at a different size than it bakes at. + const indexed_triangle_set patch = patch_in_world(extract_painted_patch(mv->mesh().its, state.facets)); if (patch.indices.empty()) { + // Nothing painted to unwrap: clear what the pane showed but keep it open - its status line says what to do. m_uv_editor_state = UVEditorState{}; m_uv_editor_unwrap = PatchUnwrap{}; m_uv_editor_distortion_colors.clear(); - plater->show_uv_editor(false); + uv_canvas->set_islands({}); + push_uv_pane_state(); + plater->show_uv_editor(true); return; } // Padding disabled (0): the user asked to pack islands with no gap between them. m_uv_editor_unwrap = compute_patch_unwrap(patch, layer->lscm_seam_angle_deg, 0.f, layer->lscm_seam_edges); - // Re-apply any stored per-vertex UV edits onto the fresh unwrap, so the pane shows exactly what - // compute_lscm_uvs() will bake (which applies the same overrides). Keyed by mesh vertex, so every - // unwrapped copy of that vertex gets it - matching the bake's single-UV-per-vertex settle. - if (!layer->lscm_uv_overrides.empty()) { - std::map ov; - for (const auto &[mv2, uv] : layer->lscm_uv_overrides) - ov[mv2] = uv; - for (size_t i = 0; i < m_uv_editor_unwrap.uvs.size(); ++i) { - const int sv = (i < m_uv_editor_unwrap.source_vertex.size()) ? m_uv_editor_unwrap.source_vertex[i] : -1; - const auto it = ov.find(sv); - if (it != ov.end()) - m_uv_editor_unwrap.uvs[i] = it->second; - } - } + // Re-apply any stored UV edits onto the fresh unwrap, so the pane shows exactly what + // compute_lscm_uvs() will bake (which applies the same overrides). + apply_lscm_uv_overrides(m_uv_editor_unwrap, layer->lscm_uv_overrides); m_uv_editor_state = std::move(state); unwrap_changed = true; // Precompute the distortion heatmap now, while the patch is in hand - relative stretch doesn't @@ -1806,19 +1881,15 @@ void GLGizmoTextureDisplacement::update_uv_editor() compute_uv_editor_distortion_colors(patch); } - if (m_uv_editor_unwrap.empty()) { - // Nothing has been unwrapped yet (or the paint was cleared): keep the pane hidden until the user - // presses Unwrap. The panel shows a "Press Unwrap" hint in this state. - plater->show_uv_editor(false); - return; - } - - // The pane background either mirrors the height texture (default) or shows a UV checker (#7), and is - // only re-uploaded when that choice, or the unwrap, actually changes - the height image is large. + // The pane background either mirrors the height texture (default) or shows a UV checker (#7). It is uploaded + // before, and independently of, the unwrap - the texture belongs on screen before anything is unwrapped - and + // only when the choice, the layer's image or its smoothing changed, because the height image is large. const UVBackground desired_bg = (m_uv_check_mode == UVCheckMode::Checker) ? UVBackground::Checker : UVBackground::Height; const bool bg_smoothing_changed = (desired_bg == UVBackground::Height) && (m_uv_editor_bg_smoothing != layer->smoothing); - if (unwrap_changed || desired_bg != m_uv_editor_bg || bg_smoothing_changed) { + if (desired_bg != m_uv_editor_bg || layer->image_data.get() != m_uv_editor_bg_image || bg_smoothing_changed) { m_uv_editor_bg_smoothing = layer->smoothing; + m_uv_editor_bg_image = layer->image_data.get(); + m_uv_editor_bg = desired_bg; if (desired_bg == UVBackground::Checker) { // An even squares-per-axis count so the pattern tiles seamlessly across the UV unit // boundary (texcoord == position repeats it once per tile). Softened grays, not pure @@ -1831,12 +1902,25 @@ void GLGizmoTextureDisplacement::update_uv_editor() uv_canvas->set_background_texture(checker, tex, tex); } else { const DecodedHeightTexture height = decode_height_texture(*layer); - if (!height.empty()) + if (!height.empty()) { uv_canvas->set_background_texture(height.pixels, height.width, height.height); - else + } else { + // Not recorded as uploaded, so the next update tries again rather than keeping a blank backdrop. uv_canvas->set_background_texture({}, 0, 0); + m_uv_editor_bg = UVBackground::None; + } } - m_uv_editor_bg = desired_bg; + } + // The backdrop's extent and repeat follow the tile settings, with or without an unwrap. + uv_canvas->set_uv_transform(layer->tiling_scale, layer->rotation_deg, layer->tile_enabled, + layer->tile_method == TextureTileMethod::MirroredRepeat); + + if (m_uv_editor_unwrap.empty()) { + // Nothing unwrapped yet: the pane opens anyway, because Unwrap itself lives in it. Its canvas shows the + // texture and its status line says to paint the area and press Unwrap. + push_uv_pane_state(); + plater->show_uv_editor(true); + return; } // Grow (never shrink) the layer's island list to cover every chart. Shrinking would throw away a @@ -1882,14 +1966,10 @@ void GLGizmoTextureDisplacement::update_uv_editor() }); uv_canvas->set_vertex_edit_callback( [this](const std::vector> &edits) { on_uv_vertex_edited(edits); }); - uv_canvas->set_command_callback([this](UVEditorCanvas::Command cmd) { on_uv_command(int(cmd)); }); uv_canvas->set_islands(std::move(view)); } uv_canvas->set_select_mode(static_cast(m_uv_select_mode)); - - uv_canvas->set_uv_transform(layer->tiling_scale, layer->rotation_deg, layer->tile_enabled, - layer->tile_method == TextureTileMethod::MirroredRepeat); uv_canvas->set_island_transforms(uv_editor_island_transforms(*layer)); // The distortion heatmap tints the island fills only while its check mode is on; otherwise the // canvas falls back to its default light-green wash. @@ -1898,6 +1978,7 @@ void GLGizmoTextureDisplacement::update_uv_editor() else uv_canvas->set_island_fill_colors({}); + push_uv_pane_state(); plater->show_uv_editor(true); } @@ -1962,8 +2043,131 @@ void GLGizmoTextureDisplacement::compute_uv_editor_distortion_colors(const index } } -void GLGizmoTextureDisplacement::on_uv_command(int cmd) +void GLGizmoTextureDisplacement::on_uv_command(int cmd, float value) { + m_uv_command_queue.emplace_back(cmd, value); + m_parent.set_as_dirty(); // the panel render that runs it +} + +void GLGizmoTextureDisplacement::process_uv_commands() +{ + std::vector> queue; + queue.swap(m_uv_command_queue); + for (const auto &[cmd, value] : queue) + run_uv_command(cmd, value); +} + +void GLGizmoTextureDisplacement::apply_view_mode(int mode) +{ + m_use_bump_preview = (mode == 1); + m_uv_check_mode = (mode == 2) ? UVCheckMode::Checker : + (mode == 3) ? UVCheckMode::Distortion : UVCheckMode::None; + rebuild_uvcheck_mesh(); + if (m_use_bump_preview) + rebuild_bump_preview_mesh(); + else + // The Fast view skips the CPU displacement entirely (see rebuild_preview()), so leaving it means + // m_preview_glmodel may be stale or absent - ask for it now. + queue_preview_job(); + // ...and the paint tint between the base and the displaced surface, for the same reason. + m_paint_overlay_dirty = true; + refresh_wireframe(); // Normal<->Fast swaps the wireframe between displaced and base mesh + update_uv_editor(); // mirror the checker / distortion heatmap into the UV pane too + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::push_uv_pane_state() +{ + UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas(); + if (canvas == nullptr) + return; + + UVEditorCanvas::PaneState st; + const ModelVolume *mv = texture_volume(); + const TextureDisplacementLayer *layer = active_layer(); + if (mv != nullptr && layer != nullptr && layer->projection_method == TextureProjectionMethod::LSCM) { + st.has_layer = true; + st.layer_name = from_u8(layer->name.empty() ? Slic3r::format(_u8L("Layer %1%"), layer->slot + 1) : layer->name); + st.tile_mm = layer->tiling_scale; + st.seam_angle_deg = layer->lscm_seam_angle_deg; + st.connect_islands = layer->auto_connect_islands; + st.has_seams = !layer->lscm_seam_edges.empty(); + st.has_uv_edits = !layer->lscm_uv_overrides.empty(); + + // Retried while empty: the image may not decode yet, and a blank thumbnail must not stick. + if (layer->image_data.get() != m_uv_thumb_source || m_uv_thumb_rgb.empty()) { + m_uv_thumb_source = nullptr; + m_uv_thumb_rgb.clear(); + const DecodedHeightTexture tex = decode_height_texture(*layer); + if (!tex.empty()) { + m_uv_thumb_source = layer->image_data.get(); + // Box-filtered, so a fine pattern such as bricks averages out instead of aliasing into noise. + const int n = UV_THUMB_PX; + const bool color = tex.has_color(); + m_uv_thumb_rgb.resize(size_t(n) * n * 3); + for (int y = 0; y < n; ++y) { + const int y0 = y * tex.height / n, y1 = std::max(y0 + 1, (y + 1) * tex.height / n); + for (int x = 0; x < n; ++x) { + const int x0 = x * tex.width / n, x1 = std::max(x0 + 1, (x + 1) * tex.width / n); + uint64_t sum[3]{}; + for (int sy = y0; sy < y1; ++sy) + for (int sx = x0; sx < x1; ++sx) { + const size_t src = size_t(sy) * size_t(tex.width) + size_t(sx); + for (int ch = 0; ch < 3; ++ch) + sum[ch] += color ? tex.rgb[src * 3 + ch] : tex.pixels[src]; + } + const uint64_t count = uint64_t(y1 - y0) * uint64_t(x1 - x0); + for (int ch = 0; ch < 3; ++ch) + m_uv_thumb_rgb[size_t(y * n + x) * 3 + ch] = (unsigned char) (sum[ch] / count); + } + } + } + } + if (!m_uv_thumb_rgb.empty()) { + st.thumbnail_px = UV_THUMB_PX; + st.thumbnail_rgb = m_uv_thumb_rgb; + } + + if (!m_uv_editor_unwrap.empty()) { + // Stale when the unwrap on screen was made from another layer, other paint, other seams or another + // seam angle than this layer has now - the same things update_uv_editor() re-solves for. + UVEditorState now; + now.slot = m_active_layer_slot; + now.image_data = layer->image_data.get(); + now.seam_angle = layer->lscm_seam_angle_deg; + now.padding = layer->island_padding_mm; + now.facets = mv->texture_displacement_facet(m_active_layer_slot).get_data(); + now.seam_edges = layer->lscm_seam_edges; + st.unwrapped = true; + st.unwrap_stale = !(now == m_uv_editor_state); + st.island_count = m_uv_editor_unwrap.chart_count; + st.face_count = m_uv_editor_unwrap.indices.size(); + } + } + st.mark_seams = m_seam_edit_mode; + st.seam_path = m_seam_path_mode; + st.background = m_uv_check_mode == UVCheckMode::Checker ? UVEditorCanvas::Background::Checker : + m_uv_check_mode == UVCheckMode::Distortion ? UVEditorCanvas::Background::Distortion : + UVEditorCanvas::Background::Height; + canvas->set_pane_state(std::move(st)); +} + +void GLGizmoTextureDisplacement::run_uv_command(int cmd, float value) +{ + using Command = UVEditorCanvas::Command; + if (cmd == int(Command::PaneClosed)) { + // Closed with the pane's own X: keep it closed until asked again, and upload the background afresh then. + m_show_uv_editor = false; + m_uv_editor_bg = UVBackground::None; + return; + } + if (cmd == int(Command::SetBackground)) { + // Height goes back to whichever of Normal / Fast was showing; Checker and Distortion are views of their own. + const int background = std::clamp(int(std::lround(value)), 0, 2); + apply_view_mode(background == 1 ? 2 : background == 2 ? 3 : (m_use_bump_preview ? 1 : 0)); + return; + } + TextureDisplacementLayer *layer = active_layer(); if (layer == nullptr) return; @@ -2032,7 +2236,77 @@ void GLGizmoTextureDisplacement::on_uv_command(int cmd) layer->island_groups[size_t(chart)] = chart; rebuild_preview(); } + else if (cmd == int(Command::PickTexture)) { + // The same texture library the layer card opens; the panel renders it this frame. + m_picker_slot = m_active_layer_slot; + m_picker_open_request = true; + } else if (cmd == int(Command::Unwrap)) { + // The solve runs only on this - painting, the seam angle and seam marking never trigger it. + m_uv_unwrap_pending = true; + m_uv_apply_connected_net = true; // a genuine re-unwrap may relayout the islands + m_show_uv_editor = true; + update_uv_editor(); + } else if (cmd == int(Command::SetSeamAngle)) { + const float angle = std::clamp(value, 5.f, 90.f); + if (angle != layer->lscm_seam_angle_deg) { + layer->lscm_seam_angle_deg = angle; + rebuild_preview(); // the bake unwraps with it; this also marks the pane's unwrap stale + } + } else if (cmd == int(Command::SetConnectIslands)) { + const bool connect = value != 0.f; + if (connect != layer->auto_connect_islands) { + layer->auto_connect_islands = connect; + // Apply (or, when turned off, just stop re-applying) right away rather than waiting for the next re-unwrap. + if (connect && !m_uv_editor_unwrap.empty()) { + std::vector net = compute_connected_net(m_uv_editor_unwrap); + if (net.size() == size_t(m_uv_editor_unwrap.chart_count)) { + if (layer->islands.size() < net.size()) + layer->islands.resize(net.size()); + for (size_t i = 0; i < net.size(); ++i) + layer->islands[i] = net[i]; + } + } + rebuild_preview(); + } + } else if (cmd == int(Command::SetSelectMode)) { + m_uv_select_mode = std::clamp(int(std::lround(value)), 0, 2); // the canvas has already switched + } else if (cmd == int(Command::SetMarkSeams)) { + m_seam_edit_mode = value != 0.f; + if (m_seam_edit_mode) + m_adjust_texture_mode = false; // the two click modes are mutually exclusive + else { + m_seam_hover_edge = { -1, -1 }; // drop the hover highlight when leaving the mode + m_seam_hover_vertex = -1; + m_seam_hover_glmodel.reset(); + m_seam_path_anchor = -1; + m_seam_anchor_glmodel.reset(); + } + push_uv_pane_state(); + } else if (cmd == int(Command::SetSeamPath)) { + m_seam_path_mode = value != 0.f; + m_seam_path_anchor = -1; + m_seam_hover_edge = { -1, -1 }; // hover target type changes with the mode + m_seam_hover_vertex = -1; + m_seam_hover_glmodel.reset(); + m_seam_anchor_glmodel.reset(); + push_uv_pane_state(); + } else if (cmd == int(Command::ClearSeams)) { + if (!layer->lscm_seam_edges.empty()) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture seams"), UndoRedo::SnapshotType::GizmoAction); + layer->lscm_seam_edges.clear(); + rebuild_preview(); + } + } else if (cmd == int(Command::ClearUVEdits)) { + if (!layer->lscm_uv_overrides.empty()) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture UV edits"), UndoRedo::SnapshotType::GizmoAction); + layer->lscm_uv_overrides.clear(); + m_uv_unwrap_pending = true; // re-solve so the pane drops the edited coords + update_uv_editor(); + rebuild_preview(); + } + } // FrameAll/ToggleSnap are handled inside the canvas; ProjectFromView is not wired yet. + m_parent.set_as_dirty(); } void GLGizmoTextureDisplacement::capture_view_projection(TextureDisplacementLayer &layer) @@ -2372,16 +2646,15 @@ void GLGizmoTextureDisplacement::on_uv_vertex_edited(const std::vectorlscm_uv_overrides.begin(), layer->lscm_uv_overrides.end(), - [mesh_v](const std::pair &p) { return p.first == mesh_v; }); + [key](const std::pair &p) { return p.first == key; }); if (it != layer->lscm_uv_overrides.end()) it->second = raw_uv; else - layer->lscm_uv_overrides.emplace_back(mesh_v, raw_uv); + layer->lscm_uv_overrides.emplace_back(key, raw_uv); } rebuild_preview(); // the baked displacement samples the moved uv now @@ -2785,16 +3058,6 @@ void GLGizmoTextureDisplacement::set_active_layer(int slot) rebuild_preview(); } -unsigned int GLGizmoTextureDisplacement::tool_icon_id() -{ - if (!m_tool_icon_tried) { - m_tool_icon_tried = true; - // Runs from the panel render, i.e. with a GL context current, so the upload is safe here. - m_tool_icon.load_from_svg_file(resources_dir() + "/images/texture_displacement_add.svg", false, false, false, 32); - } - return m_tool_icon.get_id(); -} - void GLGizmoTextureDisplacement::ensure_panel_icons() { if (m_panel_icons_tried) @@ -2804,12 +3067,18 @@ void GLGizmoTextureDisplacement::ensure_panel_icons() // Order is irrelevant; the map keys by file name. Loaded with color_wite_gray so each icon has both // a monochrome ("normal", grey in the current theme) and an original-colour variant. static const std::vector names = { - "toolbar_big_brush.svg", "toolbar_face.svg", "texture_displacement_connected_area.svg", + "texture_displacement_brush.svg", "texture_displacement_face.svg", "texture_displacement_connected_area.svg", "texture_displacement_real_preview.svg", "texture_displacement_fast_preview.svg", "texture_displacement_checker.svg", "texture_displacement_distortion.svg", "texture_displacement_wireframe.svg", "texture_displacement_cross.svg", "texture_displacement_uv_select_island.svg", "texture_displacement_uv_select_edge.svg", "texture_displacement_uv_select_vertex.svg", + // Paint / Erase, brush cursor shape, mapping, tiling, placement and the dock toggle. + "texture_displacement_add.svg", "texture_displacement_add_negative.svg", "circle_paint.svg", + "menu_obj_sphere.svg", "menu_obj_cube.svg", "menu_obj_cylinder.svg", "texture_displacement_map_unwrap.svg", + "texture_displacement_map_view.svg", "texture_displacement_tile_repeat.svg", "menu_mirror_x.svg", + "texture_displacement_adjust.svg", "canvas_drag.svg", "texture_displacement_move_up.svg", + "texture_displacement_move_down.svg", "texture_displacement_drag.svg", }; std::vector paths; paths.reserve(names.size()); @@ -2864,6 +3133,8 @@ void GLGizmoTextureDisplacement::add_texture_layer() layer.image_data = tex->image_data; } mv->texture_displacement_layers.push_back(std::move(layer)); + // A new layer opens with every setting showing; there is nothing on it yet to hide settings from. + m_layer_expanded[size_t(free_slot)] = true; set_active_layer(free_slot); // set_active_layer() is a no-op when the new slot happens to be the one already active (slot 0, @@ -2911,6 +3182,9 @@ void GLGizmoTextureDisplacement::set_layer_texture(TextureDisplacementLayer &lay // - whose cache is keyed by exactly this pointer - hit straight away instead of re-decoding // the PNG the first time the layer is previewed or baked. layer.image_data = tex->image_data; + // The user's own textures are listed most recently used first. + if (entry.is_user) + touch_user_texture(entry.path); rebuild_preview(); m_parent.set_as_dirty(); @@ -2936,88 +3210,175 @@ void GLGizmoTextureDisplacement::import_custom_texture(TextureDisplacementLayer set_layer_texture(layer, *entry); } -float GLGizmoTextureDisplacement::texture_row_height() const +void GLGizmoTextureDisplacement::render_texture_library_popup(const ImVec2 &panel_min, const ImVec2 &panel_max) { - return m_imgui->scaled(3.f); -} + ModelVolume *mv = texture_volume(); + TextureDisplacementLayer *layer = nullptr; + if (mv != nullptr) + for (TextureDisplacementLayer &l : mv->texture_displacement_layers) + if (l.slot == m_picker_slot) + layer = &l; -bool GLGizmoTextureDisplacement::texture_row(const char *id, const std::string &name, GLTexture *thumbnail, bool selected, float width) -{ - const float row_h = texture_row_height(); - const ImVec2 start = ImGui::GetCursorPos(); - const bool clicked = ImGui::Selectable(id, selected, 0, ImVec2(width, row_h)); - const ImVec2 after = ImGui::GetCursorPos(); - - // Lay the image and the name back over the Selectable that was just emitted, so the whole row - // - preview included - is the clickable target rather than just a strip of text next to it. - ImGui::SetCursorPos(ImVec2(start.x + ImGui::GetStyle().FramePadding.x, start.y)); - if (thumbnail != nullptr) { - // Fit inside a row_h square without distorting a non-square source image. - const float aspect = (thumbnail->get_height() > 0) ? float(thumbnail->get_width()) / float(thumbnail->get_height()) : 1.f; - const ImVec2 dim = (aspect >= 1.f) ? ImVec2(row_h, row_h / aspect) : ImVec2(row_h * aspect, row_h); - ImGui::Image((ImTextureID) (intptr_t) thumbnail->get_id(), dim); - ImGui::SameLine(); + static const char *const popup_id = "##texture_library"; + if (m_picker_open_request) { + m_picker_open_request = false; + if (layer != nullptr) + ImGui::OpenPopup(popup_id); } - ImGui::SetCursorPosY(start.y + (row_h - ImGui::GetTextLineHeight()) * 0.5f); // centre the name on the image - ImGui::TextUnformatted(name.c_str()); - ImGui::SetCursorPos(after); - return clicked; -} + const float tile = std::round(m_imgui->scaled(3.2f)); + const float spacing = std::round(m_imgui->scaled(0.4f)); + const float grid_w = 4.f * tile + 3.f * spacing; + const float gap = m_imgui->scaled(0.5f); + // Beside the panel rather than over it, so the layer being retextured stays in view: to its left when + // there is room (the docked panel sits against the right edge of the canvas), otherwise to its right. + const float popup_w = grid_w + 2.f * ImGui::GetStyle().WindowPadding.x; + const bool left = panel_min.x - gap - popup_w >= 0.f; + ImGui::SetNextWindowPos(ImVec2(left ? panel_min.x - gap : panel_max.x + gap, panel_min.y + m_imgui->scaled(4.f)), + ImGuiCond_Appearing, ImVec2(left ? 1.f : 0.f, 0.f)); + if (!ImGui::BeginPopup(popup_id)) + return; + if (layer == nullptr) { + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + return; + } -void GLGizmoTextureDisplacement::render_texture_picker(TextureDisplacementLayer &layer) -{ - const float row_h = texture_row_height(); - const float import_btn_w = m_imgui->scaled(1.6f); - const float spacing = ImGui::GetStyle().ItemSpacing.x; - const float picker_w = std::max(m_imgui->scaled(10.f), ImGui::GetContentRegionAvail().x - import_btn_w - spacing); + m_imgui->push_bold_font(); + m_imgui->text(_L("Choose a texture")); + m_imgui->pop_bold_font(); - const ImVec2 start = ImGui::GetCursorPos(); - if (texture_row("##texture_picker", layer.name.empty() ? _u8L("Choose a texture...") : layer.name, - get_layer_thumbnail(layer), false, picker_w)) - ImGui::OpenPopup("##texture_library"); - const ImVec2 after = ImGui::GetCursorPos(); + const std::vector &library = texture_library(); + ImDrawList *dl = ImGui::GetWindowDrawList(); + const ImU32 selected_col = ImGui::GetColorU32(ImGuiWrapper::COL_ORCA); + std::optional chosen; + std::optional to_remove; + bool import_clicked = false; - // Positioned explicitly rather than with SameLine(): texture_row() draws several widgets and - // then rewinds the cursor, so ImGui's notion of "the previous line" is not the row's own. - ImGui::SetCursorPos(ImVec2(start.x + picker_w + spacing, start.y)); - if (ImGui::Button("+", ImVec2(import_btn_w, row_h))) - import_custom_texture(layer); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Import your own image as a height map. It is converted to the format the slicer " - "bakes from and saved to your personal texture folder, kept separate from the " - "textures shipped with OrcaSlicer."), - m_imgui->scaled(20.f)); - ImGui::SetCursorPos(after); - - if (ImGui::BeginPopup("##texture_library")) { - const std::vector &library = texture_library(); - if (library.empty()) - m_imgui->text(_L("No textures found.")); - - bool shipped_heading = false; - bool user_heading = false; + // Tiles are placed on the four-column grid explicitly rather than flowed with SameLine(): a user tile + // overlays a remove button on its corner, and that button would otherwise be "the previous item" the + // next tile lines up against. + const auto tile_pos = [&](const ImVec2 &origin, int i) { + return ImVec2(origin.x + float(i % 4) * (tile + spacing), origin.y + float(i / 4) * (tile + spacing)); + }; + // Leaves the cursor below a grid of `count` tiles, with the grid's area registered as content. + const auto end_grid = [&](const ImVec2 &origin, int count) { + ImGui::SetCursorScreenPos(origin); + if (count > 0) { + const int rows = (count + 3) / 4; + ImGui::Dummy(ImVec2(grid_w, float(rows) * tile + float(rows - 1) * spacing)); + } + }; + // One group's tiles: shipped textures alphabetically, or the user's own most recently used first (the + // order texture_library() keeps), those with a remove button on hover. Returns how many it drew. + const auto grid = [&](const ImVec2 &origin, bool user) { + int n = 0; for (const TextureLibraryEntry &entry : library) { - if (!entry.is_user && !shipped_heading) { - ImGui::TextDisabled("%s", _u8L("Built-in").c_str()); - shipped_heading = true; - } else if (entry.is_user && !user_heading) { - if (shipped_heading) - ImGui::Separator(); - ImGui::TextDisabled("%s", _u8L("My textures").c_str()); - user_heading = true; - } - + if (entry.is_user != user) + continue; + ImGui::SetCursorScreenPos(tile_pos(origin, n++)); ImGui::PushID(entry.path.c_str()); - const LibraryTexture *tex = get_library_texture(entry.path); - if (texture_row("##entry", entry.name, tex != nullptr ? tex->thumbnail.get() : nullptr, - entry.path == layer.path, m_imgui->scaled(14.f))) { - set_layer_texture(layer, entry); - ImGui::CloseCurrentPopup(); + const LibraryTexture *tex = get_library_texture(entry.path); + GLTexture *thumb = tex != nullptr ? tex->thumbnail.get() : nullptr; + bool clicked = false; + if (thumb != nullptr) { + // Centre-crop a non-square image into the square tile instead of squashing it. + const float aspect = thumb->get_height() > 0 ? float(thumb->get_width()) / float(thumb->get_height()) : 1.f; + ImVec2 uv0(0.f, 0.f), uv1(1.f, 1.f); + if (aspect > 1.f) { + uv0.x = 0.5f * (1.f - 1.f / aspect); + uv1.x = 1.f - uv0.x; + } else if (aspect < 1.f) { + uv0.y = 0.5f * (1.f - aspect); + uv1.y = 1.f - uv0.y; + } + clicked = ImGui::ImageButton((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(tile, tile), uv0, uv1, 0); + } else { + clicked = ImGui::Button("?", ImVec2(tile, tile)); + } + if (user) + ImGui::SetItemAllowOverlap(); // the remove button drawn over its corner takes its own clicks + const ImVec2 a = ImGui::GetItemRectMin(), b = ImGui::GetItemRectMax(); + if (entry.path == layer->path) + dl->AddRect(ImVec2(a.x - 1.f, a.y - 1.f), ImVec2(b.x + 1.f, b.y + 1.f), selected_col, 0.f, 0, 2.f); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(entry.name, m_imgui->scaled(18.f)); + + if (user && ImGui::IsMouseHoveringRect(a, b)) { + const float r = std::round(tile * 0.17f); + const ImVec2 c(b.x - r - 2.f, a.y + r + 2.f); + ImGui::SetCursorScreenPos(ImVec2(c.x - r, c.y - r)); + const bool remove_clicked = ImGui::InvisibleButton("##remove", ImVec2(2.f * r, 2.f * r)); + const bool hot = ImGui::IsItemHovered(); + dl->AddCircleFilled(c, r, hot ? IM_COL32(214, 72, 64, 235) : IM_COL32(0, 0, 0, 170)); + const float k = 0.42f * r; + dl->AddLine(ImVec2(c.x - k, c.y - k), ImVec2(c.x + k, c.y + k), IM_COL32_WHITE, 1.5f); + dl->AddLine(ImVec2(c.x - k, c.y + k), ImVec2(c.x + k, c.y - k), IM_COL32_WHITE, 1.5f); + if (hot) + m_imgui->tooltip(_u8L("Remove from My textures"), m_imgui->scaled(18.f)); + if (remove_clicked) + to_remove = entry; } ImGui::PopID(); + if (clicked) + chosen = entry; } - ImGui::EndPopup(); + return n; + }; + + ImGui::TextDisabled("%s", _u8L("Built-in").c_str()); + { + const ImVec2 origin = ImGui::GetCursorScreenPos(); + const int count = grid(origin, false); + end_grid(origin, count); + if (count == 0) + ImGui::TextDisabled("%s", _u8L("No textures found.").c_str()); + } + ImGui::TextDisabled("%s", _u8L("My textures").c_str()); + { + const ImVec2 origin = ImGui::GetCursorScreenPos(); + const int count = grid(origin, true); + ImGui::SetCursorScreenPos(tile_pos(origin, count)); + if (ImGui::Button("+##import_texture", ImVec2(tile, tile))) + import_clicked = true; + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Import your own image as a height map. It is converted to the format the slicer " + "bakes from and saved to your personal texture folder, kept separate from the " + "textures shipped with OrcaSlicer."), + m_imgui->scaled(20.f)); + end_grid(origin, count + 1); + } + + ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + grid_w); + ImGui::TextDisabled("%s", _u8L("PNG, JPG or BMP - converted to a height map on import. Hover one of yours to remove it.").c_str()); + ImGui::PopTextWrapPos(); + + if (chosen || import_clicked) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + + // Only once the loop over the library is done: importing, picking (which reorders the recently used) + // and removing all change the library the loop was iterating. + if (chosen) + set_layer_texture(*layer, *chosen); + else if (import_clicked) + import_custom_texture(*layer); + else if (to_remove) { + MessageDialog dlg(nullptr, + wxString::Format(_L("Remove \"%s\" from My textures?\n\nThe image file is deleted from your " + "texture folder. Layers already using it keep it."), + from_u8(to_remove->name)), + _L("Remove texture"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); + if (dlg.ShowModal() == wxID_YES) { + std::string error; + if (remove_user_texture(to_remove->path, error)) + m_library_textures.erase(to_remove->path); // its cached thumbnail and bytes go with it + else + show_error(nullptr, error); + } + // The modal dialog took the focus, which closes the popup; the user was choosing a texture and + // still is, so bring it back. + m_picker_open_request = true; } } @@ -3037,7 +3398,10 @@ void GLGizmoTextureDisplacement::remove_texture_layer(int slot) mv->texture_displacement_facet(slot).reset(); m_thumbnails[size_t(slot)].reset(); m_thumbnail_source[size_t(slot)] = nullptr; + m_layer_expanded[size_t(slot)] = false; } + if (m_picker_slot == slot) + m_picker_slot = -1; if (m_active_layer_slot == slot) update_from_model_object(false); // also rebuilds the preview @@ -3047,6 +3411,79 @@ void GLGizmoTextureDisplacement::remove_texture_layer(int slot) m_parent.set_as_dirty(); } +void GLGizmoTextureDisplacement::swap_layer_slots(int slot_a, int slot_b) +{ + ModelObject *mo = m_c->selection_info()->model_object(); + ModelVolume *mv = texture_volume(); + const int n = int(TEXTURE_DISPLACEMENT_MAX_LAYERS); + if (mo == nullptr || mv == nullptr || slot_a == slot_b || slot_a < 0 || slot_b < 0 || slot_a >= n || slot_b >= n) + return; + + // Every model part, not only the texture volume: the selectors paint the active slot on all of them. + for (ModelVolume *v : mo->volumes) { + if (!v->is_model_part()) + continue; + TriangleSelector::TriangleSplittingData data_a = v->texture_displacement_facet(slot_a).get_data(); + TriangleSelector::TriangleSplittingData data_b = v->texture_displacement_facet(slot_b).get_data(); + v->texture_displacement_facet(slot_a).set_data(std::move(data_b)); + v->texture_displacement_facet(slot_b).set_data(std::move(data_a)); + } + for (TextureDisplacementLayer &l : mv->texture_displacement_layers) { + if (l.slot == slot_a) + l.slot = slot_b; + else if (l.slot == slot_b) + l.slot = slot_a; + } + + const size_t a = size_t(slot_a), b = size_t(slot_b); + std::swap(m_thumbnails[a], m_thumbnails[b]); + std::swap(m_thumbnail_source[a], m_thumbnail_source[b]); + std::swap(m_thumbnail_smoothing[a], m_thumbnail_smoothing[b]); + std::swap(m_layer_expanded[a], m_layer_expanded[b]); + if (m_active_layer_slot == slot_a) + m_active_layer_slot = slot_b; + else if (m_active_layer_slot == slot_b) + m_active_layer_slot = slot_a; + if (m_picker_slot == slot_a) + m_picker_slot = slot_b; + else if (m_picker_slot == slot_b) + m_picker_slot = slot_a; +} + +void GLGizmoTextureDisplacement::move_texture_layer(int slot, int to_index) +{ + ModelVolume *mv = texture_volume(); + if (mv == nullptr) + return; + + std::vector slots; + for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) + slots.push_back(l.slot); + std::sort(slots.begin(), slots.end()); + const auto it = std::find(slots.begin(), slots.end(), slot); + if (it == slots.end()) + return; + const int from = int(it - slots.begin()); + // An insertion index counts the moved layer's own position, which is gone once it is lifted out. + const int to = std::clamp(to_index > from ? to_index - 1 : to_index, 0, int(slots.size()) - 1); + if (to == from) + return; + + update_model_object(); // flush the active layer's strokes into its slot before slots are exchanged + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reorder texture displacement layers"), + UndoRedo::SnapshotType::GizmoAction); + // Walked one neighbour at a time, so every layer in between shifts by one and keeps its order. + const int step = to > from ? 1 : -1; + for (int i = from; i != to; i += step) + swap_layer_slots(slots[size_t(i)], slots[size_t(i + step)]); + + // The active layer's mask now sits in a different slot, so the selectors are reloaded from there. + update_from_model_object(false); // also rebuilds the preview + if (m_adjust_texture_mode) + update_adjust_anchor(); + m_parent.set_as_dirty(); +} + int GLGizmoTextureDisplacement::select_visible_faces(const std::array *uv_clip) { ModelVolume *mv = texture_volume(); @@ -3394,6 +3831,23 @@ TextureDisplacementFacetsData GLGizmoTextureDisplacement::masks_after_subdivisio return out; } +const V2Resolution &GLGizmoTextureDisplacement::v2_recommendation(const ModelVolume &mv) +{ + // Rebuilt only when something it depends on changes: the surface area scan is O(triangles) and the + // panel asks for this every frame. + std::string key = std::to_string(mv.id().id) + ":" + std::to_string(mv.mesh().facets_count()); + for (const TextureDisplacementLayer &l : mv.texture_displacement_layers) + key += Slic3r::format("|%1%:%2%:%3%:%4%", l.slot, reinterpret_cast(l.image_data.get()), l.tiling_scale, l.depth_mm); + const Transform3d trafo = texture_displacement_volume_to_world(mv); + for (int i = 0; i < 12; ++i) + key += Slic3r::format(",%1%", trafo.matrix()(i / 4, i % 4)); + if (key != m_v2_rec_key) { + m_v2_rec = recommend_v2_resolution(mv.mesh().its, mv.texture_displacement_layers, trafo); + m_v2_rec_key = std::move(key); + } + return m_v2_rec; +} + TextureDisplacementFacetsData GLGizmoTextureDisplacement::facets_data_of(const ModelVolume &mv) { TextureDisplacementFacetsData out{}; @@ -3492,20 +3946,13 @@ std::vector GLGizmoTextureDisplacement for (int i = 0; i < n; ++i) for (int j = i + 1; j < n; ++j) { - float la, aa, ba, lb, ab, bb; - RGB2Lab(filaments[size_t(i)].r() * 255.f, filaments[size_t(i)].g() * 255.f, - filaments[size_t(i)].b() * 255.f, &la, &aa, &ba); - RGB2Lab(filaments[size_t(j)].r() * 255.f, filaments[size_t(j)].g() * 255.f, - filaments[size_t(j)].b() * 255.f, &lb, &ab, &bb); + const Vec3f lab_i = srgb_to_lab(Vec3f(filaments[size_t(i)].r(), filaments[size_t(i)].g(), filaments[size_t(i)].b())); + const Vec3f lab_j = srgb_to_lab(Vec3f(filaments[size_t(j)].r(), filaments[size_t(j)].g(), filaments[size_t(j)].b())); for (int k = 1; k <= steps; ++k) { // k/den of filament i, the rest of j - averaged in Lab, which is what the eye does // when the two are interleaved too finely to resolve. const float t = float(k) / float(den); - float r, g, b; - Lab2RGB(la * t + lb * (1.f - t), aa * t + ab * (1.f - t), ba * t + bb * (1.f - t), &r, &g, &b); - out.push_back({ Vec3f(std::clamp(r / 255.f, 0.f, 1.f), std::clamp(g / 255.f, 0.f, 1.f), - std::clamp(b / 255.f, 0.f, 1.f)), - i, j, k, den }); + out.push_back({ lab_to_srgb(lab_i * t + lab_j * (1.f - t)), i, j, k, den }); } } return out; @@ -3556,9 +4003,10 @@ ColorQuantizeFn GLGizmoTextureDisplacement::make_palette_quantizer(const std::ve // Lab once per entry, not once per lookup. struct Lab { float l, a, b; }; std::vector palette_lab(palette.size()); - for (size_t i = 0; i < palette.size(); ++i) - RGB2Lab(palette[i].rgb.x() * 255.f, palette[i].rgb.y() * 255.f, palette[i].rgb.z() * 255.f, - &palette_lab[i].l, &palette_lab[i].a, &palette_lab[i].b); + for (size_t i = 0; i < palette.size(); ++i) { + const Vec3f lab = srgb_to_lab(palette[i].rgb); + palette_lab[i] = { lab.x(), lab.y(), lab.z() }; + } constexpr int E = PALETTE_LUT_EDGE; auto lut = std::make_shared>(size_t(E) * E * E, 0); @@ -3568,7 +4016,8 @@ ColorQuantizeFn GLGizmoTextureDisplacement::make_palette_quantizer(const std::ve for (int b = 0; b < E; ++b) { // Cell centre, so the quantization error is symmetric across the cell. float l0, a0, b0; - RGB2Lab((r + 0.5f) / E * 255.f, (g + 0.5f) / E * 255.f, (b + 0.5f) / E * 255.f, &l0, &a0, &b0); + const Vec3f lab0 = srgb_to_lab(Vec3f((r + 0.5f) / E, (g + 0.5f) / E, (b + 0.5f) / E)); + l0 = lab0.x(); a0 = lab0.y(); b0 = lab0.z(); int best = 0; float best_d = std::numeric_limits::max(); for (size_t i = 0; i < palette_lab.size(); ++i) { @@ -3594,11 +4043,26 @@ ColorQuantizeFn GLGizmoTextureDisplacement::make_palette_quantizer(const std::ve TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( const indexed_triangle_set &base, const TextureDisplacementFacetsData &masks, const std::vector &layers, const TextureDisplacementPrepareParams ¶ms, - const std::vector &palette, const DisplacementProgressFn &progress) + const std::vector &palette, const DisplacementProgressFn &progress, + BakeStageRecorder *debug) { TextureDisplacementPrepareResult out; const auto report = [&progress](int pct) { return !progress || progress(pct); }; + // Stage capture for the debug view. Timed from the end of the previous stage, so the capture - + // a copy plus an edge scan - never lands inside the measurement it reports. + auto stage_clock = std::chrono::steady_clock::now(); + const auto capture = [&](const char *name, const indexed_triangle_set &m, + const std::string &detail = {}) { + if (debug == nullptr) + return; + const double ms = + std::chrono::duration(std::chrono::steady_clock::now() - stage_clock).count(); + debug->capture(name, m.vertices, m.indices, ms, detail); + stage_clock = std::chrono::steady_clock::now(); + }; + capture("input", base, "as handed to prepare"); + TriangleMesh mesh(base); TextureDisplacementFacetsData current = masks; bool changed = false; @@ -3628,10 +4092,15 @@ TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( mesh = std::move(new_mesh); current = std::move(carried); changed = true; + capture("remesh", mesh.its, + "target " + std::to_string(params.remesh_edge_mm) + " mm" + + (had_paint && !any ? ", paint lost" : "")); if (had_paint && !any) { out.paint_lost = true; return out; } + } else if (debug != nullptr) { + capture("remesh", mesh.its, "skipped: remeshing changed nothing"); } } if (!report(50)) @@ -3661,6 +4130,24 @@ TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( // must not be allowed to silently override a target the user set below it. const float tol = params.subdiv_feature ? params.subdiv_detail_mm : 0.f; const float floor = params.subdiv_feature ? params.subdiv_min_edge_mm : 0.f; + // Step textures (a grid, a knurl: two levels with sharp edges between them) are cut into + // walls after refining rather than refined into ramps, see 3. below. The verdict is taken + // here, on the coarse mesh, because the refinement has to know: in step mode it leaves + // the stepped triangles to the cutter, which is only right if the cutter then cuts them. + // The step width and the seam gap both span two texels of the finest layer: the bilinear + // blend across a step is a texel wide, so a seam copy a texel from the step samples a + // pure side. + float texel_mm = std::numeric_limits::max(); + if (params.cut_steps && sampler) + for (const TextureDisplacementLayer &layer : layers) { + if (layer.empty()) + continue; + const DecodedHeightTexture &tex = decode_height_texture(layer); + if (tex.width > 0 && layer.tiling_scale > 0.f) + texel_mm = std::min(texel_mm, layer.tiling_scale / float(tex.width)); + } + const bool cut_steps = texel_mm < std::numeric_limits::max() && + texture_has_steps_to_cut(mesh.its, region, sampler, 2.f * texel_mm, 2.f * texel_mm); bool aborted = false; std::vector source; // The budget is "triangles the refinement may *add*", so the mesh's own count is the @@ -3672,13 +4159,47 @@ TextureDisplacementPrepareResult GLGizmoTextureDisplacement::prepare_mesh( aborted = !report(50 + pct / 2); return !aborted; }, - color, params.subdiv_color_edge_mm); + color, params.subdiv_color_edge_mm, cut_steps); if (aborted) return {}; if (refined.indices.size() != mesh.its.indices.size()) { mesh = TriangleMesh(std::move(refined)); current = masks_after_subdivision(mesh, source, paint); changed = true; + capture("adaptive subdivide", mesh.its, + params.subdiv_feature ? "feature adaptive" : "length only"); + } else if (debug != nullptr) { + capture("adaptive subdivide", mesh.its, "skipped: already meets the criteria"); + } + + // 3. Cut the painted area along the texture's sharp steps so they bake as walls. The + // refinement above left the stepped triangles alone, so this is where a binary + // texture's edges get their geometry. Paint is carried the same way as through the + // subdivision: every output triangle lies inside the pre-subdivision triangle it + // descends from, so the source maps compose. + if (cut_steps) { + std::vector cut_region(mesh.its.indices.size(), 0); + for (size_t i = 0; i < cut_region.size() && i < source.size(); ++i) + cut_region[i] = (region[size_t(source[i])] & REFINE_PAINTED) ? 1 : 0; + const HeightFieldSampler refined_sampler = + make_combined_displacement_sampler(mesh.its, layers, current); + std::vector cut_source; + size_t cuts = 0; + indexed_triangle_set cut = cut_mesh_at_steps(mesh.its, cut_region, refined_sampler, + 2.f * texel_mm, 2.f * texel_mm, 0.f, + &cut_source, &cuts); + if (cuts > 0) { + for (int &s : cut_source) + s = source[size_t(s)]; + mesh = TriangleMesh(std::move(cut)); + current = masks_after_subdivision(mesh, cut_source, paint); + changed = true; + capture("step cut", mesh.its, std::to_string(cuts) + " triangles cut"); + } else if (debug != nullptr) { + capture("step cut", mesh.its, "skipped: the refined mesh has no steps left to cut"); + } + } else if (debug != nullptr && params.cut_steps) { + capture("step cut", mesh.its, "skipped: not a step texture"); } } } @@ -4050,7 +4571,10 @@ static constexpr float STD_REMESH_EDGE_MM = 1.0f; static constexpr float STD_REMESH_SHARP_DEG = 40.f; static constexpr float STD_SUBDIV_MAX_EDGE_MM = 20.f; static constexpr float STD_SUBDIV_DETAIL_MM = 0.02f; -static constexpr float STD_SUBDIV_MIN_EDGE_MM = 0.02f; +// 0.1 mm rather than the 0.02 it was: a hard step in the texture never satisfies the chord tolerance +// however finely it is split, so the floor is what stops it, and 0.02 mm carpeted every step edge with +// triangles five times finer than a nozzle can print - most of the budget, on almost every texture. +static constexpr float STD_SUBDIV_MIN_EDGE_MM = 0.1f; // Edge length the band straddling the paint's edge is refined to. This is the one number that decides // how clean the rim of an unpainted island looks: the bake steps the surface from full displacement to // zero across that band, and nothing else in the criteria can see the step (see collect_paint_region()). @@ -4122,9 +4646,202 @@ void GLGizmoTextureDisplacement::bake_standard() params.subdiv_color_edge_mm = STD_SUBDIV_COLOR_MM; params.subdiv_feature = true; params.subdiv_added_triangles = m_subdivide_budget_k * 1000; + params.cut_steps = true; queue_prepare(params, _u8L("Bake texture displacement"), /* then_bake */ true, {}); } +// --------------------------------------------------------------------------------------------- +// Bake stage debug view +// --------------------------------------------------------------------------------------------- + +void GLGizmoTextureDisplacement::run_stage_debug() +{ + ModelVolume *mv = texture_volume(); + if (mv == nullptr || m_debug_in_progress || m_bake_in_progress || m_prepare_in_progress) + return; + + update_model_object(); // flush the active layer's in-progress strokes, as a real bake would + if (!mv->is_texture_displacement_painted()) { + show_error(nullptr, _u8L("Nothing is painted, so there are no bake stages to capture.")); + return; + } + + // The default (one-run) pipeline refines as part of the bake, and Pro mode has the user prepare the + // mesh themselves with the controls above - in both cases there is no preparation to replay, and + // running one anyway would show stages the Bake button would not. + const bool run_prepare = !pro_mode() && !mv->texture_displacement_options.pipeline_v2; + + TextureDisplacementPrepareParams params; + if (run_prepare) { + apply_standard_mode_presets(mv); // capture what the panel is showing, exactly as Bake does + params.remesh_edge_mm = STD_REMESH_EDGE_MM; + params.remesh_sharp_deg = STD_REMESH_SHARP_DEG; + params.subdiv_target_mm = STD_SUBDIV_MAX_EDGE_MM; + params.subdiv_detail_mm = STD_SUBDIV_DETAIL_MM; + params.subdiv_min_edge_mm = STD_SUBDIV_MIN_EDGE_MM; + params.subdiv_border_mm = STD_SUBDIV_BORDER_MM; + params.subdiv_color_edge_mm = STD_SUBDIV_COLOR_MM; + params.subdiv_feature = true; + params.subdiv_added_triangles = m_subdivide_budget_k * 1000; + params.cut_steps = true; + } + + // Free the previous run's meshes before the next one allocates its own: every stage is a full + // copy of the mesh, so holding two runs at once doubles what is already the expensive part. + exit_debug_view(); + + m_debug_in_progress = true; + queue_texture_displacement_debug( + *mv, color_settings_for(*mv), params, run_prepare, m_debug_check_topology, + [this](std::vector stages) { + m_debug_in_progress = false; + if (!stages.empty()) { + m_debug_stages = std::move(stages); + // Land on the last stage: that is what a real bake would have committed, and the + // question is usually what it looks like before stepping back to find where it went + // wrong. + show_debug_stage(int(m_debug_stages.size()) - 1); + } + m_parent.set_as_dirty(); + }); +} + +void GLGizmoTextureDisplacement::show_debug_stage(int index) +{ + if (index < 0 || size_t(index) >= m_debug_stages.size()) + return; + const BakeStageSnapshot &stage = m_debug_stages[size_t(index)]; + if (stage.mesh.empty()) + return; // over the memory cap: its counts are still listed, there is just nothing to draw + + m_debug_stage = index; + // Drawn through the ordinary true-displacement preview, so the Fast view - a shader trick over + // the base mesh that never draws m_preview_glmodel - has to be left first. + m_use_bump_preview = false; + m_preview_color_runs.clear(); + m_preview_glmodel.reset(); + + indexed_triangle_set its; + its.vertices = stage.mesh.vertices; + its.indices = stage.mesh.indices; + m_preview_glmodel.init_from(its); + m_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); + // Kept so the wireframe overlay draws this stage's edges rather than the base mesh's - which is + // most of the point, since what a refinement stage did is a change in the edges, not the surface. + m_preview_its = std::move(its); + refresh_wireframe(); + m_parent.set_as_dirty(); +} + +void GLGizmoTextureDisplacement::exit_debug_view() +{ + const bool was_showing = m_debug_stage >= 0; + m_debug_stage = -1; + m_debug_stages.clear(); + m_debug_stages.shrink_to_fit(); + if (was_showing) + rebuild_preview(); // hands m_preview_glmodel back to the live preview +} + +void GLGizmoTextureDisplacement::render_debug_stage_panel(ModelVolume *mv) +{ + if (mv == nullptr) + return; + + ImGui::Separator(); + if (!ImGui::CollapsingHeader(_u8L("Bake stages (debug)").c_str())) + return; + + m_imgui->disabled_begin(m_debug_in_progress || m_bake_in_progress || m_prepare_in_progress || + !mv->is_texture_displacement_painted()); + if (m_imgui->button(m_debug_in_progress ? _L("Capturing...") : _L("Capture stages"))) + run_stage_debug(); + m_imgui->disabled_end(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Run the same bake the Bake button runs, keeping the mesh after every " + "stage, and step through them here. Nothing is committed to the model - " + "this is for finding which stage a bad result came from."), + m_imgui->scaled(20.f)); + + ImGui::SameLine(); + ImGui::Checkbox(_u8L("Check topology").c_str(), &m_debug_check_topology); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Count open and non-manifold edges after each stage, which is how a stage " + "that tore the mesh is spotted. It scans every edge, so it adds noticeably " + "to the capture on a fine bake."), + m_imgui->scaled(20.f)); + + if (m_debug_stages.empty()) + return; + + // Stage list. Selecting one draws it; the wireframe overlay is what makes a refinement stage + // readable, so it is worth leaving on while stepping. + ImGui::BeginChild("##bake_stages", ImVec2(0.f, m_imgui->scaled(9.f)), true, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize); + for (size_t i = 0; i < m_debug_stages.size(); ++i) { + const BakeStageSnapshot &st = m_debug_stages[i]; + char label[256]; + std::snprintf(label, sizeof(label), "%zu. %-18s %8zu tris %8.1f ms##stage%zu", i, + st.name.c_str(), st.triangles, st.ms, i); + if (ImGui::Selectable(label, int(i) == m_debug_stage) && !st.mesh.empty()) + show_debug_stage(int(i)); + } + ImGui::EndChild(); + + // Prev / next, so stepping does not mean aiming at a list entry. + m_imgui->disabled_begin(m_debug_stage <= 0); + if (m_imgui->button(_L("< Previous"))) + show_debug_stage(m_debug_stage - 1); + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(m_debug_stage < 0 || size_t(m_debug_stage + 1) >= m_debug_stages.size()); + if (m_imgui->button(_L("Next >"))) + show_debug_stage(m_debug_stage + 1); + m_imgui->disabled_end(); + ImGui::SameLine(); + if (m_imgui->button(_L("Close"))) + exit_debug_view(); + + if (m_debug_stage >= 0 && size_t(m_debug_stage) < m_debug_stages.size()) { + const BakeStageSnapshot &st = m_debug_stages[size_t(m_debug_stage)]; + m_imgui->text(from_u8(st.name) + ": " + std::to_string(st.triangles) + " " + + _u8L("triangles") + ", " + std::to_string(st.vertices) + " " + _u8L("vertices")); + if (!st.detail.empty()) + m_imgui->text(from_u8(st.detail)); + if (st.topology_checked) { + // Zero on both counts is the claim every stage's header makes; anything else is the bug. + const std::string topo = _u8L("Open edges") + ": " + std::to_string(st.open_edges) + " " + + _u8L("Non-manifold") + ": " + std::to_string(st.non_manifold_edges) + + " " + _u8L("Degenerate") + ": " + std::to_string(st.degenerate); + if (st.open_edges > 0 || st.non_manifold_edges > 0) + m_imgui->warning_text(from_u8(topo)); + else + m_imgui->text(topo); + } + } + + double total_ms = 0.0; + for (const BakeStageSnapshot &st : m_debug_stages) + total_ms += st.ms; + char total[128]; + std::snprintf(total, sizeof(total), "%s: %.1f ms", _u8L("Total").c_str(), total_ms); + m_imgui->text(total); + + if (m_imgui->button(_L("Write stage meshes"))) { + const std::string dir = + (boost::filesystem::temp_directory_path() / "orca-bake-stages").string(); + const size_t written = dump_bake_stages(m_debug_stages, dir); + if (written > 0) + show_info(nullptr, from_u8(_u8L("Wrote the stage meshes as OBJ files to:") + "\n" + dir)); + else + show_error(nullptr, _u8L("Could not write the stage meshes.")); + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Write every captured stage out as an OBJ file, so they can be opened " + "side by side in a mesh viewer."), + m_imgui->scaled(20.f)); +} + void GLGizmoTextureDisplacement::queue_prepare(const TextureDisplacementPrepareParams ¶ms, const std::string &snapshot_name, bool then_bake, const std::string &unchanged_msg) @@ -4189,8 +4906,8 @@ void GLGizmoTextureDisplacement::render_paint_cursor_hint() if (io.WantCaptureMouse || !ImGui::IsMousePosValid()) return; - // Shift erases (see handle_snapshot_action_name()); a plain stroke adds. - const bool removing = io.KeyShift; + // Shift erases (see handle_snapshot_action_name()), and so does a plain stroke in Erase mode. + const bool removing = io.KeyShift || m_erase_mode; const ImU32 color = removing ? IM_COL32(235, 70, 60, 255) : IM_COL32(90, 210, 110, 255); const char *glyph = removing ? "-" : "+"; @@ -4226,225 +4943,444 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); GizmoImguiBegin(get_name(), flags); + ensure_panel_icons(); + process_uv_commands(); // clicks from the UV editor pane, run here where the GL context is current + + // Pinned every frame while Standard is active, so what Preview shows is always what Bake will do. + if (!pro_mode() && apply_standard_mode_presets(mv)) + m_preview_params_dirty = true; + + // Layout follows the Option C design: a fixed-width panel, a header pinned at the top, a body that + // scrolls as one, and a footer with Bake pinned at the bottom. Rows are laid out against panel_w and + // against the body child's own fixed width - never against this window's width, which under + // AlwaysAutoResize is derived from its content and would feed back into it. + const bool dark = wxGetApp().dark_mode(); + const bool busy = m_bake_in_progress || m_prepare_in_progress; + const ImGuiStyle &style = ImGui::GetStyle(); + const float panel_w = m_imgui->scaled(21.5f); + const float frame_h = ImGui::GetFrameHeight(); + const float icon_md = std::round(frame_h * 1.25f); // tool and view buttons + const float icon_sm = std::round(frame_h * 1.05f); // buttons sitting in a row of text + const float gap_s = std::round(m_imgui->scaled(0.4f)); + const float label_w = m_imgui->scaled(4.8f); // label column of a label + slider row + const float card_pad = std::round(m_imgui->scaled(0.55f)); + const float wrap_w = m_imgui->scaled(20.f); + const ImVec4 orca = ImGuiWrapper::COL_ORCA; + const ImVec4 col_link = dark ? ImVec4(0.30f, 0.71f, 0.67f, 1.f) : ImVec4(0.f, 0.47f, 0.42f, 1.f); + const ImVec4 col_frame = dark ? ImVec4(0.212f, 0.212f, 0.235f, 1.f) : ImVec4(0.808f, 0.808f, 0.808f, 1.f); + const ImU32 col_card = dark ? IM_COL32(255, 255, 255, 10) : IM_COL32(0, 0, 0, 12); + const ImU32 col_line = dark ? IM_COL32(255, 255, 255, 18) : IM_COL32(0, 0, 0, 23); + const ImU32 col_sep = ImGui::GetColorU32(ImGuiCol_Separator); // Combo drop-downs otherwise inherit ImGui's near-black default popup background; under the light // theme that leaves the dark item text unreadable ("the dropbox is black"). Pushed only around each // Combo below (never around a tooltip, whose own near-black default is what makes it readable). - const ImVec4 combo_popup_bg = wxGetApp().dark_mode() ? ImVec4(0.18f, 0.18f, 0.19f, 1.f) - : ImVec4(0.93f, 0.93f, 0.93f, 1.f); - const auto scoped_combo = [&](const char *id, int *v, const char *const items[], int n) { + const ImVec4 combo_popup_bg = dark ? ImVec4(0.18f, 0.18f, 0.19f, 1.f) : ImVec4(0.93f, 0.93f, 0.93f, 1.f); + const auto scoped_combo = [&](const char *id, int *v, const char *const items[], int n) { ImGui::PushStyleColor(ImGuiCol_PopupBg, combo_popup_bg); const bool changed = ImGui::Combo(id, v, items, n); ImGui::PopStyleColor(); return changed; }; + const auto hover_tip = [&](const auto &text) { + if (ImGui::IsItemHovered()) + m_imgui->tooltip(text, wrap_w); + }; - // Icon toggle button shared by the selection-mode and view-mode rows, styled like the main toolbar: - // an inactive button shows the icon in the theme's normal (grey) monochrome, an active one shows it - // in its original colours. All icons are the same square size. Falls back to a text checkbox if the - // icon set could not be loaded, so the control is never lost. - ensure_panel_icons(); - // Sized to match the 3D toolbar's icons exactly, rather than to the panel's font. It is the same - // expression GLCanvas3D::_update_toolbar_icons_scale() uses, and it is valid here because ImGui's - // DisplaySize is set from the canvas's pixel size (GLCanvas3D::_resize()) - so one ImGui unit is - // one canvas pixel, the very units the toolbar is drawn in. Deriving it rather than hard-coding a - // font multiple also keeps the two in step when the toolbar auto-fit shrinks its icons to make - // them fit a narrow window, which it does by lowering the same toolbar_icon_scale() read here. - const float icon_btn_sz = GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale() * m_parent.get_scale(); - // The icon SVGs carry their own border, so the ImGui button's own frame border and idle fill are - // suppressed here (FrameBorderSize 0 + transparent ImGuiCol_Button) to avoid a doubled border - the - // hover/active fill is left in place for feedback. Applied only around these gizmo icon buttons. - const auto push_borderless_icon_style = []() { + // Framed icon toggle: a 1 px frame, and when on a teal frame over a teal tint with the icon in its + // original colours (IconManager's color_wite_gray variant [1]); off, the theme's grey variant [0]. + // `unavailable`, when not empty, draws it faded but still hoverable, so the tooltip can say why it + // cannot be used - a disabled ImGui item would swallow the hover and leave the user guessing. + const auto icon_toggle = [&](int uid, const std::string &iconfile, bool active, float sz, const wxString &label, + const wxString &tip_text, const wxString &unavailable = wxString()) -> bool { + const bool na = !unavailable.empty(); + const auto it = m_panel_icon_map.find(iconfile); + const float pad = std::max(1.f, std::round(sz * 0.14f)); + const int variant = active ? 1 : 0; + bool clicked = false; + ImGui::PushID(uid); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, na ? style.Alpha * 0.32f : style.Alpha); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); + ImGui::PushStyleColor(ImGuiCol_Button, active ? ImVec4(orca.x, orca.y, orca.z, 0.22f) : ImVec4(0.f, 0.f, 0.f, 0.f)); + if (na) { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.f, 0.f, 0.f, 0.f)); + } + if (it != m_panel_icon_map.end() && int(it->second.size()) > variant && it->second[size_t(variant)]->is_valid()) { + const IconManager::Icon &ic = *it->second[size_t(variant)]; + clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz - 2.f * pad, sz - 2.f * pad), ic.tl, ic.br, + int(pad)); + } else { + clicked = ImGui::Button(label.ToUTF8().data(), ImVec2(0.f, sz)); // the control is never lost to a missing icon + } + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), + ImGui::GetColorU32(active ? orca : col_frame), style.FrameRounding); + ImGui::PopStyleColor(na ? 3 : 1); + ImGui::PopStyleVar(2); + ImGui::PopID(); + if (ImGui::IsItemHovered()) + m_imgui->tooltip(na ? tip_text + "\n\n" + unavailable : tip_text, wrap_w); + return clicked && !na; + }; + // Borderless icon button (non-toggle): always the grey monochrome variant. + const auto icon_button = [&](int uid, const std::string &iconfile, float sz, const wxString &label, + const wxString &tip_text) -> bool { + const auto it = m_panel_icon_map.find(iconfile); + const float pad = std::max(1.f, std::round(sz * 0.14f)); + bool clicked = false; + ImGui::PushID(uid); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f)); - }; - const auto pop_borderless_icon_style = []() { - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); - }; - const auto icon_toggle = [&](int uid, const std::string &iconfile, bool active, const wxString &label, - const wxString &tip) -> bool { - bool clicked = false; - const auto it = m_panel_icon_map.find(iconfile); - ImGui::PushID(uid); - // color_wite_gray variants: [0] normal/grey, [1] original colour, [2] disabled. - if (it != m_panel_icon_map.end() && it->second.size() >= 2 && it->second[active ? 1 : 0]->is_valid()) { - const IconManager::Icon &ic = *it->second[active ? 1 : 0]; - push_borderless_icon_style(); - clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(icon_btn_sz, icon_btn_sz), - ic.tl, ic.br, -1, ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)); - pop_borderless_icon_style(); - } else { - bool v = active; - clicked = ImGui::Checkbox(label.ToUTF8().data(), &v); - } - ImGui::PopID(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(tip, m_imgui->scaled(18.f)); - return clicked; - }; - // Borderless icon button (non-toggle): always the grey monochrome variant. Falls back to a plain - // text button when the icon file is absent, so the control is never lost before the art lands. - const auto icon_button = [&](int uid, const std::string &iconfile, float sz, const wxString &label, - const wxString &tip) -> bool { - bool clicked = false; - const auto it = m_panel_icon_map.find(iconfile); - ImGui::PushID(uid); if (it != m_panel_icon_map.end() && !it->second.empty() && it->second[0]->is_valid()) { const IconManager::Icon &ic = *it->second[0]; - push_borderless_icon_style(); - clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz, sz), ic.tl, ic.br, -1, - ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)); - pop_borderless_icon_style(); + clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz - 2.f * pad, sz - 2.f * pad), ic.tl, ic.br, + int(pad)); } else { - clicked = m_imgui->button(label); + clicked = ImGui::Button(label.ToUTF8().data(), ImVec2(0.f, sz)); } + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); ImGui::PopID(); - if (!tip.empty() && ImGui::IsItemHovered()) - m_imgui->tooltip(tip, m_imgui->scaled(18.f)); + if (!tip_text.empty() && ImGui::IsItemHovered()) + m_imgui->tooltip(tip_text, wrap_w); return clicked; }; - - if (m_imgui->button(m_undocked ? _L("Dock panel") : _L("Undock panel"))) - m_undocked = !m_undocked; - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Detach this panel so it can be dragged anywhere over the 3D view, or dock it " - "back beside the toolbar."), - m_imgui->scaled(20.f)); - - // Standard / Pro, right-aligned on the header row. A two-position slider rather than a checkbox - // because it is a mode, not an option: Standard hides every mesh-preparation control and folds the - // whole recipe into Bake, Pro shows all of it and hands the ordering to the user. - { - const float mode_w = m_imgui->scaled(6.2f); - ImGui::SameLine(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - mode_w)); - ImGui::PushItemWidth(mode_w); - // The format string carries no conversion, so ImGui prints it verbatim as the slider's label - - // which is how a two-position slider gets word labels instead of "0" and "1". - const std::string mode_label = pro_mode() ? _u8L("Pro") : _u8L("Standard"); - if (ImGui::SliderInt("##panel_mode", &m_panel_mode, 0, 1, mode_label.c_str())) { - m_panel_mode = std::clamp(m_panel_mode, 0, 1); - if (!pro_mode()) { - // Leaving the subdivision preview open would strand a wireframe whose controls just - // disappeared, so close it as part of the switch. - m_subdivide_editing = false; - m_subdivide_preview_tris = -1; - m_subdivide_preview_glmodel.reset(); - if (apply_standard_mode_presets(mv)) - m_preview_params_dirty = true; - } - m_parent.set_as_dirty(); + // A short vertical rule between groups of buttons on one row; call it right after an item. + const auto vsep = [&](float h) { + ImGui::SameLine(0.f, gap_s); + const ImVec2 p = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + h * 0.2f), ImVec2(p.x, p.y + h * 0.8f), col_sep); + ImGui::Dummy(ImVec2(1.f, h)); + ImGui::SameLine(0.f, gap_s); + }; + const auto heading = [&](const wxString &text) { + m_imgui->push_bold_font(); + m_imgui->text(text); + m_imgui->pop_bold_font(); + }; + // Cuts a UTF-8 string down to `max_w` pixels, ending it in "..." when it had to be cut. + const auto ellipsize = [](std::string s, float max_w) { + if (ImGui::CalcTextSize(s.c_str()).x <= max_w) + return s; + while (!s.empty() && ImGui::CalcTextSize((s + "...").c_str()).x > max_w) { + unsigned char c; + do { // a whole code point at a time + c = static_cast(s.back()); + s.pop_back(); + } while (!s.empty() && (c & 0xC0) == 0x80); } - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Standard - paint, then press Bake: the mesh is remeshed, refined where the " - "texture bends, and displaced in one step.\n\nPro - every mesh-preparation " - "control is shown and you run Remesh, Subdivide and Bake yourself, in the " - "order you want."), - m_imgui->scaled(20.f)); - } - // Pinned every frame while Standard is active, so what Preview shows is always what Bake will do. - if (!pro_mode() && apply_standard_mode_presets(mv)) - m_preview_params_dirty = true; + return s + "..."; + }; + // Label column + a slider filling the rest of the row, value printed inside the track. Ctrl+click on + // any of these types a value in. `right_inset` keeps a layer card's inner padding. + const auto slider_label = [&](const wxString &label) { + const float x0 = ImGui::GetCursorPosX(); + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); + }; + const auto float_row = [&](const char *id, const wxString &label, float *v, float v_min, float v_max, const char *format, + bool log, float right_inset) -> bool { + slider_label(label); + ImGui::SetNextItemWidth(-std::max(right_inset, 1.f)); + return ImGui::SliderFloat(id, v, v_min, v_max, format, ImGuiSliderFlags_AlwaysClamp | (log ? ImGuiSliderFlags_Logarithmic : 0)); + }; + const auto int_row = [&](const char *id, const wxString &label, int *v, int v_min, int v_max, const char *format, + float right_inset) -> bool { + slider_label(label); + ImGui::SetNextItemWidth(-std::max(right_inset, 1.f)); + return ImGui::SliderInt(id, v, v_min, v_max, format, ImGuiSliderFlags_AlwaysClamp); + }; + const auto layer_name = [](const TextureDisplacementLayer &l) { + return l.name.empty() ? Slic3r::format(_u8L("Layer %1%"), l.slot + 1) : l.name; + }; + // Painted on any model part. The selectors flush into the model at the end of every stroke. + const auto slot_painted = [mo](int slot) { + for (const ModelVolume *v : mo->volumes) + if (v->is_model_part() && !v->texture_displacement_facet(slot).empty()) + return true; + return false; + }; + // ---- Header: title, Standard / Pro, dock toggle. Pinned above the body. ---- + { + const float x0 = ImGui::GetCursorPosX(); + ImGui::AlignTextToFramePadding(); + heading(_L("Texture displacement")); + + const std::string labels[2] = { _u8L("Standard"), _u8L("Pro") }; + const float seg_pad = m_imgui->scaled(0.5f); + const float seg_w[2] = { ImGui::CalcTextSize(labels[0].c_str()).x + 2.f * seg_pad, ImGui::CalcTextSize(labels[1].c_str()).x + 2.f * seg_pad }; + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + panel_w - (seg_w[0] + seg_w[1] + gap_s + icon_sm))); + + // Standard / Pro is a mode, not an option: Standard hides every mesh-preparation control and folds + // the whole recipe into Bake, Pro shows all of it and hands the ordering to the user. + const ImVec2 seg_min = ImGui::GetCursorScreenPos(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, style.ItemSpacing.y)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); + m_imgui->disabled_begin(busy); + for (int m = 0; m < 2; ++m) { + if (m > 0) + ImGui::SameLine(); + const bool on = m_panel_mode == m; + ImGui::PushStyleColor(ImGuiCol_Button, on ? orca : ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, on ? orca : ImVec4(orca.x, orca.y, orca.z, 0.25f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, orca); + ImGui::PushStyleColor(ImGuiCol_Text, on ? ImVec4(1.f, 1.f, 1.f, 1.f) : style.Colors[ImGuiCol_TextDisabled]); + const bool clicked = ImGui::Button((labels[m] + "##panel_mode").c_str(), ImVec2(seg_w[m], frame_h)); + ImGui::PopStyleColor(4); + hover_tip(m == 0 ? _u8L("Standard - paint, then press Bake: the mesh is remeshed, refined where the texture " + "bends, and displaced in one step.") : + _u8L("Pro - every mesh-preparation control is shown and you run Remesh, Subdivide and " + "Bake yourself, in the order you want.")); + if (clicked && !on) { + m_panel_mode = m; + if (!pro_mode()) { + // Leaving the subdivision preview open would strand a wireframe whose controls just + // disappeared, so close it as part of the switch. + m_subdivide_editing = false; + m_subdivide_preview_tris = -1; + m_subdivide_preview_glmodel.reset(); + if (apply_standard_mode_presets(mv)) + m_preview_params_dirty = true; + } + m_parent.set_as_dirty(); + } + } + m_imgui->disabled_end(); + ImGui::PopStyleVar(2); + ImGui::GetWindowDrawList()->AddRect(seg_min, ImVec2(ImGui::GetItemRectMax().x, seg_min.y + frame_h), + ImGui::GetColorU32(col_frame), style.FrameRounding); + + ImGui::SameLine(0.f, gap_s); + if (icon_button(806, "canvas_drag.svg", icon_sm, m_undocked ? _L("Dock panel") : _L("Undock panel"), + _L("Detach this panel so it can be dragged anywhere over the 3D view, or dock it back beside " + "the toolbar."))) + m_undocked = !m_undocked; + } ImGui::Separator(); - // Selection mode: which of TriangleSelector's existing click/brush mechanisms drives painting. - // "Face" and "Connected area" reuse the exact same underlying selection machinery every other - // paint gizmo already has (single-facet click, and angle-limited flood fill respectively) - - // just exposed here as an alternative to brushing, one triangle/region at a time. - m_imgui->text(_L("Selection mode")); - const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER; - const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER; - const bool is_area_mode = m_tool_type == ToolType::SMART_FILL; - ImGui::SameLine(); - if (icon_toggle(801, "toolbar_big_brush.svg", is_brush_mode, _L("Brush"), - _L("Brush - paint over the surface by dragging"))) { - m_tool_type = ToolType::BRUSH; - m_cursor_type = TriangleSelector::CursorType::CIRCLE; - } - ImGui::SameLine(); - if (icon_toggle(802, "toolbar_face.svg", is_face_mode, _L("Face"), - _L("Face - click individual triangles"))) { - m_tool_type = ToolType::BRUSH; - m_cursor_type = TriangleSelector::CursorType::POINTER; - } - ImGui::SameLine(); - if (icon_toggle(803, "texture_displacement_connected_area.svg", is_area_mode, _L("Connected area"), - _L("Connected area - flood-fill the region reachable without crossing an edge sharper than the " - "angle threshold"))) { - m_tool_type = ToolType::SMART_FILL; - m_cursor_type = TriangleSelector::CursorType::POINTER; - } + // ---- Body: everything between the header and the footer, scrolling as one. ---- + // Sized from last frame's content and footer heights (see m_panel_body_h): as tall as its content, + // but never so tall that the footer drops below the bottom of the canvas. + const float bottom = m_undocked ? ImGui::GetIO().DisplaySize.y : bottom_limit; + const float max_body_h = std::max(m_imgui->scaled(8.f), bottom - ImGui::GetCursorScreenPos().y - m_panel_footer_h - + style.WindowPadding.y - style.ItemSpacing.y); + const float body_h = m_panel_body_h > 0.f ? std::min(m_panel_body_h + 1.f, max_body_h) : max_body_h; - if (is_brush_mode) { - m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); - ImGui::PopItemWidth(); - - bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE; - if (ImGui::RadioButton(m_desc.at("circle").ToUTF8().data(), is_circle)) - m_cursor_type = TriangleSelector::CursorType::CIRCLE; - ImGui::SameLine(); - if (ImGui::RadioButton(m_desc.at("sphere").ToUTF8().data(), !is_circle)) - m_cursor_type = TriangleSelector::CursorType::SPHERE; - } else if (is_area_mode) { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_imgui->slider_float(_u8L("Angle threshold"), &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, "%.0f"); - ImGui::PopItemWidth(); - } - - if (m_imgui->button(_u8L("Select whole model"))) - select_whole_model(); - - // View mode (#: "make View Mode with just icons"): Normal / Fast / Checker / Distortion behave as - // one radio group, Wireframe as an independent toggle. Each has its own icon; the tooltip carries - // the meaning. The underlying state stays m_use_bump_preview + m_uv_check_mode. + // ImGui's stock scrollbar is a wide, square-cornered slab in a tinted track - against this flat panel + // it reads as a raw widget bolted onto the edge. Slim it to a rounded thumb over an invisible track. + const float scrollbar_w = m_imgui->scaled(0.5f); + const ImVec4 grab = dark ? ImVec4(1.f, 1.f, 1.f, 0.26f) : ImVec4(0.f, 0.f, 0.f, 0.26f); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, scrollbar_w); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding, 0.5f * scrollbar_w); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, grab); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(grab.x, grab.y, grab.z, 0.45f)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(grab.x, grab.y, grab.z, 0.65f)); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.f, 0.f, 0.f, 0.f)); + // NoScrollWithMouse: the wheel is handled below so the scroll can be eased instead of teleporting + // five text lines per notch, which on blocks this tall lost the reader's place. + ImGui::BeginChild("##td_body", ImVec2(panel_w, body_h), false, ImGuiWindowFlags_NoScrollWithMouse); { - const int cur_mode = m_use_bump_preview ? 1 : - (m_uv_check_mode == UVCheckMode::Checker ? 2 : - m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0); - int new_mode = cur_mode; - bool wf_toggle = false; + ImGuiIO &io = ImGui::GetIO(); + const float scroll_now = ImGui::GetScrollY(); + const float scroll_max = ImGui::GetScrollMaxY(); - m_imgui->text(_L("View")); - ImGui::SameLine(); - if (icon_toggle(701, "texture_displacement_real_preview.svg", cur_mode == 0, _L("Normal"), - _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0; - ImGui::SameLine(); - if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, _L("Fast"), - _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1; - ImGui::SameLine(); - if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, _L("Checker"), - _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2; - ImGui::SameLine(); - if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, _L("Distortion"), - _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3; - ImGui::SameLine(); - ImGui::Dummy(ImVec2(m_imgui->scaled(0.6f), 0.f)); - ImGui::SameLine(); - if (icon_toggle(705, "texture_displacement_wireframe.svg", m_wireframe_overlay, _L("Wireframe"), - _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true; + // Anything that moved the scroll without us - dragging the grab, a keyboard/gamepad nav step, the + // content shrinking under a clamped offset - has to re-seed the target, or the easing below would + // immediately drag the view back to where it last animated to. + if (m_panel_scroll_applied < 0.f || std::abs(scroll_now - m_panel_scroll_applied) > 0.5f) + m_panel_scroll_target = scroll_now; - if (new_mode != cur_mode) { - m_use_bump_preview = (new_mode == 1); - m_uv_check_mode = (new_mode == 2) ? UVCheckMode::Checker : - (new_mode == 3) ? UVCheckMode::Distortion : UVCheckMode::None; - rebuild_uvcheck_mesh(); - if (m_use_bump_preview) - rebuild_bump_preview_mesh(); - else - // The Fast view skips the CPU displacement entirely (see rebuild_preview()), so - // leaving it means m_preview_glmodel may be stale or absent - ask for it now. - queue_preview_job(); - // ...and the paint tint between the base and the displaced surface, for the same reason. - m_paint_overlay_dirty = true; - refresh_wireframe(); // Normal<->Fast swaps the wireframe between displaced and base mesh - update_uv_editor(); // mirror the checker / distortion heatmap into the UV pane too (#7) + if (io.MouseWheel != 0.f && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) + m_panel_scroll_target -= io.MouseWheel * ImGui::GetFontSize() * 4.f; + // Whole pixels: ImGui floors whatever SetScrollY() is given, so a fractional target could never be + // reached and the "still gliding" test below would stay true forever, repainting the canvas for good. + m_panel_scroll_target = std::floor(std::clamp(m_panel_scroll_target, 0.f, scroll_max)); + + const float delta = m_panel_scroll_target - scroll_now; + if (std::abs(delta) >= 1.f) { + // Exponential ease, formulated against the frame time so the glide takes the same wall time + // whether the canvas is running at 30 or 144 fps. The last sub-pixel step would be floored + // away, so land on the target outright once the remainder is that small. + const float t = 1.f - std::exp(-20.f * std::clamp(io.DeltaTime, 1.f / 240.f, 1.f / 15.f)); + const float step = (std::abs(delta * t) < 1.f) ? delta : delta * t; + const float next = std::floor(scroll_now + step); + ImGui::SetScrollY(next); + m_panel_scroll_applied = next; + m_parent.set_as_dirty(); // nothing else would redraw mid-glide once the mouse stops + } else { + m_panel_scroll_applied = scroll_now; + } + } + + // Everything that changes the layer list is deferred until the list is no longer being drawn: + // removing or reordering shifts mv->texture_displacement_layers under the pointers the loop holds, + // and switching the active layer mid-loop would draw two cards open for a frame. + int slot_to_remove = -1; + int move_slot = -1; + int move_to = -1; + int activate_slot = -1; + + TextureDisplacementLayer *active = mv != nullptr ? active_layer() : nullptr; + + // ---- Paint: which layer strokes land in, and Paint / Erase ---- + { + ImGui::AlignTextToFramePadding(); + heading(_L("Paint")); + const std::string into = active != nullptr ? Slic3r::format(_u8L("into %1%"), layer_name(*active)) : + _u8L("add a layer to paint"); + const float toggles_x = ImGui::GetWindowContentRegionMax().x - (2.f * icon_sm + gap_s); + const float text_end = toggles_x - (2.f * gap_s + 1.f); + ImGui::SameLine(); + const std::string shown = ellipsize(into, std::max(0.f, text_end - ImGui::GetCursorPosX())); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), text_end - ImGui::CalcTextSize(shown.c_str()).x)); + ImGui::TextDisabled("%s", shown.c_str()); + vsep(icon_sm); + ImGui::SetCursorPosX(toggles_x); + if (icon_toggle(820, "texture_displacement_add.svg", !m_erase_mode, icon_sm, _L("Paint"), + _L("Paint - add the active layer where you click or drag. The right button erases, and so does " + "holding Shift."))) + m_erase_mode = false; + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(821, "texture_displacement_add_negative.svg", m_erase_mode, icon_sm, _L("Erase"), + _L("Erase - remove the active layer where you click or drag. The right button paints."))) + m_erase_mode = true; + } + + // ---- Tools: brush / face / connected area, and the active tool's own control ---- + // "Face" and "Connected area" reuse the exact same selection machinery every other paint gizmo has + // (single-facet click, and angle-limited flood fill respectively). + { + const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER; + const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER; + const bool is_area_mode = m_tool_type == ToolType::SMART_FILL; + if (icon_toggle(801, "texture_displacement_brush.svg", is_brush_mode, icon_md, _L("Brush"), + _L("Brush - paint over the surface by dragging"))) { + m_tool_type = ToolType::BRUSH; + if (m_cursor_type == TriangleSelector::CursorType::POINTER) + m_cursor_type = TriangleSelector::CursorType::CIRCLE; + } + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(802, "texture_displacement_face.svg", is_face_mode, icon_md, _L("Face"), _L("Face - click individual triangles"))) { + m_tool_type = ToolType::BRUSH; + m_cursor_type = TriangleSelector::CursorType::POINTER; + } + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(803, "texture_displacement_connected_area.svg", is_area_mode, icon_md, _L("Connected area"), + _L("Connected area - flood-fill the region reachable without crossing an edge sharper than the " + "angle threshold"))) { + m_tool_type = ToolType::SMART_FILL; + m_cursor_type = TriangleSelector::CursorType::POINTER; + } + ImGui::SameLine(); + if (is_brush_mode) { + ImGui::SetNextItemWidth(-(3.f * gap_s + 1.f + 2.f * icon_sm)); + ImGui::SliderFloat("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f mm", + ImGuiSliderFlags_AlwaysClamp); + hover_tip(m_desc.at("cursor_size")); + vsep(icon_sm); + const bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE; + if (icon_toggle(804, "circle_paint.svg", is_circle, icon_sm, m_desc.at("circle"), + _L("Circle - paints everything under the brush as seen from the camera"))) + m_cursor_type = TriangleSelector::CursorType::CIRCLE; + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(805, "menu_obj_sphere.svg", !is_circle, icon_sm, m_desc.at("sphere"), + _L("Sphere - paints only within a ball around the point under the cursor"))) + m_cursor_type = TriangleSelector::CursorType::SPHERE; + } else if (is_area_mode) { + ImGui::SetNextItemWidth(-1.f); + ImGui::SliderFloat("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, "%.0f°", + ImGuiSliderFlags_AlwaysClamp); + hover_tip(_u8L("Angle threshold - the fill stops at edges sharper than this")); + } else { + ImGui::AlignTextToFramePadding(); + ImGui::TextDisabled("%s", _u8L("Click a triangle to paint it").c_str()); + } + } + + // ---- Whole model ---- + { + const float half = std::floor((ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) * 0.5f); + m_imgui->disabled_begin(busy || active == nullptr); + if (ImGui::Button(_u8L("Select whole model").c_str(), ImVec2(half, 0.f))) + select_whole_model(); + m_imgui->disabled_end(); + hover_tip(_u8L("Paint every face of the model with the active layer")); + ImGui::SameLine(); + m_imgui->disabled_begin(busy || active == nullptr || !slot_painted(m_active_layer_slot)); + if (ImGui::Button(_u8L("Erase whole model").c_str(), ImVec2(half, 0.f))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset texture displacement selection"), + UndoRedo::SnapshotType::GizmoAction); + int idx = -1; + for (ModelVolume *v : mo->volumes) + if (v->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + m_triangle_selectors[idx]->request_update_render_data(); + } + update_model_object(); m_parent.set_as_dirty(); } + m_imgui->disabled_end(); + hover_tip(_u8L("Clear the active layer's paint from every face")); + } + + // ---- View: Normal / Fast / Checker / Distortion as one group, Wireframe on its own ---- + // The underlying state stays m_use_bump_preview + m_uv_check_mode. + { + const int cur_mode = m_use_bump_preview ? 1 : + m_uv_check_mode == UVCheckMode::Checker ? 2 : + m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0; + int new_mode = cur_mode; + bool wf_toggle = false; + const wxString distortion_na = active == nullptr ? _L("Add a layer first.") : + active->projection_method != TextureProjectionMethod::LSCM ? + _L("Needs the active layer mapped with Unwrap (LSCM).") : + wxString(); + // Distortion over a layer that stopped being an unwrap shows nothing at all, so fall back to Normal. + if (cur_mode == 3 && !distortion_na.empty()) + new_mode = 0; + + const float x0 = ImGui::GetCursorPosX(); + ImGui::AlignTextToFramePadding(); + ImGui::TextDisabled("%s", _u8L("View").c_str()); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + m_imgui->scaled(2.6f))); + if (icon_toggle(701, "texture_displacement_real_preview.svg", cur_mode == 0, icon_md, _L("Normal"), + _L("Normal - the true displaced geometry (what Bake produces)"))) + new_mode = 0; + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, icon_md, _L("Fast"), + _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) + new_mode = 1; + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, icon_md, _L("Checker"), + _L("Checker - a test grid over the texture mapping; squares stay square where it does not stretch"))) + new_mode = 2; + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, icon_md, _L("Distortion"), + _L("Distortion - blue-to-red stretch heatmap over the unwrap"), distortion_na)) + new_mode = 3; + vsep(icon_md); + if (icon_toggle(705, "texture_displacement_wireframe.svg", m_wireframe_overlay, icon_md, _L("Wireframe"), + _L("Wireframe - overlay the mesh edges; independent of the view above"))) + wf_toggle = true; + + const std::string auto_label = _u8L("Auto"); + const float auto_w = frame_h + style.ItemInnerSpacing.x + ImGui::CalcTextSize(auto_label.c_str()).x; + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - auto_w)); + if (ImGui::Checkbox((auto_label + "##auto_update").c_str(), &m_auto_update) && m_auto_update) + rebuild_preview(); // catch up anything that changed while it was off + hover_tip(_u8L("Auto update - rebuild the displaced geometry as soon as anything changes (painting, textures, " + "sliders). Turn off to only rebuild when you release a slider, on very heavy models.")); + + if (new_mode != cur_mode) + apply_view_mode(new_mode); if (wf_toggle) { m_wireframe_overlay = !m_wireframe_overlay; refresh_wireframe(); @@ -4452,478 +5388,384 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } } - if (ImGui::Checkbox(_u8L("Auto update").c_str(), &m_auto_update)) - if (m_auto_update) - rebuild_preview(); // catch up anything that changed while it was off - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Rebuild the displaced geometry as soon as anything changes (painting, textures, " - "sliders). Turn off to only rebuild when you release a slider, on very heavy models."), - m_imgui->scaled(20.f)); - ImGui::Separator(); - m_imgui->text(_L("Texture layers")); - if (mv != nullptr) { - // Add-layer affordance as an icon beside the heading. It is deliberately *not* right-aligned - // against the window edge: this panel uses ImGuiWindowFlags_AlwaysAutoResize, and positioning - // an item at GetWindowContentRegionMax().x - w feeds the window's own width back into its - // auto-fit, growing it by one item-spacing every frame - which, with the panel docked and - // anchored by its right edge, walked it left off-screen on hover. A plain SameLine can't do that. - const unsigned int add_icon = tool_icon_id(); - const float sz = m_imgui->scaled(1.3f); - ImGui::SameLine(); - bool add_clicked; - if (add_icon != 0) { - // The add icon SVG carries its own border; suppress the ImGui frame border/idle fill. - push_borderless_icon_style(); - add_clicked = m_imgui->image_button((ImTextureID) (intptr_t) add_icon, ImVec2(sz, sz)); - pop_borderless_icon_style(); - } else { - add_clicked = m_imgui->button(m_desc.at("add_texture")); + // ---- Texture layers: the active layer as an open card, every other one as a single row ---- + { + ImGui::AlignTextToFramePadding(); + heading(_L("Texture layers")); + if (mv != nullptr) { + const std::string count = std::to_string(mv->texture_displacement_layers.size()) + " / " + + std::to_string(TEXTURE_DISPLACEMENT_MAX_LAYERS); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - ImGui::CalcTextSize(count.c_str()).x)); + ImGui::TextDisabled("%s", count.c_str()); } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Add a texture layer"), m_imgui->scaled(20.f)); - if (add_clicked) - add_texture_layer(); } + std::vector ordered; if (mv != nullptr) { - std::vector ordered; for (TextureDisplacementLayer &l : mv->texture_displacement_layers) ordered.push_back(&l); std::sort(ordered.begin(), ordered.end(), [](const auto *a, const auto *b) { return a->slot < b->slot; }); + } - // Removing a layer erases it from mv->texture_displacement_layers, which shifts every later - // element down and leaves `ordered` - and `layer` itself - pointing at the wrong element - // (or past the end). Removing mid-loop would therefore keep rendering this row's remaining - // widgets against freed/shifted memory. Defer it to after the loop instead. - int slot_to_remove = -1; + ImDrawList *dl = ImGui::GetWindowDrawList(); + const float content_rx = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; + const float rounding = style.FrameRounding + 1.f; - // The layer stack lives in its own scrolling, tinted region (#10, #12): with eight layers' - // worth of controls the panel otherwise runs off the bottom of the screen, and there was - // nothing to tell "settings that belong to this layer" apart from "settings that belong to - // the tool". - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.f, 1.f, 1.f, 0.04f)); - ImGui::BeginChild("##texture_layers", ImVec2(0.f, m_imgui->scaled(20.f)), true); + // Opens the texture library for a layer, making it the active one too (after the loop). + const auto open_picker = [&](int slot) { + m_picker_slot = slot; + m_picker_open_request = true; + }; + // The six-dot drag icon, drawn into `list` over [x, x + w] x [y, y + h]. + const auto draw_drag_icon = [&](ImDrawList *list, float x, float y, float w, float h, ImU32 tint) { + const auto it = m_panel_icon_map.find("texture_displacement_drag.svg"); + if (it != m_panel_icon_map.end() && !it->second.empty() && it->second[0]->is_valid()) { + // The icon's dots sit in the middle of its square, so it is drawn at the row's height centred on the + // narrow hit area; only its transparent margins overhang the neighbouring widgets. + const IconManager::Icon &ic = *it->second[0]; + const float cx = x + 0.5f * w; + list->AddImage((ImTextureID) (intptr_t) ic.tex_id, ImVec2(cx - 0.5f * h, y), ImVec2(cx + 0.5f * h, y + h), ic.tl, ic.br, tint); + } else { + const float r = std::max(1.f, std::round(m_imgui->scaled(0.08f))); + for (int row = 0; row < 3; ++row) + for (int col = 0; col < 2; ++col) + list->AddCircleFilled(ImVec2(x + w * (0.3f + 0.4f * col), y + h * 0.5f + float(row - 1) * 4.f * r), r, tint); + } + }; + const auto drag_tint = [dark](int alpha) { return dark ? IM_COL32(255, 255, 255, alpha) : IM_COL32(90, 90, 90, alpha); }; - for (size_t li = 0; li < ordered.size(); ++li) { - TextureDisplacementLayer *layer = ordered[li]; - ImGui::PushID(layer->slot); - const bool is_active = layer->slot == m_active_layer_slot; + const float grip_w = std::round(m_imgui->scaled(0.7f)); + // Six-dot handle that drags a layer onto another one to reorder the stack. + const auto grip = [&](const TextureDisplacementLayer &l, float h) { + const ImVec2 p = ImGui::GetCursorScreenPos(); + ImGui::InvisibleButton("##grip", ImVec2(grip_w, h)); + const bool hot = ImGui::IsItemHovered() || ImGui::IsItemActive(); + draw_drag_icon(dl, p.x, p.y, grip_w, h, drag_tint(hot ? 255 : 150)); + hover_tip(_u8L("Drag onto another layer to reorder. The first layer is the base; each one after it blends onto " + "the layers before it.")); + // ImGui's own preview is a tooltip holding whatever is submitted here. The panel draws a copy of the whole + // layer row under the cursor instead (after the layer list), so that one is switched off. + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip)) { + const int slot = l.slot; + ImGui::SetDragDropPayload("TD_LAYER", &slot, sizeof(slot)); + ImGui::EndDragDropSource(); + } + }; - // Tint the whole active layer's block, not just its header (#: "background color for the - // selected layer ... whole plate"). The block's height isn't known until it is laid out, so - // draw the block into a foreground channel and the backing rectangle into a background one, - // then merge - the standard ImGui "rect behind a group" trick. - ImDrawList *dl = ImGui::GetWindowDrawList(); - const ImVec2 block_min = ImGui::GetCursorScreenPos(); - const float block_rx = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; - if (is_active) { - dl->ChannelsSplit(2); - dl->ChannelsSetCurrent(1); + // The layer being dragged, while a reorder drag is in flight. Its card stays in place as a faded ghost. + int dragged_slot = -1; + if (const ImGuiPayload *payload = ImGui::GetDragDropPayload(); payload != nullptr && payload->IsDataType("TD_LAYER")) + dragged_slot = *static_cast(payload->Data); + int dragged_index = -1; + for (size_t i = 0; i < ordered.size(); ++i) + if (ordered[i]->slot == dragged_slot) + dragged_index = int(i); + const ImU32 ghost_col = ImGui::GetColorU32(ImGuiCol_WindowBg, 0.65f); + + // A layer card or row [mn, mx] as a drop target. Dropped anywhere on another layer, the dragged one takes its + // place and the layers in between shift over by one - so it lands above the target when moving up and below it + // when moving down. A teal line on that side shows where; ImGui's default highlight rectangle says only + // "something is here", so it is replaced by the line. + // + // Deciding by which half of the target the cursor is over instead made dropping onto a neighbour's near half a + // silent no-op ("after the layer I am already after"), and that is exactly where a drag to the top ends. + const auto drop_target = [&](size_t index, int slot, const ImVec2 &mn, const ImVec2 &mx) { + if (!ImGui::BeginDragDropTarget()) + return; + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("TD_LAYER", ImGuiDragDropFlags_AcceptBeforeDelivery | + ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { + const int from = *static_cast(payload->Data); + const bool after = dragged_index >= 0 && dragged_index < int(index); + if (from != slot) { + const float y = after ? mx.y + 2.f : mn.y - 2.f; + dl->AddLine(ImVec2(mn.x, y), ImVec2(mx.x, y), ImGui::GetColorU32(orca), 2.f); } + if (payload->IsDelivery()) { + move_slot = from; + move_to = int(index) + (after ? 1 : 0); + } + } + ImGui::EndDragDropTarget(); + }; - // Clicking the header - or anywhere in the layer's block, see the group below - makes - // it the active layer (#11, #12). A radio button was doing this before, which worked but - // gave no sense of which block of controls belonged to which layer. - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.f, 0.68f, 0.58f, 0.55f)); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.f, 0.68f, 0.58f, 0.35f)); - const std::string header = layer->name.empty() ? Slic3r::format(_u8L("Layer %1%"), li + 1) : layer->name; - if (ImGui::Selectable(header.c_str(), is_active, 0, ImVec2(ImGui::GetContentRegionAvail().x - m_imgui->scaled(3.f), 0.f))) - set_active_layer(layer->slot); - ImGui::PopStyleColor(2); - ImGui::SameLine(); - if (icon_button(600 + layer->slot, "texture_displacement_cross.svg", m_imgui->scaled(1.3f), - m_desc.at("remove_layer"), _u8L("Remove this layer"))) - slot_to_remove = layer->slot; + for (size_t li = 0; li < ordered.size(); ++li) { + TextureDisplacementLayer &layer = *ordered[li]; + const bool is_active = layer.slot == m_active_layer_slot; + const bool painted = slot_painted(layer.slot); + ImGui::PushID(layer.slot); - // Everything below is this layer's own; the group lets a click anywhere inside it select - // the layer, which is what makes the block feel like one object rather than loose widgets. + if (is_active) { + // The card's height is not known until it is laid out, so its content goes into a foreground + // channel and the backing rectangle into a background one, merged at the end - the standard + // ImGui "rect behind a group" trick. + const ImVec2 card_min = ImGui::GetCursorScreenPos(); + dl->ChannelsSplit(2); + dl->ChannelsSetCurrent(1); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + card_pad); + ImGui::Indent(card_pad); ImGui::BeginGroup(); - render_texture_picker(*layer); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - // Depth and tile size are logarithmic: see ImGuiLogSlider. - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Depth (mm)"), &layer->depth_mm, 0.01f, 10.f, "%.3f", ImGuiLogSlider); - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Tile size (mm)"), &layer->tiling_scale, 0.2f, 200.f, "%.2f", ImGuiLogSlider); - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Rotation"), &layer->rotation_deg, 0.f, 360.f, "%.0f"); - // Midlevel (#19): the height that means "don't move". At 0 the surface only ever bulges - // outwards; at 0.5 mid-grey is neutral and darker texels cut inwards. - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Midlevel"), &layer->midlevel, 0.f, 10.f, "%.2f"); - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("The grey level that stays put. At 0 the texture can only push the surface " - "outwards. Raise it and anything darker cuts inwards instead, so one height " - "map both embosses and engraves -0.5 makes mid-grey neutral.\n\n" - "Cutting inwards can fold the surface through itself where normals converge: " - "inside a sharp concave corner, or through a thin wall. Keep Depth small " - "relative to the feature you are cutting into."), - m_imgui->scaled(20.f)); - if (layer->midlevel > 0.f && layer->depth_mm > 1.f) - m_imgui->warning_text(_L("Deep inward displacement may self-intersect.")); - - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Smoothing"), &layer->smoothing, 0.f, 1.f, "%.2f"); - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Blurs the height texture before it displaces the surface, rounding hard edges " - "and removing speckle without needing a softer source image. Affects the preview " - "and the bake alike."), - m_imgui->scaled(20.f)); - - // Edge smoothing: fade the displacement to flat toward the boundary of the painted area. - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Edge smoothing").c_str(), &layer->edge_smoothing); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Fade the relief to flat toward the edge of the painted area, so it blends into " - "the surrounding surface. A small amount only softens a thin band at the very " - "edge; the maximum flattens the whole painted face."), - m_imgui->scaled(20.f)); - if (layer->edge_smoothing) { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Edge amount"), &layer->edge_smoothing_amount, - 0.02f, 1.f, "%.2f"); - ImGui::PopItemWidth(); - } - - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer->invert); - - // Colour. Only offered for a texture that actually has some - the shipped library is - // grayscale, and a checkbox that silently does nothing on nine textures out of ten is - // worse than no checkbox. Disabled rather than hidden so it is clear the feature exists - // and what it wants. + // Header: grip, thumbnail and name (both open the library), paint state, order, remove. { - const bool has_color = decode_height_texture(*layer).has_color(); - m_imgui->disabled_begin(!has_color); - bool color_enabled = layer->color_enabled && has_color; - if (ImGui::Checkbox(_u8L("Use image colours").c_str(), &color_enabled)) { - layer->color_enabled = color_enabled; - m_preview_params_dirty = true; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(gap_s, style.ItemSpacing.y)); + grip(layer, frame_h); + ImGui::SameLine(); + if (GLTexture *thumb = get_layer_thumbnail(layer)) { + if (ImGui::ImageButton((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(frame_h, frame_h), ImVec2(0.f, 0.f), + ImVec2(1.f, 1.f), 0)) + open_picker(layer.slot); + } else if (ImGui::Button("?##thumb", ImVec2(frame_h, frame_h))) { + open_picker(layer.slot); + } + hover_tip(_u8L("Change texture")); + { + // The teal outline on the thumbnail is what marks this card as the layer being painted. + const ImVec2 a = ImGui::GetItemRectMin(), b = ImGui::GetItemRectMax(); + dl->AddRect(ImVec2(a.x - 2.f, a.y - 2.f), ImVec2(b.x + 2.f, b.y + 2.f), ImGui::GetColorU32(orca), style.FrameRounding); + } + ImGui::SameLine(); + + const std::string not_painted = _u8L("not painted"); + const float right_w = (painted ? 0.f : ImGui::CalcTextSize(not_painted.c_str()).x + gap_s) + 3.f * frame_h + 2.f * gap_s; + const float name_w = std::max(frame_h, ImGui::GetWindowContentRegionMax().x - card_pad - ImGui::GetCursorPosX() - gap_s - right_w); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(orca.x, orca.y, orca.z, 0.2f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(orca.x, orca.y, orca.z, 0.35f)); + ImGui::AlignTextToFramePadding(); + const std::string shown = ellipsize(layer_name(layer), name_w - 2.f * style.FramePadding.x); + if (ImGui::Selectable((shown + "##name").c_str(), false, 0, ImVec2(name_w, frame_h))) + open_picker(layer.slot); + ImGui::PopStyleColor(2); + hover_tip(_u8L("Change texture")); + if (!painted) { + ImGui::SameLine(); + ImGui::TextDisabled("%s", not_painted.c_str()); + } + ImGui::SameLine(); + m_imgui->disabled_begin(busy || li == 0); + if (icon_button(611, "texture_displacement_move_up.svg", frame_h, _L("Move up"), _L("Move up - applied earlier"))) { + move_slot = layer.slot; + move_to = int(li) - 1; } m_imgui->disabled_end(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(has_color ? - _u8L("Colours the painted area from the texture's own colours, as well as " - "displacing it. Each colour is matched to the closest printable " - "colour, and the result is written as multi-material paint - so the " - "area prints in those filaments. Everything you did not paint keeps " - "the object's own filament.") : - _u8L("This texture is a grayscale height map, so it has no colours to " - "apply. Import a colour image to use this."), - m_imgui->scaled(20.f)); + ImGui::SameLine(); + m_imgui->disabled_begin(busy || li + 1 >= ordered.size()); + if (icon_button(612, "texture_displacement_move_down.svg", frame_h, _L("Move down"), _L("Move down - applied later"))) { + move_slot = layer.slot; + move_to = int(li) + 2; + } + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(busy); + if (icon_button(600 + layer.slot, "texture_displacement_cross.svg", frame_h, m_desc.at("remove_layer"), + _L("Remove this layer"))) + slot_to_remove = layer.slot; + m_imgui->disabled_end(); + ImGui::PopStyleVar(); + } - // The rest of colour belongs to the whole stack, not to this layer, so it only appears - // once - under whichever layer turned colour on. - if (color_enabled && mv != nullptr) { - TextureDisplacementOptions &opts = mv->texture_displacement_options; + // The three controls nearly every layer needs; everything else waits behind "More settings". + // Depth and tile size are logarithmic, so both ends of their wide ranges stay usable. + m_preview_params_dirty |= float_row("##depth", _L("Depth"), &layer.depth_mm, 0.01f, 10.f, "%.3f mm", true, card_pad); + hover_tip(_u8L("How far the brightest part of the texture moves the surface.")); + m_preview_params_dirty |= float_row("##tile_size", _L("Tile size"), &layer.tiling_scale, 0.2f, 200.f, "%.2f mm", true, card_pad); + hover_tip(_u8L("The size of one repeat of the texture on the model.")); + m_preview_params_dirty |= float_row("##rotation", _L("Rotation"), &layer.rotation_deg, 0.f, 360.f, "%.0f°", false, card_pad); - if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled)) + if (m_layer_expanded[size_t(layer.slot)]) { + { + // A dashed rule between the basic and the advanced settings. + const ImVec2 p = ImGui::GetCursorScreenPos(); + const float x1 = content_rx - card_pad; + const float dash = std::round(m_imgui->scaled(0.25f)); + for (float xx = p.x; xx < x1; xx += 2.f * dash) + dl->AddLine(ImVec2(xx, p.y), ImVec2(std::min(xx + dash, x1), p.y), col_line); + ImGui::Dummy(ImVec2(1.f, 1.f)); + } + + // Midlevel: the height that means "don't move". At 0 the surface only ever bulges outwards; + // at 0.5 mid-grey is neutral and darker texels cut inwards. + m_preview_params_dirty |= float_row("##midlevel", _L("Midlevel"), &layer.midlevel, 0.f, 10.f, "%.2f", false, card_pad); + hover_tip(_u8L("The grey level that stays put. At 0 the texture can only push the surface " + "outwards. Raise it and anything darker cuts inwards instead, so one height " + "map both embosses and engraves -0.5 makes mid-grey neutral.\n\n" + "Cutting inwards can fold the surface through itself where normals converge: " + "inside a sharp concave corner, or through a thin wall. Keep Depth small " + "relative to the feature you are cutting into.")); + if (layer.midlevel > 0.f && layer.depth_mm > 1.f) + m_imgui->warning_text(_L("Deep inward displacement may self-intersect.")); + + m_preview_params_dirty |= float_row("##smoothing", _L("Smoothing"), &layer.smoothing, 0.f, 1.f, "%.2f", false, card_pad); + hover_tip(_u8L("Blurs the height texture before it displaces the surface, rounding hard edges " + "and removing speckle without needing a softer source image. Affects the preview " + "and the bake alike.")); + + // Edge fade: the relief flattens toward the boundary of the painted area. + m_preview_params_dirty |= ImGui::Checkbox((_u8L("Edge fade") + "##edge_smoothing").c_str(), &layer.edge_smoothing); + hover_tip(_u8L("Fade the relief to flat toward the edge of the painted area, so it blends into " + "the surrounding surface. A small amount only softens a thin band at the very " + "edge; the maximum flattens the whole painted face.")); + ImGui::SameLine(); + m_imgui->disabled_begin(!layer.edge_smoothing); + ImGui::SetNextItemWidth(-card_pad); + m_preview_params_dirty |= ImGui::SliderFloat("##edge_amount", &layer.edge_smoothing_amount, 0.02f, 1.f, "%.2f", + ImGuiSliderFlags_AlwaysClamp); + m_imgui->disabled_end(); + hover_tip(_u8L("How far the fade reaches into the painted area.")); + + // Invert and Colours share a row. + { + const float x0 = ImGui::GetCursorPosX(); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer.invert); + hover_tip(_u8L("Swap high and low: what stood out now cuts in.")); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + 0.5f * (ImGui::GetWindowContentRegionMax().x - card_pad - x0))); + + // Only offered for a texture that actually has colour - the shipped library is grayscale, + // and a checkbox that silently does nothing on nine textures out of ten is worse than no + // checkbox. Disabled rather than hidden so it is clear the feature exists and what it wants. + const bool has_color = decode_height_texture(layer).has_color(); + bool color_enabled = layer.color_enabled && has_color; + m_imgui->disabled_begin(!has_color); + if (ImGui::Checkbox(_u8L("Colours").c_str(), &color_enabled)) { + layer.color_enabled = color_enabled; m_preview_params_dirty = true; - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Interleave pairs of filaments to reach colours between them, so a few " - "filaments cover far more than a few colours. Off means every triangle " - "prints in one of the filaments exactly."), - m_imgui->scaled(20.f)); + } + m_imgui->disabled_end(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + m_imgui->tooltip(has_color ? + _u8L("Colours the painted area from the texture's own colours, as well as " + "displacing it. Each colour is matched to the closest printable " + "colour, and the result is written as multi-material paint - so the " + "area prints in those filaments. Everything you did not paint keeps " + "the object's own filament.") : + _u8L("This texture is a grayscale height map, so it has no colours to " + "apply. Import a colour image to use this."), + wrap_w); - if (opts.color_mix_enabled) { - m_imgui->text(_u8L("Mix by")); - ImGui::SameLine(); - const std::string mix_z = _u8L("Layers"); - const std::string mix_xy = _u8L("Surface"); - const char *mix_items[] = { mix_z.c_str(), mix_xy.c_str() }; - int mix_mode = int(opts.color_mix_mode); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (scoped_combo("##color_mix_mode", &mix_mode, mix_items, IM_ARRAYSIZE(mix_items))) { - opts.color_mix_mode = ColorMixMode(mix_mode); + // The rest of colour belongs to the whole stack, not to this layer, so it only appears once - + // under whichever layer turned colour on. + if (color_enabled) { + TextureDisplacementOptions &opts = mv->texture_displacement_options; + if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled)) m_preview_params_dirty = true; - } - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Layers: the two filaments alternate between print layers, which " - "blends smoothly on upright surfaces but disappears on flat-facing " - "ones, where a whole layer is a single band.\n" - "Surface: a fine checkerboard across the surface, which works at " - "any angle but can read as texture rather than as a blend."), - m_imgui->scaled(20.f)); - - const int count = int(cached_palette().size()); - ImGui::TextDisabled("%s", from_u8(Slic3r::format( - _u8L("%1% printable colours from %2% filaments"), count, - int(m_palette_filaments.size()))).ToUTF8().data()); - } - - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (ImGui::SliderInt(_u8L("Denoise").c_str(), &opts.color_despeckle, 0, 6)) - m_preview_params_dirty = true; - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Removes stray single triangles of the wrong colour, which is what " - "detail in the image finer than the mesh leaves behind. Each step " - "replaces a triangle's colour with the one most of its neighbours " - "have, so features wider than a triangle are kept. Raise it if the " - "result looks speckled; lower it if fine detail is being eaten."), - m_imgui->scaled(20.f)); - } - } - - // The lowest painted layer has nothing underneath it to combine with - it *is* the - // base - so a blend mode would be meaningless (and Multiply/Divide against an implicit - // zero would annihilate it). build_texture_displacement() forces the first layer to - // reach a given vertex to behave additively regardless; say so rather than offering a - // control that silently does nothing. - if (li == 0) { - ImGui::TextDisabled("%s", _u8L("Base layer").c_str()); - } else { - m_imgui->text(_u8L("Blend")); - ImGui::SameLine(); - const std::string blend_add = _u8L("Add"); - const std::string blend_subtract = _u8L("Subtract"); - const std::string blend_multiply = _u8L("Multiply"); - const std::string blend_divide = _u8L("Divide"); - const char *blend_items[] = { blend_add.c_str(), blend_subtract.c_str(), blend_multiply.c_str(), - blend_divide.c_str() }; - int blend_mode = static_cast(layer->blend_mode); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (scoped_combo("##blend_mode", &blend_mode, blend_items, IM_ARRAYSIZE(blend_items))) { - layer->blend_mode = static_cast(blend_mode); - m_preview_params_dirty = true; - } - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How this layer combines with the layers below it, wherever they overlap. " - "Add and Subtract pile relief on or carve it away. Multiply and Divide " - "scale the relief underneath, which makes this layer act as a mask over " - "it - for those two, Depth is a gain, and a depth of 1 mm on a white " - "part of the texture leaves the layers below unchanged."), - m_imgui->scaled(20.f)); - } - - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Tile").c_str(), &layer->tile_enabled); - if (layer->tile_enabled) { - ImGui::SameLine(); - const std::string tile_method_repeat = _u8L("Repeat"); - const std::string tile_method_mirrored = _u8L("Mirrored repeat"); - const char *tile_method_items[] = { tile_method_repeat.c_str(), tile_method_mirrored.c_str() }; - int tile_method = static_cast(layer->tile_method); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (scoped_combo("##tile_method", &tile_method, tile_method_items, IM_ARRAYSIZE(tile_method_items))) { - layer->tile_method = static_cast(tile_method); - m_preview_params_dirty = true; - } - ImGui::PopItemWidth(); - } - - { - m_imgui->text(_u8L("Projection")); - ImGui::SameLine(); - const std::string projection_triplanar = _u8L("Triplanar (blended)"); - const std::string projection_cylindrical = _u8L("Cylindrical"); - const std::string projection_spherical = _u8L("Spherical"); - const std::string projection_lscm = _u8L("Unwrap (LSCM)"); - const std::string projection_view = _u8L("From view"); - const char *projection_items[] = { projection_triplanar.c_str(), projection_cylindrical.c_str(), - projection_spherical.c_str(), projection_lscm.c_str(), - projection_view.c_str() }; - int projection_method = static_cast(layer->projection_method); - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (scoped_combo("##projection_method", &projection_method, projection_items, IM_ARRAYSIZE(projection_items))) { - const auto new_method = static_cast(projection_method); - // Capture the current view the moment "From view" is chosen, so it does something - // sensible immediately rather than projecting from a stale/default direction. - if (new_method == TextureProjectionMethod::ViewProjected && - layer->projection_method != TextureProjectionMethod::ViewProjected) - capture_view_projection(*layer); - layer->projection_method = new_method; - m_preview_params_dirty = true; - } - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(layer->projection_method == TextureProjectionMethod::LSCM ? - _u8L("Flattens the painted area and maps the texture onto it with as little " - "stretching as possible. The area is cut into pieces at its sharp edges " - "first (see Seam angle), so each piece can lie flat on its own.") : - _u8L("Triplanar projects the texture from all three axes at once and blends " - "between them, so a patch wrapping around a sharp edge has no seam. " - "Cylindrical and Spherical wrap the texture around the painted area's " - "own centre, for round shapes."), - m_imgui->scaled(20.f)); - - if (layer->projection_method == TextureProjectionMethod::LSCM) { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_preview_params_dirty |= m_imgui->slider_float(_u8L("Seam angle"), &layer->lscm_seam_angle_deg, - 5.f, 90.f, "%.0f"); - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Edges sharper than this are cut, and the pieces either side of them are " - "flattened separately. Lower it to cut more: each piece then lies flat " - "with less stretching, at the cost of the texture not running continuously " - "across the cut. Raise it to keep more of the area in one piece. Corners " - "of a box are 90 degrees, so the default cuts them apart; a smoothly " - "rounded surface stays whole."), - m_imgui->scaled(20.f)); - - if (ImGui::Checkbox(_u8L("Connect islands").c_str(), &layer->auto_connect_islands)) { - // Apply (or, when turned off, just stop re-applying) right away rather than - // waiting for the next re-unwrap. - if (layer->auto_connect_islands && !m_uv_editor_unwrap.empty()) { - std::vector net = compute_connected_net(m_uv_editor_unwrap); - if (net.size() == size_t(m_uv_editor_unwrap.chart_count)) { - if (layer->islands.size() < net.size()) - layer->islands.resize(net.size()); - for (size_t i = 0; i < net.size(); ++i) - layer->islands[i] = net[i]; + hover_tip(_u8L("Interleave pairs of filaments to reach colours between them, so a few " + "filaments cover far more than a few colours. Off means every triangle " + "prints in one of the filaments exactly.")); + if (opts.color_mix_enabled) { + slider_label(_L("Mix by")); + const std::string mix_z = _u8L("Layers"); + const std::string mix_xy = _u8L("Surface"); + const char *mix_items[] = { mix_z.c_str(), mix_xy.c_str() }; + int mix_mode = int(opts.color_mix_mode); + ImGui::SetNextItemWidth(-card_pad); + if (scoped_combo("##color_mix_mode", &mix_mode, mix_items, IM_ARRAYSIZE(mix_items))) { + opts.color_mix_mode = ColorMixMode(mix_mode); + m_preview_params_dirty = true; } + hover_tip(_u8L("Layers: the two filaments alternate between print layers, which " + "blends smoothly on upright surfaces but disappears on flat-facing " + "ones, where a whole layer is a single band.\n" + "Surface: a fine checkerboard across the surface, which works at " + "any angle but can read as texture rather than as a blend.")); + ImGui::TextDisabled("%s", Slic3r::format(_u8L("%1% printable colours from %2% filaments"), + int(cached_palette().size()), int(m_palette_filaments.size())).c_str()); } - m_preview_params_dirty = true; - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Lay the unwrap out as a connected net: pieces that share an edge are " - "unfolded next to each other (a cube becomes a joined net rather than six " - "loose squares). They stay separate islands, so you can still move any of " - "them by hand afterwards."), - m_imgui->scaled(20.f)); - - if (is_active) { - // Explicit unwrap (#: "Add unwrap button so it does not recompute on every - // change"). The LSCM solve runs only when this is pressed - painting, the seam - // angle slider and seam marking no longer trigger it - and the pane opens right - // afterwards. Re-press it to fold in any edits made since. - if (m_imgui->button(_u8L("Unwrap"))) { - m_uv_unwrap_pending = true; - m_uv_apply_connected_net = true; // a genuine re-unwrap may relayout the islands - m_show_uv_editor = true; - update_uv_editor(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Flatten the painted area into UV islands and open the UV editor. The " - "unwrap is computed only when you press this, not on every edit - so " - "paint, change the seam angle or mark seams first, then press Unwrap to " - "see the result. Islands can then be moved, rotated and scaled."), - m_imgui->scaled(20.f)); - - // Select mode for the UV pane: whole islands, single vertices, or single edges. - // Vertex/Edge are free-form UV editing and feed the per-vertex overrides that the - // bake honours; Island is the move/rotate/scale-with-grouping behaviour. - if (!m_uv_editor_unwrap.empty()) { - m_imgui->text(_u8L("Select:")); - const auto set_uv_mode = [&](int mode) { - if (mode != m_uv_select_mode) { - m_uv_select_mode = mode; - if (UVEditorCanvas *c = wxGetApp().plater()->get_uv_editor_canvas()) - c->set_select_mode(static_cast(mode)); - } - }; - ImGui::SameLine(); - if (icon_toggle(810, "texture_displacement_uv_select_island.svg", m_uv_select_mode == 0, - _L("Island"), _L("Island - move, rotate and scale whole islands"))) - set_uv_mode(0); - ImGui::SameLine(); - if (icon_toggle(811, "texture_displacement_uv_select_vertex.svg", m_uv_select_mode == 1, - _L("Vertex"), _L("Vertex - drag vertices to reshape; Shift/Ctrl to multi-select"))) - set_uv_mode(1); - ImGui::SameLine(); - if (icon_toggle(812, "texture_displacement_uv_select_edge.svg", m_uv_select_mode == 2, - _L("Edge"), _L("Edge - drag edges to reshape; Shift/Ctrl to multi-select"))) - set_uv_mode(2); - if (!layer->lscm_uv_overrides.empty()) { - ImGui::SameLine(); - if (m_imgui->button(_u8L("Clear UV edits"))) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture UV edits"), - UndoRedo::SnapshotType::GizmoAction); - layer->lscm_uv_overrides.clear(); - m_uv_unwrap_pending = true; // re-solve so the pane drops the edited coords - update_uv_editor(); - rebuild_preview(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Discard all manual vertex/edge moves and return the unwrap to its " - "automatic shape."), - m_imgui->scaled(20.f)); - } - } - - // Manual seam marking (#9): a click mode that toggles mesh edges as seams. - bool seam_mode = m_seam_edit_mode; - if (ImGui::Checkbox(_u8L("Mark seams").c_str(), &seam_mode)) { - m_seam_edit_mode = seam_mode; - if (seam_mode) - m_adjust_texture_mode = false; // the two click modes are mutually exclusive - else { - m_seam_hover_edge = { -1, -1 }; // drop the hover highlight when leaving the mode - m_seam_hover_vertex = -1; - m_seam_hover_glmodel.reset(); - m_seam_path_anchor = -1; - m_seam_anchor_glmodel.reset(); - } - m_parent.set_as_dirty(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Click edges on the model to cut the unwrap along them, like marking a " - "seam in Blender. The edge under the cursor is highlighted yellow; click " - "to mark it red. Click a marked (red) edge again to unmark it. Hold Ctrl " - "and drag to rotate the view. Painting is paused while this is on."), - m_imgui->scaled(20.f)); - if (m_seam_edit_mode) { - // Shortest-path mode, for dense meshes: click two points, seam the whole path. - ImGui::SameLine(); - bool path_mode = m_seam_path_mode; - if (ImGui::Checkbox(_u8L("Path").c_str(), &path_mode)) { - m_seam_path_mode = path_mode; - m_seam_path_anchor = -1; - m_seam_hover_edge = { -1, -1 }; // hover target type changes with the mode - m_seam_hover_vertex = -1; - m_seam_hover_glmodel.reset(); - m_seam_anchor_glmodel.reset(); - m_parent.set_as_dirty(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Instead of clicking every edge, click a start point and then an end " - "point: the whole shortest path between them is seamed at once (green " - "marks the start). Each click extends the seam from the last point. " - "Best for dense meshes."), - m_imgui->scaled(20.f)); - } - if (!layer->lscm_seam_edges.empty()) { - ImGui::SameLine(); - if (m_imgui->button(_u8L("Clear seams"))) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture seams"), - UndoRedo::SnapshotType::GizmoAction); - layer->lscm_seam_edges.clear(); - rebuild_preview(); - } - } - - // What the UV editor is actually showing. Cheap, and the only way to tell an - // unwrap that produced nothing apart from one merely framed off-screen. The - // island tools (snap, average scale, cut) and the gesture hints now live in the - // UV pane itself - its toolbar and status line - rather than here (#18). - if (m_uv_editor_unwrap.empty()) - m_imgui->text(_u8L("Press Unwrap to flatten the painted area.")); - else - m_imgui->text(Slic3r::format(_u8L("Unwrap: %1% islands, %2% faces, %3% verts."), - m_uv_editor_unwrap.chart_count, m_uv_editor_unwrap.indices.size(), - m_uv_editor_unwrap.uvs.size())); + if (int_row("##color_despeckle", _L("Denoise"), &opts.color_despeckle, 0, 6, "%d", card_pad)) + m_preview_params_dirty = true; + hover_tip(_u8L("Removes stray single triangles of the wrong colour, which is what " + "detail in the image finer than the mesh leaves behind. Each step " + "replaces a triangle's colour with the one most of its neighbours " + "have, so features wider than a triangle are kept. Raise it if the " + "result looks speckled; lower it if fine detail is being eaten.")); } } - if (layer->projection_method == TextureProjectionMethod::ViewProjected) { - // Re-capture the projector from wherever the camera is now (#6): orbit the model, - // press this, and the texture is re-laid from the new angle. + // Mapping, as five icons in TextureProjectionMethod's own order. + { + const float x0 = ImGui::GetCursorPosX(); + ImGui::AlignTextToFramePadding(); + ImGui::TextDisabled("%s", _u8L("Mapping").c_str()); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); + struct MappingIcon + { + const char *file; + wxString label; + wxString tip; + }; + const MappingIcon mappings[] = { + { "menu_obj_cube.svg", _L("Triplanar (blended)"), + _L("Triplanar - projects the texture from all three axes at once and blends between them, so a " + "patch wrapping around a sharp edge has no seam.") }, + { "menu_obj_cylinder.svg", _L("Cylindrical"), + _L("Cylindrical - wraps the texture around the painted area's own centre, for round shapes.") }, + { "menu_obj_sphere.svg", _L("Spherical"), + _L("Spherical - wraps the texture around the painted area's own centre in both directions.") }, + { "texture_displacement_map_unwrap.svg", _L("Unwrap (LSCM)"), + _L("Unwrap - flattens the painted area and maps the texture onto it with as little stretching as " + "possible. The area is cut into pieces at its sharp edges first (see Seam angle), so each " + "piece can lie flat on its own.") }, + { "texture_displacement_map_view.svg", _L("From view"), + _L("From view - projects straight onto the painted area from where you are looking, like a slide " + "projector.") }, + }; + for (int mi = 0; mi < int(IM_ARRAYSIZE(mappings)); ++mi) { + if (mi > 0) + ImGui::SameLine(0.f, gap_s); + const auto method = static_cast(mi); + if (icon_toggle(830 + mi, mappings[mi].file, layer.projection_method == method, icon_sm, mappings[mi].label, + mappings[mi].tip) && + layer.projection_method != method) { + // Capture the current view the moment "From view" is chosen, so it does something + // sensible immediately rather than projecting from a stale/default direction. + if (method == TextureProjectionMethod::ViewProjected) + capture_view_projection(layer); + layer.projection_method = method; + m_preview_params_dirty = true; + // Unwrap and its tools live in the UV editor pane, so picking it opens the pane + // (via the preview rebuild this triggers). + if (method == TextureProjectionMethod::LSCM) + m_show_uv_editor = true; + } + } + } + + if (layer.projection_method == TextureProjectionMethod::LSCM) { + // Unwrap and everything that edits it live in the UV editor pane, next to the islands they change. + // Picking this mapping opens the pane; this brings it back after it has been closed. + if (m_imgui->button(_u8L("Open UV editor"))) { + m_show_uv_editor = true; + update_uv_editor(); + } + hover_tip(_u8L("Show the UV editor pane, where the painted area is unwrapped, seams are marked and " + "islands are laid out.")); + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); + if (m_uv_editor_unwrap.empty()) + ImGui::TextDisabled("%s", _u8L("Not unwrapped yet").c_str()); + else + ImGui::TextDisabled("%s", Slic3r::format(_u8L("%1% islands"), m_uv_editor_unwrap.chart_count).c_str()); + } + + if (layer.projection_method == TextureProjectionMethod::ViewProjected) { + // Re-capture the projector from wherever the camera is now: orbit the model, press this, and + // the texture is re-laid from the new angle. if (m_imgui->button(_u8L("Capture current view"))) { - capture_view_projection(*layer); - // Selecting the visible faces *after* capturing means the projector axes are - // already the ones the selection was made against - the two describe the - // same viewpoint, which is the whole point of the option. + capture_view_projection(layer); + // Selecting the visible faces *after* capturing means the projector axes are already the + // ones the selection was made against - the two describe the same viewpoint. if (m_project_only_visible && select_visible_faces() == 0) show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the " "part you want to project onto.")); m_preview_params_dirty = true; update_projector(); } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Projects the texture straight onto the painted area from the direction " - "you are currently looking, like a slide projector. Faces angled away from " - "that direction will stretch - turn the model to where you want the " - "texture crisp, then capture."), - m_imgui->scaled(20.f)); + hover_tip(_u8L("Projects the texture straight onto the painted area from the direction " + "you are currently looking, like a slide projector. Faces angled away from " + "that direction will stretch - turn the model to where you want the " + "texture crisp, then capture.")); if (ImGui::Checkbox(_u8L("Project only on visible").c_str(), &m_project_only_visible)) { if (m_project_only_visible && select_visible_faces() == 0) @@ -4932,544 +5774,756 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_preview_params_dirty = true; update_projector(); } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Paints exactly the faces you can currently see - facing the camera and " - "not hidden behind anything else - and projects onto those. This replaces " - "the layer's painted area, and is re-applied each time you capture the " - "view."), - m_imgui->scaled(20.f)); + hover_tip(_u8L("Paints exactly the faces you can currently see - facing the camera and " + "not hidden behind anything else - and projects onto those. This replaces " + "the layer's painted area, and is re-applied each time you capture the view.")); - ImGui::Separator(); bool projector_open = m_projector_frame != nullptr && m_projector_frame->IsShown(); if (ImGui::Checkbox(_u8L("Projection frame").c_str(), &projector_open)) show_projector(projector_open); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Opens a see-through window you drag over the model. Whatever shows " - "through it is what the texture is projected onto, and the window's " - "border becomes the edge of the projection. Move and resize it to frame " - "the area you want, then press Apply."), - m_imgui->scaled(20.f)); + hover_tip(_u8L("Opens a see-through window you drag over the model. Whatever shows " + "through it is what the texture is projected onto, and the window's " + "border becomes the edge of the projection. Move and resize it to frame " + "the area you want, then press Apply.")); if (projector_open) { - ImGui::PushItemWidth(m_imgui->scaled(6.f)); - if (ImGui::SliderInt(_u8L("Opacity").c_str(), &m_projector_opacity, 20, 255)) + if (int_row("##projector_opacity", _L("Opacity"), &m_projector_opacity, 20, 255, "%d", card_pad)) m_projector_frame->set_opacity(m_projector_opacity); - ImGui::PopItemWidth(); if (m_imgui->button(_u8L("Apply projection frame"))) { - const int painted = apply_projection_frame(); - if (painted == 0) + const int painted_count = apply_projection_frame(); + if (painted_count == 0) show_error(nullptr, _u8L("Nothing of the model is inside the frame - move it over the " "part you want to project onto.")); - else if (painted < 0) - show_error(nullptr, _u8L("The frame could not be applied. Make sure it overlaps the " - "3D view.")); + else if (painted_count < 0) + show_error(nullptr, _u8L("The frame could not be applied. Make sure it overlaps the 3D view.")); } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Projects the texture through the frame from the direction you are " - "looking now, and paints the visible faces inside it. This replaces " - "the layer's painted area. The result is fixed to the model, so you " - "can orbit freely afterwards."), - m_imgui->scaled(20.f)); + hover_tip(_u8L("Projects the texture through the frame from the direction you are " + "looking now, and paints the visible faces inside it. This replaces " + "the layer's painted area. The result is fixed to the model, so you " + "can orbit freely afterwards.")); } - if (layer->view_project_projective) { - m_imgui->text(_u8L("Placed by projection frame.")); + if (layer.view_project_projective) { + ImGui::AlignTextToFramePadding(); + ImGui::TextDisabled("%s", _u8L("Placed by projection frame.").c_str()); ImGui::SameLine(); if (m_imgui->button(_u8L("Clear"))) { - // Back to the plain axis projection, where tiling/rotation/offset mean - // something again - the matrix path deliberately ignores them. - layer->view_project_projective = false; - layer->tile_enabled = true; - m_preview_params_dirty = true; + // Back to the plain axis projection, where tiling/rotation/offset mean something again - + // the matrix path deliberately ignores them. + layer.view_project_projective = false; + layer.tile_enabled = true; + m_preview_params_dirty = true; + } + } + } + + // Tile, and how it repeats. + { + const float x0 = ImGui::GetCursorPosX(); + m_preview_params_dirty |= ImGui::Checkbox((_u8L("Tile") + "##tile_enabled").c_str(), &layer.tile_enabled); + hover_tip(_u8L("Repeat the texture across the painted area. Off places it once, like a decal.")); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); + const wxString tile_na = layer.tile_enabled ? wxString() : _L("Turn Tile on to choose how the texture repeats."); + if (icon_toggle(840, "texture_displacement_tile_repeat.svg", layer.tile_method == static_cast(0), + icon_sm, _L("Repeat"), _L("Repeat"), tile_na)) { + layer.tile_method = static_cast(0); + m_preview_params_dirty = true; + } + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(841, "menu_mirror_x.svg", layer.tile_method == static_cast(1), icon_sm, + _L("Mirrored repeat"), _L("Mirrored repeat - every other tile is flipped, so edges meet seamlessly"), + tile_na)) { + layer.tile_method = static_cast(1); + m_preview_params_dirty = true; + } + } + + // Blend, and placement on the model. + { + const float x0 = ImGui::GetCursorPosX(); + ImGui::AlignTextToFramePadding(); + ImGui::TextDisabled("%s", _u8L("Blend").c_str()); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); + const std::string blend_add = _u8L("Add"); + const std::string blend_subtract = _u8L("Subtract"); + const std::string blend_multiply = _u8L("Multiply"); + const std::string blend_divide = _u8L("Divide"); + const char *blend_items[] = { blend_add.c_str(), blend_subtract.c_str(), blend_multiply.c_str(), blend_divide.c_str() }; + // The first layer has nothing before it to combine with - build_texture_displacement() makes it + // add regardless - so its combo shows Add and cannot be changed. + const bool base_layer = li == 0; + int blend_mode = base_layer ? 0 : static_cast(layer.blend_mode); + ImGui::SetNextItemWidth(-(card_pad + gap_s + icon_sm)); + m_imgui->disabled_begin(base_layer); + if (scoped_combo("##blend_mode", &blend_mode, blend_items, IM_ARRAYSIZE(blend_items)) && !base_layer) { + layer.blend_mode = static_cast(blend_mode); + m_preview_params_dirty = true; + } + m_imgui->disabled_end(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + m_imgui->tooltip(base_layer ? + _u8L("The base layer has nothing beneath it to combine with, so it always adds.") : + _u8L("How this layer combines with the layers before it, wherever they overlap. " + "Add and Subtract pile relief on or carve it away. Multiply and Divide " + "scale the relief underneath, which makes this layer act as a mask over " + "it - for those two, Depth is a gain, and a depth of 1 mm on a white " + "part of the texture leaves the layers below unchanged."), + wrap_w); + + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(842, "texture_displacement_adjust.svg", m_adjust_texture_mode, icon_sm, _L("Adjust placement"), + _L("Adjust placement - drag the handle on the model to move the texture"))) { + if (!m_adjust_texture_mode) { + update_model_object(); // flush any pending strokes before anchoring + if (update_adjust_anchor()) + m_adjust_texture_mode = true; + else + show_error(nullptr, _u8L("Paint something with this layer first.")); + } else { + m_adjust_texture_mode = false; + m_adjust_drag_handle = AdjustHandle::None; } } } } - if (is_active) { - bool adjust_on = m_adjust_texture_mode; - if (ImGui::Checkbox(_u8L("Adjust placement").c_str(), &adjust_on)) { - if (adjust_on) { - update_model_object(); // flush any pending strokes before anchoring - if (update_adjust_anchor()) - m_adjust_texture_mode = true; - else - show_error(nullptr, _u8L("Paint something with this layer first.")); - } else { + // "More settings" / "Fewer settings", drawn as a link. + { + const bool expanded = m_layer_expanded[size_t(layer.slot)]; + const std::string text = expanded ? _u8L("Fewer settings") : _u8L("More settings"); + const float arrow = std::round(ImGui::GetFontSize() * 0.55f); + const ImVec2 ts = ImGui::CalcTextSize(text.c_str()); + const ImVec2 p = ImGui::GetCursorScreenPos(); + if (ImGui::InvisibleButton("##more", ImVec2(arrow + gap_s + ts.x, ts.y))) { + m_layer_expanded[size_t(layer.slot)] = !expanded; + // Placement is one of the settings being hidden, so it stops with them. + if (expanded && m_adjust_texture_mode) { m_adjust_texture_mode = false; m_adjust_drag_handle = AdjustHandle::None; } } + const ImU32 c = ImGui::GetColorU32(col_link); + const float cx = p.x + 0.5f * arrow, cy = p.y + 0.5f * ts.y, hs = 0.3f * arrow; + if (expanded) + dl->AddTriangleFilled(ImVec2(cx - hs, cy + 0.5f * hs), ImVec2(cx + hs, cy + 0.5f * hs), ImVec2(cx, cy - 0.5f * hs), c); + else + dl->AddTriangleFilled(ImVec2(cx - hs, cy - 0.5f * hs), ImVec2(cx + hs, cy - 0.5f * hs), ImVec2(cx, cy + 0.5f * hs), c); + const float tx = p.x + arrow + gap_s; + dl->AddText(ImVec2(tx, p.y), c, text.c_str()); + if (ImGui::IsItemHovered()) + dl->AddLine(ImVec2(tx, p.y + ts.y), ImVec2(tx + ts.x, p.y + ts.y), c); } + ImGui::EndGroup(); - // A click that lands on the layer's body (not on a widget, which consumes its own click) - // selects it too, so the whole block reads as one clickable object (#12). - if (!is_active && ImGui::IsItemClicked()) - set_active_layer(layer->slot); + ImGui::Unindent(card_pad); + const float card_max_y = ImGui::GetItemRectMax().y + card_pad; + drop_target(li, layer.slot, card_min, ImVec2(content_rx, card_max_y)); + if (layer.slot == dragged_slot) + dl->AddRectFilled(card_min, ImVec2(content_rx, card_max_y), ghost_col, rounding); + dl->ChannelsSetCurrent(0); + dl->AddRectFilled(card_min, ImVec2(content_rx, card_max_y), col_card, rounding); + dl->ChannelsMerge(); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + card_pad); + } else { + // A closed layer: one outlined row - grip, chevron, thumbnail, name, depth, remove. A click + // anywhere that is not one of its buttons opens it. + const float inner = std::round(m_imgui->scaled(0.15f)); + const float row_h = frame_h + 2.f * inner; + const ImVec2 p0 = ImGui::GetCursorScreenPos(); + const float row_w = content_rx - p0.x; + if (ImGui::InvisibleButton("##row", ImVec2(row_w, row_h))) + activate_slot = layer.slot; + const bool row_hovered = ImGui::IsItemHovered(); + ImGui::SetItemAllowOverlap(); // the row's own buttons, submitted after it, still take their clicks + drop_target(li, layer.slot, p0, ImVec2(p0.x + row_w, p0.y + row_h)); + if (row_hovered) + dl->AddRectFilled(p0, ImVec2(p0.x + row_w, p0.y + row_h), col_card, rounding); + dl->AddRect(p0, ImVec2(p0.x + row_w, p0.y + row_h), col_line, rounding); - if (is_active) { - const float pad = m_imgui->scaled(0.25f); - const ImVec2 rmin(block_min.x - pad, block_min.y - pad); - const ImVec2 rmax(block_rx, ImGui::GetCursorScreenPos().y); - dl->ChannelsSetCurrent(0); - dl->AddRectFilled(rmin, rmax, ImGui::GetColorU32(ImVec4(0.0f, 0.59f, 0.53f, 0.22f)), m_imgui->scaled(0.2f)); - dl->ChannelsMerge(); + ImGui::SetCursorScreenPos(ImVec2(p0.x + inner, p0.y + inner)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(gap_s, style.ItemSpacing.y)); + grip(layer, frame_h); + ImGui::SameLine(); + { + const ImVec2 c = ImGui::GetCursorScreenPos(); + const float aw = std::round(ImGui::GetFontSize() * 0.5f), hs = 0.3f * aw; + const float cx = c.x + 0.5f * aw, cy = c.y + 0.5f * frame_h; + dl->AddTriangleFilled(ImVec2(cx - 0.5f * hs, cy - hs), ImVec2(cx - 0.5f * hs, cy + hs), ImVec2(cx + 0.5f * hs, cy), + ImGui::GetColorU32(ImGuiCol_TextDisabled)); + ImGui::Dummy(ImVec2(aw, frame_h)); } + ImGui::SameLine(); + const float th = std::round(frame_h * 0.9f); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 0.5f * (frame_h - th)); + if (GLTexture *thumb = get_layer_thumbnail(layer)) { + if (ImGui::ImageButton((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(th, th), ImVec2(0.f, 0.f), ImVec2(1.f, 1.f), 0)) + open_picker(layer.slot); + } else if (ImGui::Button("?##thumb", ImVec2(th, th))) { + open_picker(layer.slot); + } + hover_tip(_u8L("Change texture")); + ImGui::SameLine(); - ImGui::PopID(); - ImGui::Separator(); + char depth_text[32]; + std::snprintf(depth_text, sizeof(depth_text), "%.3f mm", layer.depth_mm); + const std::string summary = painted ? std::string(depth_text) : _u8L("not painted"); + const float sum_w = ImGui::CalcTextSize(summary.c_str()).x; + const float right_edge = ImGui::GetWindowContentRegionMax().x - inner; + const float name_w = std::max(0.f, right_edge - ImGui::GetCursorPosX() - 2.f * gap_s - sum_w - frame_h); + ImGui::SetCursorPosY(p0.y - ImGui::GetWindowPos().y + ImGui::GetScrollY() + inner); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(ellipsize(layer_name(layer), name_w).c_str()); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), right_edge - frame_h - gap_s - sum_w)); + ImGui::TextDisabled("%s", summary.c_str()); + ImGui::SameLine(); + ImGui::SetCursorPosX(right_edge - frame_h); + m_imgui->disabled_begin(busy); + if (icon_button(600 + layer.slot, "texture_displacement_cross.svg", frame_h, m_desc.at("remove_layer"), _L("Remove this layer"))) + slot_to_remove = layer.slot; + m_imgui->disabled_end(); + ImGui::PopStyleVar(); + if (layer.slot == dragged_slot) + dl->AddRectFilled(p0, ImVec2(p0.x + row_w, p0.y + row_h), ghost_col, rounding); + ImGui::SetCursorScreenPos(ImVec2(p0.x, p0.y + row_h + 0.5f * style.ItemSpacing.y)); } - ImGui::EndChild(); - ImGui::PopStyleColor(); - - if (slot_to_remove >= 0) - remove_texture_layer(slot_to_remove); // deferred: see slot_to_remove's declaration + ImGui::PopID(); } - // ImGui sliders report "changed" continuously on every frame while being dragged, not just - // once on release - rebuilding the preview (a real CPU mesh recompute) on every one of those - // frames is what made dragging these sliders feel slow. Only rebuild once the mouse button - // that's driving the drag is released, i.e. once per edit instead of dozens of times per drag. - // The feature-adaptive subdivision follows the *displaced* surface (it samples depth * texture), - // so depth / tile-size / rotation / invert all change where it puts triangles. The heavy - // displacement preview below only rebuilds on release, which made the subdivide wireframe look - // like it ignored those edits - so rebuild it live here (during the drag), the same cadence its - // own sliders use. Cheap: it is bounded by the painted region. - if (m_preview_params_dirty && m_subdivide_editing && m_subdivide_adaptive && m_subdivide_feature) - rebuild_subdivide_preview(); - if (m_preview_params_dirty && (m_auto_update || !ImGui::IsMouseDown(ImGuiMouseButton_Left))) { - rebuild_preview(); - // Same edits (tile size, rotation, offset, a new texture) are what the projector window - // draws, so it refreshes on the same one-per-edit cadence. No-op while it is closed. - update_projector(); - m_preview_params_dirty = false; - } - // (The "Add layer" button now lives next to the "Texture layers" heading, as an icon.) + // The dragged layer follows the cursor as a copy of its row, held by its grip. On the foreground draw list, so + // it stays visible over the rest of the panel and anywhere the drag wanders. + if (dragged_slot >= 0) { + const auto it = std::find_if(ordered.begin(), ordered.end(), [dragged_slot](const auto *l) { return l->slot == dragged_slot; }); + if (it != ordered.end()) { + const TextureDisplacementLayer &l = **it; + ImDrawList *fg = ImGui::GetForegroundDrawList(); + const float inner = std::round(m_imgui->scaled(0.15f)); + const float row_h = frame_h + 2.f * inner; + const float list_x = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMin().x; + const ImVec2 mouse = ImGui::GetIO().MousePos; + const ImVec2 mn(mouse.x - inner - 0.5f * grip_w, mouse.y - 0.5f * row_h); + const ImVec2 mx(mn.x + (content_rx - list_x), mn.y + row_h); + fg->AddRectFilled(ImVec2(mn.x + 2.f, mn.y + 4.f), ImVec2(mx.x + 2.f, mx.y + 4.f), IM_COL32(0, 0, 0, 70), rounding); + fg->AddRectFilled(mn, mx, dark ? IM_COL32(0x3a, 0x3a, 0x40, 245) : IM_COL32(255, 255, 255, 245), rounding); + fg->AddRect(mn, mx, ImGui::GetColorU32(orca), rounding, 0, 1.5f); - // Settings for the whole stack rather than one layer, so they sit outside the layer list. Standard - // mode pins them (see apply_standard_mode_presets()) instead of showing them. - if (pro_mode() && mv != nullptr) { - TextureDisplacementOptions &opts = mv->texture_displacement_options; + float x = mn.x + inner; + draw_drag_icon(fg, x, mn.y + inner, grip_w, frame_h, drag_tint(255)); + x += grip_w + gap_s; + const float th = std::round(frame_h * 0.9f); + if (GLTexture *thumb = get_layer_thumbnail(l)) { + const float ty = mn.y + 0.5f * (row_h - th); + fg->AddImage((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(x, ty), ImVec2(x + th, ty + th)); + } + x += th + gap_s; + + char depth_text[32]; + std::snprintf(depth_text, sizeof(depth_text), "%.3f mm", l.depth_mm); + const std::string summary = slot_painted(l.slot) ? std::string(depth_text) : _u8L("not painted"); + const float sum_w = ImGui::CalcTextSize(summary.c_str()).x; + const float text_y = mn.y + 0.5f * (row_h - ImGui::GetFontSize()); + fg->AddText(ImVec2(mx.x - inner - sum_w, text_y), ImGui::GetColorU32(ImGuiCol_TextDisabled), summary.c_str()); + const std::string name = ellipsize(layer_name(l), std::max(0.f, mx.x - inner - sum_w - gap_s - x)); + fg->AddText(ImVec2(x, text_y), ImGui::GetColorU32(ImGuiCol_Text), name.c_str()); + } + } + + if (mv != nullptr) { + const bool full = mv->texture_displacement_layers.size() >= TEXTURE_DISPLACEMENT_MAX_LAYERS; + const std::string label = full ? Slic3r::format(_u8L("All %1% layers used"), TEXTURE_DISPLACEMENT_MAX_LAYERS) : + "+ " + _u8L("Add layer"); + m_imgui->disabled_begin(busy || full); + if (ImGui::Button((label + "##add_layer").c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0.f))) + add_texture_layer(); + m_imgui->disabled_end(); + } + + // ---- Mesh preparation: Pro only. Standard pins all of it and runs it from Bake. ---- + if (pro_mode()) { ImGui::Separator(); + heading(_L("Subdivision")); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Displace up to the border").c_str(), &opts.displace_border); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Move the outermost ring of painted vertices as well, so the relief runs all the " - "way to the edge of the painted area. Turn this off to hold that ring flat, which " - "keeps the displacement strictly inside the paint but flattens the pattern right " - "at the border."), - m_imgui->scaled(20.f)); - - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Smooth result").c_str(), &opts.smooth_enabled); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Relax the displaced surface after the textures have been applied, to round off " - "the hard steps a bitmap height map leaves behind. Only the vertices the " - "displacement moved are touched. This is geometry smoothing - the per-layer " - "\"Blur\" slider instead softens the height map before it is sampled."), - m_imgui->scaled(20.f)); - if (opts.smooth_enabled) { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - float percent = opts.smooth_strength * 100.f; - if (m_imgui->slider_float(std::string(_u8L("Smoothing (%)")) + "##dispsmooth", &percent, 1.f, 100.f, "%.0f", 1.f)) { - opts.smooth_strength = std::clamp(percent / 100.f, 0.01f, 1.f); - m_preview_params_dirty = true; - } - if (ImGui::SliderInt((_u8L("Passes") + "##dispsmoothit").c_str(), &opts.smooth_iterations, 1, 10)) { - opts.smooth_iterations = std::clamp(opts.smooth_iterations, 1, 10); - m_preview_params_dirty = true; - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How many relaxation passes to run. More passes reach further across the " - "surface; strength controls how much each one moves a vertex."), - m_imgui->scaled(20.f)); - ImGui::PopItemWidth(); - - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Ignore outer ring").c_str(), &opts.smooth_skip_border); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Leave the outermost ring of painted vertices out of the smoothing. Their " - "neighbours outside the paint never move, so relaxing them drags the relief " - "back down and the pattern comes out half-melted right at the edge. Turn this " - "off only if you want that edge softened on purpose."), - m_imgui->scaled(20.f)); - - // The settings above ride along with Preview/Bake. This button is for geometry that has - // *already* been baked, where there is no displacement left to fold the smoothing into. - if (m_imgui->button(_u8L("Smooth baked mesh now"))) - smooth_model(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Apply the smoothing above to the model's real geometry right now, using the " - "painted area to decide what to touch. For relief that is already baked in - " - "unbaked displacement is smoothed by Bake itself."), - m_imgui->scaled(20.f)); - } - } - - ImGui::Separator(); - m_imgui->text(_u8L("Subdivision")); - - // The triangle budget is the one subdivision control Standard mode keeps: its right value depends - // on the part rather than on the recipe (a big model, or a fine texture, simply needs more of them), - // and raising or lowering it is safe without understanding anything else in this section. Shared - // with the Pro layout below so there is one definition of the widget. - const auto budget_slider = [this]() { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (ImGui::SliderInt((_u8L("Added triangles (k)") + "##subdivbudget").c_str(), &m_subdivide_budget_k, 10, 2000)) { - m_subdivide_budget_k = std::clamp(m_subdivide_budget_k, 10, 2000); + if (ImGui::Checkbox(_u8L("Only painted area (adaptive)").c_str(), &m_subdivide_adaptive)) { if (m_subdivide_editing) - rebuild_subdivide_preview(); + rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result m_parent.set_as_dirty(); } - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How many thousand triangles the refinement may add. It always splits the " - "worst-fitting triangle first, so a run that uses the whole budget has still spent " - "it where it shows most - raise this if the result still looks too coarse."), - m_imgui->scaled(20.f)); - }; + hover_tip(_u8L("Refine only where you have painted, down to a target edge length, instead of splitting " + "the whole model. Keeps the triangle count down on a big part with a small decal, and - " + "unlike whole-model subdivision - your paint is carried onto the finer mesh instead of " + "being cleared.")); - // Everything else from here to the Bake row is mesh preparation, which is exactly what Standard - // mode takes over: it pins those to one recipe and runs them from the Bake button (see - // bake_standard()), so showing them would only invite changing numbers that get overwritten. - if (!pro_mode()) - budget_slider(); - if (pro_mode()) { + if (m_subdivide_adaptive) { + if (m_subdivide_target_mm <= 0.f && mv != nullptr) { + // Seed the target at about half the mesh's mean edge length, so the default already adds a + // useful amount of detail rather than landing on "no change". + const indexed_triangle_set &its = mv->mesh().its; + double sum = 0.0; + size_t cnt = 0; + for (const stl_triangle_vertex_indices &tri : its.indices) + for (int i = 0; i < 3; ++i) { + sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm(); + ++cnt; + } + m_subdivide_target_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)) * 0.5f, 0.001f, 20.f) : 1.f; + } + // Live preview: rebuild the wireframe as the slider moves, not only on release. The rebuild is + // bounded by the painted region, so it stays responsive. + const auto preview_live = [this]() { + if (m_subdivide_editing) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + }; - if (ImGui::Checkbox(_u8L("Only painted area (adaptive)").c_str(), &m_subdivide_adaptive)) { - if (m_subdivide_editing) - rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result - m_parent.set_as_dirty(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Refine only where you have painted, down to a target edge length, instead of splitting " - "the whole model. Keeps the triangle count down on a big part with a small decal, and - " - "unlike whole-model subdivision - your paint is carried onto the finer mesh instead of " - "being cleared."), - m_imgui->scaled(20.f)); + if (ImGui::Checkbox(_u8L("Follow texture detail").c_str(), &m_subdivide_feature)) + preview_live(); + hover_tip(_u8L("Put triangles only where the texture actually bends - dense over hills, ridges and " + "noise, sparse over flat areas and smooth slopes - instead of an even density " + "everywhere. Uses the combined displacement of all painted layers.")); - if (m_subdivide_adaptive) { - if (m_subdivide_target_mm <= 0.f && mv != nullptr) { - // Seed the target at about half the mesh's mean edge length, so the default already adds - // a useful amount of detail rather than landing on "no change". + // The edge-length target is a baseline in both modes. In feature mode it is what guarantees the + // curvature test can actually see the texture: left too coarse, a big triangle over a fine + // pattern can sample four points that all happen to land at similar heights, report no error, + // and stall before refinement ever starts. + if (float_row("##subdiv_target", m_subdivide_feature ? _L("Max edge") : _L("Target edge"), &m_subdivide_target_mm, + 0.001f, 20.f, "%.3f mm", true, 0.f)) + preview_live(); + hover_tip(m_subdivide_feature ? + _u8L("Nothing in the painted area stays coarser than this, even where the texture is " + "flat. Keep it near the size of the features you want picked up - too coarse and " + "fine detail can be missed entirely.") : + _u8L("Triangles in the painted area are split until every edge is at or below this " + "length. Smaller means finer detail and more triangles.")); + + if (m_subdivide_feature) { + if (float_row("##subdiv_detail", _L("Detail"), &m_subdivide_detail_mm, 0.001f, 1.f, "%.3f mm", true, 0.f)) + preview_live(); + hover_tip(_u8L("How closely the mesh follows the texture's relief. Smaller captures finer bumps; " + "larger only chases the big features.")); + if (float_row("##subdiv_min", _L("Min edge"), &m_subdivide_min_edge_mm, 0.001f, 20.f, "%.3f mm", true, 0.f)) + preview_live(); + hover_tip(_u8L("The finest triangle size refinement will ever produce. Smaller captures finer " + "relief; also stops runaway subdivision at a sharp texture step, where the " + "surface never becomes flat.")); + } + + // Applies in both adaptive sub-modes: it is about the step the bake puts at the paint's edge, which + // exists whether or not "Follow texture detail" is on. 0 turns the band off. + if (float_row("##subdiv_border", _L("Edge detail"), &m_subdivide_border_mm, 0.f, 5.f, "%.3f mm", false, 0.f)) + preview_live(); + hover_tip(_u8L("Triangle size along the boundary of the painted area. The relief drops back to the " + "flat surface across that boundary, and the triangles spanning the drop are what you " + "see as a jagged rim around an unpainted region - smaller values make the outline " + "cleaner. Costs triangles along the outline only, not over the whole area. " + "0 turns it off.")); + + // Only worth showing when a layer is actually colouring: with no colour there is no boundary for it + // to refine and the control would do nothing whatever it is set to. + if (mv != nullptr && any_layer_colors(*mv)) { + if (float_row("##subdiv_color", _L("Colour detail"), &m_subdivide_color_mm, 0.f, 5.f, "%.3f mm", false, 0.f)) + preview_live(); + hover_tip(_u8L("Triangle size along a boundary between two colours. Colour is assigned per " + "triangle, so an edge between two filaments can only be as clean as the " + "triangles along it - and the relief criteria cannot see it at all, because " + "the surface is perfectly smooth across a change of colour. Costs triangles " + "along the boundaries only. 0 turns it off.")); + } + + ImGui::TextDisabled("%s", _u8L("Triangle budget: set with Triangles, below.").c_str()); + if (m_subdivide_editing && m_subdivide_preview_tris > 0) + m_imgui->text(Slic3r::format(_u8L("Preview: %1% triangles"), m_subdivide_preview_tris)); + } else { + if (int_row("##subdiv_steps", _L("Steps"), &m_subdivide_count, 0, 5, "%d", 0.f)) { + if (m_subdivide_editing) + rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one + m_parent.set_as_dirty(); + } + hover_tip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " + "triangle count, so there are enough vertices for the height texture to displace. " + "0 means no subdivision.")); + } + + { + // Subdivide is a no-op when there is nothing to commit: 0 uniform passes, or an adaptive target that + // is not set. + const bool subdivide_ready = m_subdivide_adaptive ? (m_subdivide_target_mm > 0.f) : (m_subdivide_count >= 1); + const float half = std::floor((ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) * 0.5f); + m_imgui->disabled_begin(busy); + if (ImGui::Button((m_subdivide_editing ? _u8L("Hide preview") : _u8L("Preview subdivision")).c_str(), ImVec2(half, 0.f))) { + if (m_subdivide_editing) { + m_subdivide_editing = false; + m_subdivide_preview_tris = -1; + m_subdivide_preview_glmodel.reset(); + } else { + m_subdivide_editing = true; + rebuild_subdivide_preview(); + } + m_parent.set_as_dirty(); + } + m_imgui->disabled_end(); + hover_tip(_u8L("Shows the subdivided mesh as a wireframe without changing the model.")); + ImGui::SameLine(); + m_imgui->disabled_begin(!subdivide_ready || busy); + if (ImGui::Button(_u8L("Subdivide").c_str(), ImVec2(half, 0.f))) { + if (m_subdivide_adaptive) { + subdivide_model_adaptive(); // refines only the painted area, carrying the paint forward + } else { + subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) + // Back to 0 rather than staying at the count just applied: the mesh is now up to 4^N times + // denser, so re-previewing the same N passes on top of it is both pointless and by far the + // slowest thing this panel does. + m_subdivide_count = 0; + } + if (m_subdivide_editing) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + } + m_imgui->disabled_end(); + hover_tip(m_subdivide_adaptive ? + _u8L("Refines the painted area to the target edge length and carries your paint onto " + "the finer mesh. The rest of the model is left as it is.") : + _u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " + "paint on it (already-baked bumps are unaffected).")); + } + + // Remesh: even out uneven triangle sizes (CGAL isotropic remeshing). + ImGui::Separator(); + heading(_L("Remeshing")); + if (m_remesh_target_edge_mm <= 0.f && mv != nullptr) { + // Seed the target with the model's current mean edge length, so the default is a sensible "make + // everything about the size it already averages". const indexed_triangle_set &its = mv->mesh().its; - double sum = 0.0; size_t cnt = 0; + double sum = 0.0; + size_t cnt = 0; for (const stl_triangle_vertex_indices &tri : its.indices) for (int i = 0; i < 3; ++i) { sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm(); ++cnt; } - m_subdivide_target_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)) * 0.5f, 0.001f, 20.f) : 1.f; + m_remesh_target_edge_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)), 0.1f, 20.f) : 1.f; } - // Live preview: rebuild the wireframe as the slider moves, not only on release, so it tracks - // the value. The rebuild is bounded by the painted region, so it stays responsive. - const auto preview_live = [this]() { - if (m_subdivide_editing) - rebuild_subdivide_preview(); - m_parent.set_as_dirty(); - }; + float_row("##remesh_edge", _L("Target edge"), &m_remesh_target_edge_mm, 0.1f, 20.f, "%.2f mm", true, 0.f); + hover_tip(_u8L("The triangle edge length the whole model is rebuilt with.")); - if (ImGui::Checkbox(_u8L("Follow texture detail").c_str(), &m_subdivide_feature)) { - if (m_subdivide_editing) - rebuild_subdivide_preview(); - m_parent.set_as_dirty(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Put triangles only where the texture actually bends - dense over hills, ridges and " - "noise, sparse over flat areas and smooth slopes - instead of an even density " - "everywhere. Uses the combined displacement of all painted layers."), - m_imgui->scaled(20.f)); - - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - // The edge-length target is a baseline in both modes. In feature mode it is what guarantees - // the curvature test can actually see the texture: left too coarse, a big triangle over a - // fine pattern can sample four points that all happen to land at similar heights, report no - // error, and stall before refinement ever starts. "##subdiv" avoids an ID clash with the - // remesh "Target edge (mm)" slider below (same label == same widget to ImGui). - if (m_imgui->slider_float(std::string(m_subdivide_feature ? _u8L("Max edge (mm)") : _u8L("Target edge (mm)")) + - "##subdiv", - &m_subdivide_target_mm, 0.001f, 20.f, "%.3f", ImGuiLogSlider)) - preview_live(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(m_subdivide_feature ? - _u8L("Nothing in the painted area stays coarser than this, even where the texture is " - "flat. Keep it near the size of the features you want picked up - too coarse and " - "fine detail can be missed entirely.") : - _u8L("Triangles in the painted area are split until every edge is at or below this " - "length. Smaller means finer detail and more triangles."), - m_imgui->scaled(20.f)); - - if (m_subdivide_feature) { - // On top of the baseline: the chord-error tolerance, and the resolution floor. - if (m_imgui->slider_float(std::string(_u8L("Detail (mm)")) + "##subdivdetail", &m_subdivide_detail_mm, - 0.001f, 1.f, "%.3f", ImGuiLogSlider)) - preview_live(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How closely the mesh follows the texture's relief. Smaller captures finer bumps; " - "larger only chases the big features."), - m_imgui->scaled(20.f)); - if (m_imgui->slider_float(std::string(_u8L("Min edge (mm)")) + "##subdivmin", &m_subdivide_min_edge_mm, - 0.001f, 20.f, "%.3f", ImGuiLogSlider)) - preview_live(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("The finest triangle size refinement will ever produce. Smaller captures finer " - "relief; also stops runaway subdivision at a sharp texture step, where the " - "surface never becomes flat."), - m_imgui->scaled(20.f)); - } - - // Applies in both adaptive sub-modes: it is not a texture-detail criterion, it is about the - // step the bake puts at the paint's edge, which exists whether or not "Follow texture detail" - // is on. 0 turns the band off and restores the old behaviour. - if (m_imgui->slider_float(std::string(_u8L("Edge detail (mm)")) + "##subdivborder", &m_subdivide_border_mm, - 0.f, 5.f, "%.3f")) - preview_live(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Triangle size along the boundary of the painted area. The relief drops back to the " - "flat surface across that boundary, and the triangles spanning the drop are what you " - "see as a jagged rim around an unpainted region - smaller values make the outline " - "cleaner. Costs triangles along the outline only, not over the whole area. " - "0 turns it off."), - m_imgui->scaled(20.f)); - - // Only worth showing when a layer is actually colouring: with no colour there is no boundary - // for it to refine and the control would do nothing whatever it is set to. - if (mv != nullptr && any_layer_colors(*mv)) { - if (m_imgui->slider_float(std::string(_u8L("Colour detail (mm)")) + "##subdivcolor", - &m_subdivide_color_mm, 0.f, 5.f, "%.3f")) - preview_live(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Triangle size along a boundary between two colours. Colour is assigned per " - "triangle, so an edge between two filaments can only be as clean as the " - "triangles along it - and the relief criteria cannot see it at all, because " - "the surface is perfectly smooth across a change of colour. Costs triangles " - "along the boundaries only. 0 turns it off."), - m_imgui->scaled(20.f)); - } - - ImGui::PopItemWidth(); - budget_slider(); - - if (m_subdivide_editing && m_subdivide_preview_tris > 0) - m_imgui->text(Slic3r::format(_u8L("Preview: %1% triangles"), m_subdivide_preview_tris)); - } else { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (ImGui::SliderInt(_u8L("Subdivide steps").c_str(), &m_subdivide_count, 0, 5)) { - m_subdivide_count = std::clamp(m_subdivide_count, 0, 5); - if (m_subdivide_editing) - rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one - m_parent.set_as_dirty(); - } - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " - "triangle count, so there are enough vertices for the height texture to displace. " - "0 means no subdivision."), - m_imgui->scaled(20.f)); - } - - // Apply is a no-op when there is nothing to commit: 0 uniform passes, or an adaptive target that - // is not set (the preview covers the painted-area check itself). - const bool subdivide_ready = m_subdivide_adaptive ? (m_subdivide_target_mm > 0.f) : (m_subdivide_count >= 1); - if (!m_subdivide_editing) { - if (m_imgui->button(_u8L("Preview subdivision"))) { - m_subdivide_editing = true; - rebuild_subdivide_preview(); - m_parent.set_as_dirty(); - } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Shows the subdivided mesh as a wireframe without changing the model. Press Apply to " - "commit it, or Done to leave the model as it is."), - m_imgui->scaled(20.f)); - } else { - m_imgui->disabled_begin(!subdivide_ready || m_prepare_in_progress || m_bake_in_progress); - if (m_imgui->button(_u8L("Apply"))) { - if (m_subdivide_adaptive) { - subdivide_model_adaptive(); // refines only the painted area, carrying the paint forward - } else { - subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot) - // Back to 0 rather than staying at the count just applied: the mesh is now up to 4^N - // times denser, so re-previewing the same N passes on top of it is both pointless (the - // density asked for is already committed) and by far the slowest thing this panel does. - m_subdivide_count = 0; - } - rebuild_subdivide_preview(); - m_parent.set_as_dirty(); - } - m_imgui->disabled_end(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(m_subdivide_adaptive ? - _u8L("Refines the painted area to the target edge length and carries your paint onto " - "the finer mesh. The rest of the model is left as it is.") : - _u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " - "paint on it (already-baked bumps are unaffected)."), - m_imgui->scaled(20.f)); + ImGui::Checkbox((_u8L("Keep sharp edges") + "##remesh_sharp").c_str(), &m_remesh_keep_sharp_edges); + hover_tip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it " + "the remesher slides vertices along the surface and rounds every crisp edge off - " + "a cube comes back with wobbly edges.")); ImGui::SameLine(); - if (m_imgui->button(_u8L("Done"))) { - m_subdivide_editing = false; - m_subdivide_preview_tris = -1; - m_subdivide_preview_glmodel.reset(); - m_parent.set_as_dirty(); + m_imgui->disabled_begin(!m_remesh_keep_sharp_edges); + ImGui::SetNextItemWidth(-1.f); + ImGui::SliderFloat("##remesh_sharp_angle", &m_remesh_sharp_angle_deg, 5.f, 90.f, "%.0f°", ImGuiSliderFlags_AlwaysClamp); + m_imgui->disabled_end(); + hover_tip(_u8L("Edges bent by more than this count as hard features and are kept. Lower keeps " + "more detail but leaves more of the mesh untouched; higher remeshes more freely.")); + + m_imgui->disabled_begin(mv == nullptr || busy); + if (ImGui::Button(_u8L("Remesh").c_str())) + remesh_model(); + m_imgui->disabled_end(); + hover_tip(_u8L("Rebuilds the whole model with triangles close to this edge length - splitting the big " + "ones and merging the small ones - so displacement has an even density to work with. " + "Replaces the geometry; your paint is carried onto the new triangles spatially, so it " + "survives (already-baked bumps are kept too).")); + + // Settings for the whole stack rather than one layer. Standard mode pins them instead of showing them. + if (mv != nullptr) { + TextureDisplacementOptions &opts = mv->texture_displacement_options; + ImGui::Separator(); + heading(_L("Result")); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Displace up to the border").c_str(), &opts.displace_border); + hover_tip(_u8L("Move the outermost ring of painted vertices as well, so the relief runs all the " + "way to the edge of the painted area. Turn this off to hold that ring flat, which " + "keeps the displacement strictly inside the paint but flattens the pattern right " + "at the border.")); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Smooth result").c_str(), &opts.smooth_enabled); + hover_tip(_u8L("Relax the displaced surface after the textures have been applied, to round off " + "the hard steps a bitmap height map leaves behind. Only the vertices the " + "displacement moved are touched. This is geometry smoothing - the per-layer " + "Smoothing slider instead softens the height map before it is sampled.")); + if (opts.smooth_enabled) { + float percent = opts.smooth_strength * 100.f; + if (float_row("##dispsmooth", _L("Strength"), &percent, 1.f, 100.f, "%.0f %%", false, 0.f)) { + opts.smooth_strength = std::clamp(percent / 100.f, 0.01f, 1.f); + m_preview_params_dirty = true; + } + if (int_row("##dispsmoothit", _L("Passes"), &opts.smooth_iterations, 1, 10, "%d", 0.f)) + m_preview_params_dirty = true; + hover_tip(_u8L("How many relaxation passes to run. More passes reach further across the " + "surface; strength controls how much each one moves a vertex.")); + + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Ignore outer ring").c_str(), &opts.smooth_skip_border); + hover_tip(_u8L("Leave the outermost ring of painted vertices out of the smoothing. Their " + "neighbours outside the paint never move, so relaxing them drags the relief " + "back down and the pattern comes out half-melted right at the edge. Turn this " + "off only if you want that edge softened on purpose.")); + + // The settings above ride along with Preview/Bake. This button is for geometry that has *already* + // been baked, where there is no displacement left to fold the smoothing into. + if (m_imgui->button(_u8L("Smooth baked mesh now"))) + smooth_model(); + hover_tip(_u8L("Apply the smoothing above to the model's real geometry right now, using the " + "painted area to decide what to touch. For relief that is already baked in - " + "unbaked displacement is smoothed by Bake itself.")); + } } } - // Remesh: even out uneven triangle sizes (CGAL isotropic remeshing). GPU Delaunay isn't practical - // here, but this delivers the same goal - a consistent triangle size across the whole model. - m_imgui->text(_u8L("Remeshing")); - if (m_remesh_target_edge_mm <= 0.f && mv != nullptr) { - // Seed the target with the model's current mean edge length, so the default is a sensible - // "make everything about the size it already averages". - const indexed_triangle_set &its = mv->mesh().its; - double sum = 0.0; size_t cnt = 0; - for (const stl_triangle_vertex_indices &tri : its.indices) - for (int i = 0; i < 3; ++i) { - sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm(); - ++cnt; - } - m_remesh_target_edge_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)), 0.1f, 20.f) : 1.f; - } - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_imgui->slider_float(_u8L("Target edge (mm)"), &m_remesh_target_edge_mm, 0.1f, 20.f, "%.2f", ImGuiLogSlider); - ImGui::PopItemWidth(); - - ImGui::Checkbox(_u8L("Keep sharp edges").c_str(), &m_remesh_keep_sharp_edges); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it " - "the remesher slides vertices along the surface and rounds every crisp edge off - " - "a cube comes back with wobbly edges."), - m_imgui->scaled(20.f)); - if (m_remesh_keep_sharp_edges) { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - m_imgui->slider_float(_u8L("Sharp edge angle"), &m_remesh_sharp_angle_deg, 5.f, 90.f, "%.0f deg"); - ImGui::PopItemWidth(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Edges bent by more than this count as hard features and are kept. Lower keeps " - "more detail but leaves more of the mesh untouched; higher remeshes more freely."), - m_imgui->scaled(20.f)); - } - - m_imgui->disabled_begin(mv == nullptr || m_prepare_in_progress || m_bake_in_progress); - if (m_imgui->button(_u8L("Remesh"))) - remesh_model(); - m_imgui->disabled_end(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Rebuilds the whole model with triangles close to this edge length - splitting the big " - "ones and merging the small ones - so displacement has an even density to work with. " - "Replaces the geometry; your paint is carried onto the new triangles spatially, so it " - "survives (already-baked bumps are kept too)."), - m_imgui->scaled(20.f)); - - } // pro_mode() - - ImGui::Separator(); - - m_imgui->disabled_begin(mv == nullptr || !mv->is_texture_displacement_painted()); - if (m_imgui->button(m_desc.at("remove_all"))) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset texture displacement selection"), UndoRedo::SnapshotType::GizmoAction); - int idx = -1; - for (ModelVolume *v : mo->volumes) - if (v->is_model_part()) { - ++idx; - m_triangle_selectors[idx]->reset(); - m_triangle_selectors[idx]->request_update_render_data(); - } - update_model_object(); - m_parent.set_as_dirty(); - } - m_imgui->disabled_end(); - - // Next to Bake and shown in both modes: the two pipelines have to be switchable on their own. + // Shown in both modes. The pipeline switch, the two v2 experiments and the stage debugger are + // developer controls: kept, but off the panel unless this is flipped. + static constexpr bool SHOW_PIPELINE_DEV_CONTROLS = false; if (mv != nullptr) { TextureDisplacementOptions &opts = mv->texture_displacement_options; ImGui::Separator(); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Experimental bake pipeline").c_str(), &opts.pipeline_v2); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Bake with the alternative pipeline: it refines, cleans up sliver triangles, " - "displaces and simplifies in one run, instead of moving the vertices the mesh " - "already has. Nothing needs preparing first - Subdivide and Remesh are ignored. " - "It does not produce colours yet, because it rebuilds the topology."), - m_imgui->scaled(20.f)); - if (opts.pipeline_v2) { - ImGui::PushItemWidth(m_imgui->scaled(8.4f)); - if (m_imgui->slider_float(std::string(_u8L("Edge length (mm)")) + "##v2edge", &opts.v2_refine_mm, - 0.02f, 2.f, "%.1f", ImGuiLogSlider)) { - opts.v2_refine_mm = std::clamp(opts.v2_refine_mm, 0.02f, 2.f); - m_preview_params_dirty = true; + if (SHOW_PIPELINE_DEV_CONTROLS) { + // The classic path is the opt-in: the one-run pipeline is the default, and its resolution + // control lives in the footer next to Bake (see below). + bool classic = !opts.pipeline_v2; + if (ImGui::Checkbox(_u8L("Experimental: classic bake pipeline").c_str(), &classic)) { + opts.pipeline_v2 = !classic; + m_preview_params_dirty = true; + } + hover_tip(_u8L("Bake by moving the vertices the mesh already has, after preparing it (remesh, " + "adaptive subdivision, and a cut along sharp steps in the texture). Keeps the " + "topology, which is what colours need. The default pipeline instead refines, " + "cleans up sliver triangles, displaces and simplifies in one run; nothing needs " + "preparing first, but it does not produce colours yet.")); } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Triangle size the painted area is refined to before displacement. This is " - "what decides how much of the texture the mesh can carry."), - m_imgui->scaled(20.f)); - if (ImGui::SliderInt((_u8L("Triangle budget (k)") + "##v2budget").c_str(), &opts.v2_max_triangles_k, - 0, 4000)) { - opts.v2_max_triangles_k = std::clamp(opts.v2_max_triangles_k, 0, 4000); - m_preview_params_dirty = true; + if (opts.pipeline_v2) { + // -1 is "auto": the row shows the recommendation, greyed; editing it makes it a fixed value. + const bool auto_budget = opts.v2_max_triangles_k < 0; + int shown_k = auto_budget ? v2_recommendation(*mv).budget_k : opts.v2_max_triangles_k; + m_imgui->disabled_begin(auto_budget); + if (int_row("##v2budget", _L("Budget"), &shown_k, 0, 4000, auto_budget ? "%d k (auto)" : "%d k", 0.f)) { + opts.v2_max_triangles_k = shown_k; + m_preview_params_dirty = true; + } + m_imgui->disabled_end(); + hover_tip(_u8L("Triangles to simplify down to after displacement, in thousands. Automatic " + "with the resolution; 0 turns simplification off.")); } - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Triangles to simplify down to after displacement, in thousands. 0 turns " - "simplification off, which is worth comparing on its own."), - m_imgui->scaled(20.f)); - ImGui::PopItemWidth(); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Keep above build plate").c_str(), - &opts.v2_clamp_below_plate); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Push anything the displacement drove below the build plate back up to it. " - "Only geometry that would end up under the model's own bottom is moved - " - "relief that goes downward but stays above the plate is left as it is."), - m_imgui->scaled(20.f)); - - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Align mesh to texture edges").c_str(), - &opts.v2_relocate); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Slide vertices sideways onto the edges in the texture before displacing them. " - "Displacement can only move vertices up and down, so without this a sharp step " - "in the image lands wherever the triangles happen to be and comes out as a " - "staircase. Moving the vertices onto the step first gives a straight wall at the " - "same triangle count."), - m_imgui->scaled(20.f)); - - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(_u8L("Collapse the thin triangles refinement inherits from the model's own " - "tessellation, before displacement samples them. A sliver's three corners " - "land on three unrelated parts of the texture, which is what makes the " - "relief look jagged."), - m_imgui->scaled(20.f)); - } + if (SHOW_PIPELINE_DEV_CONTROLS && opts.pipeline_v2) { + // Keeping the relief above the plate is not a checkbox: it is unconditional, in both pipelines + // (see build_texture_displacement()). + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Align mesh to texture edges").c_str(), &opts.v2_relocate); + hover_tip(_u8L("Slide vertices sideways onto the edges in the texture before displacing them. " + "Displacement can only move vertices up and down, so without this a sharp step " + "in the image lands wherever the triangles happen to be and comes out as a " + "staircase. Moving the vertices onto the step first gives a straight wall at the " + "same triangle count.")); + m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize); + hover_tip(_u8L("Collapse the thin triangles refinement inherits from the model's own " + "tessellation, before displacement samples them. A sliver's three corners " + "land on three unrelated parts of the texture, which is what makes the " + "relief look jagged.")); + } + // Below both pipelines' own settings, because it replays whichever of them is selected. + if (SHOW_PIPELINE_DEV_CONTROLS) + render_debug_stage_panel(mv); } - ImGui::SameLine(); - m_imgui->disabled_begin(m_bake_in_progress || m_prepare_in_progress || mv == nullptr || - !mv->is_texture_displacement_painted()); - if (m_imgui->button(m_prepare_in_progress ? _L("Preparing...") : - m_bake_in_progress ? _L("Baking...") : - m_desc.at("bake"))) { - // Standard mode's Bake is the whole pipeline (remesh -> refine -> displace); Pro's is only the - // displacement, because there the user has already prepared the mesh with the controls above. - if (pro_mode()) - bake(); - else - bake_standard(); - } - m_imgui->disabled_end(); - if (ImGui::IsItemHovered()) - m_imgui->tooltip(pro_mode() ? - _u8L("Turn the painted height maps into real geometry, by moving the vertices that are " - "already there. Use Subdivide first if the mesh is too coarse to show the detail.") : - _u8L("Turn the painted height maps into real geometry. The mesh is remeshed to an even " - "density and refined where the texture bends first, so the detail has vertices to " - "land on - all in one step."), - m_imgui->scaled(20.f)); + // Content height, for next frame's body size. The cursor position is scroll-compensated, so this is the + // height of everything above, whether or not it is scrolled. + m_panel_body_h = ImGui::GetCursorPosY() - style.ItemSpacing.y; + ImGui::EndChild(); + ImGui::PopStyleColor(5); + ImGui::PopStyleVar(2); + // The layer-list changes deferred from the loop above. + if (slot_to_remove >= 0) + remove_texture_layer(slot_to_remove); + else if (move_slot >= 0) + move_texture_layer(move_slot, move_to); + else if (activate_slot >= 0) + set_active_layer(activate_slot); + // Retexturing a layer makes it the one being painted, too. + if (m_picker_open_request && m_picker_slot >= 0) + set_active_layer(m_picker_slot); + + // In this window's scope, where the popup's open request and the popup itself share one ID stack. + { + const ImVec2 win_min = ImGui::GetWindowPos(); + const ImVec2 win_sz = ImGui::GetWindowSize(); + render_texture_library_popup(win_min, ImVec2(win_min.x + win_sz.x, win_min.y + win_sz.y)); + } + + // ImGui sliders report "changed" continuously on every frame while being dragged, not just once on + // release - rebuilding the preview (a real CPU mesh recompute) on every one of those frames is what made + // dragging these sliders feel slow. Only rebuild once the mouse button that's driving the drag is + // released, i.e. once per edit instead of dozens of times per drag. + // The feature-adaptive subdivision follows the *displaced* surface (it samples depth * texture), so depth / + // tile-size / rotation / invert all change where it puts triangles. The heavy displacement preview below + // only rebuilds on release, which made the subdivide wireframe look like it ignored those edits - so + // rebuild it live here (during the drag), the same cadence its own sliders use. Cheap: it is bounded by + // the painted region. + if (m_preview_params_dirty && m_subdivide_editing && m_subdivide_adaptive && m_subdivide_feature) + rebuild_subdivide_preview(); + if (m_preview_params_dirty && (m_auto_update || !ImGui::IsMouseDown(ImGuiMouseButton_Left))) { + rebuild_preview(); + // Same edits (tile size, rotation, offset, a new texture) are what the projector window draws, so it + // refreshes on the same one-per-edit cadence. No-op while it is closed. + update_projector(); + m_preview_params_dirty = false; + } + + // ---- Footer: pinned below the body, so Bake never scrolls away. ---- + const float footer_top = ImGui::GetCursorScreenPos().y; ImGui::Separator(); - if (m_imgui->button(_L("Close"))) - m_parent.reset_all_gizmos(); + { + const float x0 = ImGui::GetCursorPosX(); + const int base_k = mv != nullptr ? int((mv->mesh().facets_count() + 500) / 1000) : 0; + const bool v2 = mv != nullptr && mv->texture_displacement_options.pipeline_v2; + + // The triangle budget is the one subdivision control Standard mode keeps: its right value depends on + // the part rather than on the recipe (a big model, or a fine texture, simply needs more of them), and + // raising or lowering it is safe without understanding anything else. Pro's Subdivide uses it too. + ImGui::AlignTextToFramePadding(); + if (v2) { + // The one control the default pipeline needs: the edge length its refinement targets, which + // decides how much of the texture the mesh can carry. About eight texels per edge is where + // fine detail (mortar joints, knurl ridges) stops being lost between vertices. + TextureDisplacementOptions &opts = mv->texture_displacement_options; + const V2Resolution &rec = v2_recommendation(*mv); + m_imgui->text(_L("Resolution")); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); + // Auto: the edge (and the budget) follow the texture and the model. Off: the slider holds + // a fixed value, seeded with the recommendation so it starts from something sensible. + bool auto_res = opts.v2_refine_mm <= 0.f; + m_imgui->disabled_begin(busy); + if (ImGui::Checkbox("##v2auto", &auto_res)) { + if (auto_res) { + opts.v2_refine_mm = 0.f; + opts.v2_max_triangles_k = -1; + } else { + opts.v2_refine_mm = rec.edge_mm > 0.f ? rec.edge_mm : 0.3f; + opts.v2_max_triangles_k = rec.budget_k > 0 ? rec.budget_k : 750; + } + m_preview_params_dirty = true; + m_parent.set_as_dirty(); + } + m_imgui->disabled_end(); + hover_tip(_u8L("Auto: the resolution and the budget are chosen from the texture (its pixel " + "size on the model and how sharp it is) and the model's size, the way " + "BumpMesh's smart resolution does. Untick to set them by hand.")); + ImGui::SameLine(); + ImGui::SetNextItemWidth(x0 + panel_w - ImGui::GetCursorPosX()); + float shown = auto_res ? rec.edge_mm : opts.v2_refine_mm; + m_imgui->disabled_begin(busy || auto_res); + if (ImGui::SliderFloat("##v2edge", &shown, 0.02f, 2.f, auto_res ? "%.2f mm (auto)" : "%.2f mm", + ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic)) { + opts.v2_refine_mm = shown; + m_preview_params_dirty = true; + m_parent.set_as_dirty(); + } + m_imgui->disabled_end(); + if (auto_res && rec.edge_mm > 0.f) + hover_tip(Slic3r::format(_u8L("%1% texture pixels per edge x %2% mm per pixel%3%. Budget %4% k."), + rec.pixels_per_edge, Slic3r::format("%.3f", rec.texel_mm), + rec.budget_bound ? _u8L(", held back by the triangle cap") : std::string(), + rec.budget_k)); + else + hover_tip(_u8L("Triangle edge length the painted area is refined to before displacement. " + "Smaller carries finer texture detail and costs more triangles; the budget " + "caps the result.")); + } else { + m_imgui->text(_L("Triangles")); + ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); + ImGui::SetNextItemWidth(x0 + panel_w - ImGui::GetCursorPosX()); + m_imgui->disabled_begin(busy); + if (ImGui::SliderInt("##subdivbudget", &m_subdivide_budget_k, 10, 2000, "+%d k", + ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic)) { + if (m_subdivide_editing) + rebuild_subdivide_preview(); + m_parent.set_as_dirty(); + } + m_imgui->disabled_end(); + hover_tip(_u8L("How many thousand triangles the refinement may add. It always splits the " + "worst-fitting triangle first, so a run that uses the whole budget has still spent " + "it where it shows most - raise this if the result still looks too coarse.")); + } + + const float button_h = std::round(frame_h * 1.25f); + const float third = std::floor((panel_w - style.ItemSpacing.x) / 3.f); + if (busy) { + if (ImGui::Button((_u8L("Stop") + "##stop").c_str(), ImVec2(third, button_h))) + wxGetApp().plater()->get_ui_job_worker().cancel_all(); + hover_tip(_u8L("Stop the running bake. A step it has already finished stays on the model and can be undone.")); + } else { + if (ImGui::Button((_u8L("Close") + "##close").c_str(), ImVec2(third, button_h))) + m_parent.reset_all_gizmos(); + hover_tip(_u8L("Close the tool without baking. Paint and layers are kept with the model.")); + } + + ImGui::SameLine(); + const bool can_bake = !busy && mv != nullptr && mv->is_texture_displacement_painted(); + const std::string bake_label = m_prepare_in_progress ? _u8L("Preparing...") : + m_bake_in_progress ? _u8L("Baking...") : + into_u8(m_desc.at("bake")); + ImGui::PushStyleColor(ImGuiCol_Button, orca); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGuiWrapper::COL_ORCA_HOVER); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, orca); + ImGui::PushStyleColor(ImGuiCol_Border, orca); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f)); + m_imgui->push_bold_font(); + m_imgui->disabled_begin(!can_bake); + if (ImGui::Button((bake_label + "##bake").c_str(), ImVec2(x0 + panel_w - ImGui::GetCursorPosX(), button_h))) { + // Standard mode's Bake is the whole pipeline (remesh -> refine -> displace); Pro's is only the + // displacement, because there the user has already prepared the mesh with the controls above. + if (pro_mode()) + bake(); + else + bake_standard(); + } + m_imgui->disabled_end(); + m_imgui->pop_bold_font(); + ImGui::PopStyleColor(5); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + m_imgui->tooltip(mv != nullptr && !mv->is_texture_displacement_painted() ? + _u8L("Nothing is painted yet.") : + pro_mode() ? + _u8L("Turn the painted height maps into real geometry, by moving the vertices that are " + "already there. Use Subdivide first if the mesh is too coarse to show the detail.") : + _u8L("Turn the painted height maps into real geometry. The mesh is remeshed to an even " + "density and refined where the texture bends first, so the detail has vertices to " + "land on - all in one step."), + wrap_w); + + // What Bake will produce, and which layers it will skip. + if (mv != nullptr) { + std::string note = v2 ? Slic3r::format(_u8L("Model: %1% k triangles - the bake refines to the resolution above, up to its budget"), base_k) : + pro_mode() ? Slic3r::format(_u8L("Bake moves existing vertices - the model stays at %1% k triangles"), base_k) : + Slic3r::format(_u8L("Bake result up to ~%1% k triangles"), base_k + m_subdivide_budget_k); + if (mv->is_texture_displacement_painted()) { + std::vector skipped; + for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) + if (!slot_painted(l.slot)) + skipped.push_back(layer_name(l)); + if (skipped.size() > 2) + note += " · " + Slic3r::format(_u8L("%1% layers not painted, skipped"), skipped.size()); + else if (!skipped.empty()) + note += " · " + Slic3r::format(_u8L("%1% not painted, skipped"), + skipped.size() == 2 ? skipped[0] + ", " + skipped[1] : skipped[0]); + } + ImGui::PushTextWrapPos(x0 + panel_w); + ImGui::TextDisabled("%s", note.c_str()); + ImGui::PopTextWrapPos(); + } + } + m_panel_footer_h = ImGui::GetCursorScreenPos().y - footer_top; GizmoImguiEnd(); ImGuiWrapper::pop_toolbar_style(); - // Drawn last, over everything, via the foreground draw list: the +/- add-remove sign next to the - // 3D cursor. Still inside the gizmo's ImGui frame here, which is what render_paint_cursor_hint() needs. + // Drawn last, over everything, via the foreground draw list: the +/- add-remove sign next to the 3D + // cursor. Still inside the gizmo's ImGui frame here, which is what render_paint_cursor_hint() needs. render_paint_cursor_hint(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 05d2fa20eb..8cbfa9c84e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -2,6 +2,7 @@ #define slic3r_GLGizmoTextureDisplacement_hpp_ #include "GLGizmoPainterBase.hpp" +#include "libslic3r/TextureBake/TextureBakeDebug.hpp" #include "libslic3r/TextureDisplacement.hpp" #include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GLTexture.hpp" @@ -46,7 +47,11 @@ public: const std::vector &layers, const TextureDisplacementPrepareParams ¶ms, const std::vector &palette, - const DisplacementProgressFn &progress); + const DisplacementProgressFn &progress, + // Optional step capture: receives the mesh + // after the remesh and after the refinement, + // so a debug run can show the whole recipe. + BakeStageRecorder *debug = nullptr); // The volume's eight texture-displacement masks, gathered into the array every pure function here // (and every job input) takes. static TextureDisplacementFacetsData facets_data_of(const ModelVolume &mv); @@ -135,8 +140,10 @@ protected: std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Texture displacement painting"); } std::string get_action_snapshot_name() const override { return _u8L("Texture displacement editing"); } - EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType::ENFORCER; } - EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType::NONE; } + // The panel's Paint / Erase toggle swaps what the two buttons do, so the right button always does the + // opposite of the left. Shift still erases in both modes (GLGizmoPainterBase::gizmo_event()). + EnforcerBlockerType get_left_button_state_type() const override { return m_erase_mode ? EnforcerBlockerType::NONE : EnforcerBlockerType::ENFORCER; } + EnforcerBlockerType get_right_button_state_type() const override { return m_erase_mode ? EnforcerBlockerType::ENFORCER : EnforcerBlockerType::NONE; } private: bool on_init() override; @@ -154,6 +161,13 @@ private: void add_texture_layer(); void remove_texture_layer(int slot); + // Moves the layer in `slot` to position `to_index` of the slot-ordered stack (an insertion index, + // 0..layer count), which is the order blend modes apply in. Layers keep their data; what moves is + // the slot each one occupies, so their paint masks move with them on every model part. + void move_texture_layer(int slot, int to_index); + // Exchanges everything two slots hold - layer definition, paint masks, panel caches. No snapshot and + // no selector reload; move_texture_layer() does both once around a run of these. + void swap_layer_slots(int slot_a, int slot_b); void set_active_layer(int slot); // flushes the previous layer's edits, then reloads selectors // `own_snapshot` false when the caller has already taken an undo step that is meant to cover the // displacement too - see bake_standard(). @@ -299,18 +313,21 @@ private: }; const LibraryTexture *get_library_texture(const std::string &path); - // The layer's texture chooser: a drop-down whose closed state and every one of whose entries - // shows a large preview image on the left and the texture's name on the right, plus an adjacent - // button that imports an image file from disk into the user texture folder. Shipped and - // user-imported textures are listed under separate headings. - void render_texture_picker(TextureDisplacementLayer &layer); + // The texture library: a popup grid of thumbnails beside the panel, shipped textures and the user's + // own under separate headings, with a tile that imports an image from disk. Opened for + // m_picker_slot by a click on a layer's thumbnail or name. `panel_min`/`panel_max` are the panel + // window's screen rectangle, which the popup is placed against. + void render_texture_library_popup(const ImVec2 &panel_min, const ImVec2 &panel_max); + int m_picker_slot = -1; + bool m_picker_open_request = false; void set_layer_texture(TextureDisplacementLayer &layer, const TextureLibraryEntry &entry); void import_custom_texture(TextureDisplacementLayer &layer); - // Draws a picker row (image left, name right) on top of a full-width Selectable, and leaves the - // cursor below it. Shared by the drop-down's closed state and its individual entries so the two - // cannot drift apart. Returns true when the row is clicked. - bool texture_row(const char *id, const std::string &name, GLTexture *thumbnail, bool selected, float width); - float texture_row_height() const; + + // Paint / Erase, from the panel. See get_left_button_state_type(). + bool m_erase_mode = false; + // Per slot: whether the layer's card shows every setting or only Depth, Tile size and Rotation. + // Panel state only, not saved with the project. + std::array m_layer_expanded{}; // "Adjust Texture" mode: instead of painting, dragging an on-canvas handle changes the active // layer's offset. The handle is a flat panel lying in the paint patch's own tangent plane @@ -400,7 +417,21 @@ private: // Handles a toolbar command forwarded from the UV pane that needs the layer data the canvas // doesn't hold (average island scale, cut island). Takes the command as an int (a cast of // UVEditorCanvas::Command) so this header needn't pull in glad/wxGLCanvas via the canvas header. - void on_uv_command(int cmd); + void on_uv_command(int cmd, float value); + // Pane commands, queued by on_uv_command() and run from the panel render: pane clicks arrive in wx event + // handlers, outside the 3D canvas's GL frame, and several of these rebuild GPU meshes. + std::vector> m_uv_command_queue; + void process_uv_commands(); + void run_uv_command(int cmd, float value); + // Sends the UV editor pane what its controls show (see UVEditorCanvas::PaneState). + void push_uv_pane_state(); + // The panel's view modes: 0 Normal, 1 Fast, 2 Checker, 3 Distortion. Shared by the View row and the pane's + // background buttons, so both switch views the same way. + void apply_view_mode(int mode); + // The pane header's thumbnail of the active layer's texture, rebuilt only when its image changes. + static constexpr int UV_THUMB_PX = 64; + const void *m_uv_thumb_source = nullptr; + std::vector m_uv_thumb_rgb; // Splits one unwrap chart in two by marking the mesh edges that straddle the plane through its // 3D centroid, perpendicular to its longest axis, as seams (#17). The re-unwrap then separates it. void cut_island(TextureDisplacementLayer &layer, int chart); @@ -543,6 +574,41 @@ private: bool m_remesh_keep_sharp_edges = true; void remesh_model(); + // ---- Bake stage debug view ---- + // + // A bake is a chain of stages that each rewrite the whole mesh, so when the result looks wrong the + // only useful question is which stage made it wrong. "Capture stages" runs the same recipe Bake + // runs, keeps every intermediate mesh, and draws the selected one through the ordinary + // true-displacement preview - so this needs no rendering code of its own. + // + // The run commits nothing: TextureDisplacementDebugJob never touches the Model. + std::vector m_debug_stages; + // Which stage is on screen. -1 means the debug view is off and the live preview owns + // m_preview_glmodel again; rebuild_preview() checks this before replacing it. + int m_debug_stage = -1; + bool m_debug_in_progress = false; + // The open / non-manifold counts come from a sort over every half-edge, which on a multi-million + // triangle stage costs more than the stage did. On by default because a stage that tore the mesh + // is exactly what this exists to find. + bool m_debug_check_topology = true; + + // The default pipeline's automatic resolution/budget for the volume, cached on what it depends on. + const V2Resolution &v2_recommendation(const ModelVolume &mv); + V2Resolution m_v2_rec; + std::string m_v2_rec_key; + + // Queues the capture run. Uses Standard mode's recipe (remesh, refine, displace) unless Pro mode + // has already prepared the mesh or the experimental pipeline is on, either of which has nothing + // to prepare. + void run_stage_debug(); + // Puts stage `index` on screen. A stage over the memory cap has counts but no geometry and is + // skipped. + void show_debug_stage(int index); + // Drops the captured stages and hands m_preview_glmodel back to the live preview. + void exit_debug_view(); + // The panel: capture button, stage list, and what each stage produced. + void render_debug_stage_panel(ModelVolume *mv); + // Live, pre-bake preview of the true displaced geometry (built by the same algorithm Bake // uses). Empty/uninitialized whenever nothing is painted yet, in which case the gizmo falls // back to the standard paint-mask overlay like every other painting gizmo. @@ -621,6 +687,10 @@ private: // the shader projects on its own. Shared by the bump preview and the UV-check overlay. std::vector compute_layer_vertex_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) const; + // `patch` with its vertices moved into world millimetres - the space the bake maps the texture in + // (see build_texture_displacement()). Returned by value because the caller usually still needs the + // original: the patch doubles as render geometry, which is drawn through the volume's own matrix. + indexed_triangle_set patch_in_world(const indexed_triangle_set &patch) const; // UV-check overlay drawn over the painted patch to sanity-check the unwrap (#13/#14). Built by // rebuild_uvcheck_mesh(), drawn by render_uvcheck_mesh() with the "texture_displacement_uvcheck" @@ -655,6 +725,7 @@ private: enum class UVBackground { None, Height, Checker }; UVBackground m_uv_editor_bg = UVBackground::None; float m_uv_editor_bg_smoothing = -1.f; // smoothing the height backdrop was uploaded at + const void *m_uv_editor_bg_image = nullptr; // the layer image it was uploaded from // Per-chart distortion heatmap colour for the UV pane (#7/#14), computed once when the unwrap is // re-solved (relative stretch doesn't change when islands are merely moved), fed to the canvas only // while the Distortion check mode is on. Empty otherwise. @@ -751,6 +822,18 @@ private: // open/close within a session, so the choice sticks while working. bool m_undocked = false; + // Smooth scrolling for the panel body (everything between the header and the pinned Bake footer). + // ImGui jumps a fixed number of lines per wheel notch, which on tall layer cards reads as a hard + // jolt rather than a scroll. The wheel is intercepted (ImGuiWindowFlags_NoScrollWithMouse) and + // moves a *target* offset instead; the real scroll is eased toward it over the following frames. + float m_panel_scroll_target = 0.f; + float m_panel_scroll_applied = -1.f; // what the easing wrote last frame; <0 until the first one + // Last frame's body content height and footer height. The body is a child window that has to be + // given its height before its content is laid out, so it is sized from the previous frame: as tall + // as its content, capped so the footer still fits above the bottom of the canvas. + float m_panel_body_h = 0.f; + float m_panel_footer_h = 0.f; + // See the "Adjust Texture" block of private methods above. bool m_adjust_texture_mode = false; bool m_adjust_anchor_valid = false; @@ -774,14 +857,7 @@ private: std::map m_desc; - // The tool's SVG (toolbar_texture_displacement.svg) uploaded once as a GL texture, so it can be - // used as an ImGui image button in the panel (currently the "add layer" affordance next to the - // Texture layers heading). Lazily loaded on first use, when a GL context is guaranteed current. - GLTexture m_tool_icon; - bool m_tool_icon_tried = false; - unsigned int tool_icon_id(); // 0 if the icon could not be loaded - - // Icons for the panel's selection-mode and view-mode button rows. Loaded through IconManager with + // Icons for the panel's icon buttons (tools, views, mapping, tiling, layer actions). Loaded through IconManager with // the same colour/monochrome variants the main toolbar uses, so an inactive button shows the icon in // the theme's normal (grey) foreground colour and an active one shows it in its original colours - // matching the toolbar's selected/unselected look. Uploaded once on first panel render. diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index eac60eb392..05df414ca5 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -61,7 +61,7 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) } return true; }, - color)); + color, m_input.volume_to_world)); // Always finish at 100: this is what closes the notification. Reported even on cancel, where // build_texture_displacement() returns an empty mesh and finalize() commits nothing. @@ -152,6 +152,7 @@ void queue_texture_displacement_bake(const ModelVolume &volume, const TextureCol input.base_mesh = volume.mesh().its; input.layers = volume.texture_displacement_layers; input.options = volume.texture_displacement_options; + input.volume_to_world = texture_displacement_volume_to_world(volume); for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) input.facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp index cd3772ae47..753dd20742 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -23,6 +23,9 @@ struct TextureDisplacementBakeInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Mesh coordinates -> world millimetres. Captured here with everything else so the worker never + // reaches back into the live Model for it. See build_texture_displacement(). + Transform3d volume_to_world = Transform3d::Identity(); // Captured on the main thread. Empty unless some layer is colouring, in which case the bake also // writes the volume's mmu_segmentation_facets - the same per-triangle filament assignment the MMU // paint gizmo writes - alongside the displaced geometry. diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.cpp new file mode 100644 index 0000000000..97958ead9d --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.cpp @@ -0,0 +1,113 @@ +#include "TextureDisplacementDebugJob.hpp" + +#include "libslic3r/Model.hpp" + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/Plater.hpp" + +namespace Slic3r::GUI { + +TextureDisplacementDebugJob::TextureDisplacementDebugJob( + TextureDisplacementDebugInput &&input, std::function)> on_finished) + : m_input(std::move(input)), m_on_finished(std::move(on_finished)) +{ +} + +void TextureDisplacementDebugJob::process(Ctl &ctl) +{ + const std::string status = _u8L("Capturing bake stages"); + ctl.update_status(1, status); + + // Only ever touches m_input (captured by value before this job was queued) and local state - and, + // unlike the bake job, never writes anything back either. + int last_reported = 1; + const auto report = [&ctl, &status, &last_reported](int percent) { + if (ctl.was_canceled()) + return false; + if (percent > last_reported) { + last_reported = percent; + ctl.update_status(percent, status); + } + return true; + }; + + BakeStageRecorder recorder; + recorder.enable(true); + recorder.set_check_topology(m_input.check_topology); + recorder.set_mesh_cap(m_input.mesh_cap); + + indexed_triangle_set mesh = m_input.base_mesh; + TextureDisplacementFacetsData masks = m_input.facets_data; + + // The default pipeline can only move vertices the mesh already has, so its recipe starts with the + // preparation - which is where "first it remeshes, then it refines" actually happens. The + // experimental pipeline refines as part of the bake and has nothing to prepare. + if (m_input.run_prepare && !m_input.options.pipeline_v2) { + const TextureDisplacementPrepareResult prepared = + GLGizmoTextureDisplacement::prepare_mesh(mesh, masks, m_input.layers, m_input.prepare_params, + m_input.color.palette, + // Preparation is roughly half the run; the bake + // takes the progress bar from there. + [&report](int pct) { return report(1 + pct / 2); }, + &recorder); + if (ctl.was_canceled()) + return; + // An empty result means nothing needed doing, which is not a failure - the bake below simply + // runs on the mesh as it stands. paint_lost means the remesh dropped every layer's paint, so + // there is nothing left to displace and the stages recorded so far are the whole story. + if (prepared.paint_lost) { + m_stages = recorder.take(); + ctl.update_status(100, status); + return; + } + if (!prepared.mesh.indices.empty()) { + mesh = prepared.mesh; + masks = prepared.masks; + } + } + + build_texture_displacement(mesh, m_input.layers, masks, m_input.options, + [&report](int pct) { return report(50 + pct / 2); }, + /* color */ nullptr, m_input.volume_to_world, &recorder); + + m_stages = recorder.take(); + ctl.update_status(100, status); // always finish at 100: this is what closes the notification +} + +void TextureDisplacementDebugJob::finalize(bool canceled, std::exception_ptr &eptr) +{ + if (!m_on_finished) + return; + // The handler must run on every outcome - the caller uses it to clear its in-progress latch, and + // an empty vector is its signal that nothing usable came back. + if (canceled || eptr) + m_on_finished({}); + else + m_on_finished(std::move(m_stages)); +} + +void queue_texture_displacement_debug(const ModelVolume &volume, const TextureColorSettings &color, + const TextureDisplacementPrepareParams &prepare_params, + bool run_prepare, bool check_topology, + std::function)> on_finished) +{ + TextureDisplacementDebugInput input; + input.volume_id = volume.id(); + input.base_mesh = volume.mesh().its; + input.layers = volume.texture_displacement_layers; + input.options = volume.texture_displacement_options; + input.prepare_params = prepare_params; + input.run_prepare = run_prepare; + input.check_topology = check_topology; + input.color = color; + input.volume_to_world = texture_displacement_volume_to_world(volume); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + input.facets_data[size_t(i)] = volume.texture_displacement_facet(i).get_data(); + + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::make_unique(std::move(input), std::move(on_finished))); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.hpp new file mode 100644 index 0000000000..0e7ca5c8a2 --- /dev/null +++ b/src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.hpp @@ -0,0 +1,82 @@ +#ifndef slic3r_TextureDisplacementDebugJob_hpp_ +#define slic3r_TextureDisplacementDebugJob_hpp_ + +#include +#include + +#include "libslic3r/ObjectID.hpp" +#include "libslic3r/TextureBake/TextureBakeDebug.hpp" +#include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TriangleMesh.hpp" + +#include "Job.hpp" + +namespace Slic3r { +class ModelVolume; +} + +namespace Slic3r::GUI { + +// Runs a bake with stage capture on and commits nothing. +// +// A bake is a chain of stages that each rewrite the whole mesh - remesh, refine, displace, smooth, or +// for the experimental pipeline subdivide, regularize, relocate, displace, decimate, repair - and when +// the result looks wrong the only useful question is which of them made it wrong. This runs the same +// recipe the Bake button runs, keeps every intermediate mesh, and hands them back for the gizmo to +// step through. +// +// Deliberately separate from TextureDisplacementBakeJob rather than a flag on it: this must never +// touch the Model, and keeping the committing path free of a "but not this time" branch is what +// guarantees that. +struct TextureDisplacementDebugInput +{ + ObjectID volume_id; + indexed_triangle_set base_mesh; + std::vector layers; + TextureDisplacementFacetsData facets_data; + TextureDisplacementOptions options; + // The preparation stages - remesh and adaptive refinement. Only the default pipeline uses them; + // the experimental one refines as part of the bake and ignores this entirely. + TextureDisplacementPrepareParams prepare_params; + bool run_prepare = true; + // Mesh coordinates -> world millimetres, as the bake gets it. The captured stages are brought + // back into mesh coordinates before they are handed over, so the gizmo can draw them directly. + Transform3d volume_to_world = Transform3d::Identity(); + // Only the quantizer is used, and only by the refinement's colour criterion - the debug run + // produces no colour of its own. + TextureColorSettings color; + + // The edge scan behind the open / non-manifold counts is a sort over every half-edge, which on a + // multi-million triangle stage costs more than the stage did. On by default because a stage that + // tore the mesh is exactly what this exists to find. + bool check_topology = true; + // Stages above this keep their counts but not their geometry - see BakeStageRecorder. + size_t mesh_cap = 4'000'000; +}; + +class TextureDisplacementDebugJob : public Job +{ +public: + TextureDisplacementDebugJob(TextureDisplacementDebugInput &&input, + std::function)> on_finished); + + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; + +private: + TextureDisplacementDebugInput m_input; + std::vector m_stages; + std::function)> m_on_finished; +}; + +// Captures `volume`'s current mesh, layers and paint and queues one debug run. `on_finished` always +// runs exactly once on the UI thread, with an empty vector if the run was cancelled or threw, so the +// caller can clear its own in-progress state. Must be called from the main thread. +void queue_texture_displacement_debug(const ModelVolume &volume, const TextureColorSettings &color, + const TextureDisplacementPrepareParams &prepare_params, + bool run_prepare, bool check_topology, + std::function)> on_finished); + +} // namespace Slic3r::GUI + +#endif // slic3r_TextureDisplacementDebugJob_hpp_ diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index adde26f884..013ea79750 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -43,7 +43,7 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) (!m_current_generation || m_current_generation->load() == m_generation); }, - color); + color, m_input.volume_to_world); } void TextureDisplacementPreviewJob::finalize(bool canceled, std::exception_ptr &eptr) diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp index 2a67b8c7cc..312cf6593e 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp @@ -23,6 +23,9 @@ struct TextureDisplacementPreviewInput std::vector layers; TextureDisplacementFacetsData facets_data; TextureDisplacementOptions options; + // Mesh coordinates -> world millimetres, so the preview is displaced in the same space the bake + // is and the two cannot disagree. See build_texture_displacement(). + Transform3d volume_to_world = Transform3d::Identity(); // Empty unless a layer is colouring, in which case the preview reports the filament per triangle // alongside the mesh, so the Normal view shows what the bake will produce - interleaving included. TextureColorSettings color; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 497c75ef06..efbd20f133 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -7484,6 +7484,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) .Right() .Hide() .BestSize(wxSize(40 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()))); + // Closing the pane with its own X has to reach the gizmo, or its next update would simply show the pane again. + q->Bind(wxEVT_AUI_PANE_CLOSE, [this](wxAuiManagerEvent &evt) { + evt.Skip(); + if (evt.GetPane() != nullptr && evt.GetPane()->window == uv_editor_panel && uv_editor_canvas != nullptr) + uv_editor_canvas->run_command(UVEditorCanvas::Command::PaneClosed); + }); auto* panel_sizer = new wxBoxSizer(wxHORIZONTAL); panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0); diff --git a/src/slic3r/GUI/TextureLibrary.cpp b/src/slic3r/GUI/TextureLibrary.cpp index 3af043a462..30df4af22c 100644 --- a/src/slic3r/GUI/TextureLibrary.cpp +++ b/src/slic3r/GUI/TextureLibrary.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -15,7 +17,10 @@ #include "libslic3r/PNGReadWrite.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/AppConfig.hpp" + #include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/I18N.hpp" namespace Slic3r::GUI { @@ -168,6 +173,70 @@ bool is_supported_height_map(const std::vector &bytes) std::vector g_library; bool g_library_scanned = false; +// The user's own textures, most recently used first, kept in the app config. File names are stored rather +// than paths so the order survives a moved data directory; '/' separates them because no platform allows +// it inside a file name. Bounded so a long-lived config does not grow a line without end. +const char *const RECENT_SECTION = "texture_displacement"; +const char *const RECENT_KEY = "recent_textures"; +const size_t RECENT_MAX = 200; + +std::vector recent_texture_names() +{ + std::vector names; + if (wxGetApp().app_config == nullptr) + return names; + const std::string value = wxGetApp().app_config->get(RECENT_SECTION, RECENT_KEY); + for (size_t begin = 0; begin <= value.size();) { + const size_t end = std::min(value.find('/', begin), value.size()); + if (end > begin) + names.push_back(value.substr(begin, end - begin)); + begin = end + 1; + } + return names; +} + +void store_recent_texture_names(std::vector names) +{ + if (wxGetApp().app_config == nullptr) + return; + if (names.size() > RECENT_MAX) + names.resize(RECENT_MAX); + std::string value; + for (const std::string &name : names) + value += (value.empty() ? "" : "/") + name; + wxGetApp().app_config->set(RECENT_SECTION, RECENT_KEY, value); +} + +// Reorders the user's entries (the tail of `library`) by recent use. Stable, so textures never used keep +// the name order scan_dir() gave them, after every used one. +void sort_user_textures(std::vector &library) +{ + const std::vector names = recent_texture_names(); + std::unordered_map rank; + for (size_t i = 0; i < names.size(); ++i) + rank.emplace(names[i], int(i)); + const auto rank_of = [&rank](const TextureLibraryEntry &e) { + const auto it = rank.find(boost::filesystem::path(e.path).filename().string()); + return it == rank.end() ? std::numeric_limits::max() : it->second; + }; + const auto first_user = std::find_if(library.begin(), library.end(), [](const TextureLibraryEntry &e) { return e.is_user; }); + std::stable_sort(first_user, library.end(), + [&rank_of](const TextureLibraryEntry &a, const TextureLibraryEntry &b) { return rank_of(a) < rank_of(b); }); +} + +// True if `path` is a file directly inside user_texture_dir() - the only place textures may be removed +// from, or have their use recorded. +bool in_user_texture_dir(const std::string &path) +{ + const std::string dir = user_texture_dir(); + if (dir.empty()) + return false; + boost::system::error_code ec; + const bool same = boost::filesystem::equivalent(boost::filesystem::path(path).parent_path(), + boost::filesystem::path(dir), ec); + return !ec && same; +} + } // namespace std::string user_texture_dir() @@ -192,11 +261,47 @@ const std::vector &texture_library(bool force_rescan) const std::string user_dir = user_texture_dir(); if (!user_dir.empty()) scan_dir(boost::filesystem::path(user_dir), true, g_library); + sort_user_textures(g_library); g_library_scanned = true; return g_library; } +void touch_user_texture(const std::string &path) +{ + if (!in_user_texture_dir(path)) + return; + const std::string name = boost::filesystem::path(path).filename().string(); + std::vector names = recent_texture_names(); + names.erase(std::remove(names.begin(), names.end(), name), names.end()); + names.insert(names.begin(), name); + store_recent_texture_names(std::move(names)); + if (g_library_scanned) + sort_user_textures(g_library); +} + +bool remove_user_texture(const std::string &path, std::string &error) +{ + if (!in_user_texture_dir(path)) { + error = _u8L("Only your own imported textures can be removed."); + return false; + } + boost::system::error_code ec; + boost::filesystem::remove(boost::filesystem::path(path), ec); + if (ec) { + BOOST_LOG_TRIVIAL(error) << "Could not delete texture " << path << ": " << ec.message(); + error = _u8L("Could not delete the texture file."); + return false; + } + + const std::string name = boost::filesystem::path(path).filename().string(); + std::vector names = recent_texture_names(); + names.erase(std::remove(names.begin(), names.end(), name), names.end()); + store_recent_texture_names(std::move(names)); + texture_library(true); // drop it from the list + return true; +} + std::optional import_texture_to_library(const std::string &source_path, std::string &error) { const std::string user_dir = user_texture_dir(); diff --git a/src/slic3r/GUI/TextureLibrary.hpp b/src/slic3r/GUI/TextureLibrary.hpp index 4140acaadc..65b35bcea8 100644 --- a/src/slic3r/GUI/TextureLibrary.hpp +++ b/src/slic3r/GUI/TextureLibrary.hpp @@ -16,8 +16,9 @@ struct TextureLibraryEntry bool is_user; // imported by the user, as opposed to shipped with OrcaSlicer }; -// Every available height-map texture: the ones shipped in resources/textures/displacement first, -// then the user's own from /textures/displacement, each group sorted by name. +// Every available height-map texture: the ones shipped in resources/textures/displacement first, sorted +// by name, then the user's own from /textures/displacement - most recently used first (see +// touch_user_texture()), then any never used, by name. // // The two live in separate directories deliberately: an app update replaces the resources tree // wholesale, so anything the user imported has to sit somewhere that update can never overwrite or @@ -27,6 +28,17 @@ struct TextureLibraryEntry // into either folder by hand while the app was running. const std::vector &texture_library(bool force_rescan = false); +// Moves one of the user's own textures to the front of the recently-used order texture_library() lists +// them in, and stores that order in the app config so it survives a restart. No-op for anything that is +// not in user_texture_dir(). +void touch_user_texture(const std::string &path); + +// Deletes one of the user's own imported textures from user_texture_dir() and forgets it. Anything outside +// that folder is refused, so a shipped texture can never be removed this way. Layers already using the +// texture are unaffected: they hold the image bytes themselves, and a project stores those. Returns false +// with `error` set on failure. +bool remove_user_texture(const std::string &path, std::string &error); + // /textures/displacement, created if it does not exist yet. Empty string on failure. std::string user_texture_dir(); diff --git a/src/slic3r/GUI/UVEditorCanvas.cpp b/src/slic3r/GUI/UVEditorCanvas.cpp index 25d737f2bb..855e8630d7 100644 --- a/src/slic3r/GUI/UVEditorCanvas.cpp +++ b/src/slic3r/GUI/UVEditorCanvas.cpp @@ -7,14 +7,21 @@ #include +#include +#include + #include "3DScene.hpp" +#include "BitmapCache.hpp" #include "GLShader.hpp" #include "GUI_App.hpp" #include "I18N.hpp" #include "OpenGLManager.hpp" #include "Plater.hpp" #include "wxExtensions.hpp" +#include "Widgets/CheckBox.hpp" +#include "Widgets/SpinInput.hpp" #include "libslic3r/AppConfig.hpp" +#include "libslic3r/Utils.hpp" namespace Slic3r::GUI { @@ -23,22 +30,47 @@ namespace { // prefix is not decoration: COLOR_BACKGROUND (and several other COLOR_*) are Win32 system-colour // macros from WinUser.h, and an unprefixed name here expands to an integer literal mid-declaration. const ColorRGBA UV_COLOR_BG = { 0.16f, 0.16f, 0.16f, 1.f }; -const ColorRGBA UV_COLOR_GRID = { 1.f, 1.f, 1.f, 0.10f }; -const ColorRGBA UV_COLOR_TILE_OUTLINE = { 1.f, 1.f, 1.f, 0.45f }; -const ColorRGBA UV_COLOR_WIRE = { 0.85f, 0.85f, 0.85f, 0.35f }; // interior edges, unselected -const ColorRGBA UV_COLOR_BOUNDARY = { 1.f, 0.35f, 0.15f, 0.85f }; // island outline == a seam, so Blender's seam red -// Island fills (#4): unselected a light green kept translucent so the texture/checker/wireframe under -// it still reads (the user asked for both these fills *and* to see the check overlay through them, so -// the alphas stay low); the selected one switches to the app accent teal #009688 and is marked mainly -// by its bold boundary and brighter wire rather than a heavy fill. -const ColorRGBA UV_COLOR_FILL = { 0.55f, 0.85f, 0.45f, 0.40f }; // unselected: light green -const ColorRGBA UV_COLOR_SEL_FILL = { 0.0f, 0.588f, 0.533f, 0.20f }; // selected: #009688 teal -const ColorRGBA UV_COLOR_SEL_WIRE = { 0.75f, 1.f, 0.7f, 0.55f }; -const ColorRGBA UV_COLOR_SEL_BOUNDARY = { 0.2f, 1.f, 0.55f, 1.f }; // bold light-green edge (#7) -const ColorRGBA UV_COLOR_DIAL = { 1.f, 0.85f, 0.2f, 0.9f }; // rotation protractor (#11) +// The texture outside every island is washed towards the background, so the islands read as the lit areas +// whatever the texture looks like - a white brick texture no longer swallows a light outline. +const ColorRGBA UV_COLOR_OUTSIDE = { 0.16f, 0.16f, 0.16f, 0.72f }; +const ColorRGBA UV_COLOR_GRID = { 1.f, 1.f, 1.f, 0.08f }; +// Every stroke that must stay legible is drawn twice: a wider near-black halo, then the colour on top, so it +// holds up on light and dark texels alike. +const ColorRGBA UV_COLOR_HALO = { 0.06f, 0.06f, 0.07f, 1.f }; +const ColorRGBA UV_COLOR_TILE_OUTLINE = { 0.70f, 0.70f, 0.72f, 1.f }; +const ColorRGBA UV_COLOR_WIRE = { 1.f, 1.f, 1.f, 0.30f }; // interior edges, unselected +const ColorRGBA UV_COLOR_WIRE_SHADOW = { 0.f, 0.f, 0.f, 0.35f }; // 1 px offset under the wire +const ColorRGBA UV_COLOR_BOUNDARY = { 0.93f, 0.93f, 0.94f, 1.f }; +const ColorRGBA UV_COLOR_HOVER = { 0.72f, 0.98f, 0.93f, 1.f }; // what a click would grab +const ColorRGBA UV_COLOR_HOVER_FILL = { 1.f, 1.f, 1.f, 0.12f }; +// No wash on unselected islands: the texture inside them is exactly what gets baked, so it shows unaltered. +const ColorRGBA UV_COLOR_FILL = { 1.f, 1.f, 1.f, 0.f }; +const ColorRGBA UV_COLOR_SEL_FILL = { 0.15f, 0.85f, 0.75f, 0.24f }; +const ColorRGBA UV_COLOR_SEL_WIRE = { 0.55f, 1.f, 0.92f, 0.55f }; +const ColorRGBA UV_COLOR_SEL_BOUNDARY = { 0.16f, 0.90f, 0.78f, 1.f }; // the app teal, brightened to read on texture +const ColorRGBA UV_COLOR_DIAL = { 1.f, 0.85f, 0.2f, 0.9f }; // rotation protractor (#11) constexpr float SNAP_PIXELS = 28.f; // how close a boundary vertex has to come before it sticks (#2) +// wxWidgets reports this canvas' size in *logical* points, while the GL drawable behind it is sized +// in device pixels. On the backends where those differ under HiDPI (the same pair GLCanvas3D guards +// its RetinaHelper with) a viewport built straight from GetSize() covers only the bottom-left +// 1/scale of the drawable, which is exactly where the whole editor ended up drawn, shrunken. +// Mouse coordinates arrive in logical points, so only the viewport needs converting - every other +// GetSize() use here is compared against event coordinates and must stay logical. +wxSize gl_drawable_size(const wxWindow *win, const wxSize &logical_size) +{ +#if defined(__APPLE__) || defined(__WXGTK3__) + const double scale = (win != nullptr) ? win->GetContentScaleFactor() : 1.0; + if (scale > 0.0) + return wxSize(std::max(1, int(std::lround(logical_size.GetWidth() * scale))), + std::max(1, int(std::lround(logical_size.GetHeight() * scale)))); +#else + (void) win; +#endif + return wxSize(std::max(1, logical_size.GetWidth()), std::max(1, logical_size.GetHeight())); +} + // The pixel format this canvas is created with has to match the one the app's single shared // wxGLContext was created against (that of View3D's canvas, from OpenGLManager::create_wxglcanvas()), // so this mirrors that attribute list *including its multisampling*: WGL requires the HDC passed to @@ -180,20 +212,47 @@ void UVEditorCanvas::set_islands(Islands islands) m_active_edge = { -1, -1 }; m_sel_vertices.clear(); m_sel_edges.clear(); + m_hover_island = m_hover_vertex = -1; + m_hover_edge = { -1, -1 }; } - // Boundary vertices, bucketed per island, for snapping. - m_island_boundary_verts.assign(size_t(std::max(m_islands.island_count, 0)), {}); + // Boundary vertices and edges, bucketed per island: the vertices for snapping, the edges for the outline. + const size_t n_islands = size_t(std::max(m_islands.island_count, 0)); + m_island_boundary_verts.assign(n_islands, {}); + m_island_boundary_edges.assign(n_islands, {}); std::vector seen(m_islands.uvs.size(), false); - for (const auto &[a, b] : m_islands.boundary_edges) - for (const int v : { a, b }) { - if (v < 0 || size_t(v) >= m_islands.uvs.size() || seen[size_t(v)]) - continue; - seen[size_t(v)] = true; - const int island = m_islands.vertex_island[size_t(v)]; - if (island >= 0 && size_t(island) < m_island_boundary_verts.size()) + for (const auto &[a, b] : m_islands.boundary_edges) { + if (a < 0 || b < 0 || size_t(a) >= m_islands.uvs.size() || size_t(b) >= m_islands.uvs.size()) + continue; + const int island = m_islands.vertex_island[size_t(a)]; + if (island < 0 || size_t(island) >= n_islands) + continue; + m_island_boundary_edges[size_t(island)].emplace_back(a, b); + for (const int v : { a, b }) + if (!seen[size_t(v)]) { + seen[size_t(v)] = true; m_island_boundary_verts[size_t(island)].push_back(v); + } + } + + // Triangles and raw bounds per island, for picking. + m_island_tris.assign(n_islands, {}); + m_island_raw_bounds.assign(n_islands, { Vec2f::Constant(std::numeric_limits::max()), + Vec2f::Constant(std::numeric_limits::lowest()) }); + for (size_t t = 0; t < m_islands.indices.size(); ++t) { + const Vec3i32 &tri = m_islands.indices[t]; + if (tri.minCoeff() < 0 || size_t(tri.maxCoeff()) >= m_islands.uvs.size()) + continue; + const int island = m_islands.vertex_island[size_t(tri[0])]; + if (island < 0 || size_t(island) >= n_islands) + continue; + m_island_tris[size_t(island)].push_back(int(t)); + auto &[lo, hi] = m_island_raw_bounds[size_t(island)]; + for (int k = 0; k < 3; ++k) { + lo = lo.cwiseMin(m_islands.uvs[size_t(tri[k])]); + hi = hi.cwiseMax(m_islands.uvs[size_t(tri[k])]); } + } m_mesh_dirty = true; m_background_quad_dirty = true; // the backdrop is sized to the unwrap, so it moved too @@ -249,21 +308,29 @@ void UVEditorCanvas::reset_view() Refresh(); } -void UVEditorCanvas::run_command(Command cmd) +void UVEditorCanvas::run_command(Command cmd, float value) { switch (cmd) { case Command::FrameAll: reset_view(); return; case Command::ToggleSnap: m_snap_enabled = !m_snap_enabled; update_status(); return; - // The rest need the layer data the canvas doesn't hold; hand them to the gizmo. - case Command::AverageScale: - case Command::CutSelectedIsland: - case Command::ProjectFromView: - case Command::JoinSelected: - case Command::UnjoinSelected: - if (m_on_command) - m_on_command(cmd); - return; + case Command::SetSelectMode: + // Switched here straight away so the strip and the canvas agree at once; the gizmo is still told, + // because it is what keeps the mode when the canvas is next refreshed from the layer. + set_select_mode(static_cast(std::clamp(int(value), 0, 2))); + break; + default: break; } + // Everything else needs the layer data the canvas doesn't hold; hand it to the gizmo. + if (m_on_command) + m_on_command(cmd, value); +} + +void UVEditorCanvas::set_pane_state(PaneState state) +{ + m_pane_state = std::move(state); + update_status(); + if (m_on_pane_state) + m_on_pane_state(m_pane_state); } void UVEditorCanvas::update_status() @@ -285,7 +352,10 @@ void UVEditorCanvas::update_status() case Gesture::Pan: msg = _L("Panning"); break; case Gesture::None: default: - if (m_select_mode == SelectMode::Vertex) + if (!has_islands()) + msg = m_pane_state.has_layer ? _L("Paint the area on the model, then press Unwrap") : + _L("Pick Unwrap as a texture layer's mapping to edit its UVs here"); + else if (m_select_mode == SelectMode::Vertex) msg = m_sel_vertices.size() > 1 ? wxString::Format(_L("%d vertices selected | drag = move together, Shift/Ctrl click = add/remove"), int(m_sel_vertices.size())) : @@ -333,13 +403,37 @@ void UVEditorCanvas::content_bounds(Vec2f &min_uv, Vec2f &max_uv) const } } +void UVEditorCanvas::framed_bounds(Vec2f &min_uv, Vec2f &max_uv) const +{ + content_bounds(min_uv, max_uv); + // With tiling on, the backdrop is snapped out to whole tiles, so it is bigger than the raw + // bounds - and by a different amount on each side. Framing the raw bounds therefore left that + // backdrop visibly off-centre: hanging past one edge of the pane with dead space against the + // other. Frame what is drawn instead. (Tiling off draws only the first tile, which the bounds + // already contain, so there is nothing to snap.) + if (m_tile_enabled) { + min_uv = Vec2f(std::floor(min_uv.x()), std::floor(min_uv.y())); + max_uv = Vec2f(std::ceil(max_uv.x()), std::ceil(max_uv.y())); + } +} + void UVEditorCanvas::fit_view_to_content() { Vec2f min_uv, max_uv; - content_bounds(min_uv, max_uv); + framed_bounds(min_uv, max_uv); - m_pan = 0.5f * (min_uv + max_uv); - m_zoom = std::max(0.5f * (max_uv - min_uv).maxCoeff() * 1.1f, 0.05f); + const wxSize size = GetSize(); + const float aspect = float(std::max(1, size.GetWidth())) / float(std::max(1, size.GetHeight())); + const Vec2f half = 0.5f * (max_uv - min_uv); + + m_pan = 0.5f * (min_uv + max_uv); + // m_zoom is the half-extent shown across the *shorter* pane edge (see view_half_extents()), so + // each axis' required half-extent has to be converted back into that unit before the larger of + // the two is taken. Sizing off the bigger axis alone, as this did, ignores the pane's shape and + // zooms out further than either axis needs on anything but a square pane. The 1.1 leaves a + // margin so the outermost island edge is not flush against the frame. + m_zoom = std::max(1.1f * std::max(half.x() / std::max(aspect, 1.f), half.y() * std::min(aspect, 1.f)), + 0.05f); m_needs_fit = false; } @@ -376,23 +470,58 @@ Vec2f UVEditorCanvas::screen_to_uv(const wxPoint &px) const int UVEditorCanvas::island_at(const Vec2f &uv) const { int found = -1; - for (const Vec3i32 &tri : m_islands.indices) { - if (tri.minCoeff() < 0 || size_t(tri.maxCoeff()) >= m_islands.uvs.size()) + for (int island = 0; island < int(m_island_tris.size()); ++island) { + // The raw bounds through the island's affine: the box of the four transformed corners contains the island. + const auto &[lo, hi] = m_island_raw_bounds[size_t(island)]; + if (lo.x() > hi.x()) continue; - if (!point_in_triangle(uv, island_uv(size_t(tri[0])), island_uv(size_t(tri[1])), island_uv(size_t(tri[2])))) + const IslandTransform m = size_t(island) < m_transforms.size() ? m_transforms[size_t(island)] : + IslandTransform(IslandTransform::Identity()); + Vec2f bmin = Vec2f::Constant(std::numeric_limits::max()), bmax = -bmin; + for (const Vec2f &corner : { lo, hi, Vec2f(lo.x(), hi.y()), Vec2f(hi.x(), lo.y()) }) { + const Vec2f p = m.block<2, 2>(0, 0) * corner + m.col(2); + bmin = bmin.cwiseMin(p); + bmax = bmax.cwiseMax(p); + } + if (uv.x() < bmin.x() || uv.y() < bmin.y() || uv.x() > bmax.x() || uv.y() > bmax.y()) continue; - const int island = m_islands.vertex_island[size_t(tri[0])]; - // Islands are allowed to overlap, so a point can be inside several. Keep whichever is already - // selected - otherwise a drag of a partly-covered island would be stolen mid-gesture by the - // one on top of it. - if (is_selected(island)) - return island; - found = island; + for (const int t : m_island_tris[size_t(island)]) { + const Vec3i32 &tri = m_islands.indices[size_t(t)]; + if (!point_in_triangle(uv, island_uv(size_t(tri[0])), island_uv(size_t(tri[1])), island_uv(size_t(tri[2])))) + continue; + // Islands are allowed to overlap, so a point can be inside several. Keep whichever is already + // selected - otherwise a drag of a partly-covered island would be stolen mid-gesture by the + // one on top of it. + if (is_selected(island)) + return island; + found = island; + break; + } } return found; } +void UVEditorCanvas::update_hover(const wxPoint &pos) +{ + int island = -1, vertex = -1; + std::pair edge{ -1, -1 }; + if (has_islands()) { + const Vec2f uv = screen_to_uv(pos); + switch (m_select_mode) { + case SelectMode::Island: island = island_at(uv); break; + case SelectMode::Vertex: vertex = vertex_at(uv); break; + case SelectMode::Edge: edge = edge_at(uv); break; + } + } + if (island != m_hover_island || vertex != m_hover_vertex || edge != m_hover_edge) { + m_hover_island = island; + m_hover_vertex = vertex; + m_hover_edge = edge; + Refresh(); + } +} + void UVEditorCanvas::set_select_mode(SelectMode mode) { if (m_select_mode == mode) @@ -400,6 +529,8 @@ void UVEditorCanvas::set_select_mode(SelectMode mode) m_select_mode = mode; m_active_vertex = -1; m_active_edge = { -1, -1 }; + m_hover_island = m_hover_vertex = -1; + m_hover_edge = { -1, -1 }; m_sel_vertices.clear(); m_sel_edges.clear(); m_gesture = Gesture::None; @@ -467,6 +598,11 @@ void UVEditorCanvas::move_vertex_raw(int v, const Vec2f &delta_uv) const float det = lin.determinant(); const Vec2f raw_delta = (std::abs(det) > 1e-12f) ? Vec2f(lin.inverse() * delta_uv) : delta_uv; m_islands.uvs[size_t(v)] += raw_delta; + if (island >= 0 && size_t(island) < m_island_raw_bounds.size()) { // keep the pick box around the moved vertex + auto &[lo, hi] = m_island_raw_bounds[size_t(island)]; + lo = lo.cwiseMin(m_islands.uvs[size_t(v)]); + hi = hi.cwiseMax(m_islands.uvs[size_t(v)]); + } m_mesh_dirty = true; // the edited raw uv is redrawn from rebuild_island_models() next frame } @@ -673,8 +809,11 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) // Track the pointer so the +/- add-remove hint can be drawn next to it in Vertex/Edge mode. m_cursor_px = pos; m_cursor_inside = true; - if (type == wxEVT_MOTION && m_select_mode != SelectMode::Island && m_gesture == Gesture::None) - Refresh(); // animate the hint (and its +/- flip) as the pointer/modifiers move + if (type == wxEVT_MOTION && m_gesture == Gesture::None) { + update_hover(pos); + if (m_select_mode != SelectMode::Island) + Refresh(); // animate the hint (and its +/- flip) as the pointer/modifiers move + } // Key events (R/S/Home) only arrive if this canvas has focus, and clicking it is the natural way // to ask for it - the pane is not in the tab order. @@ -885,6 +1024,8 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt) m_zoom = std::clamp(m_zoom * factor, 0.001f, 5000.f); const Vec2f after = screen_to_uv(pos); m_pan += before - after; + if (m_gesture == Gesture::None) + update_hover(pos); Refresh(); } } @@ -893,7 +1034,6 @@ void UVEditorCanvas::rebuild_island_models() { m_mesh_dirty = false; m_island_wireframe.clear(); - m_island_boundary.clear(); m_island_fill.clear(); const int islands = std::max(m_islands.island_count, 0); @@ -901,7 +1041,6 @@ void UVEditorCanvas::rebuild_island_models() return; m_island_wireframe.resize(size_t(islands)); - m_island_boundary.resize(size_t(islands)); m_island_fill.resize(size_t(islands)); const int vertex_count = int(m_islands.uvs.size()); @@ -929,12 +1068,11 @@ void UVEditorCanvas::rebuild_island_models() island_verts[size_t(island)].push_back(m_islands.uvs[i]); } - std::vector wire(n_islands), outline(n_islands), fill(n_islands); + std::vector wire(n_islands), fill(n_islands); for (int c = 0; c < islands; ++c) { - for (GLModel::Geometry *g : { &wire[size_t(c)], &outline[size_t(c)] }) - g->format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + wire[size_t(c)].format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; fill[size_t(c)].format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - for (GLModel::Geometry *g : { &wire[size_t(c)], &outline[size_t(c)], &fill[size_t(c)] }) { + for (GLModel::Geometry *g : { &wire[size_t(c)], &fill[size_t(c)] }) { g->reserve_vertices(island_verts[size_t(c)].size()); for (const Vec2f &uv : island_verts[size_t(c)]) g->add_vertex(Vec3f(uv.x(), uv.y(), 0.f)); @@ -959,20 +1097,9 @@ void UVEditorCanvas::rebuild_island_models() } } - for (const auto &[a, b] : m_islands.boundary_edges) { - if (a < 0 || b < 0 || a >= vertex_count || b >= vertex_count) - continue; - const int island = m_islands.vertex_island[size_t(a)]; - if (island < 0 || island >= islands) - continue; - outline[size_t(island)].add_line(unsigned(local[size_t(a)]), unsigned(local[size_t(b)])); - } - for (int c = 0; c < islands; ++c) { if (!wire[size_t(c)].is_empty()) m_island_wireframe[size_t(c)].init_from(std::move(wire[size_t(c)])); - if (!outline[size_t(c)].is_empty()) - m_island_boundary[size_t(c)].init_from(std::move(outline[size_t(c)])); if (!fill[size_t(c)].is_empty()) m_island_fill[size_t(c)].init_from(std::move(fill[size_t(c)])); } @@ -1073,12 +1200,11 @@ void UVEditorCanvas::rebuild_background_quad() return; Vec2f lo, hi; - content_bounds(lo, hi); if (m_tile_enabled) { - // Snap out to whole tiles. Only cosmetic, but it keeps the backdrop's edge on a tile - // boundary instead of slicing a brick in half at an arbitrary place. - lo = Vec2f(std::floor(lo.x()), std::floor(lo.y())); - hi = Vec2f(std::ceil(hi.x()), std::ceil(hi.y())); + // Whole tiles, so the backdrop's edge lands on a tile boundary instead of slicing a brick in + // half. framed_bounds() applies exactly this, and the view is framed on its result - the two + // must not drift apart or the backdrop stops being centred in the pane. + framed_bounds(lo, hi); } else { // Tiling off: the sampler reads 0 outside the first tile and nothing else exists, so the // backdrop is exactly that one tile (GL_CLAMP_TO_BORDER in render() gives it the same @@ -1137,8 +1263,12 @@ void UVEditorCanvas::on_leave(wxMouseEvent &evt) evt.Skip(); if (m_cursor_inside) { m_cursor_inside = false; - if (m_select_mode != SelectMode::Island) - Refresh(); // the +/- hint was following the cursor; drop it now the pointer is gone + // The +/- hint and the hover highlight were following the cursor; drop them now the pointer is gone. + if (m_gesture == Gesture::None) { + m_hover_island = m_hover_vertex = -1; + m_hover_edge = { -1, -1 }; + } + Refresh(); } } @@ -1154,6 +1284,30 @@ void UVEditorCanvas::render() if (!SetCurrent(*m_context)) return; + // The context is shared with the 3D view, which leaves its own state behind: GLCanvas3D turns face culling on at + // init and many of its passes turn it back on, and the pane's projection flips Y, so every triangle drawn here + // faces away from it. With culling left on, the texture and the islands simply vanish - which is why they showed + // the first time and were gone, fully or partly, after the 3D view had drawn. A scissor rectangle left on would + // clip the clear and everything else the same way. Set what this pass relies on; put the 3D view's back after. + const GLboolean prev_cull = ::glIsEnabled(GL_CULL_FACE); + const GLboolean prev_scissor = ::glIsEnabled(GL_SCISSOR_TEST); + const GLboolean prev_stencil = ::glIsEnabled(GL_STENCIL_TEST); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glDisable(GL_SCISSOR_TEST)); + glsafe(::glDisable(GL_STENCIL_TEST)); + glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); + glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + + if (m_gl_reset_pending) { + m_gl_reset_pending = false; + m_mesh_dirty = true; + m_background_dirty = true; // re-uploads m_background_pixels, or drops the texture if there are none + m_tile_outline_glmodel.reset(); + m_vertex_marker_glmodel.reset(); + m_dim_quad_glmodel.reset(); + m_stroke_glmodel.reset(); + m_stencil_bits = -1; + } if (m_mesh_dirty) rebuild_island_models(); if (m_background_dirty) @@ -1165,10 +1319,17 @@ void UVEditorCanvas::render() rebuild_grid(); rebuild_rotation_dial(); - const wxSize size = GetSize(); - glsafe(::glViewport(0, 0, size.GetWidth(), size.GetHeight())); + const wxSize size = GetSize(); // logical points; the on-screen handle sizes below use it + const wxSize viewport = gl_drawable_size(this, size); + glsafe(::glViewport(0, 0, viewport.GetWidth(), viewport.GetHeight())); + // Line widths below are authored in logical points (they are chosen against the same scale the + // hit-test thresholds use), so they take the same logical -> device conversion as the viewport. + const float px_scale = float(viewport.GetWidth()) / float(std::max(1, size.GetWidth())); + const auto line_width = [px_scale](float w) { set_line_width(w * px_scale); }; glsafe(::glClearColor(UV_COLOR_BG.r(), UV_COLOR_BG.g(), UV_COLOR_BG.b(), 1.f)); - glsafe(::glClear(GL_COLOR_BUFFER_BIT)); + glsafe(::glClearStencil(0)); + glsafe(::glStencilMask(0xFF)); + glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -1185,19 +1346,22 @@ void UVEditorCanvas::render() Transform3d view_matrix = Transform3d::Identity(); view_matrix.translate(Vec3d(-double(m_pan.x()), -double(m_pan.y()), 0.0)); - const auto draw = [&](GLModel &model, const ColorRGBA &color, const Transform3d &model_matrix) { + const auto draw_raw = [&](GLModel &model, const ColorRGBA &color, const Transform3d &view_model, const Transform3d &projection) { if (!model.is_initialized()) return; GLShaderProgram *shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; shader->start_using(); - shader->set_uniform("view_model_matrix", view_matrix * model_matrix); - shader->set_uniform("projection_matrix", projection_matrix); + shader->set_uniform("view_model_matrix", view_model); + shader->set_uniform("projection_matrix", projection); model.set_color(color); model.render(); shader->stop_using(); }; + const auto draw = [&](GLModel &model, const ColorRGBA &color, const Transform3d &model_matrix) { + draw_raw(model, color, view_matrix * model_matrix, projection_matrix); + }; const Transform3d identity = Transform3d::Identity(); if (m_background_glmodel.is_initialized()) { @@ -1224,148 +1388,221 @@ void UVEditorCanvas::render() } } - set_line_width(1.f); + const auto island_matrix = [this, &identity](int c) { + return (size_t(c) < m_transforms.size()) ? to_transform3d(m_transforms[size_t(c)]) : identity; + }; + const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); + + // Strokes are built as quads `width_px` wide on screen, rebuilt every paint at the current zoom. Square caps + // cover the joints of an outline; the colours passed here are opaque, so the overlapping caps don't show. + using Segments = std::vector>; + const auto draw_segments = [&](const Segments &segments, float width_px, const ColorRGBA &color) { + if (segments.empty()) + return; + const float half = 0.5f * width_px * uv_per_px; + GLModel::Geometry quads; + quads.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + quads.reserve_vertices(segments.size() * 4); + quads.reserve_indices(segments.size() * 6); + unsigned base = 0; + for (const auto &[a, b] : segments) { + const Vec2f d = b - a; + const float len = d.norm(); + if (len <= 1e-12f) + continue; + const Vec2f t = d * (half / len); + const Vec2f n(-t.y(), t.x()); + for (const Vec2f &p : { Vec2f(a - t + n), Vec2f(a - t - n), Vec2f(b + t - n), Vec2f(b + t + n) }) + quads.add_vertex(Vec3f(p.x(), p.y(), 0.f)); + quads.add_triangle(base, base + 1, base + 2); + quads.add_triangle(base, base + 2, base + 3); + base += 4; + } + m_stroke_glmodel.reset(); + if (quads.is_empty()) + return; + m_stroke_glmodel.init_from(std::move(quads)); + draw(m_stroke_glmodel, color, identity); + }; + const auto draw_stroke = [&](const Segments &segments, float width_px, const ColorRGBA &color, float halo_px) { + draw_segments(segments, width_px + 2.f * halo_px, UV_COLOR_HALO); + draw_segments(segments, width_px, color); + }; + const auto edge_segments = [this](const std::vector> &edges) { + Segments out; + out.reserve(edges.size()); + for (const auto &[a, b] : edges) + if (a >= 0 && b >= 0 && size_t(a) < m_islands.uvs.size() && size_t(b) < m_islands.uvs.size()) + out.emplace_back(island_uv(size_t(a)), island_uv(size_t(b))); + return out; + }; + + // Dim the texture everywhere but inside the islands: the fills go into the stencil only, then one + // full-viewport quad washes the rest towards the background. Skipped without a stencil buffer, where the + // quad would cover the islands too. + if (m_stencil_bits < 0) { + GLint bits = 0; + ::glGetError(); // drop anything pending, so the check below sees only this query + ::glGetIntegerv(GL_STENCIL_BITS, &bits); // compatibility profiles + if (::glGetError() != GL_NO_ERROR) { + bits = 0; + ::glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &bits); + ::glGetError(); + } + m_stencil_bits = std::max(0, int(bits)); + } + if (has_islands() && !m_island_fill.empty() && m_stencil_bits > 0) { + if (!m_dim_quad_glmodel.is_initialized()) { + GLModel::Geometry q; + q.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + q.add_vertex(Vec3f(-1.f, -1.f, 0.f)); + q.add_vertex(Vec3f(1.f, -1.f, 0.f)); + q.add_vertex(Vec3f(1.f, 1.f, 0.f)); + q.add_vertex(Vec3f(-1.f, 1.f, 0.f)); + q.add_triangle(0, 1, 2); + q.add_triangle(0, 2, 3); + m_dim_quad_glmodel.init_from(std::move(q)); + } + glsafe(::glEnable(GL_STENCIL_TEST)); + glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF)); + glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); + for (int c = 0; c < int(m_island_fill.size()); ++c) + draw(m_island_fill[size_t(c)], ColorRGBA::WHITE(), island_matrix(c)); + glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + glsafe(::glStencilFunc(GL_EQUAL, 0, 0xFF)); + glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)); + draw_raw(m_dim_quad_glmodel, UV_COLOR_OUTSIDE, identity, identity); + glsafe(::glDisable(GL_STENCIL_TEST)); + } + + line_width(1.f); draw(m_grid_glmodel, UV_COLOR_GRID, identity); // The texture's first tile. Always drawn, even with nothing painted, so the pane always has a // fixed landmark: the unwrap is packed in mm and divided by the tile size, so it is routinely // many tiles away from here, and without this there is no way to tell "the islands are somewhere // else" apart from "there are no islands". - if (!m_tile_outline_glmodel.is_initialized()) { - GLModel::Geometry tile; - tile.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - tile.reserve_vertices(4); - tile.reserve_indices(8); - tile.add_vertex(Vec3f(0.f, 0.f, 0.f)); - tile.add_vertex(Vec3f(1.f, 0.f, 0.f)); - tile.add_vertex(Vec3f(1.f, 1.f, 0.f)); - tile.add_vertex(Vec3f(0.f, 1.f, 0.f)); - for (unsigned i = 0; i < 4; ++i) - tile.add_line(i, (i + 1) % 4); - m_tile_outline_glmodel.init_from(std::move(tile)); - } - set_line_width(2.f); - draw(m_tile_outline_glmodel, UV_COLOR_TILE_OUTLINE, identity); + draw_stroke({ { Vec2f(0.f, 0.f), Vec2f(1.f, 0.f) }, { Vec2f(1.f, 0.f), Vec2f(1.f, 1.f) }, + { Vec2f(1.f, 1.f), Vec2f(0.f, 1.f) }, { Vec2f(0.f, 1.f), Vec2f(0.f, 0.f) } }, + 1.5f, UV_COLOR_TILE_OUTLINE, 1.f); - const auto island_matrix = [this, &identity](int c) { - return (size_t(c) < m_transforms.size()) ? to_transform3d(m_transforms[size_t(c)]) : identity; - }; - - // Island fills first, as a translucent wash under the wires: every island gets a faint one so it - // reads as a solid patch rather than a wire cage, and the selected one gets the accent teal (#7). - // When a distortion heatmap has been supplied (set_island_fill_colors), unselected islands take - // their heatmap colour instead of the default wash; the selected one still wins its highlight. + // Island fills. Unselected islands get none, so the texture that will be baked shows unaltered; the one a + // click would grab gets a light wash, the selection a teal one. A distortion heatmap (set_island_fill_colors) + // replaces the default for unselected islands and is left alone by the hover wash. for (int c = 0; c < int(m_island_fill.size()); ++c) { - ColorRGBA fill = UV_COLOR_FILL; - if (size_t(c) < m_island_fill_colors.size()) - fill = m_island_fill_colors[size_t(c)]; - draw(m_island_fill[size_t(c)], is_selected(c) ? UV_COLOR_SEL_FILL : fill, island_matrix(c)); + ColorRGBA fill = size_t(c) < m_island_fill_colors.size() ? m_island_fill_colors[size_t(c)] : UV_COLOR_FILL; + if (is_selected(c)) + fill = UV_COLOR_SEL_FILL; + else if (c == m_hover_island && m_island_fill_colors.empty()) + fill = UV_COLOR_HOVER_FILL; + if (fill.a() > 0.f) + draw(m_island_fill[size_t(c)], fill, island_matrix(c)); } - set_line_width(1.f); - for (int c = 0; c < int(m_island_wireframe.size()); ++c) + // Interior edges stay GL lines (a patch can have a million of them), each with a dark copy one pixel down + // and right, so the light wire still reads on a light texture. + Transform3d shadow_projection = projection_matrix; + shadow_projection.pretranslate(Vec3d(2.0 / std::max(1, size.GetWidth()), -2.0 / std::max(1, size.GetHeight()), 0.0)); + line_width(1.f); + for (int c = 0; c < int(m_island_wireframe.size()); ++c) { + draw_raw(m_island_wireframe[size_t(c)], UV_COLOR_WIRE_SHADOW, view_matrix * island_matrix(c), shadow_projection); draw(m_island_wireframe[size_t(c)], is_selected(c) ? UV_COLOR_SEL_WIRE : UV_COLOR_WIRE, island_matrix(c)); - - // The island outlines - i.e. exactly the edges the seam angle cut the patch along. Drawn last - // and brightest, because "where does one island end and the next begin" is the single thing this - // pane exists to answer. The selected one gets a bold edge (#7). - for (int c = 0; c < int(m_island_boundary.size()); ++c) { - const bool selected = is_selected(c); - set_line_width(selected ? 3.f : 1.5f); - draw(m_island_boundary[size_t(c)], selected ? UV_COLOR_SEL_BOUNDARY : UV_COLOR_BOUNDARY, island_matrix(c)); } - set_line_width(1.f); + + // The island outlines - i.e. exactly the edges the seam angle cut the patch along - because "where does one + // island end and the next begin" is the single thing this pane exists to answer. Plain first, then the hovered + // one, then the selection, so a highlighted outline is never overdrawn by its neighbour's. + { + std::vector> plain, hovered, selected; + for (int c = 0; c < int(m_island_boundary_edges.size()); ++c) { + auto &dst = is_selected(c) ? selected : (c == m_hover_island ? hovered : plain); + dst.insert(dst.end(), m_island_boundary_edges[size_t(c)].begin(), m_island_boundary_edges[size_t(c)].end()); + } + draw_stroke(edge_segments(plain), 1.5f, UV_COLOR_BOUNDARY, 1.25f); + draw_stroke(edge_segments(hovered), 2.5f, UV_COLOR_HOVER, 1.5f); + draw_stroke(edge_segments(selected), 3.f, UV_COLOR_SEL_BOUNDARY, 1.75f); + } // The rotation protractor, on top of everything while a rotation gesture is live (#11). if (m_dial_glmodel.is_initialized()) { - set_line_width(2.f); + line_width(2.f); draw(m_dial_glmodel, UV_COLOR_DIAL, identity); - set_line_width(1.f); + line_width(1.f); } - // Vertex/Edge mode handles: a small square drawn over the picked vertex (or each endpoint of the - // picked edge), so it is obvious which sub-element a drag will move. The already-drawn boundary - // sits between an edge's two markers, so the pair reads as "this edge". - if (m_select_mode != SelectMode::Island && - (!m_sel_vertices.empty() || !m_sel_edges.empty() || m_active_vertex >= 0 || m_active_edge.first >= 0)) { - if (!m_vertex_marker_glmodel.is_initialized()) { - GLModel::Geometry q; - q.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - q.reserve_vertices(4); - q.reserve_indices(6); - q.add_vertex(Vec3f(-0.5f, -0.5f, 0.f)); - q.add_vertex(Vec3f(0.5f, -0.5f, 0.f)); - q.add_vertex(Vec3f(0.5f, 0.5f, 0.f)); - q.add_vertex(Vec3f(-0.5f, 0.5f, 0.f)); - q.add_triangle(0, 1, 2); - q.add_triangle(0, 2, 3); - m_vertex_marker_glmodel.init_from(std::move(q)); - } - const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); - const float marker = uv_per_px * 10.f; // ~10 px square, constant on screen at any zoom - const auto draw_marker = [&](int v) { - if (v < 0 || size_t(v) >= m_islands.uvs.size()) - return; - const Vec2f p = island_uv(size_t(v)); - Transform3d m = Transform3d::Identity(); - m.translate(Vec3d(double(p.x()), double(p.y()), 0.0)); - m.scale(double(marker)); - draw(m_vertex_marker_glmodel, UV_COLOR_SEL_BOUNDARY, m); - }; - // Mark every element of the multi-selection (falling back to the primary when nothing is in the - // set yet), so a Shift/Ctrl group is all visibly highlighted. - if (m_select_mode == SelectMode::Vertex) { - if (m_sel_vertices.empty()) - draw_marker(m_active_vertex); - else - for (const int v : m_sel_vertices) - draw_marker(v); - } else { - if (m_sel_edges.empty()) { - draw_marker(m_active_edge.first); - draw_marker(m_active_edge.second); - } else - for (const auto &[a, b] : m_sel_edges) { - draw_marker(a); - draw_marker(b); - } + // Vertex/Edge mode: the element under the cursor in the hover colour, and the selection as teal - a selected + // edge drawn over its whole length, a vertex as a square handle, both on a dark halo. + if (!m_vertex_marker_glmodel.is_initialized()) { + GLModel::Geometry q; + q.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + q.reserve_vertices(4); + q.reserve_indices(6); + q.add_vertex(Vec3f(-0.5f, -0.5f, 0.f)); + q.add_vertex(Vec3f(0.5f, -0.5f, 0.f)); + q.add_vertex(Vec3f(0.5f, 0.5f, 0.f)); + q.add_vertex(Vec3f(-0.5f, 0.5f, 0.f)); + q.add_triangle(0, 1, 2); + q.add_triangle(0, 2, 3); + m_vertex_marker_glmodel.init_from(std::move(q)); + } + const auto draw_marker = [&](int v, float px, const ColorRGBA &color) { + if (v < 0 || size_t(v) >= m_islands.uvs.size()) + return; + const Vec2f p = island_uv(size_t(v)); + Transform3d m = Transform3d::Identity(); + m.translate(Vec3d(double(p.x()), double(p.y()), 0.0)); + m.scale(double(px * uv_per_px)); + draw(m_vertex_marker_glmodel, color, m); + }; + const auto draw_handle = [&](int v, const ColorRGBA &color, float px) { + draw_marker(v, px + 4.f, UV_COLOR_HALO); + draw_marker(v, px, color); + }; + if (m_select_mode == SelectMode::Vertex) { + if (m_hover_vertex >= 0 && !is_vertex_selected(m_hover_vertex)) + draw_handle(m_hover_vertex, UV_COLOR_HOVER, 7.f); + // Every element of the multi-selection, falling back to the primary while the set is still empty. + if (m_sel_vertices.empty()) + draw_handle(m_active_vertex, UV_COLOR_SEL_BOUNDARY, 8.f); + else + for (const int v : m_sel_vertices) + draw_handle(v, UV_COLOR_SEL_BOUNDARY, 8.f); + } else if (m_select_mode == SelectMode::Edge) { + if (m_hover_edge.first >= 0 && !is_edge_selected(m_hover_edge)) + draw_stroke(edge_segments({ m_hover_edge }), 3.f, UV_COLOR_HOVER, 2.f); + std::vector> edges = m_sel_edges; + if (edges.empty() && m_active_edge.first >= 0) + edges.push_back(m_active_edge); + draw_stroke(edge_segments(edges), 4.f, UV_COLOR_SEL_BOUNDARY, 2.f); + for (const auto &[a, b] : edges) { + draw_handle(a, UV_COLOR_SEL_BOUNDARY, 6.f); + draw_handle(b, UV_COLOR_SEL_BOUNDARY, 6.f); } } // Add/remove hint next to the cursor in Vertex/Edge mode: a green '+' when a click will add to the // selection (plain or Shift), a red '-' when Ctrl is held and a click will remove one - the UV-side - // twin of the 3D paint cursor's own sign. Rebuilt each frame at the pointer, sized in pixels. + // twin of the 3D paint cursor's own sign. if (m_select_mode != SelectMode::Island && m_cursor_inside) { - const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight()))); - const Vec2f centre = screen_to_uv(m_cursor_px) + Vec2f(14.f, -14.f) * uv_per_px; // up-right of the pointer - const float r = 6.f * uv_per_px; - const bool removing = wxGetKeyState(WXK_CONTROL); - - GLModel::Geometry sign; - sign.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - unsigned idx = 0; - const auto seg = [&](const Vec2f &a, const Vec2f &b) { - sign.add_vertex(Vec3f(a.x(), a.y(), 0.f)); - sign.add_vertex(Vec3f(b.x(), b.y(), 0.f)); - sign.add_line(idx, idx + 1); - idx += 2; - }; - seg(centre - Vec2f(r, 0.f), centre + Vec2f(r, 0.f)); // the '-' bar, shared by both signs + const Vec2f centre = screen_to_uv(m_cursor_px) + Vec2f(14.f, -14.f) * uv_per_px; // up-right of the pointer + const float r = 6.f * uv_per_px; + const bool removing = wxGetKeyState(WXK_CONTROL); + Segments sign{ { centre - Vec2f(r, 0.f), centre + Vec2f(r, 0.f) } }; // the '-' bar, shared by both signs if (!removing) - seg(centre - Vec2f(0.f, r), centre + Vec2f(0.f, r)); // the extra stroke that makes it a '+' - - m_cursor_sign_glmodel.reset(); - if (!sign.is_empty()) - m_cursor_sign_glmodel.init_from(std::move(sign)); - set_line_width(3.f); - draw(m_cursor_sign_glmodel, removing ? ColorRGBA(1.f, 0.30f, 0.25f, 0.95f) : ColorRGBA(0.35f, 0.90f, 0.45f, 0.95f), - identity); - set_line_width(1.f); + sign.emplace_back(centre - Vec2f(0.f, r), centre + Vec2f(0.f, r)); // the extra stroke that makes it a '+' + draw_stroke(sign, 2.5f, removing ? ColorRGBA(1.f, 0.36f, 0.30f, 1.f) : ColorRGBA(0.40f, 0.92f, 0.50f, 1.f), 1.f); } // The GL context is shared with the 3D view; leave the bits we touched as we found them. glsafe(::glDisable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); + if (prev_cull) + glsafe(::glEnable(GL_CULL_FACE)); + if (prev_scissor) + glsafe(::glEnable(GL_SCISSOR_TEST)); + if (prev_stencil) + glsafe(::glEnable(GL_STENCIL_TEST)); SwapBuffers(); @@ -1385,74 +1622,529 @@ enum : int { ID_UV_SNAP, ID_UV_AVG_SCALE, ID_UV_CUT, - ID_UV_PROJECT, ID_UV_JOIN, ID_UV_UNJOIN, + ID_UV_UNWRAP, + ID_UV_MARK_SEAMS, + ID_UV_SEAM_PATH, + ID_UV_CLEAR_SEAMS, + ID_UV_CLEAR_EDITS, + ID_UV_SELECT_ISLAND, // + SelectMode + ID_UV_SELECT_VERTEX, + ID_UV_SELECT_EDGE, + ID_UV_BG_HEIGHT, // + Background + ID_UV_BG_CHECKER, + ID_UV_BG_DISTORTION, + ID_UV_PICK_TEXTURE, }; + +// An icon from resources/images, rasterised at the window's real pixel density. On GTK3 create_scaled_bitmap() +// renders at the DIP size and lets GTK upscale that on a HiDPI screen, which is what made these small icons blurry; +// here the SVG is rendered at device pixels instead and tagged with the scale, so it is drawn 1:1. (Windows renders +// at device pixels already, and macOS through BitmapCache's own backing scale.) +wxBitmap pane_icon(wxWindow *win, const std::string &name, int size_dip) +{ +#ifdef __WXGTK3__ + if (const double scale = win->GetContentScaleFactor(); scale > 1.0) { + static BitmapCache cache; + const unsigned px = unsigned(std::lround(size_dip * scale)); + if (wxBitmap *bmp = cache.load_svg(name, 0, px, false, wxGetApp().dark_mode()); bmp != nullptr && bmp->IsOk()) + return wxBitmap(bmp->ConvertToImage(), -1, scale); + } +#endif + return create_scaled_bitmap(name, win, size_dip); +} + +// The size a bitmap takes on screen, in the units a wxDC draws in on this platform. +wxSize drawn_size(const wxBitmap &bmp) +{ +#ifdef __WXGTK3__ + return bmp.GetLogicalSize(); +#else + return ScalableBitmap::GetBmpSize(bmp); +#endif +} + +// `bmp` with its alpha scaled down, for a disabled control. Keeps the bitmap's scale factor, so it stays sharp. +wxBitmap faded(const wxBitmap &bmp, double alpha) +{ + wxImage image = bmp.ConvertToImage(); + if (!image.HasAlpha()) + image.InitAlpha(); + unsigned char *a = image.GetAlpha(); + for (int i = 0, n = image.GetWidth() * image.GetHeight(); i < n; ++i) + a[i] = (unsigned char) std::lround(a[i] * alpha); + return wxBitmap(image, -1, bmp.GetScaleFactor()); +} + +// The pane's colours, matching the texture displacement panel's ImGui style in both themes. +struct PaneColors +{ + wxColour bg, ink, dim, rule, frame, hover; + static PaneColors current() + { + if (wxGetApp().dark_mode()) + return { wxColour(0x2d, 0x2d, 0x31), wxColour(0xef, 0xef, 0xf0), wxColour(0x90, 0x90, 0x96), + wxColour(0x3d, 0x3d, 0x45), wxColour(0x36, 0x36, 0x3c), wxColour(0x49, 0x49, 0x50) }; + return { wxColour(0xff, 0xff, 0xff), wxColour(0x32, 0x3a, 0x3d), wxColour(0x7c, 0x82, 0x82), + wxColour(0xed, 0xed, 0xed), wxColour(0xce, 0xce, 0xce), wxColour(0xee, 0xee, 0xee) }; + } +}; + +wxColour mix(const wxColour &a, const wxColour &b, double t) +{ + const auto ch = [t](unsigned char x, unsigned char y) { return (unsigned char) std::lround(x + (y - x) * t); }; + return wxColour(ch(a.Red(), b.Red()), ch(a.Green(), b.Green()), ch(a.Blue(), b.Blue())); +} } // namespace +// An icon button - square, or with a label beside the icon - drawn the way the texture displacement panel draws +// its own: a 1 px frame, a teal frame over a teal tint while a toggle is on, a solid teal fill for the one +// accent action, and an optional amber dot for "needs attention". Drawn by hand rather than with wxButton / +// wxToggleButton so the pane looks the same on every platform and in both themes. +// +// A click emits wxEVT_BUTTON with the button's id; for a toggle the event's int is the new state. The owner +// may overwrite that state again with SetValue() - which is how radio groups and gizmo-owned flags work. +class UVToolButton : public wxWindow +{ +public: + UVToolButton(wxWindow *parent, wxWindowID id, const std::string &icon, const wxString &label, const wxString &tip, + bool toggle, bool accent = false, int size_dip = 26) + : wxWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | wxFULL_REPAINT_ON_RESIZE) + , m_icon_name(icon), m_icon_dip(size_dip >= 26 ? 16 : 14), m_label(label), m_toggle(toggle), m_accent(accent) + , m_size_dip(size_dip) + { + SetBackgroundStyle(wxBG_STYLE_PAINT); + SetToolTip(tip); + if (accent) { + wxFont font = GetFont(); + font.MakeBold(); + SetFont(font); + } + Bind(wxEVT_PAINT, &UVToolButton::on_paint, this); + Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent &) { m_hover = true; Refresh(); }); + Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &) { m_hover = false; m_pressed = false; Refresh(); }); + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { + if (IsEnabled()) { + m_pressed = true; + Refresh(); + } + }); + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &e) { + const bool was_pressed = m_pressed; + m_pressed = false; + Refresh(); + if (!was_pressed || !IsEnabled() || !GetClientRect().Contains(e.GetPosition())) + return; + if (m_toggle) + m_on = !m_on; + wxCommandEvent evt(wxEVT_BUTTON, GetId()); + evt.SetEventObject(this); + evt.SetInt(m_on ? 1 : 0); + ProcessWindowEvent(evt); + }); + SetMinSize(DoGetBestSize()); + } + + void SetValue(bool on) + { + if (on != m_on) { + m_on = on; + Refresh(); + } + } + bool GetValue() const { return m_on; } + // Shows a ready-made bitmap instead of the SVG icon (the layer thumbnail). Its scale factor sets its size. + void SetBitmap(const wxBitmap &bmp) + { + m_bitmap = bmp; + Refresh(); + } + void SetBadge(bool badge) + { + if (badge != m_badge) { + m_badge = badge; + Refresh(); + } + } + bool Enable(bool enable = true) override + { + const bool changed = wxWindow::Enable(enable); + if (changed) + Refresh(); + return changed; + } + +protected: + wxSize DoGetBestSize() const override + { + const int h = FromDIP(m_size_dip); + if (m_label.empty()) + return wxSize(h, h); + const int icon_w = m_icon_name.empty() ? 0 : FromDIP(m_icon_dip) + FromDIP(6); + return wxSize(FromDIP(9) + icon_w + GetTextExtent(m_label).x + FromDIP(10), h); + } + +private: + void on_paint(wxPaintEvent &) + { + wxAutoBufferedPaintDC dc(this); + const PaneColors c = PaneColors::current(); + const wxRect r = GetClientRect(); + const wxColour teal(0x00, 0x96, 0x88); + const bool enabled = IsEnabled(); + + wxColour fill = c.bg, border = c.frame, text = c.ink; + if (m_accent) { + fill = (m_hover && enabled) ? wxColour(0x26, 0xa6, 0x9a) : teal; + border = fill; + text = *wxWHITE; + } else if (m_on) { + fill = mix(c.bg, teal, 0.22); + border = teal; + } else if (m_hover && enabled) { + fill = c.hover; + } + if (m_pressed) + fill = mix(fill, teal, 0.18); + + dc.SetBackground(wxBrush(c.bg)); + dc.Clear(); + dc.SetBrush(wxBrush(fill)); + dc.SetPen(wxPen(border, 1)); + dc.DrawRoundedRectangle(r, FromDIP(2)); + + // Rasterised on first paint rather than in the constructor: the content scale is only reliable once the + // window is on screen, and it changes when the window moves to a screen with a different one. + if (!m_icon_name.empty() && (!m_icon.IsOk() || m_icon_scale != GetContentScaleFactor())) { + m_icon_scale = GetContentScaleFactor(); + m_icon = pane_icon(this, m_icon_name, m_icon_dip); + } + const wxBitmap &bmp = m_bitmap.IsOk() ? m_bitmap : m_icon; + int x = 0; + if (bmp.IsOk()) { + const wxSize bs = drawn_size(bmp); + x = m_label.empty() ? (r.width - bs.x) / 2 : FromDIP(9); + dc.DrawBitmap(enabled ? bmp : faded(bmp, 0.35), x, (r.height - bs.y) / 2, true); + x += bs.x + FromDIP(6); + } else { + x = FromDIP(9); + } + if (!m_label.empty()) { + dc.SetFont(GetFont()); + dc.SetTextForeground(enabled ? text : mix(text, c.bg, 0.55)); + dc.DrawText(m_label, x, (r.height - dc.GetTextExtent(m_label).y) / 2); + } + if (m_badge) { + const int d = FromDIP(9); + dc.SetPen(wxPen(c.bg, FromDIP(2))); + dc.SetBrush(wxBrush(wxColour(0xe0, 0xa4, 0x4a))); + dc.DrawCircle(r.width - d / 2 - 1, d / 2 + 1, d / 2); + } + } + + std::string m_icon_name; + int m_icon_dip = 16; + wxBitmap m_icon; // m_icon_name rasterised for m_icon_scale + double m_icon_scale = 0.; + wxBitmap m_bitmap; + wxString m_label; + bool m_toggle = false; + bool m_accent = false; + int m_size_dip = 26; + bool m_on = false; + bool m_badge = false; + bool m_hover = false; + bool m_pressed = false; +}; + UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { - // Icon + label buttons: each has its own dedicated SVG (see the map below), with the label kept - // alongside it. A missing SVG simply leaves the button showing only its label - never bitmap-less - // garbage - so the bar stays usable before the art lands. - auto *bar = new wxBoxSizer(wxHORIZONTAL); - const auto set_icon = [this](wxAnyButton *b, const std::string &iconname) { - const wxBitmap bmp = create_scaled_bitmap(iconname, this, 16); - if (bmp.IsOk()) - b->SetBitmap(bmp); + const PaneColors c = PaneColors::current(); + const int gap = FromDIP(6); + const int pad = FromDIP(8); + SetBackgroundColour(c.bg); + + const auto rule = [&](const wxSize &size) { + auto *w = new wxWindow(this, wxID_ANY, wxDefaultPosition, size); + w->SetBackgroundColour(c.rule); + w->SetMinSize(size); + return w; }; - const auto add_button = [&](int id, const std::string &iconname, const wxString &label, const wxString &tip) { - auto *b = new wxButton(this, id, label, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - set_icon(b, iconname); - b->SetToolTip(tip); - bar->Add(b, 0, wxALL, 2); - b->Bind(wxEVT_BUTTON, &UVEditorPanel::on_tool, this); + const auto text = [&](const wxString &label, const wxColour &colour, long style = 0) { + auto *t = new wxStaticText(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, style); + t->SetForegroundColour(colour); + t->SetBackgroundColour(c.bg); + return t; + }; + + // ---- header: the layer being edited, the background under the islands, and Unwrap ---- + auto *header = new wxBoxSizer(wxHORIZONTAL); + m_thumb = new UVToolButton(this, ID_UV_PICK_TEXTURE, std::string(), wxEmptyString, + _L("Change texture - choose another one from the texture library"), false, false, 26); + m_layer_name = text(wxEmptyString, c.ink, wxST_ELLIPSIZE_END); + { + wxFont font = m_layer_name->GetFont(); + font.MakeBold(); + m_layer_name->SetFont(font); + } + // The name opens the library too, like the texture's name in the layer card. + m_layer_name->SetCursor(wxCursor(wxCURSOR_HAND)); + m_layer_name->SetToolTip(m_thumb->GetToolTipText()); + m_layer_name->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &) { + if (m_canvas->pane_state().has_layer) + m_canvas->run_command(UVEditorCanvas::Command::PickTexture); + }); + m_layer_name->SetMinSize(wxSize(FromDIP(30), -1)); + m_tile = text(wxEmptyString, c.dim); + header->Add(m_thumb, 0, wxALIGN_CENTER_VERTICAL); + header->Add(m_layer_name, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, gap); + header->Add(m_tile, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, gap); + + const char *const bg_icons[3] = { "texture_displacement_uv_bg_height", "texture_displacement_checker", + "texture_displacement_distortion" }; + const wxString bg_tips[3] = { _L("Height map - show the layer's texture under the islands"), + _L("Checker - a test grid; squares stay square where the unwrap does not stretch"), + _L("Distortion - colour each island by how much the unwrap stretches it") }; + for (int i = 0; i < 3; ++i) { + m_background[i] = new UVToolButton(this, ID_UV_BG_HEIGHT + i, bg_icons[i], wxEmptyString, bg_tips[i], true, false, 22); + header->Add(m_background[i], 0, wxALIGN_CENTER_VERTICAL | wxLEFT, i == 0 ? gap : FromDIP(3)); + } + header->Add(rule(wxSize(1, FromDIP(18))), 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, gap); + m_unwrap = new UVToolButton(this, ID_UV_UNWRAP, "texture_displacement_map_unwrap", _L("Unwrap"), wxEmptyString, false, true, 24); + header->Add(m_unwrap, 0, wxALIGN_CENTER_VERTICAL); + + // ---- settings: what the next Unwrap will do ---- + auto *settings = new wxBoxSizer(wxHORIZONTAL); + auto *seam_label = text(_L("Seam angle"), c.dim); + m_seam_angle = new ::SpinInput(this, wxEmptyString, wxString::FromUTF8("°"), wxDefaultPosition, wxSize(FromDIP(76), FromDIP(24)), 0, + 5, 90, 40); + const wxString seam_tip = _L("Edges sharper than this are cut, and the pieces either side of them are flattened separately. " + "Lower it to cut more: each piece then lies flat with less stretching, at the cost of the " + "texture not running continuously across the cut. Takes effect at the next Unwrap."); + seam_label->SetToolTip(seam_tip); + m_seam_angle->SetToolTip(seam_tip); + m_connect = new ::CheckBox(this); + auto *connect_label = text(_L("Connect islands"), c.ink); + const wxString connect_tip = _L("Lay the unwrap out as a connected net: pieces that share an edge are unfolded next to each " + "other (a cube becomes a joined net rather than six loose squares). They stay separate " + "islands, so you can still move any of them by hand afterwards."); + m_connect->SetToolTip(connect_tip); + connect_label->SetToolTip(connect_tip); + settings->Add(seam_label, 0, wxALIGN_CENTER_VERTICAL); + settings->Add(m_seam_angle, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, gap); + settings->AddStretchSpacer(); + settings->Add(m_connect, 0, wxALIGN_CENTER_VERTICAL); + settings->Add(connect_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(4)); + + // ---- tool strip ---- + auto *strip = new wxBoxSizer(wxVERTICAL); + const auto tool = [&](int id, const char *icon, const wxString &tip, bool toggle) { + auto *b = new UVToolButton(this, id, icon, wxEmptyString, tip, toggle); + strip->Add(b, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(4)); return b; }; - add_button(ID_UV_FRAME, "texture_displacement_frame", _L("Frame"), _L("Frame all islands (Home)")); - m_snap_button = new wxToggleButton(this, ID_UV_SNAP, _L("Snap"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - set_icon(m_snap_button, "texture_displacement_snap"); - m_snap_button->SetToolTip(_L("Snap islands together when dragging")); - bar->Add(m_snap_button, 0, wxALL, 2); - m_snap_button->Bind(wxEVT_TOGGLEBUTTON, &UVEditorPanel::on_tool, this); - add_button(ID_UV_AVG_SCALE, "texture_displacement_avg_scale", _L("Avg scale"), _L("Give every island the same texel density")); - add_button(ID_UV_CUT, "texture_displacement_cut", _L("Cut"), _L("Split the selected island across its long axis")); - add_button(ID_UV_JOIN, "texture_displacement_join", _L("Join"), _L("Unfold the selected island onto its nearest neighbour along their shared edge")); - add_button(ID_UV_UNJOIN, "texture_displacement_join", _L("Unjoin"), _L("Send the selected island back to its own packed position")); + // 11 px either side of a group rule (7 below plus the next button's own 4), against 4 px between buttons. + const auto strip_rule = [&]() { + auto *r = rule(wxSize(FromDIP(20), 1)); + strip->Add(r, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(11)); + strip->AddSpacer(FromDIP(7)); + }; + m_select[0] = tool(ID_UV_SELECT_ISLAND, "texture_displacement_uv_select_island", _L("Island - move, rotate and scale whole islands"), true); + m_select[1] = tool(ID_UV_SELECT_VERTEX, "texture_displacement_uv_select_vertex", _L("Vertex - drag vertices to reshape; Shift/Ctrl to multi-select"), true); + m_select[2] = tool(ID_UV_SELECT_EDGE, "texture_displacement_uv_select_edge", _L("Edge - drag edges to reshape; Shift/Ctrl to multi-select"), true); + strip_rule(); + m_mark_seams = tool(ID_UV_MARK_SEAMS, "texture_displacement_uv_seam", + _L("Mark seams - click edges on the model to cut the unwrap along them. The edge under the cursor is " + "highlighted yellow; click to mark it red, click a red edge again to unmark it. Painting is paused " + "while this is on."), + true); + m_seam_path = tool(ID_UV_SEAM_PATH, "texture_displacement_uv_path", + _L("Path - instead of clicking every edge, click a start point and then an end point: the whole " + "shortest path between them is seamed at once. Available while marking seams."), + true); + m_clear_seams = tool(ID_UV_CLEAR_SEAMS, "texture_displacement_cross", _L("Clear seams - remove every seam marked on this layer"), false); + strip_rule(); + m_avg_scale = tool(ID_UV_AVG_SCALE, "texture_displacement_uv_avg_scale", _L("Average scale - give every island the same texel density"), false); + m_cut = tool(ID_UV_CUT, "texture_displacement_uv_cut", _L("Cut - split the selected island across its long axis"), false); + m_join = tool(ID_UV_JOIN, "texture_displacement_uv_join", _L("Join - unfold the selected island onto its nearest neighbour along their shared edge"), false); + m_unjoin = tool(ID_UV_UNJOIN, "texture_displacement_uv_unjoin", _L("Unjoin - send the selected island back to its own packed position"), false); + strip->AddStretchSpacer(); + m_clear_edits = tool(ID_UV_CLEAR_EDITS, "texture_displacement_uv_clear_edits", + _L("Clear UV edits - discard all manual vertex/edge moves and return the unwrap to its automatic shape"), false); + m_snap = tool(ID_UV_SNAP, "texture_displacement_uv_snap", _L("Snap - stick islands together when dragging one against another"), true); + m_frame = tool(ID_UV_FRAME, "texture_displacement_uv_frame", _L("Frame all islands (Home)"), false); + strip->AddSpacer(FromDIP(4)); - m_canvas = new UVEditorCanvas(this); - m_canvas->set_status_callback([this](const wxString &text) { - if (m_status != nullptr && m_status->GetLabel() != text) { - m_status->SetLabel(text); - m_status->Refresh(); - } - }); + m_canvas = new UVEditorCanvas(this); + auto *body = new wxBoxSizer(wxHORIZONTAL); + body->Add(strip, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(4)); + body->Add(rule(wxSize(1, -1)), 0, wxEXPAND); + body->Add(m_canvas, 1, wxEXPAND); - m_status = new wxStaticText(this, wxID_ANY, wxEmptyString); - m_status->SetForegroundColour(wxColour(180, 180, 180)); + // ---- status line: the current gesture on the left, the unwrap summary on the right ---- + m_status = text(wxEmptyString, c.dim, wxST_ELLIPSIZE_END); + m_status->SetMinSize(wxSize(FromDIP(40), -1)); + m_stats = text(wxEmptyString, c.dim); + auto *status = new wxBoxSizer(wxHORIZONTAL); + status->Add(m_status, 1, wxALIGN_CENTER_VERTICAL); + status->Add(m_stats, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, gap); auto *sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(bar, 0, wxEXPAND); - sizer->Add(m_canvas, 1, wxEXPAND); - sizer->Add(m_status, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, 3); + sizer->Add(header, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, pad); + sizer->Add(settings, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap); + sizer->Add(rule(wxSize(-1, 1)), 0, wxEXPAND | wxTOP, gap); + sizer->Add(body, 1, wxEXPAND); + sizer->Add(rule(wxSize(-1, 1)), 0, wxEXPAND); + sizer->Add(status, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, FromDIP(5)); SetSizer(sizer); + + Bind(wxEVT_BUTTON, &UVEditorPanel::on_tool, this); + // Both settings compare against the last state the gizmo pushed before sending anything: apply_state() + // writes them back, and without the check that write would bounce straight back to the gizmo as an edit. + m_seam_angle->Bind(wxEVT_SPINCTRL, [this](wxCommandEvent &) { + const int value = m_seam_angle->GetValue(); + if (value != int(std::lround(m_canvas->pane_state().seam_angle_deg))) + m_canvas->run_command(UVEditorCanvas::Command::SetSeamAngle, float(value)); + }); + const auto send_connect = [this]() { + if (m_connect->GetValue() != m_canvas->pane_state().connect_islands) + m_canvas->run_command(UVEditorCanvas::Command::SetConnectIslands, m_connect->GetValue() ? 1.f : 0.f); + }; + m_connect->Bind(wxEVT_TOGGLEBUTTON, [send_connect](wxCommandEvent &e) { + send_connect(); + e.Skip(); + }); + connect_label->Bind(wxEVT_LEFT_DOWN, [this, send_connect](wxMouseEvent &) { + if (!m_connect->IsEnabled()) + return; + m_connect->SetValue(!m_connect->GetValue()); + send_connect(); + }); + + m_canvas->set_status_callback([this](const wxString &label) { + if (m_status != nullptr && m_status->GetLabel() != label) { + m_status->SetLabel(label); + m_status->Refresh(); + refresh_selection_tools(); // a selection or mode change is what changes the status line + } + }); + m_canvas->set_pane_state_callback([this](const UVEditorCanvas::PaneState &state) { apply_state(state); }); + apply_state(m_canvas->pane_state()); + + // AUI shows and hides the pane through this panel. Rebuild the canvas's GPU objects when it comes back, so + // the texture and islands never depend on surviving the canvas's native window being hidden. + Bind(wxEVT_SHOW, [this](wxShowEvent &e) { + e.Skip(); + if (e.IsShown()) + m_canvas->invalidate_gl(); + }); +} + +void UVEditorPanel::apply_state(const UVEditorCanvas::PaneState &s) +{ + const wxString name = s.has_layer ? s.layer_name : _L("No layer mapped with Unwrap"); + const wxString tile = s.has_layer ? wxString::Format(_L("%.1f mm tile"), s.tile_mm) : wxString(); + const bool relayout = m_layer_name->GetLabel() != name || m_tile->GetLabel() != tile; + m_layer_name->SetLabel(name); + m_tile->SetLabel(tile); + + if (s.thumbnail_px > 0 && s.thumbnail_rgb.size() == size_t(s.thumbnail_px) * size_t(s.thumbnail_px) * 3) { + // Scaled to device pixels and tagged with the content scale, so HiDPI screens get every pixel of it. + const double scale = GetContentScaleFactor(); + const int px = std::max(1, int(std::lround(FromDIP(20) * scale))); + wxImage image(s.thumbnail_px, s.thumbnail_px, false); + std::copy(s.thumbnail_rgb.begin(), s.thumbnail_rgb.end(), image.GetData()); + m_thumb->SetBitmap(wxBitmap(image.Scale(px, px, wxIMAGE_QUALITY_HIGH), -1, scale)); + } else { + m_thumb->SetBitmap(wxNullBitmap); + } + m_thumb->Enable(s.has_layer); + + for (int i = 0; i < 3; ++i) { + m_background[i]->SetValue(int(s.background) == i); + m_background[i]->Enable(s.has_layer); + } + m_unwrap->Enable(s.has_layer); + m_unwrap->SetBadge(s.unwrap_stale); + m_unwrap->SetToolTip(s.unwrap_stale ? + _L("Out of date - the paint, the seams or the seam angle changed since this unwrap was made. " + "Press to unwrap again.") : + _L("Flatten the painted area into UV islands. It is computed only when you press this, not on " + "every edit - so paint, change the seam angle or mark seams first, then press Unwrap.")); + + if (m_seam_angle->GetValue() != int(std::lround(s.seam_angle_deg))) + m_seam_angle->SetValue(int(std::lround(s.seam_angle_deg))); + m_seam_angle->Enable(s.has_layer); + m_connect->SetValue(s.connect_islands); + m_connect->Enable(s.has_layer); + + m_mark_seams->SetValue(s.mark_seams); + m_mark_seams->Enable(s.has_layer); + m_seam_path->SetValue(s.seam_path); + m_seam_path->Enable(s.has_layer && s.mark_seams); + m_clear_seams->Enable(s.has_layer && s.has_seams); + m_clear_edits->Enable(s.has_layer && s.has_uv_edits); + + m_stats->SetLabel(s.unwrapped ? wxString::Format(_L("%d islands · %s faces"), s.island_count, + wxString(std::to_string(s.face_count))) : + wxString()); + refresh_selection_tools(); + if (relayout) + Layout(); +} + +void UVEditorPanel::refresh_selection_tools() +{ + const bool has_islands = m_canvas->has_islands(); + const int mode = int(m_canvas->select_mode()); + for (int i = 0; i < 3; ++i) { + m_select[i]->SetValue(i == mode); + m_select[i]->Enable(has_islands); + } + const bool island_picked = has_islands && m_canvas->select_mode() == UVEditorCanvas::SelectMode::Island && + m_canvas->selected_island() >= 0; + m_avg_scale->Enable(has_islands); + m_cut->Enable(island_picked); + m_join->Enable(island_picked); + m_unjoin->Enable(island_picked); + m_snap->Enable(has_islands); + m_snap->SetValue(m_canvas->snap_enabled()); + m_frame->Enable(has_islands); } void UVEditorPanel::on_tool(wxCommandEvent &evt) { - switch (evt.GetId()) { - case ID_UV_FRAME: m_canvas->run_command(UVEditorCanvas::Command::FrameAll); break; - case ID_UV_SNAP: m_canvas->run_command(UVEditorCanvas::Command::ToggleSnap); break; - case ID_UV_AVG_SCALE: m_canvas->run_command(UVEditorCanvas::Command::AverageScale); break; - case ID_UV_CUT: m_canvas->run_command(UVEditorCanvas::Command::CutSelectedIsland); break; - case ID_UV_JOIN: m_canvas->run_command(UVEditorCanvas::Command::JoinSelected); break; - case ID_UV_UNJOIN: m_canvas->run_command(UVEditorCanvas::Command::UnjoinSelected); break; - default: evt.Skip(); return; + using Command = UVEditorCanvas::Command; + const int id = evt.GetId(); + const bool on = evt.GetInt() != 0; + switch (id) { + case ID_UV_FRAME: m_canvas->run_command(Command::FrameAll); break; + case ID_UV_SNAP: m_canvas->run_command(Command::ToggleSnap); break; + case ID_UV_AVG_SCALE: m_canvas->run_command(Command::AverageScale); break; + case ID_UV_CUT: m_canvas->run_command(Command::CutSelectedIsland); break; + case ID_UV_JOIN: m_canvas->run_command(Command::JoinSelected); break; + case ID_UV_UNJOIN: m_canvas->run_command(Command::UnjoinSelected); break; + case ID_UV_UNWRAP: m_canvas->run_command(Command::Unwrap); break; + case ID_UV_MARK_SEAMS: m_canvas->run_command(Command::SetMarkSeams, on ? 1.f : 0.f); break; + case ID_UV_SEAM_PATH: m_canvas->run_command(Command::SetSeamPath, on ? 1.f : 0.f); break; + case ID_UV_CLEAR_SEAMS: m_canvas->run_command(Command::ClearSeams); break; + case ID_UV_CLEAR_EDITS: m_canvas->run_command(Command::ClearUVEdits); break; + case ID_UV_PICK_TEXTURE: m_canvas->run_command(Command::PickTexture); break; + case ID_UV_SELECT_ISLAND: + case ID_UV_SELECT_VERTEX: + case ID_UV_SELECT_EDGE: m_canvas->run_command(Command::SetSelectMode, float(id - ID_UV_SELECT_ISLAND)); break; + case ID_UV_BG_HEIGHT: + case ID_UV_BG_CHECKER: + case ID_UV_BG_DISTORTION: + // Shown straight away; the gizmo confirms it with its next state push. + for (int i = 0; i < 3; ++i) + m_background[i]->SetValue(i == id - ID_UV_BG_HEIGHT); + m_canvas->run_command(Command::SetBackground, float(id - ID_UV_BG_HEIGHT)); + break; + default: evt.Skip(); return; } - // Keep the toggle button's visual state in step with the canvas, which owns the flag. - if (m_snap_button != nullptr) - m_snap_button->SetValue(m_canvas->snap_enabled()); + refresh_selection_tools(); } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/UVEditorCanvas.hpp b/src/slic3r/GUI/UVEditorCanvas.hpp index 40397470fd..0dd088c4e8 100644 --- a/src/slic3r/GUI/UVEditorCanvas.hpp +++ b/src/slic3r/GUI/UVEditorCanvas.hpp @@ -15,12 +15,17 @@ #include #include #include +#include #include #include "libslic3r/Point.hpp" #include "GLModel.hpp" #include "GLTexture.hpp" +// Orca's own widgets (slic3r/GUI/Widgets), declared at global scope. +class SpinInput; +class CheckBox; + namespace Slic3r::GUI { // Standalone 2D viewer/editor for a flattened (UV-unwrapped) mesh patch - shows the result of @@ -92,14 +97,50 @@ public: void set_snap_enabled(bool enabled) { m_snap_enabled = enabled; } bool snap_enabled() const { return m_snap_enabled; } - // High-level actions the pane toolbar triggers. The canvas handles the view-only ones (framing, + // High-level actions the pane's controls trigger. The canvas handles the view-only ones (framing, // the snap toggle) itself and forwards the rest to whoever owns the island data (the gizmo), via // the command callback - the canvas has the selection and the view, the gizmo has the layer. - enum class Command { FrameAll, ToggleSnap, AverageScale, CutSelectedIsland, ProjectFromView, JoinSelected, UnjoinSelected }; - void run_command(Command cmd); - using CommandFn = std::function; + // `value` carries the new setting for the Set* commands (an angle, a flag as 0/1, or an index into + // SelectMode / Background) and is 0 otherwise. + enum class Command { + FrameAll, ToggleSnap, AverageScale, CutSelectedIsland, ProjectFromView, JoinSelected, UnjoinSelected, + Unwrap, SetSeamAngle, SetConnectIslands, SetSelectMode, SetMarkSeams, SetSeamPath, ClearSeams, ClearUVEdits, + SetBackground, PickTexture, PaneClosed + }; + void run_command(Command cmd, float value = 0.f); + using CommandFn = std::function; void set_command_callback(CommandFn fn) { m_on_command = std::move(fn); } + // What the canvas shows under the islands; mirrors the gizmo's Normal/Checker/Distortion views. + enum class Background { Height, Checker, Distortion }; + + // Everything the pane's own controls show that the gizmo owns: the active layer and its unwrap settings. + // The gizmo pushes it whenever any of it may have changed, and the pane redraws its header, settings row + // and tool strip from it - so the controls never hold state of their own that could drift from the layer. + struct PaneState + { + bool has_layer = false; // an active layer mapped with Unwrap + wxString layer_name; + float tile_mm = 0.f; + std::vector thumbnail_rgb; // thumbnail_px square, 3 bytes per pixel; empty for none + int thumbnail_px = 0; + float seam_angle_deg = 40.f; + bool connect_islands = true; + bool unwrapped = false; // an unwrap exists for this layer + bool unwrap_stale = false; // paint, seams or the seam angle changed since it was made + bool mark_seams = false; + bool seam_path = false; + bool has_seams = false; + bool has_uv_edits = false; + Background background = Background::Height; + int island_count = 0; + size_t face_count = 0; + }; + void set_pane_state(PaneState state); + const PaneState &pane_state() const { return m_pane_state; } + using PaneStateFn = std::function; + void set_pane_state_callback(PaneStateFn fn) { m_on_pane_state = std::move(fn); } + // Called whenever the one-line status/hint text changes (current gesture + the shortcuts that // apply right now), so the pane can show it Blender-style along the bottom. using StatusFn = std::function; @@ -133,11 +174,21 @@ public: // The primary (last-clicked) island, still the pivot for rotate/scale and the target of the // single-island toolbar commands (Cut/Join/Unjoin). -1 if nothing is selected. int selected_island() const { return m_selected_island; } + // Whether there is an unwrap on screen at all. + bool has_islands() const { return m_islands.island_count > 0 && !m_islands.indices.empty(); } // The full multi-selection (Shift adds, Ctrl toggles). Always contains m_selected_island when it is // >= 0. The gizmo reads this to decide which islands a drag moves together, unioned with each // selected island's join group. const std::vector &selected_islands() const { return m_selection; } void reset_view(); + // Rebuilds every GPU object at the next paint, from the data the canvas keeps on the CPU. Called when the + // pane is shown again, so a hidden-and-reshown canvas never depends on GL objects surviving its native + // window being torn down. + void invalidate_gl() + { + m_gl_reset_pending = true; + Refresh(); + } private: void on_paint(wxPaintEvent &evt); @@ -155,7 +206,11 @@ private: // The UV region worth looking at: every island, plus always at least the texture's first tile, so // there is something sensibly framed even before anything is painted. void content_bounds(Vec2f &min_uv, Vec2f &max_uv) const; - // Frames content_bounds(). Bound to Home, and run once each time an unwrap first appears. + // What is actually *drawn*, which is content_bounds() snapped out to whole tiles whenever the + // backdrop tiles (see rebuild_background_quad()). Both the framing and the backdrop go through + // this so they cannot disagree. + void framed_bounds(Vec2f &min_uv, Vec2f &max_uv) const; + // Frames framed_bounds(). Bound to Home, and run once each time an unwrap first appears. void fit_view_to_content(); // Half-extents of the visible UV region. Split out because both rendering and every mouse @@ -185,6 +240,8 @@ private: void end_gesture(); // Rebuilds the status line from the current gesture/selection and pushes it to m_on_status. void update_status(); + // Re-picks what a click at `pos` would grab in the current select mode, and repaints when that changed. + void update_hover(const wxPoint &pos); wxGLContext *m_context = nullptr; // owned by OpenGLManager/GUI_App, not by this canvas @@ -195,13 +252,22 @@ private: // Boundary vertices per island, for snapping - a patch's boundary is a tiny fraction of it, and // rescanning the whole uv array on every snap test would not be. std::vector> m_island_boundary_verts; + // Per island: its outline edges, its triangles (indices into m_islands.indices) and the bounding box of its + // raw coordinates - so picking tests one island's triangles only when the cursor is inside its box. + std::vector>> m_island_boundary_edges; + std::vector> m_island_tris; + std::vector> m_island_raw_bounds; bool m_mesh_dirty = true; + bool m_gl_reset_pending = false; // One set of models per island, so an island can be drawn through its own transform. Built once - // per unwrap, never on a drag. + // per unwrap, never on a drag. Outlines are not among them: they are drawn as screen-width quads, + // rebuilt every paint (see render()), because GL wide lines are not reliably wider than 1 px. std::vector m_island_wireframe; // interior edges - std::vector m_island_boundary; // outline - std::vector m_island_fill; // filled, for the selected island's wash + std::vector m_island_fill; // filled; also the stencil mask that keeps islands undimmed + GLModel m_stroke_glmodel; // scratch model for the quads of one stroke pass + GLModel m_dim_quad_glmodel; // full-viewport quad that dims the texture outside the islands + int m_stencil_bits = -1; // of the default framebuffer; -1 = not queried yet GLModel m_tile_outline_glmodel; // the texture's first tile, [0,1]^2 - the "you are here" GLModel m_grid_glmodel; @@ -274,8 +340,10 @@ private: bool m_vertex_edit_moved = false; // Lazily-built small filled square, drawn at an edited/hovered vertex as a handle. GLModel m_vertex_marker_glmodel; - // Rebuilt each frame at the cursor while a +/- add-remove hint is shown (Vertex/Edge mode). - GLModel m_cursor_sign_glmodel; + // What a click would grab right now, highlighted so the user sees the target before clicking. + int m_hover_island = -1; + int m_hover_vertex = -1; + std::pair m_hover_edge{ -1, -1 }; int m_selected_island = -1; // The full multi-selection; m_selected_island is its primary (last-clicked) member. Kept as a small // vector rather than a set because it is tiny and iteration order (primary last) is convenient. @@ -315,12 +383,20 @@ private: UVVertexEditFn m_on_vertex_edit; CommandFn m_on_command; StatusFn m_on_status; + PaneState m_pane_state; + PaneStateFn m_on_pane_state; }; -// Hosts a UVEditorCanvas together with a small icon toolbar (frame, snap, average scale, cut, -// project-from-view) and a Blender-style status line along the bottom that names the current gesture -// and the shortcuts in play (#18). This is what actually goes into Plater's "uv_editor" AUI pane; -// the gizmo still talks to the inner canvas, reached via canvas(). +class UVToolButton; // a drawn icon button, defined in UVEditorCanvas.cpp + +// The UV editor pane that goes into Plater's "uv_editor" AUI pane: a header with the active layer, the +// canvas background and Unwrap; a settings row with the unwrap's seam angle and island connection; a +// narrow tool strip down the left (selection mode, seams, island actions, snap and framing); the canvas; +// and a status line naming the current gesture, with the unwrap summary on its right. +// +// It holds no editing state of its own. Every control sends a Command through the canvas to the gizmo, +// and the gizmo pushes PaneState back, from which the controls are redrawn (see apply_state()). The gizmo +// still talks to the inner canvas, reached via canvas(). class UVEditorPanel : public wxPanel { public: @@ -329,10 +405,35 @@ public: private: void on_tool(wxCommandEvent &evt); + void apply_state(const UVEditorCanvas::PaneState &state); + // Selection-mode toggles, and the tools that need a selected island, follow the canvas. + void refresh_selection_tools(); - UVEditorCanvas *m_canvas = nullptr; - wxToggleButton *m_snap_button = nullptr; - wxStaticText *m_status = nullptr; + UVEditorCanvas *m_canvas = nullptr; + + UVToolButton *m_thumb = nullptr; // the layer's texture; a click opens the texture library + wxStaticText *m_layer_name = nullptr; + wxStaticText *m_tile = nullptr; + UVToolButton *m_background[3]{}; + UVToolButton *m_unwrap = nullptr; + + ::SpinInput *m_seam_angle = nullptr; + ::CheckBox *m_connect = nullptr; + + UVToolButton *m_select[3]{}; + UVToolButton *m_mark_seams = nullptr; + UVToolButton *m_seam_path = nullptr; + UVToolButton *m_clear_seams = nullptr; + UVToolButton *m_avg_scale = nullptr; + UVToolButton *m_cut = nullptr; + UVToolButton *m_join = nullptr; + UVToolButton *m_unjoin = nullptr; + UVToolButton *m_clear_edits = nullptr; + UVToolButton *m_snap = nullptr; + UVToolButton *m_frame = nullptr; + + wxStaticText *m_status = nullptr; + wxStaticText *m_stats = nullptr; }; } // namespace Slic3r::GUI From f3d197c4ca3795b384cb0a6069fdbc182e9b6a17 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH 31/89] Tests for the step cutter, edge flips, auto resolution and v2 colours --- tests/libslic3r/test_texture_displacement.cpp | 778 +++++++++++++++++- 1 file changed, 763 insertions(+), 15 deletions(-) diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 6d6fda7ca1..e2f0147efb 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -5,9 +5,12 @@ #include #include #include +#include #include #include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TextureBake/TextureBakeFlip.hpp" +#include "libslic3r/TextureBake/TextureBakeMesh.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/TriangleSelector.hpp" #include "libslic3r/PNGReadWrite.hpp" @@ -61,6 +64,29 @@ static std::shared_ptr> make_checkerboard_png(size_t return std::make_shared>(std::move(bytes)); } +// The bake never drives relief below the model's own resting plane (see build_texture_displacement()), +// so on a fully painted closed solid the vertices already sitting on that plane - a cube's four bottom +// corners, whose normals point downwards - are clamped in Z and do not move by the full depth. The +// tests below are about the displacement maths, so they check the vertices the clamp cannot touch; +// the clamp itself has its own test. + +// The tests below check the classic, topology-preserving bake vertex by vertex, so they select it +// explicitly: the default pipeline rebuilds the topology and has no vertex correspondence to check. +static TextureDisplacementOptions classic_options() +{ + TextureDisplacementOptions o; + o.pipeline_v2 = false; + return o; +} + +static bool above_resting_plane(const indexed_triangle_set &mesh, size_t vi) +{ + float bottom = std::numeric_limits::max(); + for (const Vec3f &v : mesh.vertices) + bottom = std::min(bottom, v.z()); + return mesh.vertices[vi].z() > bottom + 1e-4f; +} + TEST_CASE("TextureDisplacement: decode_height_texture round-trips an 8-bit grayscale PNG", "[TextureDisplacement]") { TextureDisplacementLayer layer; @@ -79,7 +105,7 @@ TEST_CASE("TextureDisplacement: an empty layer list leaves the mesh unchanged", const std::vector layers; // none TextureDisplacementFacetsData facets{}; // all empty - const indexed_triangle_set result = build_texture_displacement(cube, layers, facets); + const indexed_triangle_set result = build_texture_displacement(cube, layers, facets, classic_options()); REQUIRE(result.vertices.size() == cube.vertices.size()); REQUIRE(result.indices.size() == cube.indices.size()); @@ -106,10 +132,12 @@ TEST_CASE("TextureDisplacement: fully painting a mesh displaces every vertex alo layer.tiling_scale = 5.0f; layer.image_data = make_flat_gray_png(255); // sample() == 1.0 everywhere -> full depth_mm displacement - const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets); + const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets, classic_options()); REQUIRE(result.vertices.size() == cube.vertices.size()); for (size_t i = 0; i < cube.vertices.size(); ++i) { + if (!above_resting_plane(cube, i)) + continue; const float moved = (result.vertices[i] - cube.vertices[i]).norm(); CHECK_THAT(moved, WithinAbs(layer.depth_mm, 1e-3f)); } @@ -148,13 +176,14 @@ TEST_CASE("TextureDisplacement: a second layer over the same area is applied too second.depth_mm = 0.5f; second.blend_mode = TextureBlendMode::Add; - const indexed_triangle_set result = build_texture_displacement(cube, {base, second}, facets); + const indexed_triangle_set result = build_texture_displacement(cube, {base, second}, facets, classic_options()); // Topology is preserved exactly, so vertices can be compared 1:1 with the input. REQUIRE(result.vertices.size() == cube.vertices.size()); REQUIRE(result.indices.size() == cube.indices.size()); for (size_t i = 0; i < cube.vertices.size(); ++i) - CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(1.5f, 1e-3f)); // 1.0 + 0.5, not just 1.0 + if (above_resting_plane(cube, i)) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(1.5f, 1e-3f)); // 1.0 + 0.5, not just 1.0 } TEST_CASE("TextureDisplacement: blend modes combine a layer with the ones below it", "[TextureDisplacement]") @@ -185,11 +214,12 @@ TEST_CASE("TextureDisplacement: blend modes combine a layer with the ones below })); second.blend_mode = std::get<0>(expected); - const indexed_triangle_set result = build_texture_displacement(cube, {base, second}, facets); + const indexed_triangle_set result = build_texture_displacement(cube, {base, second}, facets, classic_options()); REQUIRE(result.vertices.size() == cube.vertices.size()); for (size_t i = 0; i < cube.vertices.size(); ++i) - CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(std::get<1>(expected), 1e-3f)); + if (above_resting_plane(cube, i)) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(std::get<1>(expected), 1e-3f)); } TEST_CASE("TextureDisplacement: the lowest layer ignores its blend mode", "[TextureDisplacement]") @@ -208,10 +238,107 @@ TEST_CASE("TextureDisplacement: the lowest layer ignores its blend mode", "[Text layer.blend_mode = TextureBlendMode::Multiply; layer.image_data = make_flat_gray_png(255); - const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets); + const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets, classic_options()); for (size_t i = 0; i < cube.vertices.size(); ++i) - CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(2.0f, 1e-3f)); + if (above_resting_plane(cube, i)) + CHECK_THAT((result.vertices[i] - cube.vertices[i]).norm(), WithinAbs(2.0f, 1e-3f)); +} + +TEST_CASE("TextureDisplacement: relief is never driven below the model's resting plane", "[TextureDisplacement]") +{ + // A fully painted cube displaces outward everywhere, which on the bottom face means straight + // down - through the build plate. That geometry cannot be printed, so it is clamped back up. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(cube); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 2.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); // full depth everywhere + + float bottom = std::numeric_limits::max(); + for (const Vec3f &v : cube.vertices) + bottom = std::min(bottom, v.z()); + + const indexed_triangle_set result = build_texture_displacement(cube, {layer}, facets, classic_options()); + + REQUIRE(result.vertices.size() == cube.vertices.size()); + for (const Vec3f &v : result.vertices) + CHECK(v.z() >= bottom - 1e-4f); + + // ...and the clamp is confined to Z: a bottom corner still moves outwards in X and Y by the same + // amount it would have, rather than being pinned wholesale. + bool any_bottom_moved_sideways = false; + for (size_t i = 0; i < cube.vertices.size(); ++i) + if (!above_resting_plane(cube, i) && + (result.vertices[i].head<2>() - cube.vertices[i].head<2>()).norm() > 1e-3f) + any_bottom_moved_sideways = true; + CHECK(any_bottom_moved_sideways); +} + +TEST_CASE("TextureDisplacement: depth is measured in world millimetres, not the volume's own", "[TextureDisplacement]") +{ + // The same painted patch, baked once untransformed and once through a 3x scale. "Depth (mm)" is + // a millimetre on the printed part, so the *world* relief must come out the same height either + // way - which means the vertices of the scaled volume move by a third as much in its own + // coordinates. Baking both in volume space instead gave a 3x deeper relief on the scaled one. + indexed_triangle_set fan; + fan.vertices = { {0.f, 0.f, 1.f}, {1.f, 0.f, 1.f}, {0.f, 1.f, 1.f}, {-1.f, 0.f, 1.f}, {0.f, -1.f, 1.f} }; + fan.indices = { {0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1} }; + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(fan); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 1.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); + + const indexed_triangle_set plain = build_texture_displacement(fan, {layer}, facets, classic_options()); + Transform3d scale3 = Transform3d::Identity(); + scale3.scale(Vec3d(3.0, 3.0, 3.0)); + const indexed_triangle_set scaled = build_texture_displacement(fan, {layer}, facets, classic_options(), {}, nullptr, scale3); + + REQUIRE(plain.vertices.size() == fan.vertices.size()); + REQUIRE(scaled.vertices.size() == fan.vertices.size()); + for (size_t i = 0; i < fan.vertices.size(); ++i) { + CHECK_THAT(plain.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f, 1e-3f)); + // A third of the movement locally is the same movement once the 3x scale is applied. + CHECK_THAT(scaled.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f / 3.0f, 1e-3f)); + } +} + +TEST_CASE("TextureDisplacement: a mirrored placement still raises the relief outwards", "[TextureDisplacement]") +{ + // Mirroring reverses the winding, and every normal in the bake is derived from the winding - so + // without correcting for it the whole relief is carved into the surface instead of raised off it. + indexed_triangle_set fan; + fan.vertices = { {0.f, 0.f, 1.f}, {1.f, 0.f, 1.f}, {0.f, 1.f, 1.f}, {-1.f, 0.f, 1.f}, {0.f, -1.f, 1.f} }; + fan.indices = { {0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1} }; + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_whole_mesh(fan); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.depth_mm = 1.0f; + layer.tiling_scale = 5.0f; + layer.image_data = make_flat_gray_png(255); + + // Mirrored in X: the patch's outward direction in world space is still +Z, so in the volume's own + // coordinates the vertices must still move +Z. + Transform3d mirror_x = Transform3d::Identity(); + mirror_x.scale(Vec3d(-1.0, 1.0, 1.0)); + const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets, classic_options(), {}, nullptr, mirror_x); + + REQUIRE(result.vertices.size() == fan.vertices.size()); + for (size_t i = 0; i < fan.vertices.size(); ++i) + CHECK_THAT(result.vertices[i].z() - fan.vertices[i].z(), WithinAbs(1.0f, 1e-3f)); } TEST_CASE("TextureDisplacement: the patch border is displaced by default and pinned on request", "[TextureDisplacement]") @@ -250,7 +377,7 @@ TEST_CASE("TextureDisplacement: the patch border is displaced by default and pin SECTION("by default the whole painted patch moves, border included") { - const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets); + const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets, classic_options()); REQUIRE(result.vertices.size() == fan.vertices.size()); for (size_t i = 0; i < fan.vertices.size(); ++i) CHECK(moved(result, i)); @@ -263,7 +390,7 @@ TEST_CASE("TextureDisplacement: the patch border is displaced by default and pin SECTION("pinning the border holds exactly the vertices an unpainted triangle also uses") { - TextureDisplacementOptions options; + TextureDisplacementOptions options = classic_options(); options.displace_border = false; const indexed_triangle_set result = build_texture_displacement(fan, {layer}, facets, options); CHECK_FALSE(moved(result, 0)); // O: border @@ -322,7 +449,7 @@ TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", } REQUIRE(rim_count > 8); - TextureDisplacementOptions options; + TextureDisplacementOptions options = classic_options(); options.smooth_enabled = true; options.smooth_strength = 0.5f; options.smooth_iterations = 4; @@ -345,7 +472,7 @@ TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", return e; }; - const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets); + const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets, classic_options()); REQUIRE(roughness(raw) > 0.0); // the checkerboard really did produce relief to smooth const indexed_triangle_set smoothed = build_texture_displacement(grid, { layer }, facets, options); @@ -364,7 +491,7 @@ TEST_CASE("TextureDisplacement: post-process smoothing relaxes only what moved", // its own height and cannot move, while every rim vertex has at least one neighbour outside the // paint pinned at zero and so must come down the moment it is allowed to. layer.image_data = make_flat_gray_png(255); - const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets); + const indexed_triangle_set raw = build_texture_displacement(grid, { layer }, facets, classic_options()); options.smooth_skip_border = true; const indexed_triangle_set kept = build_texture_displacement(grid, { layer }, facets, options); @@ -742,7 +869,7 @@ TEST_CASE("TextureDisplacement: colour is reported per triangle and only where p return best; }; - const indexed_triangle_set out = build_texture_displacement(quad, { layer }, facets, {}, {}, &request); + const indexed_triangle_set out = build_texture_displacement(quad, { layer }, facets, classic_options(), {}, &request); REQUIRE_FALSE(out.indices.empty()); REQUIRE(triangle_color.size() == quad.indices.size()); @@ -776,7 +903,7 @@ TEST_CASE("TextureDisplacement: a layer that is not colouring reports no colours request.out_triangle = &triangle_color; request.quantize = [](const Vec3f &) { return 0; }; - build_texture_displacement(quad, { layer }, facets, {}, {}, &request); + build_texture_displacement(quad, { layer }, facets, classic_options(), {}, &request); REQUIRE(triangle_color.size() == quad.indices.size()); CHECK(triangle_color[0] == 0); @@ -847,3 +974,624 @@ TEST_CASE("TextureDisplacement: subdivision refines a colour boundary a flat hei CHECK(out.indices.size() == cube.indices.size()); } } + +// --------------------------------------------------------------------------------------------- +// Step cutter +// --------------------------------------------------------------------------------------------- + +// A flat square sheet in the (x, y) plane split into right triangles of the given edge, with the +// diagonal alternated so the mesh has no preferred direction. Normals +z. +static indexed_triangle_set make_flat_sheet(float size, float edge) +{ + indexed_triangle_set its; + const int n = std::max(1, int(std::lround(size / edge))); + for (int j = 0; j <= n; ++j) + for (int i = 0; i <= n; ++i) + its.vertices.emplace_back(size * float(i) / float(n), size * float(j) / float(n), 0.f); + const auto id = [n](int i, int j) { return j * (n + 1) + i; }; + for (int j = 0; j < n; ++j) + for (int i = 0; i < n; ++i) { + if ((i + j) % 2 == 0) { + its.indices.emplace_back(id(i, j), id(i + 1, j), id(i + 1, j + 1)); + its.indices.emplace_back(id(i, j), id(i + 1, j + 1), id(i, j + 1)); + } else { + its.indices.emplace_back(id(i, j), id(i + 1, j), id(i, j + 1)); + its.indices.emplace_back(id(i + 1, j), id(i + 1, j + 1), id(i, j + 1)); + } + } + return its; +} + +// Every interior edge of a sheet is shared by exactly two triangles; only the sheet's own border may +// be used once. +static bool sheet_is_manifold(const indexed_triangle_set &its, float size) +{ + std::map, int> uses; + for (const auto &t : its.indices) + for (int e = 0; e < 3; ++e) { + int a = t[e], b = t[(e + 1) % 3]; + if (a > b) + std::swap(a, b); + ++uses[{ a, b }]; + } + for (const auto &[edge, n] : uses) { + const Vec3f &pa = its.vertices[size_t(edge.first)], &pb = its.vertices[size_t(edge.second)]; + const bool border = (pa.x() == pb.x() && (pa.x() == 0.f || pa.x() == size)) || + (pa.y() == pb.y() && (pa.y() == 0.f || pa.y() == size)); + if (n > 2 || (n == 1 && !border)) + return false; + } + return true; +} + +TEST_CASE("TextureDisplacement: the step cutter turns a stepped field into walls", "[TextureDisplacement]") +{ + // Square posts 1.2 mm wide on a 2 mm pitch, as a binary field: a step everywhere along the post + // edges, flat everywhere else. 1 mm triangles, so every post edge crosses several of them. + constexpr float RELIEF = 0.4f, SIZE = 6.f, STEP_W = 0.05f, GAP = 0.075f; + const auto posts = [](float x, float y) { + const float fx = std::fmod(std::fmod(x, 2.f) + 2.f, 2.f), fy = std::fmod(std::fmod(y, 2.f) + 2.f, 2.f); + return (fx > 0.4f && fx < 1.6f && fy > 0.4f && fy < 1.6f) ? RELIEF : 0.f; + }; + const HeightFieldSampler sampler = [&](const Vec3f &p, const Vec3f &) { return posts(p.x(), p.y()); }; + const indexed_triangle_set sheet = make_flat_sheet(SIZE, 1.f); + const std::vector region(sheet.indices.size(), 1); + + std::vector source; + size_t cuts = 0; + const indexed_triangle_set cut = cut_mesh_at_steps(sheet, region, sampler, STEP_W, GAP, 0.f, &source, &cuts); + + CHECK(cuts > 0); + CHECK(cut.indices.size() > sheet.indices.size()); + REQUIRE(source.size() == cut.indices.size()); + CHECK(sheet_is_manifold(cut, SIZE)); + + // Displace along the cut mesh's own area-weighted vertex normals, as the bake does. + std::vector normal(cut.vertices.size(), Vec3f::Zero()); + for (const auto &t : cut.indices) { + const Vec3f &a = cut.vertices[size_t(t[0])], &b = cut.vertices[size_t(t[1])], &c = cut.vertices[size_t(t[2])]; + const Vec3f n = (b - a).cross(c - a); + CHECK(n.z() > 0.f); // nothing inverted, nothing degenerate + for (int k = 0; k < 3; ++k) + normal[size_t(t[k])] += n; + } + indexed_triangle_set displaced = cut; + for (size_t v = 0; v < displaced.vertices.size(); ++v) { + const Vec3f n = normal[v].normalized(); + displaced.vertices[v] += n * sampler(cut.vertices[v], n); + } + + // The defining property of the cut: a triangle that spans both heights is a wall, and a wall stands + // within the seam gap of a step. Anywhere else a mixed triangle would be a ramp - exactly what + // refinement leaves and the cutter is there to remove. + const auto near_step = [&](const Vec3f &p) { + const float h = posts(p.x(), p.y()); + for (int k = 0; k < 8; ++k) { + const float ang = float(k) * float(M_PI) / 4.f; + if (posts(p.x() + GAP * std::cos(ang), p.y() + GAP * std::sin(ang)) != h) + return true; + } + return false; + }; + size_t walls = 0, ramps = 0; + for (const auto &t : displaced.indices) { + const float z0 = displaced.vertices[size_t(t[0])].z(), z1 = displaced.vertices[size_t(t[1])].z(), + z2 = displaced.vertices[size_t(t[2])].z(); + if (std::abs(z0 - z1) < 1e-4f && std::abs(z1 - z2) < 1e-4f) + continue; // flat: on one level + bool wall = true; + for (int k = 0; k < 3; ++k) + wall = wall && near_step(cut.vertices[size_t(t[k])]); + (wall ? walls : ramps)++; + } + CHECK(walls > 0); + CHECK(ramps == 0); +} + +TEST_CASE("TextureDisplacement: the step cutter passes a smooth field through untouched", "[TextureDisplacement]") +{ + // A wide bump: its mid-level contour runs through the sheet, but nowhere is it a step, so there is + // nothing to cut - refinement is the right tool for it. + const HeightFieldSampler bump = [](const Vec3f &p, const Vec3f &) { + const float r2 = (p.x() - 3.f) * (p.x() - 3.f) + (p.y() - 3.f) * (p.y() - 3.f); + return 0.4f * std::exp(-r2 / 3.f); + }; + const indexed_triangle_set sheet = make_flat_sheet(6.f, 1.f); + const std::vector region(sheet.indices.size(), 1); + + std::vector source; + size_t cuts = 0; + const indexed_triangle_set out = cut_mesh_at_steps(sheet, region, bump, 0.05f, 0.075f, 0.f, &source, &cuts); + + CHECK(cuts == 0); + CHECK(out.indices.size() == sheet.indices.size()); + CHECK(out.vertices.size() == sheet.vertices.size()); + REQUIRE(source.size() == sheet.indices.size()); + for (size_t i = 0; i < source.size(); ++i) + CHECK(source[i] == int(i)); +} + +TEST_CASE("TextureDisplacement: the step cutter leaves features at the step's own scale to refinement", + "[TextureDisplacement]") +{ + // Square posts filling the middle half of each cell, binary and sharp at every crossing - the only + // difference between the two is the pitch (offset so no post edge lies along a mesh edge). Posts a + // step width or so across have no pure interior for a seam copy to land in, and cutting them would + // double the triangles for walls no bigger than the blur. + constexpr float STEP_W = 0.05f; + const float pitch = GENERATE(0.12f, 0.6f); + const auto posts = [pitch](const Vec3f &p, const Vec3f &) { + const float fx = std::fmod(p.x() + 0.17f, pitch) / pitch, fy = std::fmod(p.y() + 0.31f, pitch) / pitch; + return (fx > 0.25f && fx < 0.75f && fy > 0.25f && fy < 0.75f) ? 0.4f : 0.f; + }; + const indexed_triangle_set sheet = make_flat_sheet(6.f, 1.f); + const std::vector region(sheet.indices.size(), 1); + + size_t cuts = 0; + const indexed_triangle_set out = cut_mesh_at_steps(sheet, region, posts, STEP_W, STEP_W, 0.f, nullptr, &cuts); + + if (0.5f * pitch < 2.25f * STEP_W) { + CHECK(cuts == 0); + CHECK(out.indices.size() == sheet.indices.size()); + } else { + CHECK(cuts > 0); + CHECK(sheet_is_manifold(out, 6.f)); + } +} + +TEST_CASE("TextureDisplacement: the step cutter only cuts inside the region", "[TextureDisplacement]") +{ + // A single step at x = 3 across the whole sheet, but only the left half is painted. + const HeightFieldSampler stripe = [](const Vec3f &p, const Vec3f &) { return p.x() > 3.f ? 0.4f : 0.f; }; + const indexed_triangle_set sheet = make_flat_sheet(6.f, 1.f); + + SECTION("an empty region is a no-op") + { + const std::vector none(sheet.indices.size(), 0); + size_t cuts = 0; + const indexed_triangle_set out = cut_mesh_at_steps(sheet, none, stripe, 0.05f, 0.075f, 0.f, nullptr, &cuts); + CHECK(cuts == 0); + CHECK(out.indices.size() == sheet.indices.size()); + } + + SECTION("unpainted triangles away from the paint are untouched") + { + std::vector region(sheet.indices.size(), 0); + for (size_t t = 0; t < sheet.indices.size(); ++t) { + const auto &f = sheet.indices[t]; + const float cy = (sheet.vertices[size_t(f[0])].y() + sheet.vertices[size_t(f[1])].y() + + sheet.vertices[size_t(f[2])].y()) / 3.f; + region[t] = cy < 3.f ? 1 : 0; // paint the lower half + } + std::vector source; + size_t cuts = 0; + const indexed_triangle_set out = cut_mesh_at_steps(sheet, region, stripe, 0.05f, 0.075f, 0.f, &source, &cuts); + CHECK(cuts > 0); + CHECK(sheet_is_manifold(out, 6.f)); + // An unpainted triangle that shares no edge with a painted one comes out exactly as it went in. + std::vector descendants(sheet.indices.size(), 0); + for (int s : source) + ++descendants[size_t(s)]; + for (size_t t = 0; t < sheet.indices.size(); ++t) { + const auto &f = sheet.indices[t]; + float ymin = 6.f; + for (int k = 0; k < 3; ++k) + ymin = std::min(ymin, sheet.vertices[size_t(f[k])].y()); + if (ymin > 3.f) // strictly above the painted half, so no shared edge with it + CHECK(descendants[t] == 1); + } + } +} + +// --------------------------------------------------------------------------------------------- +// Texture smoothing +// --------------------------------------------------------------------------------------------- + +TEST_CASE("TextureDisplacement: texture smoothing is a wrapped box blur whatever the radius", "[TextureDisplacement]") +{ + // A small pseudo-random grey image, encoded through the PNG writer so decode_height_texture() takes + // its normal path. The expected result is the plain definition of the blur - two passes of a + // (2r+1) box, horizontal then vertical, wrapping at the edges - which the sliding-window + // implementation must reproduce byte for byte. + const size_t w = 37, h = 23; + std::vector src(w * h); + uint32_t seed = 12345; + for (uint8_t &p : src) { seed = seed * 1664525u + 1013904223u; p = uint8_t(seed >> 24); } + const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("texdisp_test_%%%%%%%%.png"); + REQUIRE(Slic3r::png::write_gray_to_file(tmp_path.string(), w, h, src)); + std::vector bytes; + { + std::ifstream ifs(tmp_path.string(), std::ios::binary); + bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp_path, ec); + REQUIRE_FALSE(bytes.empty()); + + TextureDisplacementLayer layer; + layer.image_data = std::make_shared>(std::move(bytes)); + // radius = smoothing * 0.05 * min(w, h) = 0.05 * 23 * smoothing; 1.0 gives 1.15 -> whole radius 2, + // cross-faded 57.5 % toward the blurred image (see smooth_height_pixels()). + layer.smoothing = 1.f; + const DecodedHeightTexture tex = decode_height_texture(layer); + REQUIRE(tex.width == int(w)); + REQUIRE(tex.height == int(h)); + + const int radius = 2; + const float mixf = std::clamp((0.05f * 23.f) / 2.f, 0.f, 1.f); + auto box = [&](std::vector px) { + const int window = 2 * radius + 1; + const float inv = 1.f / float(window); + std::vector t2(px.size()); + for (int pass = 0; pass < 2; ++pass) { + for (int y = 0; y < int(h); ++y) + for (int x = 0; x < int(w); ++x) { + float s = 0.f; + for (int k = -radius; k <= radius; ++k) s += float(px[size_t(y) * w + size_t(((x + k) % int(w) + int(w)) % int(w))]); + t2[size_t(y) * w + size_t(x)] = uint8_t(std::lround(s * inv)); + } + for (int x = 0; x < int(w); ++x) + for (int y = 0; y < int(h); ++y) { + float s = 0.f; + for (int k = -radius; k <= radius; ++k) s += float(t2[size_t(((y + k) % int(h) + int(h)) % int(h)) * w + size_t(x)]); + px[size_t(y) * w + size_t(x)] = uint8_t(std::lround(s * inv)); + } + } + return px; + }; + const std::vector blurred = box(src); + size_t mismatches = 0; + for (size_t i = 0; i < w * h; ++i) { + const uint8_t expected = uint8_t(std::lround(float(src[i]) + (float(blurred[i]) - float(src[i])) * mixf)); + mismatches += tex.pixels[i] != expected; + } + CHECK(mismatches == 0); + + // Asking again with the same smoothing is served from the cache and must be identical. + const DecodedHeightTexture again = decode_height_texture(layer); + CHECK(again.pixels == tex.pixels); +} + +// --------------------------------------------------------------------------------------------- +// Edge flips along the height field (v2 pipeline) +// --------------------------------------------------------------------------------------------- + +TEST_CASE("TextureDisplacement: edge flips lay a stepped field's wall along the grid's diagonals", "[TextureDisplacement]") +{ + // A regular grid crossed by a step at 30 degrees. Before flipping, the step's wall zigzags: many + // triangles have corners on both sides of it. Flipping each quad's diagonal to follow the step must + // cut that count down, without changing the triangle count, the winding, or the manifoldness. + const indexed_triangle_set sheet = make_flat_sheet(6.f, 0.2f); + const float c = std::cos(0.5236f), s = std::sin(0.5236f); + const auto side_of = [&](const Vec3f &p) { return c * p.x() + s * p.y() > 3.5f; }; + const TextureBake::HeightSampleFn field = [&](const Vec3f &p, const Vec3f &, const Vec3f &) { + return side_of(p) ? 0.4f : 0.f; + }; + const auto mixed = [&](const TextureBake::TriSoup &g) { + size_t n = 0; + for (size_t t = 0; t < g.triangle_count(); ++t) { + const bool a = side_of(g.pos[t * 3]), b = side_of(g.pos[t * 3 + 1]), d = side_of(g.pos[t * 3 + 2]); + n += (a != b || b != d); + } + return n; + }; + + const TextureBake::TriSoup before = TextureBake::to_soup(sheet); + const TextureBake::FlipResult after = TextureBake::flip_edges_to_height(before, {}, field, TextureBake::FlipSettings{}, {}); + + CHECK(after.flipped > 0); + REQUIRE(after.geometry.triangle_count() == before.triangle_count()); + const size_t mixed_before = mixed(before), mixed_after = mixed(after.geometry); + CHECK(mixed_after < mixed_before); + // Every triangle still faces up, and none collapsed. + for (size_t t = 0; t < after.geometry.triangle_count(); ++t) { + const Vec3f n = (after.geometry.pos[t * 3 + 1] - after.geometry.pos[t * 3]).cross(after.geometry.pos[t * 3 + 2] - after.geometry.pos[t * 3]); + CHECK(n.z() > 1e-6f); + } + // Manifold: rebuild an indexed mesh from the soup and count edge uses. + indexed_triangle_set rebuilt; + std::map, int> ids; + for (const Vec3f &p : after.geometry.pos) { + const auto key = std::make_tuple(int(std::lround(p.x() * 1e4)), int(std::lround(p.y() * 1e4)), int(std::lround(p.z() * 1e4))); + auto it = ids.find(key); + if (it == ids.end()) { it = ids.emplace(key, int(rebuilt.vertices.size())).first; rebuilt.vertices.push_back(p); } + (void) it; + } + for (size_t t = 0; t < after.geometry.triangle_count(); ++t) { + int idx[3]; + for (int k = 0; k < 3; ++k) { + const Vec3f &p = after.geometry.pos[t * 3 + size_t(k)]; + idx[k] = ids.at(std::make_tuple(int(std::lround(p.x() * 1e4)), int(std::lround(p.y() * 1e4)), int(std::lround(p.z() * 1e4)))); + } + rebuilt.indices.emplace_back(idx[0], idx[1], idx[2]); + } + CHECK(sheet_is_manifold(rebuilt, 6.f)); +} + +// --------------------------------------------------------------------------------------------- +// Automatic resolution (v2 pipeline) +// --------------------------------------------------------------------------------------------- + +TEST_CASE("TextureDisplacement: automatic resolution follows the texture's texel size and sharpness", "[TextureDisplacement]") +{ + // A 20 mm cube (diagonal 34.6 mm, so the edge may go up to 0.69 mm) with an 8 mm tile. + const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); + + SECTION("a hard-edged texture gets one texel per edge") + { + TextureDisplacementLayer layer; + layer.image_data = make_checkerboard_png(16, 16); // 2x2 texel checks: every other texel is a step + layer.tiling_scale = 8.f; // texel = 0.5 mm + const TextureDetail detail = analyze_texture_detail(layer); + CHECK(detail.sharp_fraction > 0.15f); + CHECK_THAT(detail.pixels_per_edge, WithinAbs(1.f, 1e-6f)); + const V2Resolution rec = recommend_v2_resolution(cube, { layer }); + CHECK_THAT(rec.texel_mm, WithinAbs(0.5f, 1e-4f)); + CHECK_THAT(rec.edge_mm, WithinAbs(0.5f, 1e-4f)); + CHECK(rec.budget_k >= 10); + CHECK(rec.budget_k <= 2000); + } + + SECTION("a flat texture gets four texels per edge, within the model's clamp") + { + TextureDisplacementLayer layer; + layer.image_data = make_flat_gray_png(128, 16, 16); + layer.tiling_scale = 8.f; // texel 0.5 mm x 4 = 2 mm, clamped to diagonal / 50 + const TextureDetail detail = analyze_texture_detail(layer); + CHECK_THAT(detail.pixels_per_edge, WithinAbs(4.f, 1e-6f)); + const V2Resolution rec = recommend_v2_resolution(cube, { layer }); + CHECK_THAT(rec.edge_mm, WithinAbs(0.70f, 0.011f)); // ceil(34.64 / 50 = 0.693) at 0.01 + } + + SECTION("the world transform scales the tile against the model") + { + TextureDisplacementLayer layer; + layer.image_data = make_checkerboard_png(16, 16); + layer.tiling_scale = 8.f; + // Scaled up 3x the cube is 60 mm; the texel is still 0.5 mm in world terms, so the edge holds. + const V2Resolution rec = recommend_v2_resolution(cube, { layer }, Transform3d(Eigen::Scaling(3.0))); + CHECK_THAT(rec.edge_mm, WithinAbs(0.5f, 1e-4f)); + const V2Resolution plain = recommend_v2_resolution(cube, { layer }); + CHECK(rec.budget_k > plain.budget_k); // nine times the area wants more triangles + } + + SECTION("no usable texture gives no recommendation") + { + TextureDisplacementLayer empty; + const V2Resolution rec = recommend_v2_resolution(cube, { empty }); + CHECK(rec.edge_mm == 0.f); + } +} + +TEST_CASE("TextureDisplacement: the default pipeline colours its rebuilt triangles where painted", "[TextureDisplacement]") +{ + // The same quad and 2x2 colour texture as the classic-path colour test, but baked through the + // one-run pipeline, which rebuilds the topology: every output triangle has to be coloured from the + // texture at its own position, and only those over the painted half of the quad. + const indexed_triangle_set quad = color_test_quad(); + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_rgb_png_2x2(); + layer.color_enabled = true; + layer.depth_mm = 0.f; + layer.tiling_scale = 10.f; // one tile over the whole quad, so all four texels show + layer.projection_method = TextureProjectionMethod::Triplanar; + + TriangleMesh mesh(quad); + TriangleSelector selector(mesh); + selector.set_facet(0, EnforcerBlockerType::ENFORCER); // the lower-right half only + TextureDisplacementFacetsData facets; + facets[0] = selector.serialize(); + + const std::array palette = { Vec3f(1, 0, 0), Vec3f(0, 1, 0), Vec3f(0, 0, 1) }; + TextureColorRequest request; + std::vector triangle_color; + request.out_triangle = &triangle_color; + request.quantize = [&palette](const Vec3f &rgb) { + int best = 0; + float bd = std::numeric_limits::max(); + for (int i = 0; i < 3; ++i) + if (const float d = (palette[size_t(i)] - rgb).squaredNorm(); d < bd) { bd = d; best = i; } + return best; + }; + + TextureDisplacementOptions options; + options.pipeline_v2 = true; + options.v2_refine_mm = 1.f; + options.v2_max_triangles_k = 0; // no simplification: a plain refined quad + const indexed_triangle_set out = build_texture_displacement(quad, { layer }, facets, options, {}, &request); + + REQUIRE(out.indices.size() > 2); + REQUIRE(triangle_color.size() == out.indices.size()); + size_t coloured = 0, plain = 0; + std::set distinct; + for (size_t i = 0; i < out.indices.size(); ++i) { + const stl_triangle_vertex_indices &t = out.indices[i]; + const Vec3f c = (out.vertices[size_t(t[0])] + out.vertices[size_t(t[1])] + out.vertices[size_t(t[2])]) / 3.f; + const bool painted_side = c.y() < c.x(); // below the diagonal: triangle 0 of the quad + if (triangle_color[i] > 0) { ++coloured; distinct.insert(triangle_color[i]); CHECK(painted_side); } + else { ++plain; CHECK_FALSE(painted_side); } + } + CHECK(coloured > 0); + CHECK(plain > 0); + CHECK(distinct.size() >= 2); // the texture has four colours across the quad, so one side sees several +} + +// Per chart of an unwrap: whether it is a topological disk (V - E + F = 1, one boundary loop), the only shape a +// single island can be laid flat from. +static std::vector unwrap_charts_are_disks(const PatchUnwrap &u) +{ + std::vector> verts(size_t(u.chart_count)); + std::vector>> edges(size_t(u.chart_count)); + std::vector faces(size_t(u.chart_count), 0); + for (const stl_triangle_vertex_indices &tri : u.indices) { + const int c = u.vertex_chart[size_t(tri[0])]; + ++faces[size_t(c)]; + for (int i = 0; i < 3; ++i) { + verts[size_t(c)].insert(tri[i]); + edges[size_t(c)].insert({ std::min(tri[i], tri[(i + 1) % 3]), std::max(tri[i], tri[(i + 1) % 3]) }); + } + } + // Boundary loops per chart: flood the boundary edges' vertices. + std::vector> boundary_adj(u.uvs.size()); + for (const auto &[a, b] : u.boundary_edges) { + boundary_adj[size_t(a)].push_back(b); + boundary_adj[size_t(b)].push_back(a); + } + std::vector loops(size_t(u.chart_count), 0); + std::vector seen(u.uvs.size(), false); + for (size_t v = 0; v < u.uvs.size(); ++v) { + if (seen[v] || boundary_adj[v].empty()) + continue; + ++loops[size_t(u.vertex_chart[v])]; + std::vector stack{ int(v) }; + seen[v] = true; + while (!stack.empty()) { + const int x = stack.back(); + stack.pop_back(); + for (const int y : boundary_adj[size_t(x)]) + if (!seen[size_t(y)]) { + seen[size_t(y)] = true; + stack.push_back(y); + } + } + } + std::vector disks(size_t(u.chart_count)); + for (int c = 0; c < u.chart_count; ++c) + disks[size_t(c)] = int(verts[size_t(c)].size()) - int(edges[size_t(c)].size()) + faces[size_t(c)] == 1 && + loops[size_t(c)] == 1; + return disks; +} + +TEST_CASE("A cube unwraps into one island per face, laid out as a connected net", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const PatchUnwrap unwrap = compute_patch_unwrap(cube, 30.f, 0.f); + // Two triangles per face, and every face edge but the diagonals is a 90 degree crease. + REQUIRE(unwrap.chart_count == 6); + + const std::vector islands = compute_connected_net(unwrap); + REQUIRE(islands.size() == 6); + + std::vector> placed; + std::vector placed_chart; + for (const stl_triangle_vertex_indices &tri : unwrap.indices) { + const int c = unwrap.vertex_chart[size_t(tri[0])]; + std::array t; + for (int k = 0; k < 3; ++k) + t[size_t(k)] = apply_island_transform(unwrap.uvs[size_t(tri[k])], c, unwrap, islands); + placed.push_back(t); + placed_chart.push_back(c); + } + + SECTION("no two faces overlap") + { + // A 10 mm cube: the six 100 mm2 faces laid flat without overlap cover 600 mm2, which triangles overlapping + // anywhere could not add up to within their joint bounding box... so test it directly, by sampling. + float lo_x = 1e9f, lo_y = 1e9f, hi_x = -1e9f, hi_y = -1e9f; + for (const auto &t : placed) + for (const Vec2f &p : t) { + lo_x = std::min(lo_x, p.x()); + lo_y = std::min(lo_y, p.y()); + hi_x = std::max(hi_x, p.x()); + hi_y = std::max(hi_y, p.y()); + } + const auto inside = [](const std::array &t, const Vec2f &p) { + const auto cross = [](const Vec2f &a, const Vec2f &b) { return a.x() * b.y() - a.y() * b.x(); }; + const float d0 = cross(t[1] - t[0], p - t[0]), d1 = cross(t[2] - t[1], p - t[1]), d2 = cross(t[0] - t[2], p - t[2]); + return (d0 > 1e-3f && d1 > 1e-3f && d2 > 1e-3f) || (d0 < -1e-3f && d1 < -1e-3f && d2 < -1e-3f); + }; + int overlapping = 0; + for (float x = lo_x + 0.13f; x < hi_x; x += 0.5f) + for (float y = lo_y + 0.17f; y < hi_y; y += 0.5f) { + int covering = 0; + for (const auto &t : placed) + covering += inside(t, Vec2f(x, y)) ? 1 : 0; + overlapping += covering > 1 ? 1 : 0; + } + CHECK(overlapping == 0); + } + + SECTION("every face shares an edge with another face of the net") + { + std::vector joined(6, false); + for (size_t i = 0; i < placed.size(); ++i) + for (size_t j = 0; j < placed.size(); ++j) { + if (placed_chart[i] == placed_chart[j]) + continue; + int shared_corners = 0; + for (const Vec2f &p : placed[i]) + for (const Vec2f &q : placed[j]) + shared_corners += (p - q).norm() < 1e-3f ? 1 : 0; + if (shared_corners >= 2) + joined[size_t(placed_chart[i])] = true; + } + for (int c = 0; c < 6; ++c) + CHECK(joined[size_t(c)]); + } +} + +TEST_CASE("Unwrapping a closed cylinder cuts its side until every island is a disk", "[TextureDisplacement]") +{ + // The side is smooth, so the seam angle alone leaves it as one ring-shaped chart, which cannot be laid flat. + const indexed_triangle_set cylinder = its_make_cylinder(5., 20., 2. * PI / 36.); + const PatchUnwrap unwrap = compute_patch_unwrap(cylinder, 30.f, 0.f); + REQUIRE(unwrap.chart_count >= 4); // two caps and at least two side pieces + + const std::vector disks = unwrap_charts_are_disks(unwrap); + for (int c = 0; c < unwrap.chart_count; ++c) + CHECK(disks[size_t(c)]); +} + +TEST_CASE("A UV edit on one copy of a seam vertex leaves its other copies alone", "[TextureDisplacement]") +{ + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + PatchUnwrap unwrap = compute_patch_unwrap(cube, 30.f, 0.f); + + // A cube corner has a copy in each of the three faces around it. + const int edited_copy = 0; + const int mesh_vertex = unwrap.source_vertex[size_t(edited_copy)]; + std::vector other_copies; + for (size_t i = 0; i < unwrap.uvs.size(); ++i) + if (int(i) != edited_copy && unwrap.source_vertex[i] == mesh_vertex) + other_copies.push_back(int(i)); + REQUIRE_FALSE(other_copies.empty()); + + const std::vector before = unwrap.uvs; + const Vec2f target = before[size_t(edited_copy)] + Vec2f(3.f, -2.f); + const std::vector edited = apply_lscm_uv_overrides(unwrap, { { lscm_uv_override_key(edited_copy), target } }); + + CHECK_THAT(unwrap.uvs[size_t(edited_copy)].x(), WithinAbs(target.x(), 1e-6)); + CHECK_THAT(unwrap.uvs[size_t(edited_copy)].y(), WithinAbs(target.y(), 1e-6)); + CHECK(edited[size_t(edited_copy)]); + for (const int i : other_copies) { + CHECK_THAT(unwrap.uvs[size_t(i)].x(), WithinAbs(before[size_t(i)].x(), 1e-6)); + CHECK_THAT(unwrap.uvs[size_t(i)].y(), WithinAbs(before[size_t(i)].y(), 1e-6)); + CHECK_FALSE(edited[size_t(i)]); + } +} + +TEST_CASE("TextureDisplacement: moving the model about the plate does not move the texture on it", "[TextureDisplacement]") +{ + // The bake projects in world millimetres but anchored at the volume's origin, so an instance + // translated across the plate bakes exactly the relief the same instance bakes at the origin. The + // classic path keeps the topology, so the comparison is vertex by vertex. + const indexed_triangle_set cube = subdivide_mesh_uniform(its_make_cube(10.f, 10.f, 10.f), 1.f, 6); + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_checkerboard_png(16, 16); + layer.tiling_scale = 4.f; + layer.depth_mm = 0.5f; + TextureDisplacementFacetsData facets; + facets[0] = paint_whole_mesh(cube); + + const indexed_triangle_set at_origin = build_texture_displacement(cube, { layer }, facets, classic_options()); + const indexed_triangle_set moved = build_texture_displacement(cube, { layer }, facets, classic_options(), {}, nullptr, + Transform3d(Eigen::Translation3d(123.4, -56.7, 0.0))); + REQUIRE(moved.vertices.size() == at_origin.vertices.size()); + float max_diff = 0.f; + for (size_t i = 0; i < moved.vertices.size(); ++i) + max_diff = std::max(max_diff, (moved.vertices[i] - at_origin.vertices[i]).norm()); + CHECK(max_diff < 1e-3f); +} From a0430631b8b9a2642ab571cc9919934b3e7a86fa Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH 32/89] Texture displacement gizmo icons --- resources/images/texture_displacement_add.svg | 121 +---- .../texture_displacement_add_negative.svg | 110 +---- .../images/texture_displacement_adjust.svg | 1 + .../images/texture_displacement_brush.svg | 1 + .../images/texture_displacement_checker.svg | 166 +------ .../texture_displacement_connected_area.svg | 116 +---- .../images/texture_displacement_cross.svg | 122 +----- .../texture_displacement_distortion.svg | 198 +-------- .../images/texture_displacement_drag.svg | 1 + .../images/texture_displacement_face.svg | 1 + .../texture_displacement_fast_preview.svg | 133 +----- .../texture_displacement_map_unwrap.svg | 1 + .../images/texture_displacement_map_view.svg | 1 + .../images/texture_displacement_move_down.svg | 1 + .../images/texture_displacement_move_up.svg | 1 + .../texture_displacement_real_preview.svg | 151 +------ .../texture_displacement_tile_repeat.svg | 1 + .../texture_displacement_uv_avg_scale.svg | 1 + .../texture_displacement_uv_bg_height.svg | 1 + .../texture_displacement_uv_clear_edits.svg | 1 + .../images/texture_displacement_uv_cut.svg | 1 + .../images/texture_displacement_uv_frame.svg | 1 + .../images/texture_displacement_uv_join.svg | 1 + .../images/texture_displacement_uv_path.svg | 1 + .../images/texture_displacement_uv_seam.svg | 1 + .../texture_displacement_uv_select_edge.svg | 384 +--------------- .../texture_displacement_uv_select_island.svg | 414 +----------------- .../texture_displacement_uv_select_vertex.svg | 379 +--------------- .../images/texture_displacement_uv_snap.svg | 1 + .../images/texture_displacement_uv_unjoin.svg | 1 + .../images/texture_displacement_wireframe.svg | 164 +------ 31 files changed, 31 insertions(+), 2446 deletions(-) create mode 100644 resources/images/texture_displacement_adjust.svg create mode 100644 resources/images/texture_displacement_brush.svg create mode 100644 resources/images/texture_displacement_drag.svg create mode 100644 resources/images/texture_displacement_face.svg create mode 100644 resources/images/texture_displacement_map_unwrap.svg create mode 100644 resources/images/texture_displacement_map_view.svg create mode 100644 resources/images/texture_displacement_move_down.svg create mode 100644 resources/images/texture_displacement_move_up.svg create mode 100644 resources/images/texture_displacement_tile_repeat.svg create mode 100644 resources/images/texture_displacement_uv_avg_scale.svg create mode 100644 resources/images/texture_displacement_uv_bg_height.svg create mode 100644 resources/images/texture_displacement_uv_clear_edits.svg create mode 100644 resources/images/texture_displacement_uv_cut.svg create mode 100644 resources/images/texture_displacement_uv_frame.svg create mode 100644 resources/images/texture_displacement_uv_join.svg create mode 100644 resources/images/texture_displacement_uv_path.svg create mode 100644 resources/images/texture_displacement_uv_seam.svg create mode 100644 resources/images/texture_displacement_uv_snap.svg create mode 100644 resources/images/texture_displacement_uv_unjoin.svg diff --git a/resources/images/texture_displacement_add.svg b/resources/images/texture_displacement_add.svg index b4e5da179b..bb12b2e953 100644 --- a/resources/images/texture_displacement_add.svg +++ b/resources/images/texture_displacement_add.svg @@ -1,120 +1 @@ - - - - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_add_negative.svg b/resources/images/texture_displacement_add_negative.svg index d7979dda4f..a3fc829863 100644 --- a/resources/images/texture_displacement_add_negative.svg +++ b/resources/images/texture_displacement_add_negative.svg @@ -1,109 +1 @@ - - - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_adjust.svg b/resources/images/texture_displacement_adjust.svg new file mode 100644 index 0000000000..f3632b3f19 --- /dev/null +++ b/resources/images/texture_displacement_adjust.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_brush.svg b/resources/images/texture_displacement_brush.svg new file mode 100644 index 0000000000..cc3cd3740e --- /dev/null +++ b/resources/images/texture_displacement_brush.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_checker.svg b/resources/images/texture_displacement_checker.svg index 4631116379..833a76288e 100644 --- a/resources/images/texture_displacement_checker.svg +++ b/resources/images/texture_displacement_checker.svg @@ -1,165 +1 @@ - - - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_connected_area.svg b/resources/images/texture_displacement_connected_area.svg index b57e96d951..8c066f7be0 100644 --- a/resources/images/texture_displacement_connected_area.svg +++ b/resources/images/texture_displacement_connected_area.svg @@ -1,115 +1 @@ - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_cross.svg b/resources/images/texture_displacement_cross.svg index a2e62b9384..d7d2cfeace 100644 --- a/resources/images/texture_displacement_cross.svg +++ b/resources/images/texture_displacement_cross.svg @@ -1,121 +1 @@ - - - - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_distortion.svg b/resources/images/texture_displacement_distortion.svg index 833a34f0cf..441baf0964 100644 --- a/resources/images/texture_displacement_distortion.svg +++ b/resources/images/texture_displacement_distortion.svg @@ -1,197 +1 @@ - - - - - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_drag.svg b/resources/images/texture_displacement_drag.svg new file mode 100644 index 0000000000..75a3efe419 --- /dev/null +++ b/resources/images/texture_displacement_drag.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_face.svg b/resources/images/texture_displacement_face.svg new file mode 100644 index 0000000000..a0c5d0a4b7 --- /dev/null +++ b/resources/images/texture_displacement_face.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_fast_preview.svg b/resources/images/texture_displacement_fast_preview.svg index 9e13fb68de..84286f0c7f 100644 --- a/resources/images/texture_displacement_fast_preview.svg +++ b/resources/images/texture_displacement_fast_preview.svg @@ -1,132 +1 @@ - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_map_unwrap.svg b/resources/images/texture_displacement_map_unwrap.svg new file mode 100644 index 0000000000..a1af920f2b --- /dev/null +++ b/resources/images/texture_displacement_map_unwrap.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_map_view.svg b/resources/images/texture_displacement_map_view.svg new file mode 100644 index 0000000000..70ef64c198 --- /dev/null +++ b/resources/images/texture_displacement_map_view.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_move_down.svg b/resources/images/texture_displacement_move_down.svg new file mode 100644 index 0000000000..8f15b7c181 --- /dev/null +++ b/resources/images/texture_displacement_move_down.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_move_up.svg b/resources/images/texture_displacement_move_up.svg new file mode 100644 index 0000000000..32dfd19011 --- /dev/null +++ b/resources/images/texture_displacement_move_up.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_real_preview.svg b/resources/images/texture_displacement_real_preview.svg index f3ebdaa53d..0d132e6950 100644 --- a/resources/images/texture_displacement_real_preview.svg +++ b/resources/images/texture_displacement_real_preview.svg @@ -1,150 +1 @@ - - - - - - - - - - - - - - - - + diff --git a/resources/images/texture_displacement_tile_repeat.svg b/resources/images/texture_displacement_tile_repeat.svg new file mode 100644 index 0000000000..9a4fc8d3c8 --- /dev/null +++ b/resources/images/texture_displacement_tile_repeat.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_avg_scale.svg b/resources/images/texture_displacement_uv_avg_scale.svg new file mode 100644 index 0000000000..4734381399 --- /dev/null +++ b/resources/images/texture_displacement_uv_avg_scale.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_bg_height.svg b/resources/images/texture_displacement_uv_bg_height.svg new file mode 100644 index 0000000000..354f12d9f4 --- /dev/null +++ b/resources/images/texture_displacement_uv_bg_height.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_clear_edits.svg b/resources/images/texture_displacement_uv_clear_edits.svg new file mode 100644 index 0000000000..3d1c6c0e87 --- /dev/null +++ b/resources/images/texture_displacement_uv_clear_edits.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_cut.svg b/resources/images/texture_displacement_uv_cut.svg new file mode 100644 index 0000000000..9ae7d5e273 --- /dev/null +++ b/resources/images/texture_displacement_uv_cut.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_frame.svg b/resources/images/texture_displacement_uv_frame.svg new file mode 100644 index 0000000000..c74e648fe0 --- /dev/null +++ b/resources/images/texture_displacement_uv_frame.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_join.svg b/resources/images/texture_displacement_uv_join.svg new file mode 100644 index 0000000000..99cc51dbc0 --- /dev/null +++ b/resources/images/texture_displacement_uv_join.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_path.svg b/resources/images/texture_displacement_uv_path.svg new file mode 100644 index 0000000000..2bdf164197 --- /dev/null +++ b/resources/images/texture_displacement_uv_path.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_seam.svg b/resources/images/texture_displacement_uv_seam.svg new file mode 100644 index 0000000000..755fa231fa --- /dev/null +++ b/resources/images/texture_displacement_uv_seam.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_select_edge.svg b/resources/images/texture_displacement_uv_select_edge.svg index dbab9fdd93..d309addbd5 100644 --- a/resources/images/texture_displacement_uv_select_edge.svg +++ b/resources/images/texture_displacement_uv_select_edge.svg @@ -1,383 +1 @@ - - + diff --git a/resources/images/texture_displacement_uv_select_island.svg b/resources/images/texture_displacement_uv_select_island.svg index 733f58053e..430d187965 100644 --- a/resources/images/texture_displacement_uv_select_island.svg +++ b/resources/images/texture_displacement_uv_select_island.svg @@ -1,413 +1 @@ - - + diff --git a/resources/images/texture_displacement_uv_select_vertex.svg b/resources/images/texture_displacement_uv_select_vertex.svg index f66446f5d0..f9d5318189 100644 --- a/resources/images/texture_displacement_uv_select_vertex.svg +++ b/resources/images/texture_displacement_uv_select_vertex.svg @@ -1,378 +1 @@ - - + diff --git a/resources/images/texture_displacement_uv_snap.svg b/resources/images/texture_displacement_uv_snap.svg new file mode 100644 index 0000000000..b3beeaaf5a --- /dev/null +++ b/resources/images/texture_displacement_uv_snap.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_uv_unjoin.svg b/resources/images/texture_displacement_uv_unjoin.svg new file mode 100644 index 0000000000..bde79aec0a --- /dev/null +++ b/resources/images/texture_displacement_uv_unjoin.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_wireframe.svg b/resources/images/texture_displacement_wireframe.svg index 20ac8c1b03..44d1c0c3e0 100644 --- a/resources/images/texture_displacement_wireframe.svg +++ b/resources/images/texture_displacement_wireframe.svg @@ -1,163 +1 @@ - - - - - - - - - - - - - - - - - + From 3baec9cf2b526f5d53a9bfafb37cf52e3a43c932 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Thu, 17 Sep 2026 07:43:16 -0400 Subject: [PATCH 33/89] Add Snapmaker U1 high-flow nozzle profiles Add separate HF presets for 0.2, 0.4, 0.6 and 0.8 mm nozzles, preserving standard printer settings and machine G-code. Provide 141 HF filament presets with volumetric ceilings 3x their non-HF baseline. Generic presets inherit active Orca material definitions; remove unregistered Snorca-only Generic profiles and their obsolete PLA Wood dependent. Add 36 diameter-specific HF processes, all using Snorca's 0.4 mm HF speeds: 600 mm/s inner walls, 500 mm/s outer walls, and 600 mm/s solid/sparse infill. Preserve other process settings and keep standard-nozzle speeds unchanged. No extra-high-flow tier or C++ changes are included. Native profile validation passed. HF material slicing checks passed, and all 36 HF processes sliced with verified speed settings and tool changes. Document calibration sources, flow ceilings and remaining physical tuning. Generate canonical preset IDs and record the eight new Snapmaker generic claims in the filament ID snapshot. Regenerate the vendor index. Co-authored-by: Codex --- docs/HLSD/snapmaker-high-flow.md | 92 +++ resources/profiles/Snapmaker.json | 726 +++++++++++++++++- ...ron ASA-CF08 @U1 0.4 High Flow nozzle.json | 17 + ...on PA12-CF10 @U1 0.4 High Flow nozzle.json | 17 + ...ron PA6-CF20 @U1 0.4 High Flow nozzle.json | 17 + ...on PA612-ESD @U1 0.4 High Flow nozzle.json | 17 + ...ron PETG-ESD @U1 0.4 High Flow nozzle.json | 17 + ...ron PPS-GF20 @U1 0.4 High Flow nozzle.json | 17 + ...Generic PETG @U1 0.2 High Flow nozzle.json | 17 + ...Generic PETG @U1 0.4 High Flow nozzle.json | 17 + ...Generic PETG @U1 0.6 High Flow nozzle.json | 17 + ...Generic PETG @U1 0.8 High Flow nozzle.json | 17 + ...eric PETG HF @U1 0.2 High Flow nozzle.json | 17 + ...eric PETG HF @U1 0.4 High Flow nozzle.json | 17 + ...eric PETG HF @U1 0.6 High Flow nozzle.json | 17 + ...eric PETG HF @U1 0.8 High Flow nozzle.json | 17 + ...eric PETG-CF @U1 0.4 High Flow nozzle.json | 17 + ...eric PETG-CF @U1 0.6 High Flow nozzle.json | 17 + ...eric PETG-CF @U1 0.8 High Flow nozzle.json | 17 + .../Generic PLA @U1 0.2 High Flow nozzle.json | 17 + .../Generic PLA @U1 0.4 High Flow nozzle.json | 17 + .../Generic PLA @U1 0.6 High Flow nozzle.json | 17 + .../Generic PLA @U1 0.8 High Flow nozzle.json | 17 + ...A High Speed @U1 0.2 High Flow nozzle.json | 17 + ...A High Speed @U1 0.4 High Flow nozzle.json | 17 + ...A High Speed @U1 0.6 High Flow nozzle.json | 17 + ...A High Speed @U1 0.8 High Flow nozzle.json | 17 + ...ric PLA Silk @U1 0.2 High Flow nozzle.json | 17 + ...ric PLA Silk @U1 0.4 High Flow nozzle.json | 17 + ...ric PLA Silk @U1 0.6 High Flow nozzle.json | 17 + ...ric PLA Silk @U1 0.8 High Flow nozzle.json | 17 + ...neric PLA-CF @U1 0.4 High Flow nozzle.json | 17 + ...neric PLA-CF @U1 0.6 High Flow nozzle.json | 17 + ...neric PLA-CF @U1 0.8 High Flow nozzle.json | 17 + .../Generic TPU @U1 0.4 High Flow nozzle.json | 17 + .../Generic TPU @U1 0.6 High Flow nozzle.json | 17 + .../Generic TPU @U1 0.8 High Flow nozzle.json | 17 + ...nchroma CoPE @U1 0.4 High Flow nozzle.json | 17 + ...anchroma PLA @U1 0.4 High Flow nozzle.json | 17 + ...LA Celestial @U1 0.4 High Flow nozzle.json | 17 + ...a PLA Galaxy @U1 0.4 High Flow nozzle.json | 17 + ...oma PLA Glow @U1 0.4 High Flow nozzle.json | 17 + ...PLA Luminous @U1 0.4 High Flow nozzle.json | 17 + ...a PLA Marble @U1 0.4 High Flow nozzle.json | 17 + ...ma PLA Matte @U1 0.4 High Flow nozzle.json | 17 + ...PLA Metallic @U1 0.4 High Flow nozzle.json | 17 + ...oma PLA Neon @U1 0.4 High Flow nozzle.json | 17 + ...ma PLA Satin @U1 0.4 High Flow nozzle.json | 17 + ...oma PLA Silk @U1 0.4 High Flow nozzle.json | 17 + ...LA Starlight @U1 0.4 High Flow nozzle.json | 17 + ...A Temp Shift @U1 0.4 High Flow nozzle.json | 17 + ... Translucent @U1 0.4 High Flow nozzle.json | 17 + ...PLA UV Shift @U1 0.4 High Flow nozzle.json | 17 + ...yLite CosPLA @U1 0.4 High Flow nozzle.json | 17 + ...olyLite PETG @U1 0.4 High Flow nozzle.json | 17 + ... Translucent @U1 0.4 High Flow nozzle.json | 17 + ...PolyLite PLA @U1 0.4 High Flow nozzle.json | 17 + ...e PLA Galaxy @U1 0.4 High Flow nozzle.json | 17 + ...ite PLA Glow @U1 0.4 High Flow nozzle.json | 17 + ...PLA Luminous @U1 0.4 High Flow nozzle.json | 17 + ...ite PLA Neon @U1 0.4 High Flow nozzle.json | 17 + ...Lite PLA Pro @U1 0.4 High Flow nozzle.json | 17 + ...Pro Metallic @U1 0.4 High Flow nozzle.json | 17 + ...LA Starlight @U1 0.4 High Flow nozzle.json | 17 + ... Translucent @U1 0.4 High Flow nozzle.json | 17 + ...olyTerra PLA @U1 0.4 High Flow nozzle.json | 17 + ...a PLA Marble @U1 0.4 High Flow nozzle.json | 17 + ...lyTerra PLA+ @U1 0.4 High Flow nozzle.json | 17 + ...l PLA Family @U1 0.4 High Flow nozzle.json | 17 + ...maker HT-PLA @U1 0.4 High Flow nozzle.json | 17 + ...er HT-PLA-GF @U1 0.4 High Flow nozzle.json | 17 + ...lymaker PETG @U1 0.4 High Flow nozzle.json | 17 + ... PETG Galaxy @U1 0.4 High Flow nozzle.json | 17 + ...olymaker PLA @U1 0.4 High Flow nozzle.json | 17 + ...aker PLA Pro @U1 0.4 High Flow nozzle.json | 17 + ...Pro Metallic @U1 0.4 High Flow nozzle.json | 17 + ...k PLA Family @U1 0.4 High Flow nozzle.json | 17 + ...h PLA Family @U1 0.4 High Flow nozzle.json | 17 + ...napmaker ABS @U1 0.4 High Flow nozzle.json | 50 ++ ...r ABS Benchy @U1 0.4 High Flow nozzle.json | 20 + ...napmaker ASA @U1 0.4 High Flow nozzle.json | 50 ++ ...port For PLA @U1 0.2 High Flow nozzle.json | 17 + ...port For PLA @U1 0.4 High Flow nozzle.json | 17 + ...port For PLA @U1 0.6 High Flow nozzle.json | 17 + ...port For PLA @U1 0.8 High Flow nozzle.json | 17 + ...pmaker PA-CF @U1 0.4 High Flow nozzle.json | 17 + ...napmaker PET @U1 0.4 High Flow nozzle.json | 17 + ...apmaker PETG @U1 0.4 High Flow nozzle.json | 17 + ...aker PETG HF @U1 0.2 High Flow nozzle.json | 29 + ...aker PETG HF @U1 0.4 High Flow nozzle.json | 68 ++ ...aker PETG HF @U1 0.6 High Flow nozzle.json | 29 + ...aker PETG HF @U1 0.8 High Flow nozzle.json | 29 + ... Translucent @U1 0.2 High Flow nozzle.json | 23 + ... Translucent @U1 0.4 High Flow nozzle.json | 23 + ... Translucent @U1 0.6 High Flow nozzle.json | 23 + ... Translucent @U1 0.8 High Flow nozzle.json | 23 + ...aker PETG-CF @U1 0.4 High Flow nozzle.json | 65 ++ ...napmaker PLA @U1 0.4 High Flow nozzle.json | 17 + ...er PLA Basic @U1 0.4 High Flow nozzle.json | 17 + ...aker PLA Eco @U1 0.4 High Flow nozzle.json | 17 + ...ull Spectrum @U1 0.4 High Flow nozzle.json | 32 + ...ker PLA Glow @U1 0.4 High Flow nozzle.json | 26 + ...ker PLA Lite @U1 0.4 High Flow nozzle.json | 17 + ...er PLA Matte @U1 0.2 High Flow nozzle.json | 32 + ...er PLA Matte @U1 0.4 High Flow nozzle.json | 44 ++ ...er PLA Matte @U1 0.6 High Flow nozzle.json | 32 + ...er PLA Matte @U1 0.8 High Flow nozzle.json | 32 + ...er PLA Metal @U1 0.4 High Flow nozzle.json | 17 + ...ker PLA Silk @U1 0.2 High Flow nozzle.json | 17 + ...ker PLA Silk @U1 0.4 High Flow nozzle.json | 23 + ...ker PLA Silk @U1 0.6 High Flow nozzle.json | 17 + ...ker PLA Silk @U1 0.8 High Flow nozzle.json | 17 + ...LA SnapSpeed @U1 0.2 High Flow nozzle.json | 23 + ...LA SnapSpeed @U1 0.4 High Flow nozzle.json | 26 + ...LA SnapSpeed @U1 0.6 High Flow nozzle.json | 23 + ...LA SnapSpeed @U1 0.8 High Flow nozzle.json | 23 + ... Translucent @U1 0.2 High Flow nozzle.json | 23 + ... Translucent @U1 0.4 High Flow nozzle.json | 20 + ... Translucent @U1 0.6 High Flow nozzle.json | 23 + ... Translucent @U1 0.8 High Flow nozzle.json | 23 + ...ker PLA Wood @U1 0.4 High Flow nozzle.json | 26 + ...ker PLA Wood @U1 0.6 High Flow nozzle.json | 23 + ...ker PLA Wood @U1 0.8 High Flow nozzle.json | 23 + ...maker PLA-CF @U1 0.4 High Flow nozzle.json | 38 + ...maker PLA-CF @U1 0.6 High Flow nozzle.json | 29 + ...maker PLA-CF @U1 0.8 High Flow nozzle.json | 29 + ...napmaker PVA @U1 0.4 High Flow nozzle.json | 26 + ...napmaker PVA @U1 0.6 High Flow nozzle.json | 20 + ...napmaker PVA @U1 0.8 High Flow nozzle.json | 20 + ...napmaker TPE @U1 0.4 High Flow nozzle.json | 17 + ...aker TPU 90A @U1 0.4 High Flow nozzle.json | 17 + ...aker TPU 90A @U1 0.6 High Flow nozzle.json | 17 + ...aker TPU 90A @U1 0.8 High Flow nozzle.json | 17 + ...aker TPU 95A @U1 0.4 High Flow nozzle.json | 17 + ...r TPU 95A HF @U1 0.4 High Flow nozzle.json | 17 + ...r TPU 95A HF @U1 0.6 High Flow nozzle.json | 17 + ...r TPU 95A HF @U1 0.8 High Flow nozzle.json | 17 + ...napmaker TPU @U1 0.4 High Flow nozzle.json | 17 + ...napmaker TPU @U1 0.6 High Flow nozzle.json | 17 + ...napmaker TPU @U1 0.8 High Flow nozzle.json | 17 + ...PU High-Flow @U1 0.4 High Flow nozzle.json | 17 + ...PU High-Flow @U1 0.6 High Flow nozzle.json | 17 + ...PU High-Flow @U1 0.8 High Flow nozzle.json | 17 + .../Snapmaker U1 (0.2 High Flow nozzle).json | 18 + .../Snapmaker U1 (0.4 High Flow nozzle).json | 18 + .../Snapmaker U1 (0.6 High Flow nozzle).json | 18 + .../Snapmaker U1 (0.8 High Flow nozzle).json | 18 + .../Snapmaker/machine/Snapmaker U1.json | 2 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 + ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 + 184 files changed, 4574 insertions(+), 2 deletions(-) create mode 100644 docs/HLSD/snapmaker-high-flow.md create mode 100644 resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json create mode 100644 resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json diff --git a/docs/HLSD/snapmaker-high-flow.md b/docs/HLSD/snapmaker-high-flow.md new file mode 100644 index 0000000000..63806cbd83 --- /dev/null +++ b/docs/HLSD/snapmaker-high-flow.md @@ -0,0 +1,92 @@ +# Snapmaker U1 high-flow profiles + +The U1 provides separate High Flow printer presets for 0.2, 0.4, 0.6 and +0.8 mm nozzles. Each describes four tools fitted with the same diameter and +high-flow nozzle. They inherit the corresponding standard printer, retaining +its model identity, geometry, motion limits and machine G-code. Existing +standard and mixed-diameter presets retain their settings and names. + +Enable the High Flow variants under Snapmaker U1 in the printer setup, select +the matching printer, and select a filament whose name includes that diameter +and `High Flow nozzle`. PLA SnapSpeed is the default material for these +printers. Dedicated HF process presets preserve diameter-specific extrusion +widths and layer heights while overriding the four print speeds below. +Separate HF filament presets keep higher ceilings out of standard-nozzle +material settings. + +## Process speeds + +All 36 HF process presets, across 0.2, 0.4, 0.6 and 0.8 mm nozzles, +use the same requested speeds from Snorca's 0.4 mm HF Standard process: + +| Setting | Speed (mm/s) | +| --- | ---: | +| Inner walls | 600 | +| Outer walls | 500 | +| Internal solid infill | 600 | +| Sparse infill | 600 | + +Each HF process inherits its standard diameter/layer-height counterpart, +so geometry, accelerations, first-layer speeds, bridges, overhangs and other +settings remain diameter-specific. Standard printers retain their original +process speeds. HF printers default to the corresponding HF process. +Volumetric ceilings and machine motion limits still cap actual speeds. + +## Calibration sources + +Except for the volumetric ceiling, the original 0.4 mm HF material overrides come from Snapmaker/OrcaSlicer `snorca/main` +commit `b1831e5dcb`, under `resources/profiles/Snapmaker/filament/`. That fork +uses `filament_flow_support: ["standard", "high_flow"]` to interpret paired +values. Here the high-flow column is translated into ordinary single-valued +filament overrides; the fork-specific keys and its profile restructuring are +not imported. Companion temperature and pressure-advance enable settings are +included. Machine and filament G-code remain inherited from this branch. + +The 14 materials with upstream HF overrides are ABS, ASA, PETG HF, PETG Translucent, PETG-CF, +PLA Full Spectrum, PLA Glow, PLA Matte, PLA Silk, PLA SnapSpeed, PLA +Translucent, PLA Wood, PLA-CF and PVA. Upstream's renamed Support For PLA is +not assumed equivalent to the local Breakaway formulation. + +## Volumetric ceiling + +All 141 HF filament presets explicitly set maximum volumetric speed to three +times the fully inherited non-HF material value. Diameter-specific parents +are used where available; Generic HF presets inherit the active Orca filament +library's `@System` definitions, which are shared across diameters. The parent +is recorded in `inherits`. This replaces both the upstream 0.4 mm HF ceiling +and the earlier capped estimates. + +The ceiling is a user-selected limit, not a measured hardware capability. +Other process and machine limits still apply. Future changes to a parent +preset's volumetric speed require updating its HF child; the multiplier is +materialized in JSON, not evaluated by the slicer. + +## Other material settings + +The 24 additional HF material presets are **estimated starting points**, not +manufacturer calibrations. They exist only where this branch already has a +matching material and diameter. In particular, this does not make filled +materials available to a 0.2 mm nozzle when no such standard profile exists. + +For each material with a paired 0.4 mm standard/HF value: + +- Flow ratio and pressure advance use the 0.4 mm HF/standard ratio, + rounded to four decimal places. A zero standard value is not divided by. +- Nozzle and initial-layer temperatures use the 0.4 mm HF-minus-standard + difference, added to the diameter's original temperature. +- Pressure advance follows the source's enable setting when its value is + adjusted. Diameter-specific retraction and cooling settings are retained. +- Missing, single-valued or `nil` comparisons leave the diameter's original + setting intact. + +For example, PLA SnapSpeed has standard volumetric limits of 2 mm^3/s at +0.2 mm and 20 mm^3/s at 0.4, 0.6 and 0.8 mm. Its HF ceilings are therefore +6 and 60 mm^3/s respectively. The other 103 HF material presets retain their +standard parent's temperature, retraction, cooling and pressure-advance +settings, changing only the volumetric ceiling and profile metadata. +Each HF preset includes its baseline and ceiling in its filament notes. +Tune the material settings for the actual nozzle, filament and temperature. + +The separate preset design requires no C++ changes or new profile keys. It +does not provide independent standard/HF selection per tool within one printer +preset; a mixed hardware setup needs its own calibrated user preset. diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index 06acef0b0e..d415c7dadd 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.15", + "version": "02.04.00.16", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ @@ -194,6 +194,10 @@ "name": "fdm_process_idex", "sub_path": "process/fdm_process_idex.json" }, + { + "name": "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json" + }, { "name": "fdm_process_U1_0.06_nozzle_0.2", "sub_path": "process/fdm_process_U1_0.06_nozzle_0.2.json" @@ -614,6 +618,146 @@ "name": "0.40 Extra Draft @Snapmaker U1 (0.6 nozzle)", "sub_path": "process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json" }, + { + "name": "0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.20 Support @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "sub_path": "process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" + }, + { + "name": "0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "sub_path": "process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" + }, + { + "name": "0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "sub_path": "process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" + }, + { + "name": "0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "sub_path": "process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" + }, + { + "name": "0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "sub_path": "process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" + }, + { + "name": "0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json" + }, { "name": "0.06 Standard @Snapmaker Artisan (0.2 nozzle)", "sub_path": "process/0.06 Standard @Snapmaker Artisan (0.2 nozzle).json" @@ -756,6 +900,66 @@ "name": "fdm_filament_tpu", "sub_path": "filament/fdm_filament_tpu.json" }, + { + "name": "Snapmaker PLA Basic @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Silk @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Silk @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Silk @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Silk @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Lite @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Matte @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Matte @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Matte @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU 95A @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json" + }, { "name": "Fiberon PPS-GF20 @Snapmaker U1 base", "sub_path": "filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1 base.json" @@ -1448,6 +1652,10 @@ "name": "Snapmaker PETG HF @U1 0.2 nozzle", "sub_path": "filament/Snapmaker PETG HF @U1 0.2 nozzle.json" }, + { + "name": "Snapmaker PETG HF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json" + }, { "name": "Snapmaker PETG HF @U1 0.6 nozzle", "sub_path": "filament/Snapmaker PETG HF @U1 0.6 nozzle.json" @@ -1760,6 +1968,30 @@ "name": "Snapmaker PLA Translucent @U1 0.8 nozzle", "sub_path": "filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json" }, + { + "name": "Snapmaker PLA Wood @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Wood @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Wood @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA-CF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA-CF @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA-CF @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json" + }, { "name": "Snapmaker PLA-CF", "sub_path": "filament/Snapmaker PLA-CF.json" @@ -1840,6 +2072,30 @@ "name": "Snapmaker J1 TPU High-Flow", "sub_path": "filament/Snapmaker J1 TPU High-Flow.json" }, + { + "name": "Snapmaker TPU 90A @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU 90A @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU 90A @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json" + }, { "name": "Snapmaker TPE @U1", "sub_path": "filament/Snapmaker TPE @U1.json" @@ -1860,6 +2116,174 @@ "name": "Snapmaker TPU", "sub_path": "filament/Snapmaker TPU.json" }, + { + "name": "Fiberon PPS-GF20 @U1 0.4 High Flow nozzle", + "sub_path": "filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker ABS @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker ABS Benchy @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Fiberon ASA-CF08 @U1 0.4 High Flow nozzle", + "sub_path": "filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker ASA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Fiberon PA12-CF10 @U1 0.4 High Flow nozzle", + "sub_path": "filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Fiberon PA6-CF20 @U1 0.4 High Flow nozzle", + "sub_path": "filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Fiberon PA612-ESD @U1 0.4 High Flow nozzle", + "sub_path": "filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PA-CF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PET @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PET @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Fiberon PETG-ESD @U1 0.4 High Flow nozzle", + "sub_path": "filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PETG @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PETG @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PETG Translucent @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker PETG @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker PETG @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker PETG Galaxy @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG HF @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG HF @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG HF @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG Translucent @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG Translucent @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG Translucent @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG Translucent @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker PETG-CF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma CoPE @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Celestial @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Galaxy @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Glow @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Luminous @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Marble @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Matte @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Metallic @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Neon @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Satin @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Silk @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Starlight @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA Translucent @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Panchroma PLA UV Shift @U1 0.4 High Flow nozzle", + "sub_path": "filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite CosPLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA @U1 0.4 High Flow nozzle.json" + }, { "name": "PolyLite Dual PLA @0.2 nozzle", "sub_path": "filament/Polymaker/PolyLite Dual PLA @0.2 nozzle.json" @@ -1868,6 +2292,114 @@ "name": "PolyLite J1 PLA @0.2 nozzle", "sub_path": "filament/Polymaker/PolyLite J1 PLA @0.2 nozzle.json" }, + { + "name": "PolyLite PLA Galaxy @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Glow @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Luminous @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Neon @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Pro @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Starlight @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyLite PLA Translucent @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyTerra PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyTerra PLA Marble @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json" + }, + { + "name": "PolyTerra PLA+ @U1 0.4 High Flow nozzle", + "sub_path": "filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker HT-PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker General PLA Family @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker Silk PLA Family @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker Tough PLA Family @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker PLA Pro @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle", + "sub_path": "filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Matte @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Glow @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Metal @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Translucent @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Translucent @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Translucent @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Translucent @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json" + }, { "name": "Snapmaker Dual PLA Eco", "sub_path": "filament/Snapmaker Dual PLA Eco.json" @@ -1904,6 +2436,50 @@ "name": "Snapmaker Breakaway Support @base", "sub_path": "filament/Snapmaker Breakaway Support @base.json" }, + { + "name": "Snapmaker PVA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker PVA @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker PVA @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPE @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Snapmaker PLA Eco @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json" + }, { "name": "Snapmaker Breakaway Support For PLA @U1", "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1.json" @@ -1920,6 +2496,138 @@ "name": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle", "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json" }, + { + "name": "Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle", + "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle", + "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle", + "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PETG @U1 0.2 High Flow nozzle", + "sub_path": "filament/Generic PETG @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Generic PETG @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PETG @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PETG @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PETG @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PETG @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PETG @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PETG HF @U1 0.2 High Flow nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Generic PETG HF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PETG HF @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PETG HF @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PETG-CF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PETG-CF @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PETG-CF @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PLA @U1 0.2 High Flow nozzle", + "sub_path": "filament/Generic PLA @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Generic PLA @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PLA @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PLA @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PLA @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PLA @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PLA @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PLA High Speed @U1 0.2 High Flow nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Generic PLA High Speed @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PLA High Speed @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PLA High Speed @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PLA Silk @U1 0.2 High Flow nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json" + }, + { + "name": "Generic PLA Silk @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PLA Silk @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PLA Silk @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic PLA-CF @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic PLA-CF @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic PLA-CF @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json" + }, + { + "name": "Generic TPU @U1 0.4 High Flow nozzle", + "sub_path": "filament/Generic TPU @U1 0.4 High Flow nozzle.json" + }, + { + "name": "Generic TPU @U1 0.6 High Flow nozzle", + "sub_path": "filament/Generic TPU @U1 0.6 High Flow nozzle.json" + }, + { + "name": "Generic TPU @U1 0.8 High Flow nozzle", + "sub_path": "filament/Generic TPU @U1 0.8 High Flow nozzle.json" + }, { "name": "PolyTerra J1 PLA", "sub_path": "filament/Polymaker/PolyTerra J1 PLA.json" @@ -2238,6 +2946,22 @@ "name": "Snapmaker Artisan (0.8 nozzle)", "sub_path": "machine/Snapmaker Artisan (0.8 nozzle).json" }, + { + "name": "Snapmaker U1 (0.2 High Flow nozzle)", + "sub_path": "machine/Snapmaker U1 (0.2 High Flow nozzle).json" + }, + { + "name": "Snapmaker U1 (0.4 High Flow nozzle)", + "sub_path": "machine/Snapmaker U1 (0.4 High Flow nozzle).json" + }, + { + "name": "Snapmaker U1 (0.6 High Flow nozzle)", + "sub_path": "machine/Snapmaker U1 (0.6 High Flow nozzle).json" + }, + { + "name": "Snapmaker U1 (0.8 High Flow nozzle)", + "sub_path": "machine/Snapmaker U1 (0.8 High Flow nozzle).json" + }, { "name": "Snapmaker A250 Dual BKit (0.2 nozzle)", "sub_path": "machine/Snapmaker A250 Dual BKit (0.2 nozzle).json" diff --git a/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..794206a9ee --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Fiberon ASA-CF08 @U1 0.4 High Flow nozzle", + "inherits": "Fiberon ASA-CF08 @Snapmaker U1", + "from": "system", + "setting_id": "b1SjanapSsHClohQ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..40f28ecc21 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Fiberon PA12-CF10 @U1 0.4 High Flow nozzle", + "inherits": "Fiberon PA12-CF10 @Snapmaker U1", + "from": "system", + "setting_id": "NSFnZepHnBOgrNpc", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..dd15942c75 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Fiberon PA6-CF20 @U1 0.4 High Flow nozzle", + "inherits": "Fiberon PA6-CF20 @Snapmaker U1", + "from": "system", + "setting_id": "qBf8pKCPs51wZX8h", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..a051be58f7 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Fiberon PA612-ESD @U1 0.4 High Flow nozzle", + "inherits": "Fiberon PA612-ESD @Snapmaker U1", + "from": "system", + "setting_id": "IE8AoNlftuKceK82", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..fe4411ceba --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Fiberon PETG-ESD @U1 0.4 High Flow nozzle", + "inherits": "Fiberon PETG-ESD @Snapmaker U1", + "from": "system", + "setting_id": "tBqwA6bzMZxINvhc", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..a838e2fecc --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Fiberon PPS-GF20 @U1 0.4 High Flow nozzle", + "inherits": "Fiberon PPS-GF20 @Snapmaker U1", + "from": "system", + "setting_id": "262BRaOSZLUidxzi", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..f224a6e5f8 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.2 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "CyVMh4hZ5TrBb9Hl", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..2dbdd3a6d3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.4 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "AB6Lv3KHVfZ3F17I", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..67955d8cdc --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.6 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "2wjxry91mFaUY5Kv", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..766f30a03f --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.8 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "M5HDLQjQbdOYUkJc", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..be8af80e2d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.2 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "hmfOc244iLZRjSxC", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c13f72b1ed --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.4 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "QaYzwWA21nLkbeTd", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..d0fed24d19 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.6 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "4FW7HsZg0GOFDyCr", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..b0b75d0a17 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.8 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "DcwXjRYE21g1s7Gd", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..9ce1f45d0e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG-CF @U1 0.4 High Flow nozzle", + "inherits": "Generic PETG-CF @System", + "from": "system", + "setting_id": "xiruPdezF8sdAwJC", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "34.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG-CF @System: 11.5 x 3 = 34.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..6ecdd1894b --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG-CF @U1 0.6 High Flow nozzle", + "inherits": "Generic PETG-CF @System", + "from": "system", + "setting_id": "edRC83pnDlkg8SYB", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "34.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG-CF @System: 11.5 x 3 = 34.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..95c852bdf8 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PETG-CF @U1 0.8 High Flow nozzle", + "inherits": "Generic PETG-CF @System", + "from": "system", + "setting_id": "mwzFh7n5O4sSVbdq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "34.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG-CF @System: 11.5 x 3 = 34.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..fa8f00af48 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.2 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "jOJzxhflMDXUSXP1", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e564663de8 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.4 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "u6ZVj3bEGhEScBAn", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..e964a2d73c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.6 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "3rI0eWUn62OqztwD", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..0576e76d5f --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.8 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "Ca1ysi9BTj3SmRnf", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..2ab997f82f --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.2 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "H45bzmFIJs2jcRxK", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..4a30c36707 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.4 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "rNAH84Cth3UXfID4", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..c26c6d1d75 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.6 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "1fXum9hyhX2CXp84", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..8350a79674 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.8 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "DEHdvXmDRZ4klSfG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..66e30d591e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.2 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "rgP9xBiJi6jtTGe6", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..960fbb7ae4 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.4 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "wKeFBUbXAh371HJc", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..4749f73903 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.6 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "PoZUUozGNqKXDY6j", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..ae13436c89 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.8 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "lSxapFINSYnT0jU5", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.5" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..4ea4fa5854 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA-CF @U1 0.4 High Flow nozzle", + "inherits": "Generic PLA-CF @System", + "from": "system", + "setting_id": "LumAEUgsSqWvQcnD", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA-CF @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..6489d4c06e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA-CF @U1 0.6 High Flow nozzle", + "inherits": "Generic PLA-CF @System", + "from": "system", + "setting_id": "cbQkQEdl7ZZySWyb", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA-CF @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..d8a21dc0ff --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic PLA-CF @U1 0.8 High Flow nozzle", + "inherits": "Generic PLA-CF @System", + "from": "system", + "setting_id": "w2R1GQrrewIaFigA", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA-CF @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..af86177bce --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic TPU @U1 0.4 High Flow nozzle", + "inherits": "Generic TPU @System", + "from": "system", + "setting_id": "a1T45jY9dw7cvXT9", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "9.6" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic TPU @System: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..3bb797471f --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic TPU @U1 0.6 High Flow nozzle", + "inherits": "Generic TPU @System", + "from": "system", + "setting_id": "bHQSXsU5JQW91pkB", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "9.6" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic TPU @System: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..88d4a980f1 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Generic TPU @U1 0.8 High Flow nozzle", + "inherits": "Generic TPU @System", + "from": "system", + "setting_id": "FnByDNEkW6W6FR63", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "9.6" + ], + "filament_notes": [ + "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic TPU @System: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e9de9fd91a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma CoPE @U1 0.4 High Flow nozzle", + "inherits": "Panchroma CoPE @Snapmaker U1", + "from": "system", + "setting_id": "nBvDgnN3J4k3Uytq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..b57e11d9ae --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA @Snapmaker U1", + "from": "system", + "setting_id": "U5ss8ZIjm3fIz1Nq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..23888f197d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Celestial @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Celestial @Snapmaker U1", + "from": "system", + "setting_id": "R81OCOp3l7L8B8fJ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..7f77834c25 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Galaxy @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Galaxy @Snapmaker U1", + "from": "system", + "setting_id": "fS50UtV6RhVtB4uP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..6becd42d5e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Glow @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Glow @Snapmaker U1", + "from": "system", + "setting_id": "JCgz8jS9LuwdaBl1", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e7c1666d92 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Luminous @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Luminous @Snapmaker U1", + "from": "system", + "setting_id": "K5a28HOfNIVohTxH", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..7ed7b808a2 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Marble @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Marble @Snapmaker U1", + "from": "system", + "setting_id": "iXtNnANcE9HjT8nn", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..42fb688f20 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Matte @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Matte @Snapmaker U1", + "from": "system", + "setting_id": "c9CmXJsBkThq39c6", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..fb1c10a2bc --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Metallic @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Metallic @Snapmaker U1", + "from": "system", + "setting_id": "KQ2nKfjxSAroQBJq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..fa9087b701 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Neon @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Neon @Snapmaker U1", + "from": "system", + "setting_id": "9fYw7eMrJBaA00at", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..970adef497 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Satin @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Satin @Snapmaker U1", + "from": "system", + "setting_id": "PRlG1wqgh0KI7fuT", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..47f12bcfd8 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Silk @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Silk @Snapmaker U1", + "from": "system", + "setting_id": "EXLntLMjWCfwg2Wc", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d0a4cdee65 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Starlight @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Starlight @Snapmaker U1", + "from": "system", + "setting_id": "4QxRxMj60hcFxdJL", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..fca32afa7b --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Temp Shift @Snapmaker U1", + "from": "system", + "setting_id": "HDp1DlQLCfSKhaNx", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..f6a4f0bfc3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA Translucent @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA Translucent @Snapmaker U1", + "from": "system", + "setting_id": "4ZTw1XHqwI7QjEfR", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..295bee1ea0 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Panchroma PLA UV Shift @U1 0.4 High Flow nozzle", + "inherits": "Panchroma PLA UV Shift @Snapmaker U1", + "from": "system", + "setting_id": "0pFMgeH7aowERZoU", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..6ed9b9543d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite CosPLA @U1 0.4 High Flow nozzle", + "inherits": "PolyLite CosPLA @Snapmaker U1", + "from": "system", + "setting_id": "iZqJvDu4xfgIBrpm", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..0616e2fba3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PETG @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PETG @Snapmaker U1", + "from": "system", + "setting_id": "fCNkJQO5skF4ZJcJ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.5" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..b7dee44938 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PETG Translucent @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PETG Translucent @Snapmaker U1", + "from": "system", + "setting_id": "polhiSJb82UYZObE", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.5" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..b337a5abc3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA @Snapmaker U1", + "from": "system", + "setting_id": "AUKAVbKch9zjmuKG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d2669f41a3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Galaxy @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Galaxy @Snapmaker U1", + "from": "system", + "setting_id": "hncpZTWfBmfoUaCq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..748fe5e2c6 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Glow @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Glow @Snapmaker U1", + "from": "system", + "setting_id": "DUuZDzjm3bmNBKSA", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..7c7c661948 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Luminous @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Luminous @Snapmaker U1", + "from": "system", + "setting_id": "Mv5E1GpGrdHVF2Vs", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..8aee23d539 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Neon @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Neon @Snapmaker U1", + "from": "system", + "setting_id": "3HWUcpQIJsnxWDp7", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..0320f700c6 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Pro @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Pro @Snapmaker U1", + "from": "system", + "setting_id": "mOax5GeAHVzqs4zL", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..f3a964bebb --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Pro Metallic @Snapmaker U1", + "from": "system", + "setting_id": "Vm5ydb09RSgfd1a8", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..efcfd1518e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Starlight @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Starlight @Snapmaker U1", + "from": "system", + "setting_id": "urJVmseOC2Px1qIW", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..06e0a310c7 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyLite PLA Translucent @U1 0.4 High Flow nozzle", + "inherits": "PolyLite PLA Translucent @Snapmaker U1", + "from": "system", + "setting_id": "jCgyYlatUD3jfQXS", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e2c1f0aa26 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyTerra PLA @U1 0.4 High Flow nozzle", + "inherits": "PolyTerra PLA @Snapmaker U1", + "from": "system", + "setting_id": "MTZ6N0HHdSwkoysG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e9bf977e66 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyTerra PLA Marble @U1 0.4 High Flow nozzle", + "inherits": "PolyTerra PLA Marble @Snapmaker U1", + "from": "system", + "setting_id": "uQFqbcqaSTrlM52u", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..274caf962e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "PolyTerra PLA+ @U1 0.4 High Flow nozzle", + "inherits": "PolyTerra PLA+ @Snapmaker U1", + "from": "system", + "setting_id": "ckmc2jbEBO6kAfXn", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c36940b325 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker General PLA Family @U1 0.4 High Flow nozzle", + "inherits": "Polymaker General PLA Family @U1", + "from": "system", + "setting_id": "lTLCaycZyEv7ATPu", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..1bd1c95108 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker HT-PLA @U1 0.4 High Flow nozzle", + "inherits": "Polymaker HT-PLA @Snapmaker U1", + "from": "system", + "setting_id": "YB5Tzz4hghtQ6Iha", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..844fc2e463 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle", + "inherits": "Polymaker HT-PLA-GF @Snapmaker U1", + "from": "system", + "setting_id": "XTfLaJ4pTpmfYOHK", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d15778711a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker PETG @U1 0.4 High Flow nozzle", + "inherits": "Polymaker PETG @Snapmaker U1", + "from": "system", + "setting_id": "7RmkAZ1MMkUK4UFe", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..99eae4584a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker PETG Galaxy @U1 0.4 High Flow nozzle", + "inherits": "Polymaker PETG Galaxy @Snapmaker U1", + "from": "system", + "setting_id": "cBNSP4FfKq94BuWn", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..066c4e5d48 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker PLA @U1 0.4 High Flow nozzle", + "inherits": "Polymaker PLA @Snapmaker U1", + "from": "system", + "setting_id": "N99CwtkZbVeE9ZoP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d1c63bc805 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker PLA Pro @U1 0.4 High Flow nozzle", + "inherits": "Polymaker PLA Pro @Snapmaker U1", + "from": "system", + "setting_id": "Npw7PdfMi3qIDuIY", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c7e944ea35 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle", + "inherits": "Polymaker PLA Pro Metallic @Snapmaker U1", + "from": "system", + "setting_id": "YBy6gtPvOWIYchuS", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c60d41257c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker Silk PLA Family @U1 0.4 High Flow nozzle", + "inherits": "Polymaker Silk PLA Family @U1", + "from": "system", + "setting_id": "diMNPKgSPiNmv0EG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..4a539098ca --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Polymaker Tough PLA Family @U1 0.4 High Flow nozzle", + "inherits": "Polymaker Tough PLA Family @U1", + "from": "system", + "setting_id": "tFHtV4suxKdRifWW", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e3b53f36f3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json @@ -0,0 +1,50 @@ +{ + "type": "filament", + "name": "Snapmaker ABS @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker ABS @U1", + "from": "system", + "setting_id": "23BA0fe1OoCgoHAe", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "0" + ], + "fan_max_speed": [ + "60" + ], + "fan_min_speed": [ + "10" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_minimal_purge_on_wipe_tower": [ + "15" + ], + "filament_multitool_ramming": [ + "1" + ], + "filament_multitool_ramming_flow": [ + "30" + ], + "filament_multitool_ramming_volume": [ + "5" + ], + "filament_retraction_length": [ + "0.2" + ], + "filament_z_hop_types": [ + "Slope Lift" + ], + "nozzle_temperature": [ + "280" + ], + "nozzle_temperature_initial_layer": [ + "280" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..244351878e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Snapmaker ABS Benchy @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker ABS Benchy @U1", + "from": "system", + "setting_id": "TI7PLhJrI5VSiYtg", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "93" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 31 x 3 = 93 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "compatible_prints": [ + "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c5199df011 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,50 @@ +{ + "type": "filament", + "name": "Snapmaker ASA @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker ASA @U1", + "from": "system", + "setting_id": "UlxZs1Ey16Ic8TO8", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "0" + ], + "fan_max_speed": [ + "25" + ], + "fan_min_speed": [ + "10" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_minimal_purge_on_wipe_tower": [ + "15" + ], + "filament_multitool_ramming": [ + "1" + ], + "filament_multitool_ramming_flow": [ + "20" + ], + "filament_multitool_ramming_volume": [ + "10" + ], + "filament_z_hop_types": [ + "Slope Lift" + ], + "nozzle_temperature": [ + "280" + ], + "nozzle_temperature_initial_layer": [ + "280" + ], + "pressure_advance": [ + "0.02" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..3fff84663e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker Breakaway Support For PLA @U1 0.2 nozzle", + "from": "system", + "setting_id": "Bzmt5I0LmEGEyWq3", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "1.5" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 0.5 x 3 = 1.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..871d785016 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker Breakaway Support For PLA @U1", + "from": "system", + "setting_id": "2Wa2yfYIeZ0NfnD0", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..77717f535a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker Breakaway Support For PLA @U1 0.6 nozzle", + "from": "system", + "setting_id": "YRUG55NCp7gQtahJ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..5db74011a3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle", + "from": "system", + "setting_id": "vtGhXaMJKZrd1kgP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..f6981095b8 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PA-CF @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PA-CF @U1", + "from": "system", + "setting_id": "SRbkfUJ8K9LunPYq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "24" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..854a02c1d1 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PET @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PET @U1", + "from": "system", + "setting_id": "q0JpO9zk404c3d3B", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "20.4" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6.8 x 3 = 20.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..780f8fee71 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PETG @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PETG @U1", + "from": "system", + "setting_id": "T604QL6Utgbj4kLh", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..91b263df44 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json @@ -0,0 +1,29 @@ +{ + "type": "filament", + "name": "Snapmaker PETG HF @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker PETG HF @U1 0.2 nozzle", + "from": "system", + "setting_id": "ILQ3IFpogGbjluIN", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "3" + ], + "filament_flow_ratio": [ + "0.955" + ], + "pressure_advance": [ + "0.0525" + ], + "nozzle_temperature_initial_layer": [ + "230" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 1 x 3 = 3 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..085debf589 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,68 @@ +{ + "type": "filament", + "name": "Snapmaker PETG HF @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PETG HF @U1 base2", + "from": "system", + "setting_id": "dYZPyzUTqIxCaWMZ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "fan_max_speed": [ + "60" + ], + "fan_min_speed": [ + "30" + ], + "filament_deretraction_speed": [ + "40" + ], + "filament_flow_ratio": [ + "0.955" + ], + "filament_max_volumetric_speed": [ + "48" + ], + "filament_minimal_purge_on_wipe_tower": [ + "50" + ], + "filament_multitool_ramming": [ + "1" + ], + "filament_multitool_ramming_flow": [ + "30" + ], + "filament_multitool_ramming_volume": [ + "8" + ], + "filament_retract_length_toolchange": [ + "8" + ], + "filament_retraction_length": [ + "0.8" + ], + "filament_retraction_speed": [ + "40" + ], + "filament_wipe_distance": [ + "1" + ], + "filament_z_hop_types": [ + "Slope Lift" + ], + "nozzle_temperature": [ + "245" + ], + "nozzle_temperature_initial_layer": [ + "230" + ], + "nozzle_temperature_range_low": [ + "230" + ], + "pressure_advance": [ + "0.035" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..ca27f67555 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json @@ -0,0 +1,29 @@ +{ + "type": "filament", + "name": "Snapmaker PETG HF @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PETG HF @U1 0.6 nozzle", + "from": "system", + "setting_id": "A4AAbNDIaCgXqiIk", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_flow_ratio": [ + "0.955" + ], + "pressure_advance": [ + "0.0525" + ], + "nozzle_temperature_initial_layer": [ + "230" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..051bb8fbac --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json @@ -0,0 +1,29 @@ +{ + "type": "filament", + "name": "Snapmaker PETG HF @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PETG HF @U1 0.8 nozzle", + "from": "system", + "setting_id": "vDLX4xfsJUq4aWd8", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_flow_ratio": [ + "0.955" + ], + "pressure_advance": [ + "0.0525" + ], + "nozzle_temperature_initial_layer": [ + "230" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..44f49f7b2d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PETG Translucent @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker PETG Translucent @U1 0.2 nozzle", + "from": "system", + "setting_id": "jWQZ4FpmKof6eKP5", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "pressure_advance": [ + "0.2875" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 1 x 3 = 3 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "3" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..265b504a53 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PETG Translucent @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PETG Translucent @U1 0.4 nozzle", + "from": "system", + "setting_id": "SSb1PYKLfJmn0RaW", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "0" + ], + "pressure_advance": [ + "0.05" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "30" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..aea3ffda1c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PETG Translucent @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PETG Translucent @U1 0.6 nozzle", + "from": "system", + "setting_id": "8teECJZmuiAx3xzK", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "pressure_advance": [ + "0.025" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "48" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..7e66c59a2c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PETG Translucent @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PETG Translucent @U1 0.8 nozzle", + "from": "system", + "setting_id": "jFuSDlP3daCftYGd", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "pressure_advance": [ + "0.025" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "48" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..4fb22fd2a5 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,65 @@ +{ + "type": "filament", + "name": "Snapmaker PETG-CF @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PETG-CF @U1", + "from": "system", + "setting_id": "kTtK6gmRn2LVCgPv", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "20" + ], + "enable_pressure_advance": [ + "1" + ], + "fan_max_speed": [ + "40" + ], + "fan_min_speed": [ + "5" + ], + "filament_flow_ratio": [ + "1.01" + ], + "filament_max_volumetric_speed": [ + "19.2" + ], + "filament_minimal_purge_on_wipe_tower": [ + "30" + ], + "filament_multitool_ramming": [ + "1" + ], + "filament_multitool_ramming_flow": [ + "13" + ], + "filament_multitool_ramming_volume": [ + "0.5" + ], + "filament_retract_length_toolchange": [ + "nil" + ], + "filament_retraction_length": [ + "0.6" + ], + "nozzle_temperature": [ + "260" + ], + "nozzle_temperature_initial_layer": [ + "260" + ], + "nozzle_temperature_range_high": [ + "270" + ], + "nozzle_temperature_range_low": [ + "240" + ], + "pressure_advance": [ + "0.028" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6.4 x 3 = 19.2 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..9e52e8d145 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA @U1", + "from": "system", + "setting_id": "Iw2vC9RsmNnXlyFC", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "42" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 14 x 3 = 42 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..5b690deccc --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Basic @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Basic @U1", + "from": "system", + "setting_id": "6pP9yn7QPVxPrKlp", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..1fc7f3e6e5 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Eco @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Eco @U1", + "from": "system", + "setting_id": "yBXvtWTUvseiCC6p", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d9b39fcdb2 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json @@ -0,0 +1,32 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Full Spectrum @U1 0.4 nozzle", + "from": "system", + "setting_id": "Si1HKTz9ZSM8dIaU", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_retract_length_toolchange": [ + "5" + ], + "filament_retraction_length": [ + "0.5" + ], + "filament_z_hop_types": [ + "Slope Lift" + ], + "pressure_advance": [ + "0.024" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "45" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d6423ff5ff --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json @@ -0,0 +1,26 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Glow @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Glow @U1 0.4 nozzle", + "from": "system", + "setting_id": "z33Hc87sYPmwPL5J", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "100" + ], + "filament_flow_ratio": [ + "0.98" + ], + "filament_max_volumetric_speed": [ + "48" + ], + "pressure_advance": [ + "0.023" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..6f7f024b4c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Lite @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Lite @U1", + "from": "system", + "setting_id": "siahKp21MtWd5vha", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..a0e558096c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json @@ -0,0 +1,32 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Matte @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker PLA Matte @U1 0.2 nozzle", + "from": "system", + "setting_id": "uJJlKJE6cP1VqAhi", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "6" + ], + "filament_flow_ratio": [ + "1.0395" + ], + "pressure_advance": [ + "0.03" + ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2 x 3 = 6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..b4021aea6a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json @@ -0,0 +1,44 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Matte @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Matte @U1", + "from": "system", + "setting_id": "a0eQeSmYLKJpPxo9", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "100" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_flow_ratio": [ + "0.99" + ], + "filament_max_volumetric_speed": [ + "66" + ], + "filament_retraction_length": [ + "1" + ], + "filament_wipe_distance": [ + "1" + ], + "filament_z_hop_types": [ + "Slope Lift" + ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "pressure_advance": [ + "0.024" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 22 x 3 = 66 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..daef1b37d5 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json @@ -0,0 +1,32 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Matte @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PLA Matte @U1 0.6 nozzle", + "from": "system", + "setting_id": "DfrCVTFg0FD7XVkf", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_flow_ratio": [ + "0.9801" + ], + "pressure_advance": [ + "0.018" + ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..7f36cee705 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json @@ -0,0 +1,32 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Matte @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PLA Matte @U1 0.8 nozzle", + "from": "system", + "setting_id": "jMrzguz4b18qMWem", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_flow_ratio": [ + "0.9702" + ], + "pressure_advance": [ + "0.018" + ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c5c4b829a2 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Metal @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Metal @U1", + "from": "system", + "setting_id": "01UP0AVB1B7TBi78", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "48" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..d110009d58 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Silk @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker PLA Silk @U1 0.2 nozzle", + "from": "system", + "setting_id": "3YXcTZZ99lTaxy3j", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "6" + ], + "filament_notes": [ + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2 x 3 = 6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e05d313038 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Silk @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Silk @U1", + "from": "system", + "setting_id": "rpTq4sJFpmbdl0dy", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "80" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_z_hop_types": [ + "Slope Lift" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..ad19b1abf3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Silk @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PLA Silk @U1 0.6 nozzle", + "from": "system", + "setting_id": "SYJSv3f1LWPnWTo1", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..3856c5e5a2 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Silk @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PLA Silk @U1 0.8 nozzle", + "from": "system", + "setting_id": "GpTeZsC8ST8UvkKe", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "30" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..ef43a62cc1 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "from": "system", + "setting_id": "75HoJsG7nurT9m8o", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "6" + ], + "pressure_advance": [ + "0.24" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2 x 3 = 6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..9ab350a7ba --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json @@ -0,0 +1,26 @@ +{ + "type": "filament", + "name": "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA SnapSpeed @U1", + "from": "system", + "setting_id": "2S7b7VwKFkZuXTHB", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "filament_multitool_ramming_flow": [ + "40" + ], + "pressure_advance": [ + "0.024" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..ab657264ad --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "from": "system", + "setting_id": "e3hivxxVP4MEauB0", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "pressure_advance": [ + "0.024" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..5e9be868b6 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "from": "system", + "setting_id": "0ZOycCmSyLf9Zr3f", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "60" + ], + "pressure_advance": [ + "0.0216" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json new file mode 100644 index 0000000000..b7b8357798 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Translucent @U1 0.2 High Flow nozzle", + "inherits": "Snapmaker PLA Translucent @U1 0.2 nozzle", + "from": "system", + "setting_id": "atEZp48A9Ajefn5P", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "pressure_advance": [ + "0.18" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 1.6 x 3 = 4.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "4.8" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..67a5c77f4a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Translucent @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Translucent @U1 0.4 nozzle", + "from": "system", + "setting_id": "OhPRQfEeCCBos1Xk", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "pressure_advance": [ + "0.024" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "36" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..29767b5045 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Translucent @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PLA Translucent @U1 0.6 nozzle", + "from": "system", + "setting_id": "UkjutA0LDsne1Kr4", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "pressure_advance": [ + "0.024" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "36" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..6790f2a521 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Translucent @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PLA Translucent @U1 0.8 nozzle", + "from": "system", + "setting_id": "IEcagoFA1np00DZq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "pressure_advance": [ + "0.024" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "36" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..95ef69bb13 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json @@ -0,0 +1,26 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Wood @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA Wood @U1 0.4 nozzle", + "from": "system", + "setting_id": "7IGP2hVMK7ajiLuk", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "filament_retract_length_toolchange": [ + "6" + ], + "filament_z_hop_types": [ + "Auto Lift" + ], + "pressure_advance": [ + "0.024" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..71e3f69b98 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Wood @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PLA Wood @U1 0.6 nozzle", + "from": "system", + "setting_id": "8tH46AUyW4uua5QB", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "pressure_advance": [ + "0.0134" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..9bcde49fd5 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Snapmaker PLA Wood @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PLA Wood @U1 0.8 nozzle", + "from": "system", + "setting_id": "LW5RkXBTM4eE3eRK", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "pressure_advance": [ + "0.0067" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "60" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..c61a287426 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,38 @@ +{ + "type": "filament", + "name": "Snapmaker PLA-CF @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PLA-CF @U1 0.4 nozzle", + "from": "system", + "setting_id": "ork4mVEyJBzXs2Xj", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "additional_cooling_fan_speed": [ + "100" + ], + "filament_max_volumetric_speed": [ + "45" + ], + "filament_multitool_ramming_flow": [ + "30" + ], + "filament_retract_length_toolchange": [ + "15" + ], + "filament_retraction_length": [ + "0.8" + ], + "nozzle_temperature": [ + "230" + ], + "nozzle_temperature_initial_layer": [ + "230" + ], + "pressure_advance": [ + "0.02" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..825567e128 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json @@ -0,0 +1,29 @@ +{ + "type": "filament", + "name": "Snapmaker PLA-CF @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PLA-CF @U1 0.6 nozzle", + "from": "system", + "setting_id": "c982QxKnlzVRADnm", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "pressure_advance": [ + "0.03" + ], + "nozzle_temperature": [ + "210" + ], + "nozzle_temperature_initial_layer": [ + "210" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..52100f6bbe --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json @@ -0,0 +1,29 @@ +{ + "type": "filament", + "name": "Snapmaker PLA-CF @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PLA-CF @U1 0.8 nozzle", + "from": "system", + "setting_id": "4qXeMrTt9pZNCwEr", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "54" + ], + "pressure_advance": [ + "0.03" + ], + "nozzle_temperature": [ + "210" + ], + "nozzle_temperature_initial_layer": [ + "210" + ], + "enable_pressure_advance": [ + "1" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..e96baac97a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json @@ -0,0 +1,26 @@ +{ + "type": "filament", + "name": "Snapmaker PVA @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker PVA @U1", + "from": "system", + "setting_id": "NZwDYFscSMlZJusK", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_flow_ratio": [ + "0.97" + ], + "filament_max_volumetric_speed": [ + "15" + ], + "filament_multitool_ramming_flow": [ + "10" + ], + "filament_retraction_length": [ + "0.6" + ], + "filament_notes": [ + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 5 x 3 = 15 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..1fb7bc978a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Snapmaker PVA @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker PVA @U1 0.6 nozzle", + "from": "system", + "setting_id": "AO1DsQ9uGg1ivN8b", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_flow_ratio": [ + "0.9506" + ], + "filament_notes": [ + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6 x 3 = 18 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "18" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..cfdd941bc8 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Snapmaker PVA @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker PVA @U1 0.8 nozzle", + "from": "system", + "setting_id": "xy6MMfFlgvgrP0Xj", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_flow_ratio": [ + "0.9312" + ], + "filament_notes": [ + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6 x 3 = 18 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "18" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..4b03c0e63d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPE @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker TPE @U1", + "from": "system", + "setting_id": "wLR0Jg5KEPd18zik", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "21.6" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.2 x 3 = 21.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..130bba2045 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 90A @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker TPU 90A @U1", + "from": "system", + "setting_id": "xAMUl2DDEuMrrzAs", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "9.6" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..d999f651f1 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 90A @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker TPU 90A @U1 0.6 nozzle", + "from": "system", + "setting_id": "URpA1Br6ZxbbV0IF", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "9.6" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..b49d1d3a09 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 90A @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker TPU 90A @U1 0.8 nozzle", + "from": "system", + "setting_id": "nECpLUQc8O9JE4em", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "10.5" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.5 x 3 = 10.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..55ba8da2db --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 95A @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker TPU 95A @U1", + "from": "system", + "setting_id": "Ywz3Y08u16nlPSuD", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "9.6" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..461dcf6e69 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker TPU 95A HF @U1", + "from": "system", + "setting_id": "hVOeZYtFdD5f9n8N", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "27" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 9 x 3 = 27 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..416cfe0097 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker TPU 95A HF @U1 0.6 nozzle", + "from": "system", + "setting_id": "gkRhqrvJIZh1o3wJ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "36" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..9971dabbf2 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker TPU 95A HF @U1 0.8 nozzle", + "from": "system", + "setting_id": "ILlLnES7BQLAKxnH", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "48" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..48d709b12d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker TPU @U1", + "from": "system", + "setting_id": "fzFdqg3bAFNCOB7S", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "8.4" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2.8 x 3 = 8.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..8af4584d0a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker TPU @U1", + "from": "system", + "setting_id": "qABR1pUxRSY6jSAk", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "8.4" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2.8 x 3 = 8.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..6009f90585 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker TPU @U1", + "from": "system", + "setting_id": "fgWdX41gAzkDoAW5", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "8.4" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2.8 x 3 = 8.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json new file mode 100644 index 0000000000..d34d28c7b5 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle", + "inherits": "Snapmaker TPU High-Flow @U1", + "from": "system", + "setting_id": "UFqQnkKICuivJWiW", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.8" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.6 x 3 = 22.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json new file mode 100644 index 0000000000..34cf3796f5 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle", + "inherits": "Snapmaker TPU High-Flow @U1", + "from": "system", + "setting_id": "E5Y9PFB8elCm2yGh", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.8" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.6 x 3 = 22.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json new file mode 100644 index 0000000000..9453bf939b --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json @@ -0,0 +1,17 @@ +{ + "type": "filament", + "name": "Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle", + "inherits": "Snapmaker TPU High-Flow @U1", + "from": "system", + "setting_id": "FXqeu1WlfeqwdbrM", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_max_volumetric_speed": [ + "22.8" + ], + "filament_notes": [ + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.6 x 3 = 22.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..9afb250476 --- /dev/null +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,18 @@ +{ + "type": "machine", + "name": "Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "rypfKOLmPmXpR3Z9", + "instantiation": "true", + "printer_model": "Snapmaker U1", + "printer_variant": "0.2 High Flow", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.2 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle" + ], + "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)" +} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..f8b8d44e10 --- /dev/null +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,18 @@ +{ + "type": "machine", + "name": "Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "YVXcn9a2pyGfl8wo", + "instantiation": "true", + "printer_model": "Snapmaker U1", + "printer_variant": "0.4 High Flow", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.4 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle" + ], + "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)" +} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..53c0d682cd --- /dev/null +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,18 @@ +{ + "type": "machine", + "name": "Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "7Nqiw8MQIWaQqB1R", + "instantiation": "true", + "printer_model": "Snapmaker U1", + "printer_variant": "0.6 High Flow", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.6 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle" + ], + "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)" +} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json new file mode 100644 index 0000000000..ded207ffbf --- /dev/null +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json @@ -0,0 +1,18 @@ +{ + "type": "machine", + "name": "Snapmaker U1 (0.8 High Flow nozzle)", + "inherits": "Snapmaker U1 (0.8 nozzle)", + "from": "system", + "setting_id": "aTs5dXyBbXItUEeS", + "instantiation": "true", + "printer_model": "Snapmaker U1", + "printer_variant": "0.8 High Flow", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.8 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle" + ], + "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)" +} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1.json b/resources/profiles/Snapmaker/machine/Snapmaker U1.json index 96eb53f81a..0b5f8c8a93 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1.json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1.json @@ -8,5 +8,5 @@ "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle", "bed_model": "Snapmaker U1_bed.stl", "bed_texture": "Snapmaker U1_texture.svg", - "nozzle_diameter": "0.2;0.4;0.4+0.6;0.6;0.8" + "nozzle_diameter": "0.2;0.4;0.4+0.6;0.6;0.8;0.2 High Flow;0.4 High Flow;0.6 High Flow;0.8 High Flow" } diff --git a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..3deab05cdd --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.06 High Quality @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "LFjXyYMCx6Oy12PQ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..f48dfa5c3b --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.06 Standard @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "nMJ1UXr4uyWr62Nx", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..8fd4c1cbe7 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.08 Extra Fine @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "iyzOhKe7WcJvxmQu", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..b9e42b5d79 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.08 High Quality @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "CaOHxJpx23e32eZm", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..a7141e48f1 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.08 High Quality @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "3Jsj1rKIzF9dhszS", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..c81c177341 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.08 Standard @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "Dk5x3oI8wrO1SYDF", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..ab2f3430c6 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.10 High Quality @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "AEdUWWIQuuIvPX48", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..9dbcbb12da --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "k5ltb2fjXfHavuJl", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..77bb9d98a9 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.12 Fine @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "68ArVT2y9MR8cLZ1", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..e052a9e517 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.12 High Quality @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "m9GIExQJEdmIA0t2", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..dc46dd6014 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.12 Standard @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "VYRDtZXoZWqcHBJP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json new file mode 100644 index 0000000000..08451b8fa7 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "inherits": "0.14 Standard @Snapmaker U1 (0.2 nozzle)", + "from": "system", + "setting_id": "NPQCD2bScfCwZAGP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..cbf3b13b75 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.16 High Quality @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "Wkl0CFo4qXv79E1W", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..9ed96478f2 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.16 Optimal @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "xGaLQMk52ZD5ZqJh", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..9410d42ef5 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.18 Standard @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "aW5X8f9lb5haKlWj", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..1a680858b1 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.20 Quality @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "g8JVHkdQOCWRZZf5", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..de45cf42f9 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "5EHp34T55kiTtt68", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..dbaa4cac78 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.20 Standard @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "l3KpKbqxUGKTjO0t", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..cc1c08848c --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.20 Strength @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "jTYxPrkfNAXXeElO", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..d305323789 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20 Support @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.20 Support @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "o2uNeeRv8JNXAuD0", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..6bbd70ca8b --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.20 Support W @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "rLdRXBKLSDdjsSs9", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..ea99859bc7 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.24 Draft @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "cZfx1x9K6oYUG6nK", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..1072a08efa --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.24 Standard @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "PvF43C7HiDhd7BKg", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json new file mode 100644 index 0000000000..7844a38bf1 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "inherits": "0.24 Standard @Snapmaker U1 (0.8 nozzle)", + "from": "system", + "setting_id": "FAVs0dNRa86OWdzy", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..bc50c410c5 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.25 Benchy @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "21Yht5aHJVG0XebG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json new file mode 100644 index 0000000000..bd58038ed9 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle)", + "inherits": "0.28 Extra Draft @Snapmaker U1 (0.4 nozzle)", + "from": "system", + "setting_id": "ofB4cQcZJ2Lkze83", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..00494a3516 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.30 Draft @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "K3aD6VMSmWStekaW", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..46de759eb1 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "G8jjRPwut6AkuW7w", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..40e31b320f --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.30 Strength @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "4S1DO4sXQX2RXSjC", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json new file mode 100644 index 0000000000..c74cf8b309 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "inherits": "0.32 Standard @Snapmaker U1 (0.8 nozzle)", + "from": "system", + "setting_id": "6k3IeQoysYKUxTxG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..aab911513d --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.36 Standard @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "4c5vbxTAK6TVyTpg", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..3d3a341e2b --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.40 Extra Draft @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "vWhACedmOjulskG0", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json new file mode 100644 index 0000000000..1981348438 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "inherits": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", + "from": "system", + "setting_id": "YEOmDIj6IFBHwvTr", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json new file mode 100644 index 0000000000..cca40610a9 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "inherits": "0.42 Standard @Snapmaker U1 (0.6 nozzle)", + "from": "system", + "setting_id": "kXJPwKEtZ6kSTvcm", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json new file mode 100644 index 0000000000..b976586099 --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "inherits": "0.48 Standard @Snapmaker U1 (0.8 nozzle)", + "from": "system", + "setting_id": "BTuUnaHxKcjQlOv9", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} diff --git a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json new file mode 100644 index 0000000000..091c301acf --- /dev/null +++ b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "inherits": "0.56 Standard @Snapmaker U1 (0.8 nozzle)", + "from": "system", + "setting_id": "VrtVj7ZWFPegxXaW", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "inner_wall_speed": [ + "600" + ], + "outer_wall_speed": [ + "500" + ], + "internal_solid_infill_speed": [ + "600" + ], + "sparse_infill_speed": [ + "600" + ] +} From e9b9a79815bc94fd7aaba506f2de73f000d14f6c Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Thu, 17 Sep 2026 08:46:06 -0400 Subject: [PATCH 34/89] Configure Snapmaker U1 purifier from filament softening temperature Select purifier mode in U1 start G-code using the existing min_vitrification_temperature placeholder. Cover standard, high-flow and mixed-nozzle profiles without new slicer settings or C++ changes. Document the thresholds; retain the branch-wide Snapmaker version bump. Co-authored-by: Codex --- docs/HLSD/snapmaker-high-flow.md | 16 ++++++++++++++++ .../machine/Snapmaker U1 (0.2 nozzle).json | 2 +- .../machine/Snapmaker U1 (0.4 nozzle).json | 2 +- .../machine/Snapmaker U1 (0.6 nozzle).json | 2 +- .../machine/Snapmaker U1 (0.8 nozzle).json | 2 +- resources/profiles/Snapmaker/machine/fdm_U1.json | 2 +- 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/HLSD/snapmaker-high-flow.md b/docs/HLSD/snapmaker-high-flow.md index 63806cbd83..61da074e97 100644 --- a/docs/HLSD/snapmaker-high-flow.md +++ b/docs/HLSD/snapmaker-high-flow.md @@ -32,6 +32,22 @@ settings remain diameter-specific. Standard printers retain their original process speeds. HF printers default to the corresponding HF process. Volumetric ceilings and machine motion limits still cap actual speeds. +## Chamber and purifier control + +All U1 start sequences, including the mixed-diameter and inherited HF +profiles, issue `SET_PURIFIER_MODE` automatically. The start G-code checks the existing +`min_vitrification_temperature` placeholder, the lowest Softening temperature +(`temperature_vitrification`) among the filaments actually used on the plate: +at or below 50 C selects strong cooling, above 50 C through 70 C selects weak +cooling, and above 70 C selects keep-warm mode. Unused materials do not affect +the result. There is no extra filament flag or enable setting. + +The firmware parameters match Snorca: strong cooling uses mode 1, target +42 C, alarm 45 C, fan 0.6 and no delayed shutdown; weak cooling uses mode 3, +target 0 C, fan 0.6 and a 600-second delay; keep-warm uses mode 3, target +45 C, fan 0.6 and a 600-second delay. Mode selection happens once at print +start and depends on the accuracy of each filament's softening temperature. + ## Calibration sources Except for the volumetric ceiling, the original 0.4 mm HF material overrides come from Snapmaker/OrcaSlicer `snorca/main` diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json index 183e125c73..0eb1746895 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json @@ -47,7 +47,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.14", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json index 6d2ec2cfe6..f30fbe9813 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json @@ -47,7 +47,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.32", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json index a6cb0d0bd3..0067f3d087 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json @@ -47,7 +47,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.42", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json index ef4da1a516..390cc54281 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json @@ -47,7 +47,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.56", diff --git a/resources/profiles/Snapmaker/machine/fdm_U1.json b/resources/profiles/Snapmaker/machine/fdm_U1.json index 0b3d89a303..85a94df1d8 100644 --- a/resources/profiles/Snapmaker/machine/fdm_U1.json +++ b/resources/profiles/Snapmaker/machine/fdm_U1.json @@ -179,7 +179,7 @@ "purge_in_prime_tower": "0", "machine_pause_gcode": "M601", "change_filament_gcode": "", - "machine_start_gcode": "PRINT_START TOOL_TEMP={first_layer_temperature[initial_tool]} {if is_extruder_used[0]}T0_TEMP={first_layer_temperature[0]}{endif} {if is_extruder_used[1]}T1_TEMP={first_layer_temperature[1]}{endif} {if is_extruder_used[2]}T2_TEMP={first_layer_temperature[2]}{endif} {if is_extruder_used[3]}T3_TEMP={first_layer_temperature[3]}{endif} {if is_extruder_used[4]}T4_TEMP={first_layer_temperature[4]}{endif} {if is_extruder_used[5]}T5_TEMP={first_layer_temperature[5]}{endif} BED_TEMP=[first_layer_bed_temperature] TOOL=[initial_tool]\n\nM83\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nM109 T0 S{first_layer_temperature[0]}\nT0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 0 : (0 == 1 ? 120 : (0 == 2 ? 180 : 300)))} Y{(0 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[0]}10{else}30{endif} X40 Z0.2 F{if filament_multitool_ramming[0]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X40 E9 F800 ; continue purging and wipe the nozzle\nG0 X{40 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{40 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[0] == 0 ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nM109 T1 S{first_layer_temperature[1]}\nT1; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 0 : (1 == 1 ? 120 : (1 == 2 ? 180 : 300)))} Y{(1 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[1]}10{else}30{endif} X120 Z0.2 F{if filament_multitool_ramming[1]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X80 E9 F800 ; continue purging and wipe the nozzle\nG0 X{80 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{80 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[1] == 0 ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nM109 T2 S{first_layer_temperature[2]}\nT2; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 0 : (2 == 1 ? 120 : (2 == 2 ? 180 : 300)))} Y{(2 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[2]}10{else}30{endif} X220 Z0.2 F{if filament_multitool_ramming[2]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X220 E9 F800 ; continue purging and wipe the nozzle\nG0 X{220 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{220 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[2] == 0 ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nM109 T3 S{first_layer_temperature[3]}\nT3; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 0 : (3 == 1 ? 120 : (3 == 2 ? 180 : 300)))} Y{(3 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[3]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[3]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[3] == 0 ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nM109 T4 S{first_layer_temperature[4]}\nT4; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 0 : (4 == 1 ? 120 : (4 == 2 ? 180 : 300)))} Y{(4 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[4]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[4]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[4] == 0 ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool}; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300)))} Y{(initial_tool < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[initial_tool]}10{else}30{endif} X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if filament_multitool_ramming[initial_tool]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n", + "machine_start_gcode": "PRINT_START TOOL_TEMP={first_layer_temperature[initial_tool]} {if is_extruder_used[0]}T0_TEMP={first_layer_temperature[0]}{endif} {if is_extruder_used[1]}T1_TEMP={first_layer_temperature[1]}{endif} {if is_extruder_used[2]}T2_TEMP={first_layer_temperature[2]}{endif} {if is_extruder_used[3]}T3_TEMP={first_layer_temperature[3]}{endif} {if is_extruder_used[4]}T4_TEMP={first_layer_temperature[4]}{endif} {if is_extruder_used[5]}T5_TEMP={first_layer_temperature[5]}{endif} BED_TEMP=[first_layer_bed_temperature] TOOL=[initial_tool]\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\n\nM83\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nM109 T0 S{first_layer_temperature[0]}\nT0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 0 : (0 == 1 ? 120 : (0 == 2 ? 180 : 300)))} Y{(0 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[0]}10{else}30{endif} X40 Z0.2 F{if filament_multitool_ramming[0]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X40 E9 F800 ; continue purging and wipe the nozzle\nG0 X{40 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{40 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[0] == 0 ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nM109 T1 S{first_layer_temperature[1]}\nT1; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 0 : (1 == 1 ? 120 : (1 == 2 ? 180 : 300)))} Y{(1 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[1]}10{else}30{endif} X120 Z0.2 F{if filament_multitool_ramming[1]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X80 E9 F800 ; continue purging and wipe the nozzle\nG0 X{80 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{80 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[1] == 0 ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nM109 T2 S{first_layer_temperature[2]}\nT2; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 0 : (2 == 1 ? 120 : (2 == 2 ? 180 : 300)))} Y{(2 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[2]}10{else}30{endif} X220 Z0.2 F{if filament_multitool_ramming[2]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X220 E9 F800 ; continue purging and wipe the nozzle\nG0 X{220 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{220 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[2] == 0 ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nM109 T3 S{first_layer_temperature[3]}\nT3; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 0 : (3 == 1 ? 120 : (3 == 2 ? 180 : 300)))} Y{(3 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[3]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[3]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[3] == 0 ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nM109 T4 S{first_layer_temperature[4]}\nT4; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 0 : (4 == 1 ? 120 : (4 == 2 ? 180 : 300)))} Y{(4 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[4]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[4]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[4] == 0 ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool}; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300)))} Y{(initial_tool < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[initial_tool]}10{else}30{endif} X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if filament_multitool_ramming[initial_tool]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n", "scan_first_layer": "0", "nozzle_type": "undefine", "auxiliary_fan": "0", From 19277f44ad8d01d8a81a36940791ed9bb1425dd6 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Thu, 17 Sep 2026 09:08:16 -0400 Subject: [PATCH 35/89] HLSD seems too official for this --- docs/HLSD/snapmaker-high-flow.md | 108 ------------------ ...ron ASA-CF08 @U1 0.4 High Flow nozzle.json | 4 +- ...on PA12-CF10 @U1 0.4 High Flow nozzle.json | 4 +- ...ron PA6-CF20 @U1 0.4 High Flow nozzle.json | 4 +- ...on PA612-ESD @U1 0.4 High Flow nozzle.json | 4 +- ...ron PETG-ESD @U1 0.4 High Flow nozzle.json | 4 +- ...ron PPS-GF20 @U1 0.4 High Flow nozzle.json | 4 +- ...Generic PETG @U1 0.2 High Flow nozzle.json | 4 +- ...Generic PETG @U1 0.4 High Flow nozzle.json | 4 +- ...Generic PETG @U1 0.6 High Flow nozzle.json | 4 +- ...Generic PETG @U1 0.8 High Flow nozzle.json | 4 +- ...eric PETG HF @U1 0.2 High Flow nozzle.json | 4 +- ...eric PETG HF @U1 0.4 High Flow nozzle.json | 4 +- ...eric PETG HF @U1 0.6 High Flow nozzle.json | 4 +- ...eric PETG HF @U1 0.8 High Flow nozzle.json | 4 +- ...eric PETG-CF @U1 0.4 High Flow nozzle.json | 4 +- ...eric PETG-CF @U1 0.6 High Flow nozzle.json | 4 +- ...eric PETG-CF @U1 0.8 High Flow nozzle.json | 4 +- .../Generic PLA @U1 0.2 High Flow nozzle.json | 4 +- .../Generic PLA @U1 0.4 High Flow nozzle.json | 4 +- .../Generic PLA @U1 0.6 High Flow nozzle.json | 4 +- .../Generic PLA @U1 0.8 High Flow nozzle.json | 4 +- ...A High Speed @U1 0.2 High Flow nozzle.json | 4 +- ...A High Speed @U1 0.4 High Flow nozzle.json | 4 +- ...A High Speed @U1 0.6 High Flow nozzle.json | 4 +- ...A High Speed @U1 0.8 High Flow nozzle.json | 4 +- ...ric PLA Silk @U1 0.2 High Flow nozzle.json | 4 +- ...ric PLA Silk @U1 0.4 High Flow nozzle.json | 4 +- ...ric PLA Silk @U1 0.6 High Flow nozzle.json | 4 +- ...ric PLA Silk @U1 0.8 High Flow nozzle.json | 4 +- ...neric PLA-CF @U1 0.4 High Flow nozzle.json | 4 +- ...neric PLA-CF @U1 0.6 High Flow nozzle.json | 4 +- ...neric PLA-CF @U1 0.8 High Flow nozzle.json | 4 +- .../Generic TPU @U1 0.4 High Flow nozzle.json | 4 +- .../Generic TPU @U1 0.6 High Flow nozzle.json | 4 +- .../Generic TPU @U1 0.8 High Flow nozzle.json | 4 +- ...nchroma CoPE @U1 0.4 High Flow nozzle.json | 4 +- ...anchroma PLA @U1 0.4 High Flow nozzle.json | 4 +- ...LA Celestial @U1 0.4 High Flow nozzle.json | 4 +- ...a PLA Galaxy @U1 0.4 High Flow nozzle.json | 4 +- ...oma PLA Glow @U1 0.4 High Flow nozzle.json | 4 +- ...PLA Luminous @U1 0.4 High Flow nozzle.json | 4 +- ...a PLA Marble @U1 0.4 High Flow nozzle.json | 4 +- ...ma PLA Matte @U1 0.4 High Flow nozzle.json | 4 +- ...PLA Metallic @U1 0.4 High Flow nozzle.json | 4 +- ...oma PLA Neon @U1 0.4 High Flow nozzle.json | 4 +- ...ma PLA Satin @U1 0.4 High Flow nozzle.json | 4 +- ...oma PLA Silk @U1 0.4 High Flow nozzle.json | 4 +- ...LA Starlight @U1 0.4 High Flow nozzle.json | 4 +- ...A Temp Shift @U1 0.4 High Flow nozzle.json | 4 +- ... Translucent @U1 0.4 High Flow nozzle.json | 4 +- ...PLA UV Shift @U1 0.4 High Flow nozzle.json | 4 +- ...yLite CosPLA @U1 0.4 High Flow nozzle.json | 4 +- ...olyLite PETG @U1 0.4 High Flow nozzle.json | 4 +- ... Translucent @U1 0.4 High Flow nozzle.json | 4 +- ...PolyLite PLA @U1 0.4 High Flow nozzle.json | 4 +- ...e PLA Galaxy @U1 0.4 High Flow nozzle.json | 4 +- ...ite PLA Glow @U1 0.4 High Flow nozzle.json | 4 +- ...PLA Luminous @U1 0.4 High Flow nozzle.json | 4 +- ...ite PLA Neon @U1 0.4 High Flow nozzle.json | 4 +- ...Lite PLA Pro @U1 0.4 High Flow nozzle.json | 4 +- ...Pro Metallic @U1 0.4 High Flow nozzle.json | 4 +- ...LA Starlight @U1 0.4 High Flow nozzle.json | 4 +- ... Translucent @U1 0.4 High Flow nozzle.json | 4 +- ...olyTerra PLA @U1 0.4 High Flow nozzle.json | 4 +- ...a PLA Marble @U1 0.4 High Flow nozzle.json | 4 +- ...lyTerra PLA+ @U1 0.4 High Flow nozzle.json | 4 +- ...l PLA Family @U1 0.4 High Flow nozzle.json | 4 +- ...maker HT-PLA @U1 0.4 High Flow nozzle.json | 4 +- ...er HT-PLA-GF @U1 0.4 High Flow nozzle.json | 4 +- ...lymaker PETG @U1 0.4 High Flow nozzle.json | 4 +- ... PETG Galaxy @U1 0.4 High Flow nozzle.json | 4 +- ...olymaker PLA @U1 0.4 High Flow nozzle.json | 4 +- ...aker PLA Pro @U1 0.4 High Flow nozzle.json | 4 +- ...Pro Metallic @U1 0.4 High Flow nozzle.json | 4 +- ...k PLA Family @U1 0.4 High Flow nozzle.json | 4 +- ...h PLA Family @U1 0.4 High Flow nozzle.json | 4 +- ...napmaker ABS @U1 0.4 High Flow nozzle.json | 4 +- ...r ABS Benchy @U1 0.4 High Flow nozzle.json | 4 +- ...napmaker ASA @U1 0.4 High Flow nozzle.json | 4 +- ...port For PLA @U1 0.2 High Flow nozzle.json | 4 +- ...port For PLA @U1 0.4 High Flow nozzle.json | 4 +- ...port For PLA @U1 0.6 High Flow nozzle.json | 4 +- ...port For PLA @U1 0.8 High Flow nozzle.json | 4 +- ...pmaker PA-CF @U1 0.4 High Flow nozzle.json | 4 +- ...napmaker PET @U1 0.4 High Flow nozzle.json | 4 +- ...apmaker PETG @U1 0.4 High Flow nozzle.json | 4 +- ...aker PETG HF @U1 0.2 High Flow nozzle.json | 4 +- ...aker PETG HF @U1 0.4 High Flow nozzle.json | 4 +- ...aker PETG HF @U1 0.6 High Flow nozzle.json | 4 +- ...aker PETG HF @U1 0.8 High Flow nozzle.json | 4 +- ... Translucent @U1 0.2 High Flow nozzle.json | 4 +- ... Translucent @U1 0.4 High Flow nozzle.json | 4 +- ... Translucent @U1 0.6 High Flow nozzle.json | 4 +- ... Translucent @U1 0.8 High Flow nozzle.json | 4 +- ...aker PETG-CF @U1 0.4 High Flow nozzle.json | 4 +- ...napmaker PLA @U1 0.4 High Flow nozzle.json | 4 +- ...er PLA Basic @U1 0.4 High Flow nozzle.json | 4 +- ...aker PLA Eco @U1 0.4 High Flow nozzle.json | 4 +- ...ull Spectrum @U1 0.4 High Flow nozzle.json | 4 +- ...ker PLA Glow @U1 0.4 High Flow nozzle.json | 4 +- ...ker PLA Lite @U1 0.4 High Flow nozzle.json | 4 +- ...er PLA Matte @U1 0.2 High Flow nozzle.json | 4 +- ...er PLA Matte @U1 0.4 High Flow nozzle.json | 4 +- ...er PLA Matte @U1 0.6 High Flow nozzle.json | 4 +- ...er PLA Matte @U1 0.8 High Flow nozzle.json | 4 +- ...er PLA Metal @U1 0.4 High Flow nozzle.json | 4 +- ...ker PLA Silk @U1 0.2 High Flow nozzle.json | 4 +- ...ker PLA Silk @U1 0.4 High Flow nozzle.json | 4 +- ...ker PLA Silk @U1 0.6 High Flow nozzle.json | 4 +- ...ker PLA Silk @U1 0.8 High Flow nozzle.json | 4 +- ...LA SnapSpeed @U1 0.2 High Flow nozzle.json | 4 +- ...LA SnapSpeed @U1 0.4 High Flow nozzle.json | 4 +- ...LA SnapSpeed @U1 0.6 High Flow nozzle.json | 4 +- ...LA SnapSpeed @U1 0.8 High Flow nozzle.json | 4 +- ... Translucent @U1 0.2 High Flow nozzle.json | 4 +- ... Translucent @U1 0.4 High Flow nozzle.json | 4 +- ... Translucent @U1 0.6 High Flow nozzle.json | 4 +- ... Translucent @U1 0.8 High Flow nozzle.json | 4 +- ...ker PLA Wood @U1 0.4 High Flow nozzle.json | 4 +- ...ker PLA Wood @U1 0.6 High Flow nozzle.json | 4 +- ...ker PLA Wood @U1 0.8 High Flow nozzle.json | 4 +- ...maker PLA-CF @U1 0.4 High Flow nozzle.json | 4 +- ...maker PLA-CF @U1 0.6 High Flow nozzle.json | 4 +- ...maker PLA-CF @U1 0.8 High Flow nozzle.json | 4 +- ...napmaker PVA @U1 0.4 High Flow nozzle.json | 4 +- ...napmaker PVA @U1 0.6 High Flow nozzle.json | 4 +- ...napmaker PVA @U1 0.8 High Flow nozzle.json | 4 +- ...napmaker TPE @U1 0.4 High Flow nozzle.json | 4 +- ...aker TPU 90A @U1 0.4 High Flow nozzle.json | 4 +- ...aker TPU 90A @U1 0.6 High Flow nozzle.json | 4 +- ...aker TPU 90A @U1 0.8 High Flow nozzle.json | 4 +- ...aker TPU 95A @U1 0.4 High Flow nozzle.json | 4 +- ...r TPU 95A HF @U1 0.4 High Flow nozzle.json | 4 +- ...r TPU 95A HF @U1 0.6 High Flow nozzle.json | 4 +- ...r TPU 95A HF @U1 0.8 High Flow nozzle.json | 4 +- ...napmaker TPU @U1 0.4 High Flow nozzle.json | 4 +- ...napmaker TPU @U1 0.6 High Flow nozzle.json | 4 +- ...napmaker TPU @U1 0.8 High Flow nozzle.json | 4 +- ...PU High-Flow @U1 0.4 High Flow nozzle.json | 4 +- ...PU High-Flow @U1 0.6 High Flow nozzle.json | 4 +- ...PU High-Flow @U1 0.8 High Flow nozzle.json | 4 +- 142 files changed, 282 insertions(+), 390 deletions(-) delete mode 100644 docs/HLSD/snapmaker-high-flow.md diff --git a/docs/HLSD/snapmaker-high-flow.md b/docs/HLSD/snapmaker-high-flow.md deleted file mode 100644 index 61da074e97..0000000000 --- a/docs/HLSD/snapmaker-high-flow.md +++ /dev/null @@ -1,108 +0,0 @@ -# Snapmaker U1 high-flow profiles - -The U1 provides separate High Flow printer presets for 0.2, 0.4, 0.6 and -0.8 mm nozzles. Each describes four tools fitted with the same diameter and -high-flow nozzle. They inherit the corresponding standard printer, retaining -its model identity, geometry, motion limits and machine G-code. Existing -standard and mixed-diameter presets retain their settings and names. - -Enable the High Flow variants under Snapmaker U1 in the printer setup, select -the matching printer, and select a filament whose name includes that diameter -and `High Flow nozzle`. PLA SnapSpeed is the default material for these -printers. Dedicated HF process presets preserve diameter-specific extrusion -widths and layer heights while overriding the four print speeds below. -Separate HF filament presets keep higher ceilings out of standard-nozzle -material settings. - -## Process speeds - -All 36 HF process presets, across 0.2, 0.4, 0.6 and 0.8 mm nozzles, -use the same requested speeds from Snorca's 0.4 mm HF Standard process: - -| Setting | Speed (mm/s) | -| --- | ---: | -| Inner walls | 600 | -| Outer walls | 500 | -| Internal solid infill | 600 | -| Sparse infill | 600 | - -Each HF process inherits its standard diameter/layer-height counterpart, -so geometry, accelerations, first-layer speeds, bridges, overhangs and other -settings remain diameter-specific. Standard printers retain their original -process speeds. HF printers default to the corresponding HF process. -Volumetric ceilings and machine motion limits still cap actual speeds. - -## Chamber and purifier control - -All U1 start sequences, including the mixed-diameter and inherited HF -profiles, issue `SET_PURIFIER_MODE` automatically. The start G-code checks the existing -`min_vitrification_temperature` placeholder, the lowest Softening temperature -(`temperature_vitrification`) among the filaments actually used on the plate: -at or below 50 C selects strong cooling, above 50 C through 70 C selects weak -cooling, and above 70 C selects keep-warm mode. Unused materials do not affect -the result. There is no extra filament flag or enable setting. - -The firmware parameters match Snorca: strong cooling uses mode 1, target -42 C, alarm 45 C, fan 0.6 and no delayed shutdown; weak cooling uses mode 3, -target 0 C, fan 0.6 and a 600-second delay; keep-warm uses mode 3, target -45 C, fan 0.6 and a 600-second delay. Mode selection happens once at print -start and depends on the accuracy of each filament's softening temperature. - -## Calibration sources - -Except for the volumetric ceiling, the original 0.4 mm HF material overrides come from Snapmaker/OrcaSlicer `snorca/main` -commit `b1831e5dcb`, under `resources/profiles/Snapmaker/filament/`. That fork -uses `filament_flow_support: ["standard", "high_flow"]` to interpret paired -values. Here the high-flow column is translated into ordinary single-valued -filament overrides; the fork-specific keys and its profile restructuring are -not imported. Companion temperature and pressure-advance enable settings are -included. Machine and filament G-code remain inherited from this branch. - -The 14 materials with upstream HF overrides are ABS, ASA, PETG HF, PETG Translucent, PETG-CF, -PLA Full Spectrum, PLA Glow, PLA Matte, PLA Silk, PLA SnapSpeed, PLA -Translucent, PLA Wood, PLA-CF and PVA. Upstream's renamed Support For PLA is -not assumed equivalent to the local Breakaway formulation. - -## Volumetric ceiling - -All 141 HF filament presets explicitly set maximum volumetric speed to three -times the fully inherited non-HF material value. Diameter-specific parents -are used where available; Generic HF presets inherit the active Orca filament -library's `@System` definitions, which are shared across diameters. The parent -is recorded in `inherits`. This replaces both the upstream 0.4 mm HF ceiling -and the earlier capped estimates. - -The ceiling is a user-selected limit, not a measured hardware capability. -Other process and machine limits still apply. Future changes to a parent -preset's volumetric speed require updating its HF child; the multiplier is -materialized in JSON, not evaluated by the slicer. - -## Other material settings - -The 24 additional HF material presets are **estimated starting points**, not -manufacturer calibrations. They exist only where this branch already has a -matching material and diameter. In particular, this does not make filled -materials available to a 0.2 mm nozzle when no such standard profile exists. - -For each material with a paired 0.4 mm standard/HF value: - -- Flow ratio and pressure advance use the 0.4 mm HF/standard ratio, - rounded to four decimal places. A zero standard value is not divided by. -- Nozzle and initial-layer temperatures use the 0.4 mm HF-minus-standard - difference, added to the diameter's original temperature. -- Pressure advance follows the source's enable setting when its value is - adjusted. Diameter-specific retraction and cooling settings are retained. -- Missing, single-valued or `nil` comparisons leave the diameter's original - setting intact. - -For example, PLA SnapSpeed has standard volumetric limits of 2 mm^3/s at -0.2 mm and 20 mm^3/s at 0.4, 0.6 and 0.8 mm. Its HF ceilings are therefore -6 and 60 mm^3/s respectively. The other 103 HF material presets retain their -standard parent's temperature, retraction, cooling and pressure-advance -settings, changing only the volumetric ceiling and profile metadata. -Each HF preset includes its baseline and ceiling in its filament notes. -Tune the material settings for the actual nozzle, filament and temperature. - -The separate preset design requires no C++ changes or new profile keys. It -does not provide independent standard/HF selection per tool within one printer -preset; a mixed hardware setup needs its own calibrated user preset. diff --git a/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json index 794206a9ee..dc60a89080 100644 --- a/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "24" + "8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon ASA-CF08 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json index 40f28ecc21..ca3a3615ec 100644 --- a/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "24" + "8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA12-CF10 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json index dd15942c75..fdad376102 100644 --- a/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "24" + "8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA6-CF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json index a051be58f7..bfb433d9d8 100644 --- a/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "24" + "8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA612-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json index fe4411ceba..0712b95763 100644 --- a/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Fiberon PETG-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json index a838e2fecc..ae976bbde1 100644 --- a/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "24" + "8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PPS-GF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json index f224a6e5f8..0f3180747b 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "1" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json index 2dbdd3a6d3..dc29505cd2 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json index 67955d8cdc..7f48563c75 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json index 766f30a03f..e614e63e6b 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG @System: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json index be8af80e2d..838c6f27c8 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "1" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json index c13f72b1ed..15d0b70f30 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "16" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json index d0fed24d19..23977d0239 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "16" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json index b0b75d0a17..3a4c3ba6ad 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "16" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG HF @System: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json index 9ce1f45d0e..55dd20fa73 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "34.5" + "11" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG-CF @System: 11.5 x 3 = 34.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11 mm^3/s, from Snorca standard preset Generic PETG-CF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json index 6ecdd1894b..98a3e9f0ca 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "34.5" + "11.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG-CF @System: 11.5 x 3 = 34.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json index 95c852bdf8..c9fb8439ec 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "34.5" + "11.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PETG-CF @System: 11.5 x 3 = 34.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json index fa8f00af48..f4c562111c 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "1.6" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Generic PLA @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json index e564663de8..3042cc6942 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json index e964a2d73c..05b104ad69 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json index 0576e76d5f..b4962c3d93 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json index 2ab997f82f..b092b41fd8 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json index 4a30c36707..3cc254811a 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "18" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json index c26c6d1d75..68520dfb6e 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "18" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json index 8350a79674..7941e63d2c 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "18" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA High Speed @System: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json index 66e30d591e..7103c2aebe 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.5" + "2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json index 960fbb7ae4..cca504c6d5 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.5" + "7.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json index 4749f73903..3ba3f5e0b5 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.5" + "7.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json index ae13436c89..dcc0bc8007 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.5" + "7.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA Silk @System: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json index 4ea4fa5854..acb4001897 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA-CF @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json index 6489d4c06e..e0b91c6b08 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA-CF @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json index d8a21dc0ff..5e33595604 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic PLA-CF @System: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json index af86177bce..0e28741d4b 100644 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "9.6" + "3.2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic TPU @System: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json index 3bb797471f..8a1e5c04ff 100644 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "9.6" + "3.2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic TPU @System: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json index 88d4a980f1..34adb046aa 100644 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "9.6" + "3.2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling is 3x Generic TPU @System: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json index e9de9fd91a..d0bccc66e1 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma CoPE @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json index b57e11d9ae..783dea1078 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json index 23888f197d..9b0df8dd59 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Celestial @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json index 7f77834c25..4eb0005363 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json index 6becd42d5e..57f7e80c9c 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json index e7c1666d92..e892c5c2f7 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json index 7ed7b808a2..d9f9f9c419 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json index 42fb688f20..145f766ce9 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Matte @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json index fb1c10a2bc..b54b291bdd 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json index fa9087b701..da0389bdc7 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json index 970adef497..2f705909e8 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Satin @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json index 47f12bcfd8..9553afb7e3 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Panchroma PLA Silk @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json index d0a4cdee65..4d8310a6c3 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json index fca32afa7b..5cefff25f1 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Temp Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json index f6a4f0bfc3..45102a7a19 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json index 295bee1ea0..d252f70921 100644 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA UV Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json index 6ed9b9543d..7af0ba9b3a 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite CosPLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json index 0616e2fba3..cc4623a7cd 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.5" + "7.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json index b7dee44938..f0b3f91f49 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.5" + "7.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.5 x 3 = 22.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json index b337a5abc3..3d3a9bef72 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json index d2669f41a3..3836e62d5e 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json index 748fe5e2c6..f73ede7b25 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json index 7c7c661948..2ab15e9fd2 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json index 8aee23d539..51d1010112 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json index 0320f700c6..9b250dfb98 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json index f3a964bebb..fbfb23ac4b 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json index efcfd1518e..e1502b10de 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json index 06e0a310c7..c44e1bb633 100644 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json index e2c1f0aa26..454b86d711 100644 --- a/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json index e9bf977e66..4bfbd99b94 100644 --- a/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json index 274caf962e..20cb320ec8 100644 --- a/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA+ @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json index c36940b325..2c0f8a6936 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Polymaker General PLA Family @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json index 1bd1c95108..d6d336c3e0 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json index 844fc2e463..0d76705426 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA-GF @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json index d15778711a..f2b8f17ca2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json index 99eae4584a..afc5f6a361 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json index 066c4e5d48..b411e8960a 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Polymaker PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json index d1c63bc805..0c98d9a7ee 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json index c7e944ea35..b027a554b2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json index c60d41257c..c51dce398d 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Silk PLA Family @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json index 4a539098ca..724ab002c2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Tough PLA Family @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json index e3b53f36f3..0de983e448 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json @@ -18,7 +18,7 @@ "10" ], "filament_max_volumetric_speed": [ - "24" + "22" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -45,6 +45,6 @@ "280" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ABS @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json index 244351878e..f8c91285b6 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json @@ -9,10 +9,10 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "93" + "31" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 31 x 3 = 93 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 31 mm^3/s, from local standard fallback preset Snapmaker ABS Benchy @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "compatible_prints": [ "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json index c5199df011..212e9b1044 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json @@ -18,7 +18,7 @@ "10" ], "filament_max_volumetric_speed": [ - "24" + "22" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -45,6 +45,6 @@ "0.02" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ASA @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json index 3fff84663e..9fa11d6101 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "1.5" + "0.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 0.5 x 3 = 1.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 0.5 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.2 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json index 871d785016..9d8922829a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json index 77717f535a..63e218c554 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.6 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json index 5db74011a3..dce415fefe 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.8 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json index f6981095b8..c097954185 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "24" + "8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 8 x 3 = 24 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Snapmaker PA-CF @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json index 854a02c1d1..a7c30b3000 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "20.4" + "6.8" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6.8 x 3 = 20.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 6.8 mm^3/s, from local standard fallback preset Snapmaker PET @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json index 780f8fee71..bf18d6fb9d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "10" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10 mm^3/s, from local standard fallback preset Snapmaker PETG @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json index 91b263df44..6fded62f26 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "3" + "2" ], "filament_flow_ratio": [ "0.955" @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 1 x 3 = 3 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json index 085debf589..5b562ef932 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json @@ -21,7 +21,7 @@ "0.955" ], "filament_max_volumetric_speed": [ - "48" + "30" ], "filament_minimal_purge_on_wipe_tower": [ "50" @@ -63,6 +63,6 @@ "0.035" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 30 mm^3/s, from Snorca HF preset Snapmaker PETG HF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json index ca27f67555..1bb8fca6dd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_flow_ratio": [ "0.955" @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json index 051bb8fbac..b95ecd4911 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_flow_ratio": [ "0.955" @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json index 44f49f7b2d..eec95f80bf 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 1 x 3 = 3 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "3" + "1" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json index 265b504a53..1a17739f69 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json @@ -15,9 +15,9 @@ "0.05" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 10 mm^3/s, from Snorca HF preset Snapmaker PETG Translucent @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "30" + "10" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json index aea3ffda1c..0fceb6ef5a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "48" + "16" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json index 7e66c59a2c..26749c6ecf 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "48" + "16" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json index 4fb22fd2a5..6c06c1c4bf 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json @@ -24,7 +24,7 @@ "1.01" ], "filament_max_volumetric_speed": [ - "19.2" + "18" ], "filament_minimal_purge_on_wipe_tower": [ "30" @@ -60,6 +60,6 @@ "0.028" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6.4 x 3 = 19.2 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PETG-CF @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json index 9e52e8d145..b0e13a4f79 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "42" + "14" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 14 x 3 = 42 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 14 mm^3/s, from local standard fallback preset Snapmaker PLA @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json index 5b690deccc..3d2e16dc7f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Snapmaker PLA Basic @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json index 1fc7f3e6e5..100d225a88 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker PLA Eco @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json index d9b39fcdb2..1a63ff75cc 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json @@ -24,9 +24,9 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 15 mm^3/s, from Snorca HF preset Snapmaker PLA Full Spectrum @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "45" + "15" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json index d6423ff5ff..b2f4efcfb1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json @@ -15,12 +15,12 @@ "0.98" ], "filament_max_volumetric_speed": [ - "48" + "18" ], "pressure_advance": [ "0.023" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PLA Glow @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json index 6f7f024b4c..f281f8795b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "45" + "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Snapmaker PLA Lite @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json index a0e558096c..3b2e3541c4 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "6" + "2" ], "filament_flow_ratio": [ "1.0395" @@ -27,6 +27,6 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2 x 3 = 6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json index b4021aea6a..45bf88f8b5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json @@ -18,7 +18,7 @@ "0.99" ], "filament_max_volumetric_speed": [ - "66" + "40" ], "filament_retraction_length": [ "1" @@ -39,6 +39,6 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 22 x 3 = 66 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA Matte @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json index daef1b37d5..2430b54a9d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_flow_ratio": [ "0.9801" @@ -27,6 +27,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json index 7f36cee705..38aed6438a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "filament_flow_ratio": [ "0.9702" @@ -27,6 +27,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json index c5c4b829a2..f26eeb2114 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "48" + "16" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from local standard fallback preset Snapmaker PLA Metal @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json index d110009d58..d8eba5a227 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "6" + "2" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2 x 3 = 6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json index e05d313038..96869e7af9 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json @@ -12,12 +12,12 @@ "80" ], "filament_max_volumetric_speed": [ - "30" + "12" ], "filament_z_hop_types": [ "Slope Lift" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Silk @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json index ad19b1abf3..e71fc44138 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "10" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json index 3856c5e5a2..4b8d8be438 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "30" + "10" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 10 x 3 = 30 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json index ef43a62cc1..dd492516fb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "6" + "2" ], "pressure_advance": [ "0.24" @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2 x 3 = 6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json index 9ab350a7ba..03be272261 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json @@ -12,7 +12,7 @@ "1" ], "filament_max_volumetric_speed": [ - "60" + "40" ], "filament_multitool_ramming_flow": [ "40" @@ -21,6 +21,6 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA SnapSpeed @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json index ab657264ad..8bb018713c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "pressure_advance": [ "0.024" @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json index 5e9be868b6..6af5875213 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "60" + "20" ], "pressure_advance": [ "0.0216" @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json index b7b8357798..66ea29ff0c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 1.6 x 3 = 4.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "4.8" + "1.6" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json index 67a5c77f4a..dd3134ef2b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json @@ -12,9 +12,9 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Translucent @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "36" + "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json index 29767b5045..647971457c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "36" + "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json index 6790f2a521..ef2bcbe096 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "36" + "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json index 95ef69bb13..5f3d8d6cc7 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "20" ], "filament_retract_length_toolchange": [ "6" @@ -21,6 +21,6 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA Wood @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json index 71e3f69b98..b30036af55 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "18" ], "pressure_advance": [ "0.0134" @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json index 9bcde49fd5..9bbf33e51b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json @@ -15,9 +15,9 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 20 x 3 = 60 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "60" + "20" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json index c61a287426..310efaefe2 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json @@ -12,7 +12,7 @@ "100" ], "filament_max_volumetric_speed": [ - "45" + "20" ], "filament_multitool_ramming_flow": [ "30" @@ -33,6 +33,6 @@ "0.02" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 15 x 3 = 45 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA-CF @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json index 825567e128..3fecd0e77c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "18" ], "pressure_advance": [ "0.03" @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json index 52100f6bbe..0f5a73930a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json @@ -9,7 +9,7 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "54" + "18" ], "pressure_advance": [ "0.03" @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 18 x 3 = 54 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json index e96baac97a..ca38fd6675 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json @@ -12,7 +12,7 @@ "0.97" ], "filament_max_volumetric_speed": [ - "15" + "6" ], "filament_multitool_ramming_flow": [ "10" @@ -21,6 +21,6 @@ "0.6" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 5 x 3 = 15 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 6 mm^3/s, from Snorca HF preset Snapmaker PVA @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json index 1fb7bc978a..e5ef681dc3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json @@ -12,9 +12,9 @@ "0.9506" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6 x 3 = 18 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "18" + "6" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json index cfdd941bc8..693f9a4c27 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json @@ -12,9 +12,9 @@ "0.9312" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 6 x 3 = 18 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ - "18" + "6" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json index 4b03c0e63d..441562c8d7 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "21.6" + "7.2" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.2 x 3 = 21.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.2 mm^3/s, from local standard fallback preset Snapmaker TPE @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json index 130bba2045..1accbf432d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "9.6" + "3.2" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json index d999f651f1..51edd1e01f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "9.6" + "3.2" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json index b49d1d3a09..847f0ed8d5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "10.5" + "3.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.5 x 3 = 10.5 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.5 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json index 55ba8da2db..391cec907f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "9.6" + "3.2" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 3.2 x 3 = 9.6 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from local standard fallback preset Snapmaker TPU 95A @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json index 461dcf6e69..0b0d178f90 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "27" + "9" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 9 x 3 = 27 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 9 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json index 416cfe0097..887c94ee7b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "36" + "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 12 x 3 = 36 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json index 9971dabbf2..cb976d3246 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "48" + "16" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 16 x 3 = 48 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json index 48d709b12d..afaefa2460 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "8.4" + "10.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2.8 x 3 = 8.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json index 8af4584d0a..00dd85f773 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "8.4" + "10.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2.8 x 3 = 8.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json index 6009f90585..cb7d2fa74f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "8.4" + "10.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 2.8 x 3 = 8.4 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json index d34d28c7b5..3118864459 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.8" + "7.6" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.6 x 3 = 22.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json index 34cf3796f5..0c0a988c82 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.8" + "7.6" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.6 x 3 = 22.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json index 9453bf939b..ed24acdd57 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "22.8" + "7.6" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling is 3x the inherited non-HF value for this diameter: 7.6 x 3 = 22.8 mm^3/s. This is a configured ceiling, not measured nozzle throughput. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } From 30b3666c4759661c85f6e28219887ef8c0ce44ad Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Fri, 18 Sep 2026 07:24:32 -0400 Subject: [PATCH 36/89] U1 HF: Resolve nozzle mismatch on-screen warning Emit U1 nozzle flow metadata through profile end G-code Append filament_volume_type footer comments through the standard and High Flow U1 machine profiles. Generate one comma-separated value per logical filament using existing placeholder expressions, preserving the existing end commands. Mixed-diameter standard profiles inherit the standard footer. Bump the Snapmaker bundle to 02.04.00.17. No C++ or filament-schema changes. Validation intentionally not run at user request. Co-authored-by: Codex --- resources/profiles/Snapmaker.json | 2 +- .../Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json | 3 ++- .../profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json | 2 +- .../Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json | 3 ++- .../profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json | 2 +- .../Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json | 3 ++- .../profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json | 2 +- .../Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json | 3 ++- .../profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json | 2 +- 9 files changed, 13 insertions(+), 9 deletions(-) diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index d415c7dadd..7f2aaa2881 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.16", + "version": "02.04.00.17", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json index 9afb250476..6c3a2ff7ae 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json @@ -14,5 +14,6 @@ "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle" ], - "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)" + "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json index 0eb1746895..46273a7f44 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", "machine_max_jerk_z": [ "3", "0.4" diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json index f8b8d44e10..cd54aae644 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json @@ -14,5 +14,6 @@ "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle" ], - "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)" + "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json index f30fbe9813..d65d549d77 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", "machine_max_jerk_z": [ "3", "0.4" diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json index 53c0d682cd..12f91c71cd 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json @@ -14,5 +14,6 @@ "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle" ], - "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)" + "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json index 0067f3d087..b48d528074 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", "machine_max_jerk_z": [ "3", "0.4" diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json index ded207ffbf..2f9ecca19e 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json @@ -14,5 +14,6 @@ "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle" ], - "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)" + "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json index 390cc54281..e3ffac2bb8 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", "machine_max_jerk_z": [ "3", "0.4" From 533b68ed9f4fdeb9aeafaa00130ba54d3ea82a4c Mon Sep 17 00:00:00 2001 From: SoftFever Date: Fri, 18 Sep 2026 20:37:35 +0800 Subject: [PATCH 37/89] Fix colour textures loading upside down and crashes on damaged PNGs Colour height maps now displace the same way up as grayscale ones. A truncated or corrupt PNG in the texture folder or a project file now loads as an empty texture instead of aborting the app, and a 16-bit colour PNG is converted on load rather than displacing as noise. --- src/libslic3r/PNGReadWrite.cpp | 19 +++++-- src/libslic3r/TextureDisplacement.cpp | 36 ++++++++----- src/slic3r/GUI/TextureLibrary.cpp | 7 +-- tests/libslic3r/test_texture_displacement.cpp | 52 ++++++++++++++++++- 4 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/PNGReadWrite.cpp b/src/libslic3r/PNGReadWrite.cpp index 32b9a8f79e..adc41a164f 100644 --- a/src/libslic3r/PNGReadWrite.cpp +++ b/src/libslic3r/PNGReadWrite.cpp @@ -54,9 +54,11 @@ static void png_read_callback(png_struct *png_ptr, // Retrieve our input buffer through the png_ptr auto reader = static_cast(png_get_io_ptr(png_ptr)); - if (!reader || !reader->is_ok()) return; - - reader->read(static_cast(outBytes), byteCountToRead); + // libpng expects a short read to be reported through png_error(); returning quietly would leave + // it decoding whatever happened to be in outBytes. + if (!reader || !reader->is_ok() || + reader->read(static_cast(outBytes), byteCountToRead) != byteCountToRead) + png_error(png_ptr, "PNG data is truncated"); } bool decode_png(IStream &in_buf, ImageGreyscale &out_img) @@ -77,6 +79,11 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img) dsc.info = png_create_info_struct(dsc.png); if(!dsc.info) return false; + // libpng reports a corrupt or truncated image by longjmp()ing back here. Without a jump buffer + // it abort()s the whole process instead. + if (setjmp(png_jmpbuf(dsc.png))) + return false; + png_set_read_fn(dsc.png, static_cast(&in_buf), png_read_callback); // Tell that we have already read the first bytes to check the signature @@ -128,6 +135,12 @@ bool decode_colored_png(IStream &in_buf, ImageColorscale &out_img) return false; } + // See decode_png(). + if (setjmp(png_jmpbuf(dsc.png))) { + BOOST_LOG_TRIVIAL(error) << "decode_colored_png: corrupt or truncated PNG data"; + return false; + } + png_set_read_fn(dsc.png, static_cast(&in_buf), png_read_callback); // Tell that we have already read the first bytes to check the signature diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index cfc75c286f..0ab06e68b4 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -272,24 +272,34 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer // coefficients are wxImage::ConvertToGreyscale()'s, which is what the importer used to // apply on the way in - so a texture that used to be flattened to grey at import time // displaces identically now that its colour is preserved. + // The loop below steps bytes_per_pixel per texel, which only holds at 8 bits per channel: + // decode_colored_png() does not narrow a 16-bit image, so that would read as noise. png::ImageColorscale col; if (!png::decode_colored_png(rbuf, col) || col.cols == 0 || col.rows == 0 || - col.bytes_per_pixel < 3) + col.bytes_per_pixel < 3 || col.buf.size() != col.cols * col.rows * size_t(col.bytes_per_pixel)) return result; - const size_t n = size_t(col.cols) * size_t(col.rows); + const int w = int(col.cols); + const int h = int(col.rows); const size_t bpp = size_t(col.bytes_per_pixel); - result.width = int(col.cols); - result.height = int(col.rows); - result.pixels.resize(n); - result.rgb.resize(n * 3); - for (size_t i = 0; i < n; ++i) { - const uint8_t r = col.buf[i * bpp], g = col.buf[i * bpp + 1], b = col.buf[i * bpp + 2]; - result.rgb[i * 3] = r; - result.rgb[i * 3 + 1] = g; - result.rgb[i * 3 + 2] = b; - result.pixels[i] = uint8_t(std::lround(0.299 * r + 0.587 * g + 0.114 * b)); - } + result.width = w; + result.height = h; + result.pixels.resize(size_t(w) * size_t(h)); + result.rgb.resize(size_t(w) * size_t(h) * 3); + // decode_colored_png() hands its buffer back bottom-up - it is shared with the CLI's + // plate-thumbnail loader, which expects that - while this type, and decode_png()'s + // grayscale path above, are top-to-bottom. Reverse the rows on the way in so a colour + // height map displaces the same way up as a grayscale one. + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) { + const uint8_t *src = col.buf.data() + (size_t(h - 1 - y) * size_t(w) + size_t(x)) * bpp; + const size_t dst = size_t(y) * size_t(w) + size_t(x); + const uint8_t r = src[0], g = src[1], b = src[2]; + result.rgb[dst * 3] = r; + result.rgb[dst * 3 + 1] = g; + result.rgb[dst * 3 + 2] = b; + result.pixels[dst] = uint8_t(std::lround(0.299 * r + 0.587 * g + 0.114 * b)); + } } std::lock_guard lock(g_decoded_texture_cache.mutex); diff --git a/src/slic3r/GUI/TextureLibrary.cpp b/src/slic3r/GUI/TextureLibrary.cpp index 30df4af22c..7cbd50a240 100644 --- a/src/slic3r/GUI/TextureLibrary.cpp +++ b/src/slic3r/GUI/TextureLibrary.cpp @@ -153,8 +153,8 @@ std::vector read_file_bytes(const std::string &path) } // True if these bytes are a PNG libslic3r can decode, i.e. can be stored on a layer as-is. Mirrors -// exactly what decode_height_texture() accepts: 8-bit grayscale, or colour (whose luminance is the -// height and whose RGB is available to colour the model). +// exactly what decode_height_texture() accepts: 8-bit grayscale, or 8-bit colour (whose luminance is +// the height and whose RGB is available to colour the model). Anything else is converted on load. bool is_supported_height_map(const std::vector &bytes) { if (bytes.empty()) @@ -167,7 +167,8 @@ bool is_supported_height_map(const std::vector &bytes) return true; png::ImageColorscale color; return png::decode_colored_png(rbuf, color) && color.cols > 0 && color.rows > 0 && - color.bytes_per_pixel >= 3; + color.bytes_per_pixel >= 3 && + color.buf.size() == color.cols * color.rows * size_t(color.bytes_per_pixel); } std::vector g_library; diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index e2f0147efb..efdfd32c44 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -1,4 +1,6 @@ +#ifndef NOMINMAX #define NOMINMAX +#endif #include #include @@ -779,11 +781,27 @@ static std::shared_ptr> make_rgb_png_2x2() return std::make_shared>(std::begin(bytes), std::end(bytes)); } +// The same image with an opaque alpha channel, so four bytes per pixel instead of three. +static std::shared_ptr> make_rgba_png_2x2() +{ + static const unsigned char bytes[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x72, 0xb6, 0x0d, 0x24, 0x00, 0x00, 0x00, + 0x12, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xf8, 0xcf, 0xc0, 0xf0, + 0x1f, 0x0c, 0x81, 0x34, 0x18, 0x00, 0x00, 0x49, 0xc8, 0x09, 0xf7, 0x03, + 0xd9, 0x64, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, + 0x42, 0x60, 0x82, + }; + return std::make_shared>(std::begin(bytes), std::end(bytes)); +} + TEST_CASE("TextureDisplacement: a colour texture decodes to both colour and height", "[TextureDisplacement]") { TextureDisplacementLayer layer; layer.slot = 0; - layer.image_data = make_rgb_png_2x2(); + // Row stride differs between the two, and both must come out the same way up. + layer.image_data = GENERATE(make_rgb_png_2x2(), make_rgba_png_2x2()); const DecodedHeightTexture tex = decode_height_texture(layer); REQUIRE_FALSE(tex.empty()); @@ -806,6 +824,38 @@ TEST_CASE("TextureDisplacement: a colour texture decodes to both colour and heig CHECK(int(tex.pixels[3]) == 255); // white } +TEST_CASE("A 16-bit colour texture decodes to nothing rather than noise", "[TextureDisplacement]") +{ + // The 2x2 image above at 16 bits per channel. The texture library converts such a file to 8-bit + // on load, so it can only arrive here stored as-is, from a project file. + static const unsigned char bytes[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x02, 0x00, 0x00, 0x00, 0xad, 0x44, 0x46, 0x30, 0x00, 0x00, 0x00, + 0x12, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xf8, 0xff, 0x9f, 0x01, + 0x0c, 0x60, 0x34, 0x90, 0x01, 0x01, 0x00, 0x75, 0xa4, 0x0b, 0xf5, 0x97, + 0xf4, 0x36, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, + 0x42, 0x60, 0x82, + }; + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = std::make_shared>(std::begin(bytes), std::end(bytes)); + + CHECK(decode_height_texture(layer).empty()); +} + +TEST_CASE("A truncated texture decodes to nothing instead of aborting", "[TextureDisplacement]") +{ + // A half-copied file in the texture folder, or a damaged project file. libpng reports this by + // longjmp, and aborts the process if the decoder has not set a jump buffer to land on. + const auto whole = GENERATE(make_flat_gray_png(128, 16, 16), make_rgb_png_2x2()); + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = std::make_shared>(whole->begin(), whole->begin() + whole->size() / 2); + + CHECK(decode_height_texture(layer).empty()); +} + TEST_CASE("TextureDisplacement: a grayscale texture reports no colour", "[TextureDisplacement]") { // The shipped library is all grayscale, and has_color() is what the whole colour feature keys From 734dc1f9ac5567b66b65813c41e133ff939278fb Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Fri, 18 Sep 2026 09:03:20 -0400 Subject: [PATCH 38/89] Disambiguate nozzles by the variant too --- src/slic3r/GUI/Plater.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 30e1ae1313..f7b22eddc5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1572,8 +1572,10 @@ bool Sidebar::priv::switch_diameter(bool single) auto* nozzle_diameter = dynamic_cast(printer_preset.config.option("nozzle_diameter")); if (nozzle_diameter && nozzle_diameter->size() > 0) { auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]); - // If the selected diameter is the same as current nozzle, don't switch profiles - if (current_nozzle_dia == diameter.ToStdString()) { + // A named variant can share this diameter; selecting the plain diameter + // must still switch back to the standard profile. + if (current_nozzle_dia == diameter.ToStdString() && + printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) { return true; } } @@ -3677,11 +3679,16 @@ void Sidebar::update_presets(Preset::Type preset_type) extruder.combo_flow->SetSelection(select); }; - auto update_extruder_diameter = [&diameters, &nozzle_diameter](int extruder_index,ExtruderGroup & extruder) { + auto update_extruder_diameter = [&diameters, &nozzle_diameter, &diameter](int extruder_index,ExtruderGroup & extruder) { extruder.combo_diameter->Clear(); int select = -1; // ORCA get the actual nozzle diameter from printer config auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]); + // Named variants such as "0.4 High Flow" share a physical diameter. + // Retain the variant selection unless the diameter was customized. + const auto selected_variant = + (diameter == nozzle_dia || boost::algorithm::starts_with(diameter, nozzle_dia + " ")) && + std::find(diameters.begin(), diameters.end(), diameter) != diameters.end() ? diameter : nozzle_dia; // ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias if(diameters[0].empty() && !nozzle_dia.empty()){ diameters[0] = nozzle_dia; @@ -3691,7 +3698,7 @@ void Sidebar::update_presets(Preset::Type preset_type) diameters.push_back(nozzle_dia); } for (size_t i = 0; i < diameters.size(); ++i) { - if (diameters[i] == nozzle_dia) + if (diameters[i] == selected_variant) select = extruder.combo_diameter->GetCount(); extruder.combo_diameter->Append(diameters[i], {}); } From cef6527f9dc43a84f822c91a4eb8d04da0bf7ed4 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Mon, 21 Sep 2026 08:59:26 +0200 Subject: [PATCH 39/89] Texture displacement: per-layer bake, UV pane redesign, unwrap and layer view fixes - Bake: each layer is sampled only on its own painted area; analytic projections used to stack every layer over every painted region, so the top layer's texture showed on all of them (colour sampler too) - Auto resolution follows the texture's texel size and sharpness again - Unwrap: charts cut by each face's own normal (a cube gives 6 islands, not 12 triangles); non-disk charts (tubes, closed shells) are split until they flatten; connected nets test real triangle overlap, grow from the largest chart and are packed side by side - UV edits are stored per unwrapped copy, so dragging a seam vertex no longer moves its copies in neighbouring islands - UV pane: tool strip with unwrap settings moved in from the panel, sharp HiDPI icons, clearer island/edge/selection drawing with hover, texture picker from the thumbnail, texture no longer lost on reopen (GL state from the 3D view, background upload retries) - Panel: whole-model select/erase as icons in the tools row; inactive layers' paint shown muted; colour textures shown in colour in the picker - Built-in displacement texture library - Tests for unwrap segmentation, connected nets, UV edits and per-layer sampling --- .../images/texture_displacement_erase_all.svg | 1 + .../texture_displacement_select_all.svg | 1 + .../shaders/110/texture_displacement_bump.fs | 186 ++++- .../110/texture_displacement_uvcheck.fs | 47 +- .../shaders/140/texture_displacement_bump.fs | 194 +++++- .../140/texture_displacement_uvcheck.fs | 47 +- resources/textures/displacement/Bark.png | Bin 0 -> 317344 bytes .../textures/displacement/Basket Weave.png | Bin 0 -> 25083 bytes resources/textures/displacement/Bubbles.png | Bin 0 -> 137954 bytes .../textures/displacement/Camouflage.png | Bin 0 -> 270061 bytes .../textures/displacement/Carbon Fibre.png | Bin 0 -> 44424 bytes resources/textures/displacement/Chainmail.png | Bin 0 -> 138358 bytes resources/textures/displacement/Chevron.png | Bin 0 -> 21202 bytes .../textures/displacement/Cobblestone.png | Bin 0 -> 240937 bytes .../textures/displacement/Colour Bricks.png | Bin 0 -> 432177 bytes .../textures/displacement/Cracked Earth.png | Bin 0 -> 259705 bytes .../textures/displacement/Diamond Plate.png | Bin 0 -> 153635 bytes .../textures/displacement/Fine Knurl.png | Bin 0 -> 29200 bytes resources/textures/displacement/Hammered.png | Bin 0 -> 278647 bytes .../textures/displacement/Herringbone.png | Bin 0 -> 159017 bytes resources/textures/displacement/Hex Tiles.png | Bin 0 -> 64110 bytes resources/textures/displacement/Honeycomb.png | Bin 0 -> 29854 bytes resources/textures/displacement/Leather.png | Bin 0 -> 534739 bytes .../textures/displacement/Mosaic Tiles.png | Bin 0 -> 25998 bytes .../textures/displacement/Perforated.png | Bin 0 -> 19684 bytes resources/textures/displacement/Pyramids.png | Bin 0 -> 9529 bytes resources/textures/displacement/Ripples.png | Bin 0 -> 232978 bytes .../textures/displacement/Roof Tiles.png | Bin 0 -> 75446 bytes resources/textures/displacement/Rope.png | Bin 0 -> 110082 bytes .../textures/displacement/Sand Ripples.png | Bin 0 -> 182176 bytes resources/textures/displacement/Scales.png | Bin 0 -> 40753 bytes resources/textures/displacement/Slate.png | Bin 0 -> 208005 bytes .../textures/displacement/Star Tiles.png | Bin 0 -> 10935 bytes .../textures/displacement/Stone Wall.png | Bin 0 -> 210233 bytes resources/textures/displacement/Tartan.png | Bin 0 -> 147158 bytes resources/textures/displacement/Terrazzo.png | Bin 0 -> 241059 bytes resources/textures/displacement/Triangles.png | Bin 0 -> 31465 bytes resources/textures/displacement/Waffle.png | Bin 0 -> 17731 bytes .../textures/displacement/Wood Planks.png | Bin 0 -> 213664 bytes scripts/generate_displacement_textures.py | 477 +++++++++++++ src/libslic3r/Model.cpp | 5 + .../TextureBake/TextureBakeDecimate.cpp | 8 +- .../TextureBake/TextureBakeDecimate.hpp | 6 +- .../TextureBake/TextureBakePipeline.cpp | 78 ++- .../TextureBake/TextureBakePipeline.hpp | 16 +- src/libslic3r/TextureDisplacement.cpp | 650 ++++++++++++++---- src/libslic3r/TextureDisplacement.hpp | 99 ++- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 547 ++++++++++++--- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 74 +- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 38 +- .../Jobs/TextureDisplacementPreviewJob.cpp | 2 + tests/libslic3r/test_texture_displacement.cpp | 440 +++++++++++- 52 files changed, 2561 insertions(+), 355 deletions(-) create mode 100644 resources/images/texture_displacement_erase_all.svg create mode 100644 resources/images/texture_displacement_select_all.svg create mode 100644 resources/textures/displacement/Bark.png create mode 100644 resources/textures/displacement/Basket Weave.png create mode 100644 resources/textures/displacement/Bubbles.png create mode 100644 resources/textures/displacement/Camouflage.png create mode 100644 resources/textures/displacement/Carbon Fibre.png create mode 100644 resources/textures/displacement/Chainmail.png create mode 100644 resources/textures/displacement/Chevron.png create mode 100644 resources/textures/displacement/Cobblestone.png create mode 100644 resources/textures/displacement/Colour Bricks.png create mode 100644 resources/textures/displacement/Cracked Earth.png create mode 100644 resources/textures/displacement/Diamond Plate.png create mode 100644 resources/textures/displacement/Fine Knurl.png create mode 100644 resources/textures/displacement/Hammered.png create mode 100644 resources/textures/displacement/Herringbone.png create mode 100644 resources/textures/displacement/Hex Tiles.png create mode 100644 resources/textures/displacement/Honeycomb.png create mode 100644 resources/textures/displacement/Leather.png create mode 100644 resources/textures/displacement/Mosaic Tiles.png create mode 100644 resources/textures/displacement/Perforated.png create mode 100644 resources/textures/displacement/Pyramids.png create mode 100644 resources/textures/displacement/Ripples.png create mode 100644 resources/textures/displacement/Roof Tiles.png create mode 100644 resources/textures/displacement/Rope.png create mode 100644 resources/textures/displacement/Sand Ripples.png create mode 100644 resources/textures/displacement/Scales.png create mode 100644 resources/textures/displacement/Slate.png create mode 100644 resources/textures/displacement/Star Tiles.png create mode 100644 resources/textures/displacement/Stone Wall.png create mode 100644 resources/textures/displacement/Tartan.png create mode 100644 resources/textures/displacement/Terrazzo.png create mode 100644 resources/textures/displacement/Triangles.png create mode 100644 resources/textures/displacement/Waffle.png create mode 100644 resources/textures/displacement/Wood Planks.png create mode 100644 scripts/generate_displacement_textures.py diff --git a/resources/images/texture_displacement_erase_all.svg b/resources/images/texture_displacement_erase_all.svg new file mode 100644 index 0000000000..d45e6c7d35 --- /dev/null +++ b/resources/images/texture_displacement_erase_all.svg @@ -0,0 +1 @@ + diff --git a/resources/images/texture_displacement_select_all.svg b/resources/images/texture_displacement_select_all.svg new file mode 100644 index 0000000000..bf420df6c6 --- /dev/null +++ b/resources/images/texture_displacement_select_all.svg @@ -0,0 +1 @@ + diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index 5dff96d479..fa000cc7ba 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -28,6 +28,20 @@ uniform vec4 uniform_color; uniform vec3 palette_lab[64]; uniform vec3 palette_rgb[64]; uniform int palette_count; +uniform bool pure_only; // match against single filaments only (flat-colour image) +// How each entry prints. A pure entry is one filament (a == b); a mix interleaves filaments a and b, +// num parts of a in every den, and the print shows that interleave rather than the entry's average +// colour. The fragment resolves it exactly as GLGizmoTextureDisplacement::make_mix_resolver() does +// per triangle on the CPU, so the preview shows the pattern the bake will print. +uniform int palette_a[64]; +uniform int palette_b[64]; +uniform int palette_num[64]; +uniform int palette_den[64]; +uniform vec3 filament_rgb[16]; +uniform int filament_count; +uniform int mix_mode; // ColorMixMode: 0 Z bands, 1 XY dither, 2 auto +uniform float layer_height; // mm; one Z band per print layer +uniform float dither_cell; // mm; one XY dither cell uniform sampler2D color_tex; // the layer's colour image, sampled at the same uv as the height uniform bool has_color_tex; uniform bool volume_mirrored; @@ -53,6 +67,11 @@ uniform bool use_vertex_uv; // 140 variant. Identity when nothing is dragged. uniform vec4 island_delta_lin; uniform vec2 island_delta_tr; +// In-shader projection (0 Triplanar, 1 Cylindrical, 2 Spherical) and the painted patch's own frame the +// two wrapping ones wrap around, in the texture frame; see the 140 variant. +uniform int projection_mode; +uniform vec3 patch_center; +uniform vec3 patch_axis; varying vec3 clipping_planes_dots; varying vec4 model_pos; @@ -61,8 +80,71 @@ varying float weight; varying float island_active; varying vec2 vertex_uv; -void projection_axes(vec3 n, out vec3 t, out vec3 b) +// The cylinder's own frame, built exactly as libslic3r's project_cylindrical() builds it - including +// the handedness, which comes out left-handed for an axis of +Z. Copied rather than "corrected", so +// the preview wraps the texture the same way round as the bake. +void cylinder_frame(out vec3 up, out vec3 right, out vec3 fwd) { + up = (length(patch_axis) > 1e-8) ? normalize(patch_axis) : vec3(0.0, 0.0, 1.0); + vec3 arbitrary = (abs(up.z) < 0.9) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + right = normalize(cross(up, arbitrary)); + fwd = normalize(cross(right, up)); +} + +// The raw, millimetre-valued projection of `p` (a position in the texture frame), before the layer's +// tiling/rotation/aspect/offset - a term-for-term transcription of libslic3r's project_planar(), +// project_cylindrical() and project_spherical(). `n` is read by the planar mode only. +vec2 projection_raw(vec3 p, vec3 n) +{ + if (projection_mode == 1) { // Cylindrical: (arc length around, distance along) + vec3 up, right, fwd; + cylinder_frame(up, right, fwd); + vec3 rel = p - patch_center; + float x = dot(rel, right); + float y = dot(rel, fwd); + return vec2(atan(y, x) * sqrt(x * x + y * y), dot(rel, up)); + } + if (projection_mode == 2) { // Spherical: (longitude, latitude) * radius + vec3 rel = p - patch_center; + float radius = length(rel); + if (radius < 1e-8) + return vec2(0.0); + vec3 dir = rel / radius; + return vec2(atan(dir.y, dir.x), asin(clamp(dir.z, -1.0, 1.0))) * radius; + } + vec3 an = abs(n); // Triplanar: drop the dominant normal axis + return (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); +} + +// The two surface directions projection_raw()'s u and v run along at `p`, plus how many raw units one +// millimetre of travel along each of them covers - the factor that turns the uv-space height gradient +// into a real mm-per-mm slope. For the planar projection both axes are world axes and the factor is 1. +// Cylindrical and Spherical are arc-length parametrized, so it is 1 there too, except for the +// spherical longitude, whose circle shrinks by cos(latitude) toward the poles. Exact where the surface +// really is the cylinder/sphere the projection assumes - the same assumption libslic3r makes. +void projection_axes(vec3 p, vec3 n, out vec3 t, out vec3 b, out vec2 units_per_mm) +{ + units_per_mm = vec2(1.0, 1.0); + if (projection_mode == 1) { + vec3 up, right, fwd; + cylinder_frame(up, right, fwd); + vec3 rel = p - patch_center; + vec2 xy = vec2(dot(rel, right), dot(rel, fwd)); + float r = length(xy); + t = (r > 1e-6) ? (fwd * xy.x - right * xy.y) / r : right; // circumferential: u runs along it + b = up; // v is the distance along the axis + return; + } + if (projection_mode == 2) { + vec3 rel = p - patch_center; + float r = length(rel); + vec3 dir = (r > 1e-8) ? rel / r : vec3(0.0, 0.0, 1.0); + float c = length(dir.xy); // cos(latitude) + t = (c > 1e-6) ? vec3(-dir.y, dir.x, 0.0) / c : vec3(1.0, 0.0, 0.0); + b = cross(dir, t); // increasing latitude, unit length + units_per_mm = vec2(1.0 / max(c, 1e-3), 1.0); + return; + } vec3 an = abs(n); if (an.x >= an.y && an.x >= an.z) { // planar = p.yz t = vec3(0.0, 1.0, 0.0); @@ -78,8 +160,7 @@ void projection_axes(vec3 n, out vec3 t, out vec3 b) vec2 project_uv(vec3 p, vec3 n) { - vec3 an = abs(n); - vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + vec2 planar = projection_raw(p, n); planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; float cs = cos(rotation_rad); float sn = sin(rotation_rad); @@ -112,22 +193,99 @@ vec3 srgb_to_lab(vec3 c) // // Squared distance in Lab (CIE76) rather than the CPU's CIEDE2000: the two agree except on near-ties, // and CIEDE2000 per fragment across 64 entries is not worth its cost in a preview. -vec3 quantize_to_palette(vec3 rgb) +int nearest_palette_entry(vec3 rgb) { vec3 lab = srgb_to_lab(rgb); int best = 0; + int best_pure = -1; float bd = 1.0e20; + float bd_pure = 1.0e20; for (int i = 0; i < 64; ++i) { if (i >= palette_count) break; + if (pure_only && palette_a[i] != palette_b[i]) + continue; // a flat-colour image never takes a mix (see the bake) vec3 d = lab - palette_lab[i]; float d2 = dot(d, d); + if (palette_a[i] == palette_b[i] && d2 < bd_pure) { + bd_pure = d2; + best_pure = i; + } if (d2 < bd) { bd = d2; best = i; } } - return palette_rgb[best]; + // The same bias make_palette_quantizer() applies (PREFER_PURE_DE = 10): a mix is an interleave, so + // it is only worth taking when it beats the nearest single filament by a visible step. Without it + // this picked a mix for almost every fragment - with four filaments the palette is 4 pure entries + // against 30 mixes - while the bake picked a single filament for most of them, so the preview + // interleaved the whole wall where the bake interleaves only patches. Compared on the distances + // rather than their squares, so the threshold means the same thing as it does on the CPU (up to + // CIE76 against CIEDE2000, the approximation already noted above). + if (best_pure >= 0 && palette_a[best] != palette_b[best] && sqrt(bd_pure) - sqrt(bd) < 10.0) + best = best_pure; + return best; +} + +// One 2x2 Bayer cell, {0, 2; 3, 1}, for x and y in {0, 1}. +float bayer2(float x, float y) { return 2.0 * x + 3.0 * y - 4.0 * x * y; } + +// The colour the printer lays down at world point `pos` for palette entry `index`: its filament, or +// for a mix whichever of its two filaments this point falls on. Mirrors make_mix_resolver() on the +// CPU, floors on the band/cell size included. All the modular arithmetic is done in floats with +// mod(), which wraps negative coordinates the way the CPU's ((v % n) + n) % n does and needs no +// integer % (not available on every GLSL 1.10 target). +vec3 printed_color(int index, vec3 pos, vec3 normal, vec3 footprint) +{ + int a = palette_a[index]; + int b = palette_b[index]; + if (a < 0 || a >= filament_count || b < 0 || b >= filament_count) + return palette_rgb[index]; // no filament to resolve to: the entry's own colour + if (a == b) + return filament_rgb[a]; + float num = float(palette_num[index]); + float den = float(palette_den[index]); + // Auto: bands where the surface is steeper than ~45 degrees, the dominant filament elsewhere. + if (mix_mode == 2 && abs(normal.z) >= 0.7) + return filament_rgb[(num * 2.0 >= den) ? a : b]; + + // Pre-filter. The interleave is an ordered dither the eye is meant to blend away, and no dither + // blends when it is drawn at less than a few pixels per period - it aliases, which is what turned + // every upright wall into horizontal streaks: the Z band cycle is den * layer_height (around a + // millimetre), and every pixel of a row on a vertical wall shares one z, so each row came out as a + // 1-bit threshold of the image at that row's phase. `footprint` is mm of world position per pixel, + // so this is zoom- and resolution-correct rather than a tuned constant: where the print's own + // pattern is finer than this view can resolve, show what the print looks like from here, which is + // the entry's perceptual average. The Normal view remains where the per-facet truth lives. + float period = (mix_mode == 1) ? 2.0 * max(dither_cell, 0.01) : den * max(layer_height, 0.01); + float px = (mix_mode == 1) ? max(footprint.x, footprint.y) : footprint.z; + float sharp = clamp(period / max(4.0 * px, 1e-6) - 0.5, 0.0, 1.0); + if (sharp <= 0.0) + return palette_rgb[index]; + + vec3 picked; + if (mix_mode == 1) { + // Ordered 4x4 Bayer over floor(x / cell), floor(y / cell). The CPU's table + // 0 8 2 10 + // 12 4 14 6 + // 3 11 1 9 + // 15 7 13 5 + // is 4 * bayer2(x % 2, y % 2) + bayer2(x / 2, y / 2), which needs no array (GLSL 1.10 has + // no constant arrays). + float cell = max(dither_cell, 0.01); + float gx = mod(floor(pos.x / cell), 4.0); + float gy = mod(floor(pos.y / cell), 4.0); + float bayer = 4.0 * bayer2(mod(gx, 2.0), mod(gy, 2.0)) + bayer2(floor(gx / 2.0), floor(gy / 2.0)); + picked = filament_rgb[(num / den > (bayer + 0.5) / 16.0) ? a : b]; + } else { + // Z bands: one per band height, the band's phase in the a/b cycle picks the filament. Both + // operands are integer-valued, so the half keeps "phase < num" exact under float rounding. + float slot = floor(pos.z / max(layer_height, 0.01)); + float phase = mod(slot, den); + picked = filament_rgb[(phase < num - 0.5) ? a : b]; + } + return mix(palette_rgb[index], picked, sharp); } void main() @@ -138,6 +296,9 @@ void main() // World millimetres throughout, like the bake - see the 140 variant. vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); vec3 tex_pos = world_pos.xyz - tex_anchor; // the frame the texture is projected in, as the bake does + // World mm per pixel, for pre-filtering the interleave in printed_color(). Taken here because the + // albedo branch at the end of main() is non-uniform control flow, where derivatives are undefined. + vec3 pos_fwidth = fwidth(world_pos.xyz); if (volume_mirrored) triangle_normal = -triangle_normal; @@ -169,7 +330,8 @@ void main() triangle_normal = normalize(triangle_normal - (dHdx * R1 + dHdy * R2) / det); } else if (weight > 0.0) { vec3 t, b; - projection_axes(triangle_normal, t, b); + vec2 units_per_mm; + projection_axes(tex_pos, triangle_normal, t, b, units_per_mm); // Parallax occlusion mapping: march the view ray through the height shell and shade at the // first point where it drops below the displaced surface (see header). @@ -229,7 +391,8 @@ void main() // One uv unit is tiling_scale mm along u but tiling_scale / tex_aspect mm along v, so the v // component of the gradient carries the extra factor before being rotated back into t/b. vec2 g = vec2(dh_duv.x, dh_duv.y * tex_aspect); - vec2 slope = amplitude * vec2(g.x * cs + g.y * sn, -g.x * sn + g.y * cs); + // ...and back out of raw-projection units into millimetres along t / b; see the 140 variant. + vec2 slope = amplitude * vec2(g.x * cs + g.y * sn, -g.x * sn + g.y * cs) * units_per_mm; vec3 gradient = slope.x * t + slope.y * b; gradient -= triangle_normal * dot(triangle_normal, gradient); @@ -247,11 +410,16 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - // Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour. + // Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour - + // and, where that is a mix, the filament the interleave puts here, so the pattern that prints shows. // Only the albedo - the specular term (intensity.y) stays white - so a coloured fragment reads as // the same material under the same light, and the relief this preview exists to show is unaffected. vec3 albedo = uniform_color.rgb; if (palette_count > 0 && has_color_tex && have_uv && weight > 0.0) - albedo = quantize_to_palette(texture2D(color_tex, color_uv).rgb); + // tex_pos, not world_pos: the bake resolves the interleave in the bake frame (world + // orientation and scale about the volume's origin, see texture_displacement_bake_frame()), so + // measuring z from the bed instead shifted the band phase by the volume origin's height - a + // different filament in the same place than the bake produces. + albedo = printed_color(nearest_palette_entry(texture2D(color_tex, color_uv).rgb), tex_pos, triangle_normal, pos_fwidth); gl_FragColor = vec4(vec3(intensity.y) + albedo * intensity.x, uniform_color.a); } diff --git a/resources/shaders/110/texture_displacement_uvcheck.fs b/resources/shaders/110/texture_displacement_uvcheck.fs index 2679bc57e7..201bf38639 100644 --- a/resources/shaders/110/texture_displacement_uvcheck.fs +++ b/resources/shaders/110/texture_displacement_uvcheck.fs @@ -20,6 +20,15 @@ uniform vec3 tex_anchor; // the volume's origin in world space uniform float rotation_rad; uniform vec2 uv_offset; uniform bool use_vertex_uv; +// The in-shader projection (0 Triplanar, 1 Cylindrical, 2 Spherical) and the painted patch's frame the +// wrapping ones wrap around, in the texture frame - the same uniforms, and the same formulas, as +// texture_displacement_bump.fs, so the checker reports the projection the bake will actually use. +uniform int projection_mode; +uniform vec3 patch_center; +uniform vec3 patch_axis; +// Height map width / height, as apply_uv_transform() applies it. Without it the checker diverged from +// the bake for any non-square texture, in every in-shader projection. +uniform float tex_aspect; varying vec3 clipping_planes_dots; varying vec4 model_pos; @@ -27,14 +36,46 @@ varying vec4 world_pos; varying float distortion; varying vec2 vertex_uv; +void cylinder_frame(out vec3 up, out vec3 right, out vec3 fwd) +{ + up = (length(patch_axis) > 1e-8) ? normalize(patch_axis) : vec3(0.0, 0.0, 1.0); + vec3 arbitrary = (abs(up.z) < 0.9) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + right = normalize(cross(up, arbitrary)); + fwd = normalize(cross(right, up)); +} + +// Term for term libslic3r's project_planar() / project_cylindrical() / project_spherical(), in mm. +vec2 projection_raw(vec3 p, vec3 n) +{ + if (projection_mode == 1) { + vec3 up, right, fwd; + cylinder_frame(up, right, fwd); + vec3 rel = p - patch_center; + float x = dot(rel, right); + float y = dot(rel, fwd); + return vec2(atan(y, x) * sqrt(x * x + y * y), dot(rel, up)); + } + if (projection_mode == 2) { + vec3 rel = p - patch_center; + float radius = length(rel); + if (radius < 1e-8) + return vec2(0.0); + vec3 dir = rel / radius; + return vec2(atan(dir.y, dir.x), asin(clamp(dir.z, -1.0, 1.0))) * radius; + } + vec3 an = abs(n); + return (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); +} + vec2 project_uv(vec3 p, vec3 n) { - vec3 an = abs(n); - vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + vec2 planar = projection_raw(p, n); planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; float cs = cos(rotation_rad); float sn = sin(rotation_rad); - return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; + vec2 r = vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs); + r.y *= tex_aspect; // after the rotation, so the rotation stays a rotation rather than a shear + return r + uv_offset; } vec3 heatmap(float t) diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 8127ac35a1..baf620933a 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -87,6 +87,20 @@ uniform vec4 uniform_color; uniform vec3 palette_lab[64]; uniform vec3 palette_rgb[64]; uniform int palette_count; +uniform bool pure_only; // match against single filaments only (flat-colour image) +// How each entry prints. A pure entry is one filament (a == b); a mix interleaves filaments a and b, +// num parts of a in every den, and the print shows that interleave rather than the entry's average +// colour. The fragment resolves it exactly as GLGizmoTextureDisplacement::make_mix_resolver() does +// per triangle on the CPU, so the preview shows the pattern the bake will print. +uniform int palette_a[64]; +uniform int palette_b[64]; +uniform int palette_num[64]; +uniform int palette_den[64]; +uniform vec3 filament_rgb[16]; +uniform int filament_count; +uniform int mix_mode; // ColorMixMode: 0 Z bands, 1 XY dither, 2 auto +uniform float layer_height; // mm; one Z band per print layer +uniform float dither_cell; // mm; one XY dither cell uniform sampler2D color_tex; // the layer's colour image, sampled at the same uv as the height uniform bool has_color_tex; uniform bool volume_mirrored; @@ -114,6 +128,14 @@ uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived ta // only a uniform update uniform vec4 island_delta_lin; uniform vec2 island_delta_tr; +// Which projection to reconstruct in-shader: 0 Triplanar, 1 Cylindrical, 2 Spherical (the low three +// TextureProjectionMethod values; LSCM and ViewProjected arrive through use_vertex_uv and leave this +// at 0). The two wrapping projections wrap around the *whole painted patch*, so its centroid - and, +// for Cylindrical, its axis - are properties no single fragment can derive. They come from the CPU, +// in this same texture frame, computed with the bake's own texture_displacement_patch_frame(). +uniform int projection_mode; +uniform vec3 patch_center; +uniform vec3 patch_axis; in vec3 clipping_planes_dots; in vec4 model_pos; @@ -124,11 +146,71 @@ in vec2 vertex_uv; out vec4 out_color; -// The two world-space axes the triplanar planar coordinate is read off, per dominant normal -// component - same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y -// along b. -void projection_axes(vec3 n, out vec3 t, out vec3 b) +// The cylinder's own frame, built exactly as libslic3r's project_cylindrical() builds it - including +// the handedness, which comes out left-handed for an axis of +Z. Copied rather than "corrected", so +// the preview wraps the texture the same way round as the bake. +void cylinder_frame(out vec3 up, out vec3 right, out vec3 fwd) { + up = (length(patch_axis) > 1e-8) ? normalize(patch_axis) : vec3(0.0, 0.0, 1.0); + vec3 arbitrary = (abs(up.z) < 0.9) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + right = normalize(cross(up, arbitrary)); + fwd = normalize(cross(right, up)); +} + +// The raw, millimetre-valued projection of `p` (a position in the texture frame), before the layer's +// tiling/rotation/aspect/offset - a term-for-term transcription of libslic3r's project_planar(), +// project_cylindrical() and project_spherical(). `n` is read by the planar mode only. +vec2 projection_raw(vec3 p, vec3 n) +{ + if (projection_mode == 1) { // Cylindrical: (arc length around, distance along) + vec3 up, right, fwd; + cylinder_frame(up, right, fwd); + vec3 rel = p - patch_center; + float x = dot(rel, right); + float y = dot(rel, fwd); + return vec2(atan(y, x) * sqrt(x * x + y * y), dot(rel, up)); + } + if (projection_mode == 2) { // Spherical: (longitude, latitude) * radius + vec3 rel = p - patch_center; + float radius = length(rel); + if (radius < 1e-8) + return vec2(0.0); + vec3 dir = rel / radius; + return vec2(atan(dir.y, dir.x), asin(clamp(dir.z, -1.0, 1.0))) * radius; + } + vec3 an = abs(n); // Triplanar: drop the dominant normal axis + return (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); +} + +// The two surface directions projection_raw()'s u and v run along at `p`, plus how many raw units one +// millimetre of travel along each of them covers - the factor that turns the uv-space height gradient +// into a real mm-per-mm slope. For the planar projection both axes are world axes and the factor is 1. +// Cylindrical and Spherical are arc-length parametrized, so it is 1 there too, except for the +// spherical longitude, whose circle shrinks by cos(latitude) toward the poles. Exact where the surface +// really is the cylinder/sphere the projection assumes - the same assumption libslic3r makes. +void projection_axes(vec3 p, vec3 n, out vec3 t, out vec3 b, out vec2 units_per_mm) +{ + units_per_mm = vec2(1.0, 1.0); + if (projection_mode == 1) { + vec3 up, right, fwd; + cylinder_frame(up, right, fwd); + vec3 rel = p - patch_center; + vec2 xy = vec2(dot(rel, right), dot(rel, fwd)); + float r = length(xy); + t = (r > 1e-6) ? (fwd * xy.x - right * xy.y) / r : right; // circumferential: u runs along it + b = up; // v is the distance along the axis + return; + } + if (projection_mode == 2) { + vec3 rel = p - patch_center; + float r = length(rel); + vec3 dir = (r > 1e-8) ? rel / r : vec3(0.0, 0.0, 1.0); + float c = length(dir.xy); // cos(latitude) + t = (c > 1e-6) ? vec3(-dir.y, dir.x, 0.0) / c : vec3(1.0, 0.0, 0.0); + b = cross(dir, t); // increasing latitude, unit length + units_per_mm = vec2(1.0 / max(c, 1e-3), 1.0); + return; + } vec3 an = abs(n); if (an.x >= an.y && an.x >= an.z) { // planar = p.yz t = vec3(0.0, 1.0, 0.0); @@ -144,8 +226,7 @@ void projection_axes(vec3 n, out vec3 t, out vec3 b) vec2 project_uv(vec3 p, vec3 n) { - vec3 an = abs(n); - vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + vec2 planar = projection_raw(p, n); planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; float cs = cos(rotation_rad); float sn = sin(rotation_rad); @@ -178,22 +259,99 @@ vec3 srgb_to_lab(vec3 c) // // Squared distance in Lab (CIE76) rather than the CPU's CIEDE2000: the two agree except on near-ties, // and CIEDE2000 per fragment across 64 entries is not worth its cost in a preview. -vec3 quantize_to_palette(vec3 rgb) +int nearest_palette_entry(vec3 rgb) { vec3 lab = srgb_to_lab(rgb); int best = 0; + int best_pure = -1; float bd = 1.0e20; + float bd_pure = 1.0e20; for (int i = 0; i < 64; ++i) { if (i >= palette_count) break; + if (pure_only && palette_a[i] != palette_b[i]) + continue; // a flat-colour image never takes a mix (see the bake) vec3 d = lab - palette_lab[i]; float d2 = dot(d, d); + if (palette_a[i] == palette_b[i] && d2 < bd_pure) { + bd_pure = d2; + best_pure = i; + } if (d2 < bd) { bd = d2; best = i; } } - return palette_rgb[best]; + // The same bias make_palette_quantizer() applies (PREFER_PURE_DE = 10): a mix is an interleave, so + // it is only worth taking when it beats the nearest single filament by a visible step. Without it + // this picked a mix for almost every fragment - with four filaments the palette is 4 pure entries + // against 30 mixes - while the bake picked a single filament for most of them, so the preview + // interleaved the whole wall where the bake interleaves only patches. Compared on the distances + // rather than their squares, so the threshold means the same thing as it does on the CPU (up to + // CIE76 against CIEDE2000, the approximation already noted above). + if (best_pure >= 0 && palette_a[best] != palette_b[best] && sqrt(bd_pure) - sqrt(bd) < 10.0) + best = best_pure; + return best; +} + +// One 2x2 Bayer cell, {0, 2; 3, 1}, for x and y in {0, 1}. +float bayer2(float x, float y) { return 2.0 * x + 3.0 * y - 4.0 * x * y; } + +// The colour the printer lays down at world point `pos` for palette entry `index`: its filament, or +// for a mix whichever of its two filaments this point falls on. Mirrors make_mix_resolver() on the +// CPU, floors on the band/cell size included. All the modular arithmetic is done in floats with +// mod(), which wraps negative coordinates the way the CPU's ((v % n) + n) % n does and needs no +// integer % (not available on every GLSL 1.10 target). +vec3 printed_color(int index, vec3 pos, vec3 normal, vec3 footprint) +{ + int a = palette_a[index]; + int b = palette_b[index]; + if (a < 0 || a >= filament_count || b < 0 || b >= filament_count) + return palette_rgb[index]; // no filament to resolve to: the entry's own colour + if (a == b) + return filament_rgb[a]; + float num = float(palette_num[index]); + float den = float(palette_den[index]); + // Auto: bands where the surface is steeper than ~45 degrees, the dominant filament elsewhere. + if (mix_mode == 2 && abs(normal.z) >= 0.7) + return filament_rgb[(num * 2.0 >= den) ? a : b]; + + // Pre-filter. The interleave is an ordered dither the eye is meant to blend away, and no dither + // blends when it is drawn at less than a few pixels per period - it aliases, which is what turned + // every upright wall into horizontal streaks: the Z band cycle is den * layer_height (around a + // millimetre), and every pixel of a row on a vertical wall shares one z, so each row came out as a + // 1-bit threshold of the image at that row's phase. `footprint` is mm of world position per pixel, + // so this is zoom- and resolution-correct rather than a tuned constant: where the print's own + // pattern is finer than this view can resolve, show what the print looks like from here, which is + // the entry's perceptual average. The Normal view remains where the per-facet truth lives. + float period = (mix_mode == 1) ? 2.0 * max(dither_cell, 0.01) : den * max(layer_height, 0.01); + float px = (mix_mode == 1) ? max(footprint.x, footprint.y) : footprint.z; + float sharp = clamp(period / max(4.0 * px, 1e-6) - 0.5, 0.0, 1.0); + if (sharp <= 0.0) + return palette_rgb[index]; + + vec3 picked; + if (mix_mode == 1) { + // Ordered 4x4 Bayer over floor(x / cell), floor(y / cell). The CPU's table + // 0 8 2 10 + // 12 4 14 6 + // 3 11 1 9 + // 15 7 13 5 + // is 4 * bayer2(x % 2, y % 2) + bayer2(x / 2, y / 2), which needs no array (GLSL 1.10 has + // no constant arrays). + float cell = max(dither_cell, 0.01); + float gx = mod(floor(pos.x / cell), 4.0); + float gy = mod(floor(pos.y / cell), 4.0); + float bayer = 4.0 * bayer2(mod(gx, 2.0), mod(gy, 2.0)) + bayer2(floor(gx / 2.0), floor(gy / 2.0)); + picked = filament_rgb[(num / den > (bayer + 0.5) / 16.0) ? a : b]; + } else { + // Z bands: one per band height, the band's phase in the a/b cycle picks the filament. Both + // operands are integer-valued, so the half keeps "phase < num" exact under float rounding. + float slot = floor(pos.z / max(layer_height, 0.01)); + float phase = mod(slot, den); + picked = filament_rgb[(phase < num - 0.5) ? a : b]; + } + return mix(palette_rgb[index], picked, sharp); } void main() @@ -206,6 +364,9 @@ void main() // world position and perturb the world normal. vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); vec3 tex_pos = world_pos.xyz - tex_anchor; // the frame the texture is projected in, as the bake does + // World mm per pixel, for pre-filtering the interleave in printed_color(). Taken here because the + // albedo branch at the end of main() is non-uniform control flow, where derivatives are undefined. + vec3 pos_fwidth = fwidth(world_pos.xyz); if (volume_mirrored) triangle_normal = -triangle_normal; @@ -250,7 +411,8 @@ void main() // normal component (see header). The gradient is expressed analytically because there is a // closed-form uv here, unlike the LSCM case. vec3 t, b; - projection_axes(triangle_normal, t, b); + vec2 units_per_mm; + projection_axes(tex_pos, triangle_normal, t, b, units_per_mm); // Parallax occlusion mapping: march the view ray through the height shell and shade at the // first point where it drops below the displaced surface (see header). @@ -315,7 +477,10 @@ void main() // One uv unit is tiling_scale mm along u but tiling_scale / tex_aspect mm along v, so the v // component of the gradient carries the extra factor before being rotated back into t/b. vec2 g = vec2(dh_duv.x, dh_duv.y * tex_aspect); - vec2 slope = amplitude * vec2(g.x * cs + g.y * sn, -g.x * sn + g.y * cs); + // ...and back out of raw-projection units into millimetres of travel along t / b, which is a + // no-op except for the spherical longitude (see projection_axes()). After the inverse rotation, + // because units_per_mm is expressed in the t/b frame rather than in uv. + vec2 slope = amplitude * vec2(g.x * cs + g.y * sn, -g.x * sn + g.y * cs) * units_per_mm; vec3 gradient = slope.x * t + slope.y * b; gradient -= triangle_normal * dot(triangle_normal, gradient); @@ -333,11 +498,16 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - // Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour. + // Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour - + // and, where that is a mix, the filament the interleave puts here, so the pattern that prints shows. // Only the albedo - the specular term (intensity.y) stays white - so a coloured fragment reads as // the same material under the same light, and the relief this preview exists to show is unaffected. vec3 albedo = uniform_color.rgb; if (palette_count > 0 && has_color_tex && have_uv && weight > 0.0) - albedo = quantize_to_palette(texture(color_tex, color_uv).rgb); + // tex_pos, not world_pos: the bake resolves the interleave in the bake frame (world + // orientation and scale about the volume's origin, see texture_displacement_bake_frame()), so + // measuring z from the bed instead shifted the band phase by the volume origin's height - a + // different filament in the same place than the bake produces. + albedo = printed_color(nearest_palette_entry(texture(color_tex, color_uv).rgb), tex_pos, triangle_normal, pos_fwidth); out_color = vec4(vec3(intensity.y) + albedo * intensity.x, uniform_color.a); } diff --git a/resources/shaders/140/texture_displacement_uvcheck.fs b/resources/shaders/140/texture_displacement_uvcheck.fs index 038c915f3f..583346aec0 100644 --- a/resources/shaders/140/texture_displacement_uvcheck.fs +++ b/resources/shaders/140/texture_displacement_uvcheck.fs @@ -29,6 +29,15 @@ uniform vec3 tex_anchor; // the volume's origin in world space uniform float rotation_rad; uniform vec2 uv_offset; uniform bool use_vertex_uv; +// The in-shader projection (0 Triplanar, 1 Cylindrical, 2 Spherical) and the painted patch's frame the +// wrapping ones wrap around, in the texture frame - the same uniforms, and the same formulas, as +// texture_displacement_bump.fs, so the checker reports the projection the bake will actually use. +uniform int projection_mode; +uniform vec3 patch_center; +uniform vec3 patch_axis; +// Height map width / height, as apply_uv_transform() applies it. Without it the checker diverged from +// the bake for any non-square texture, in every in-shader projection. +uniform float tex_aspect; in vec3 clipping_planes_dots; in vec4 model_pos; @@ -38,14 +47,46 @@ in vec2 vertex_uv; out vec4 out_color; +void cylinder_frame(out vec3 up, out vec3 right, out vec3 fwd) +{ + up = (length(patch_axis) > 1e-8) ? normalize(patch_axis) : vec3(0.0, 0.0, 1.0); + vec3 arbitrary = (abs(up.z) < 0.9) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + right = normalize(cross(up, arbitrary)); + fwd = normalize(cross(right, up)); +} + +// Term for term libslic3r's project_planar() / project_cylindrical() / project_spherical(), in mm. +vec2 projection_raw(vec3 p, vec3 n) +{ + if (projection_mode == 1) { + vec3 up, right, fwd; + cylinder_frame(up, right, fwd); + vec3 rel = p - patch_center; + float x = dot(rel, right); + float y = dot(rel, fwd); + return vec2(atan(y, x) * sqrt(x * x + y * y), dot(rel, up)); + } + if (projection_mode == 2) { + vec3 rel = p - patch_center; + float radius = length(rel); + if (radius < 1e-8) + return vec2(0.0); + vec3 dir = rel / radius; + return vec2(atan(dir.y, dir.x), asin(clamp(dir.z, -1.0, 1.0))) * radius; + } + vec3 an = abs(n); + return (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); +} + vec2 project_uv(vec3 p, vec3 n) { - vec3 an = abs(n); - vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy); + vec2 planar = projection_raw(p, n); planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0; float cs = cos(rotation_rad); float sn = sin(rotation_rad); - return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset; + vec2 r = vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs); + r.y *= tex_aspect; // after the rotation, so the rotation stays a rotation rather than a shear + return r + uv_offset; } // Blue -> cyan -> green -> yellow -> red over t in [0,1]. diff --git a/resources/textures/displacement/Bark.png b/resources/textures/displacement/Bark.png new file mode 100644 index 0000000000000000000000000000000000000000..a48233d01ebbeed34bce051d0173e5b5ca9e6a56 GIT binary patch literal 317344 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�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�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νzV)0uGQXS$)qW*#!AeV4D@prW&kX)&o#E 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

cByh+{s`k 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%0DBfF*i_Kp$Ri4?j?Upr7evxK1VY`K*1 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 z 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%0ALl9OdEW0ML&7!OM={utT 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ཿWRY52z{eLZjU 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୐ z@w6kEZfmnfxyqL;R-^BpDn*0q@q3ojZ9?uYHcb8TlaeFh#_UfQjD~$)4{ZHA9ccxY z*`X?ceIJ9mKD7v8(cT&8)^T}IvObX^B`IYTZrk?)ab7w8?e@OdKt~Wmy`~|HUk%;0X3<*m)Aot&<*W2*+mUDdoI?};dmtVRZ@S7!Z--BVI0WT!B{^CA`q z@!Hl5@U*fnaPVvJF^5&W9`S}|iF(JoT@_&gk>n(Xk1G`{pI?}QOXhl4(UkrtDVHqj z6bsP>zkDli>;yL7j&y2#*j0~s{5=>`d7)3Colke(mWb*{e>Uzd7hyZ66`}&TZD9er zSW55wRMkQ8HSPWM&bw9{|D}lp7eYA6?c@1+Zu$*sfT6An1Q$6@`F6&Y^YyM2#p|Nc z1i30qz|Oq<<;KakABbfeSak#U#B01K=ZNLfscN(2HjBS#mP)6bX0Pjm302f!=U)h5 z$>ZLP+7pxcI%yNZ>Ue{3rHZZ}l~^4D1*RV$(Zg$RQWp(?*?nEBiGZ>dyC3jexnxnM z(@a`@{x(bUL#2cXo2rXCvI2cjq7sA?tm|KC+%LjlTk0noUK)FwS>gTt{q#-l-X9{b zEO0+h9Ol&@X-(zHks51}fsKO148PYFqe3AcI(+9t|G7g_PRRle{`1!H{0bet?8{h6 zEVk)f2pU zUjmExnGYLD1EX1*U%guSn+JBbVibcR#G&kpG?KH3TYo}Bn>f9_q4K^cNv*MGWw>Tr zZwuf91FCA#bDdKDK6=ORJdI%3%ZFC_q5W^l>Ys{ciAxJv<0QNwdcs07L~RvPi@Ze_ zui@ZMTR#1dCA1b=>Eqv>I|j1)x)j%BXnZJdz95CpGcu7!=7pFSMt*?!!vAzUux(NC znBhgt=0NRTAX-=_dTIVe%cGObtCbQpVZYY&Dfzq8@jr4IVg)6FJ&DR78Gl5ooUv4? zas-XKGKjYH$QZA8S8> zj@f2X-DY4ZK0R+CrFu0xOTf`%8KokQE`LuFgXvzOI5Ri*LKWGf(O6ObL+dRJwht^e z!~CQqVGT9axkThGJCi;Z%U_UW^D5+uu>gX#K35?oE5#c-?&!n~A>fb~pKZS>faYx# za6=edVhA)C4ux68>>jEoqHR%3JP>ok3Wa8!{yS-cK!Tq;s*1tMJ&nnc2Bd-Bt$z$e z^qI!QJY20({fIQv@aVK53rHMwoj`q__n6(E5on>xs7DzOE0kow-QN>EeCub~S!R6z z4E{BOFSuf9dy9tY^<T`<+o?i0c{I&R#=!R;l7Fm5eEnqdDpU&Ei}Kx2nMMs&UvbP zvJqA>R3l=z7U);B)ENp(%Nc>P)qK!X6k8yia!*1tI+zwQV96EDa| z^RLz|Il~fi+zbs5t?R2{oh0X46%~tj^HT|^@*TX0rM(bVvVSPh3WH&Y{6Vq=99C6f zu|Knhw@-VA9a~aHwbONCe~BG#Vq_7&HeQ+jGBBSdVP384%P@uSU+Juq zUeNwhIgh1VIVQhvj28q*<3asV{UPDVO|*+w9zqCp?TKALohjG?@MVd|nh9fNCsZ1d zfzB6rr>R@#js{XQDv;EcVo$Ut=Gy_Md49?&F%dzLH`^>$H+3y_guwO2}^~q5v@* z<0Vl^K7UEhZe2O$1OUz8;QGQjpTYY%lb&wQK&|+KN@pFn11Nsnh?#PfSn=shunCt= zdSEQOvC_GZk#HohEu;t?O{g`U(?0gH9pNf7btCazJp4>-UeQU{aS0r}M}{i;bQoZH zsLCf~&`>OWu@~fX9M-GzIClXE2)|}A8v}@$V@`M10Ks)qRiaM)Oe#^)2o=wB)F1@n z)1d$eG|;Yg8>J|jf}4vtNEltr7A8O|Np$hXo!1g2<_#6kLGDO#dc@6RhB0*6crEUk ze__?Cg&;X_n?5Pegx~q5c)g7h)%c)?e#4E)3M+Vz|6eYdGBfbr2L$*bVWF`K*9|Uo zRFHil5$Lsw7w5am2o_8&&O&3ou&Tt|e~$K414P|Hjd3?%a=eSoD&MvBu7cO-3HK-* z`OqadhiMi)Gu#FP*m`UXf|GGKK1$u`k8KocVTWbWHa)H!rz$!wa#t$qWILo7CqF$I zQ`~D43{lXlFl@9AdQzTD_UiCzzYQ#OicZIxF5j45siaLtBrYufTth!AXB{3lS~Q;S z*8GNG=IcvIt4s3+a=%NX?!mq_sP2)6<0S~=sV!CM2i_)*OqgqSLUkrojZfMBI<_7~ zl05-(D#D~{B9Q!uNcyrqYS09=sM7bpt&2S8C-Yzw09cFNs3eyD_FHHv6oa!fbbLPo z6L%Bthf~^fzO^$#x8Gj>d^-Q%%Y7uBiBs&3u&x0X(C`9j=2d}JLol*0G%UtV#niQV zW_UsueZ+6gYAApr!xBvu0#9H6A|cLX%t+&XVG^X1dYbG9(A8)bnQNq=LRzrAf|hyC zn=PWSObr2HvbAP7z`tMfzi-SNKqYTY{4G{fa9Rfz7Us((%eYA!u9NC}ot&)_y zjt}&)i^UOV+cMc|>B+DN@pMZtPpqcBE7ia&CJ!eeJ-z?QxnP%=>!~ z)00-3ohc5bfuSI^>wTqPSTiyN;I0aH-Nb3SGsw9vj6iBra^`u&d|PM-3KheTu}0Nb z_^w187urDC#lG`dqjN{A_t$bhVjqg0o06J(t`#MfCHC@imO;Aq$bB{Q5mbP&pOi^; z`X-t_Lq4+0VbD>E%UI5qHPD%&$KTfQWrbU_Ic@FOlw!#}z7^KB+^aV#Bf%FzMB})x zA!w~Ny5`KB%BO%5{1qZ_yD~fIIrRpocArB97E1B$_WQYA66&Q1@SZj0R7#fOWuY0P zpS5?wcWYV3soI$%)o(wkN98n~sPElicJ$Q8U_UHoC}Dc}8xK#!R?8u5muoPb^S_r^ z)~|-;xok2)N+$(qZ8dYu;aKd0rf2-Agm;r7^NpbY4t*Gne4Q6enXD^LeVQLjtT|i* zed|-dwsq@ep6T<-GM7w#O{Y|u#uC}*MQmjdGyfWXs{(%qX!4`)u{_?TsNzWpSG6b9 z+V$GuZtsn*N$QKF(Df8Fc9z^Y6;9H%WOLL?<`@&@T_?`yK?8B`E9i0+OXq~V8-2P% zl0NuoNs~2ce-cahqNKdi_9vHwn%iN=HWffm(GdQDw(I`pr2EO_w!a~AA&u#zcYpqV zhWGZP-?x#BneO|VC4Y&%Z}~oUVJAAYzW4j_W>kjP!ZTW4=dRF)s8Nx=g4u@d6FYBN z|Jg)ONaW&sY6=P>K!!MkVNit1%VqwOHOgh|dQs?ZL0&Uj2+SU|S3LE|M|1y1_R}SO z*74sL9}B|>oE3W8RPn#m+ks8b`0xDZ`^cgCBxi`AX|qi=pnhJ^nnYLdOS()(_`mc^Qq)0l ze=0YU;Mg2WrbWWe*hHdg-0Th9j-y?G1KCm%xV$j zfE?|^t*8NIAIzC6%k@WR4>FZZJV@{NW%OGd9Qp?bwc1q2cKC+v!vODpn<-Oh5q|F^ zkDgJP9&hrSum)5K%%z0jL9S`N(*wahV{2gr55wkCFZeK!lPy49P4G?z!}Un{*! zGx*8)lDpo0wQaVO1zm4n$*DS6?wCT|L{7&2tItN+pp}>+h7?i|4Tw(`rRK@lJzw%s z&zCF-G;J!$PD!IznF{c8vlo&Wmt$Lr$woODzadV)^(8@UA}9hkqbpODf~Ve8~=msDhnon@>F zj>EfVa!Naz=`Oz>QN|#M7IZ7tvOWfzYa+_EA`kMJp3^~#ZlQc0Lt#M20Fg8V^QW_KhExGxzw!V1QG`4VZ+`;L2 zTy_TEe%VN?v;E)DdX55$)5X)$NC+26@*6zd&BT7hl7}662SVnDlWW`9zX=iMGouPW zz7!Cl9S|V#-6>t&dCG`&!=^#ed+1~$f$VWc`#+ywOE|q~dV1r5Pr%R_2@n+oXsLdy zTzY9DFDl`a5P2$4hMU>`u!){1FZ?_D{hg1e2r9K^_PBiW9beSuBOxj^ z+m8$-2r`#h5E?UHg2X;k@bl7|V$4uj2qHQzX}SMEu^{o^Lf=79_TFVRKn zQdr#>vcJOr)}c2WDq0C60yW7%RsdXDlImSbuL5a(W>WpqC$VO+m#&$0vew$^X0>`W zj8f5-x$xUoxj~)Dg6Jj7Hk;}RBK{OV)b~aCArsn<_)*g}Wn@t@%*hx7*TTA?fmejOc5V_@y~|5!Tj zcq-rjkDp^_6v+r>6mo2lkyTdqI_JodZ0Z;xBg9c;kL(@c7-!grA|tDk5Dp?EBRL4! z>zv>1^Zh*@=g<3`bD#U%*L7d-_w{-`UtY@8x->OGT=|^(tn5yFJ}N-|(tm!iAMOh0 z@ekp65HA@4<1pV}P)b^u(5bc^Q+Bv4LaKN9^0h7#Ep?Z2k+n?jRe(Ahn~z!iKl;^5 z`Ue|#l#}oKmttrr^u?JLWsf+58%Px!b@vdN%?-rnQNg=Ug85HmnQObDru3_iCYiUL zzqt^T`xc~`)8q|F9vLDR!o4h{B@auXUuYwio7r}V&{N}rjdsj`wNT$&CWhDcV=BI; zI44eF**PDLC)PayBA26Ma4(3@ah>w|s@ z2DtQX-^*(DSzldj@lY0Re5TV>(&>b;#Zx#CZ$|HJi54Q&R-xgk7yUt+F+5hv$bgrx zEnwy$PtP5D0sELsRpFChp z%C2vp=_)T%8una!a;)!+0#1MjQdwV9pT@;>d>%yrThuL{E$3|db{2ntm_>6W#N0FV zU9@9{+2j^XwIlUSky&_`l?QxflfUYGGj=gc>32c8i5TErfW&*F3j~EW(IFnA`OrV> z0If7*v(3d9Y=R$y7If}Ok=KzN=KM15xT@-aD+km~h_60tP2b{GtjC-|NpRgL(s=Kk z`IT@9*P&|6-7hS)rKyI=qZ-w9lfjueW`ix zZ`14~(U-1@2lUk5y*Gfpl@y}B*yyDrll%W9tY&q1c#_1bzHo*C3hD^}Yum20#2&q; zN{6y^{;AB2;LvIiw$!E(Tiy_}w;hK?L*y&v3MhG)-Gd`~fnw50xhsrFHQ!NqcV@du zJsj{|23Ee=1cKAgsLg|(d$2+jXz9k{?e%Z9B26w*aTZz9s51p64P^&l-j*84oH;yW z0iLAbCZKzqYU8)N@FK}YOh+#RhZf4dP@|B>`t)HN7u99A#5jmS2Cff~exFI5H=3L_ z6gvj*&ZQ>15Dx}(G3-=A*LvGNq{z5Eh1;>b$kHFaOz~{-1O(2fKlc|_1-+Fs>n;YL z06gq(Af?|fhpr4~j0hlqPu<`pkje9Akb=0-MFAAN`<@zYDKuheU%p~0&_P7T;)z{m z=;6bQ+V6_S6qfm@g|5lul{ZgoPwaooAua3ISV#$k)CN=ZXtZ@1WW~v1%fM$}G)nLf zfElY1JzJ)|&VDyABJE)dmkM5O0GRWpY9Gj-fp5H|RD@J;S>GcH~Rt5ZNpJpcsQfQ)x`5w-uX zNACU?^lv)B-Wzx#g8V&*X|De#gVTT`W>zc$j~UDFhnb7JVlc$@0A{OQ{mZz;2j@&} zN(EnxMF>}&7rTHCBQWp%cQH_?Q3i()0UYdw4K}(91$@KQo(1x_fvvbV9f@B&$LA0r#a>-QOwJ-7NfT1vCO79xtYZTDEkjD&R5f7Tetw*0UbOxAvR=~Ho!~luyRO6FRZG#7>QAQg}cNUp^ zJ=kRd(v1+u(dwgPd<}4ef+#y0?L1LKi7sOl;gr4>r2kau!!3eC%oqcG#${$Y?j*MB z4V|n`5aq0;mM&H~R^dypwekAsqks_pPoywDy;DE#ve*h-x8XMEhBq@9bWJNp7M+^q zbog7wXcm!7f*LA^LZ@o^^(xh|d(XJbdM1#+2P)ZA7t}u48)Q@-E(Jz8JUpPRZkbZH z50$Qi^vQvML~Pww!XChS&n8XdV(4M%bFvIt45i0~K0Gi5smI@A5C+DF zP3L5vbA>;nr$q4*ER z;lPjr3v<$@zai<5s#AK>FY1*L5?1!bY8T5Ux7#dA# zmy-ef`dZKI+a3T4Dh)(a@5c8qFc;zDjzx+M;Jaf^8E^1w2m4)d=3FUBsmWE45i_*3 zlIW0-$lJfpsXBFbb_t-4R?1V$6PN`*7E`SvMvyMnrCA)}(ov~mlsiDLykxtV-mAS5 z5-$tNj<~T4Xf4~s{Wrix?_tjbAaZp-%9krwN|1>a3S;KCIN2FY%I$0uwt&6aWYp2t zN91+ufyiN)dD41WYup%hU0$j71)X)nX){GphgB@2?ZJ4=z2h!xNlnf#VNMFExYy>E zYDrU`+o7;|jKR3r$JPp4$MZ(vEOeErC73_Q4y-9KC^KJsEe}(8P6S7|8+vB#r0FpS zDFss4qYcLZ$>wB?!Dw$7x3%B7x;uYwV`~BbD?NGT$XmJiuNvd$>tEtd_p0i=T5-r_ zjX{C|7k=VMS!60BCJ7wpK`yDUb{tVltt&kh|H`&&4VJ{$J=suP$F_MkwH9JZj^AP% z%HcsbGLMYiYal;LRUAfr*@YBW*%mf?ma<7k#S54NWpRIv0?Gn;Ib$;Lwp;;JCB07& z4HZt*V>yoA1&#vdEn14-d%I+h1ABSK&rX0Ob#T2=SziG!_b;|Qtxwv&M4&F%Jja=d zu{7NRBTJ{-t$RzXmsQ<1*&|S&@e!R_QYfl@+L_ULieRnN%zxrcv>ufbON9MMf~8cj z0YFiBXTi8_!wXzX@Z8p)5jZWNSd zAtmVe$Ym43V8$O4H<04ZI0J2(GCeJ8oTh`@ru<%mwN!xI@c%wj(IfNr zgbiO`JJ;j?Dv2-a(fqO6$Vg@GTBLH4?ep-DsCPj+f%*&E^E++%=Hsa$XgM|oqo6N( z1OJhU?R|{zPt0a58}DgBg^;o;^xlA6xINFJr5tJ5)OQFME{>};siXQsh*etruy`#C z+C$qNz|12OX{sYERLtjBt1AYee}OA_NuOxRFpznK;jOtN^Y$ePtwQhO+y|+>p`f~n{|-$%F->k z_(yZ<{k|s6WXBpE*Dz} zzr77dDl;t0qHcyttHVi1>0`)kTGyzDItF71v9`Vhf?{}K4o8?E=?m3;wQwWM@`uGG zrOZ@z*=td(&o++n<^B@orkJ$PlD5ML1K_VxT@^7X$X4((-jmj*r7P<2WW?q$jJ(;d z<6HVHjOOHK)0)sn90zA^{1OHBA%AR|(wvgzhOa0*csY%gr%6ZCekywr#~=sy<$ByxkLXIaKCHuAJM!LB;z zuH~t_I}2mwOdDVPPtjMaLVS88g)qPUKfphoh=SPP{!uw{rO7+-yOcO=5N2yt!{3X# zn~9D5)Wq>s#-BhHWYxuI!G;|kkv7-tW_5b^>W?w#r&*>YvSbXABuG<0#i4&8 z4=1xQxH;$QUl-2SdPmiwaT$XE-(~QbU2vw_FXlN(tWv4bg<>Ufsh^){^x2jkxtm*S zxiiW)jX9~*g~8`fgbWvgRg)%om+!VWb^?pDUXAr@1&&JtoHNDi@V5vxOv_>@_cS?o}Bd93(ZIP1P`i`)0VFD;V{ww~a(6nbUaFr-as|Cpo8wOw zc){*uq#ujytIb5*GzV!i%X(Z&OqG6iCj0{=8v%?Ig!`}PBrBy0W_;*5ZSb$nWobnC zB_pl1DOSDm%+EYRGn;>%i758`a^%+`OdMt~U6fO!bm?FFNlNX%&$KolBQLv4RPq_x zy%P6FT&bV*`d)zZCVl<;Cazj`ClMAvzb0V}Th?fcYKOo|G{4ZUI)~zqtTSdP10Lxb zqD(cpfcR3hSEF?{mWRyqbgpz6GetFxsNYK-+(Oxdk2YwgrkPZ`ZJ+gD^t4vb}$mfpNy{d@0d_^xX@~yks~FaaAgJX1NP--EAOHnouksX*MZf28jktGc(jRK#<#ToYej`Oh!UEdcZ3uO#Fg*Cb^zb&nS1#lX20V9 zo$k6?wC2}7+J)7+6EFyDo`?vUhb+o3V8uDaP!+&C^!_59K8;>&_{RW}(qc39yKe48 zTh9rkn|g}+?o4ICEETyH@y*r;R9d!-K{lSZp}jVS)nY`%Is-TyVa6oW9fgl;iu=`CY0)J{1@IpLaWutg*7E!OY`|Xw+ghUQhLkL9 zBy_CvmjtTa1hc*6bv10v?nX!T@ck-W0E~+zV7`{MEDLa6Wf$ibN&MS$Y9d`crc~uM* z$n;&KEpsg4qlu@?{#%&GR5^5P`H09{?q3!0sWa&KnT zG~J$8+8lhQ?yxhlxSHCR*Q8!*ou} zc3IZ?h7CoA)!}>Z#_}$O(W9h2_CY*P$Hww;i&>aR5aoqsn}P6>KTa!0k|3tDu%NYT zs4uWwmCrOpAJUpc>n%7MI-kPtqi~|osr7pG2?aA*^k}2Os=WkCH|GUDI{4kUo50X# z!tOM&nqLi!0f2l&dG*l77!{q<98;=TCx#N#PXa{C{5zYDHt(ugsN2zM7*!&%I5mlX zlcOiUnZ_xH{%8_6)~)KXEys0)|Ic>8Ie5r7#Ht>!8snhspjzMM`erA=;<1adfE}@% zD7~;Dcjpa|Hj5?m)?7nz@^7Y<$7lGZR0}fO5ROepN|Ha@V(pc%)dr+09EpY*b;{$x z5T=A>?WRviCFo!eoNCrsY_4pq1t5*>h>Gbd#uN8vzG#j-^7n;<_u~R#|BHau09`^& zPZt*-vA^OoX6smB+pK2z+FK-NFo?f=`{(of4h%15e*kLV*5KBFg97|ynkzauwRs~B z@`C7$ljnPj^v~9u)>C{pgWEE;>v|6>>2!}MI;|?%!xsPJKCh=CXgcE*;)bqU+pS^) zz@C=CKWs{AO&{9u3V5ueH#C{2(T4llqARZ3=y!gXzX?cZCV7Ov-Rm7y zduDTL4AXce#s|^bl?oM zGR4U&bj+-2Bk#(i6xi)^2gS&q_(@@J$0!wzFkS9nW6C0e(%!v=q1r2vuk32tpWEKw zyL4$G(Ba)}D>g84CAE`dL)(AAH0QUh;UgT48R zJA*>5y}y$5^M?~z^X-DOnwXOc{dCwZ?eK48%7=W9g%ez?$x>4?Ae#l7+I4BelD+S~ z=Q!S%itym|c{tN4S`zq%Wa`JX(2{GvJ%a?X=vfv4Cl6q|sAFoMc-FnTVX)AhS*;MY zw_SIaJlz#%`kgo-`7yM*K}zF}X1Hxwc|Q15I>3&0S@hevIgM>9nVDJr$t`}SNmlSE zl{f9F#>qc;6n!esW_zfW|3KfbQHO{-Q2a1SB^L7B7Z^+2lm@MYm&(Bfrnrg@(bPJW zHok59?KP!LLT)#Y#g&AFb1Ex5^AoZ2zD3hD4=(NpTEZTEhrEma7kZWNlNB|CW=4w7 zi$fOH=h8qwt^8v;mi*Lxos;gVV83+d|=@%V2 z*ctKHOqxdF3$_D}+ch-p<*Af4#y{3KTc||Tzogv*;lYb(;Dt50_wIZArxhJ2OWXPj zC~CGQHsBG6JkeUJ<`dhTJv`ifDth-(0}$(-zmZQibCZ!eZTsb(Pelm~pA4>UWaRKBGC?_*sQ=cLmo75O^# zQsyQ0HSpOOq>%-?E1-|U&7AzaAM z>(2Z?bToF)f8VJ8HTQHKdp`LuKNKsz@@@v&-U64B75f$y+^lZAl%3*thp$1uUAW+T z`L+BoYE{5$KP%Pte6;@YT$a!AKDqjj-+ip~>gBe~`1A2-^nZiqae+01p66$~FY!&j#((;yH;VnJd#BRD0-D9e&v$gboDH9Ju68bT zY(rT*-}2p+WA#IYc}$vSmaJnS&6Cu$$%$X zf6Meq^6k2xHv871XgjlSVRrBjx8cOPyOg|7obM(6GIv#qjR>pu#yo2xqB^V-#q+Y^ z+3#lg^bc*o++g;xG4nkHqv=#F&@wxSJ=l+dI>|cF1A}e#v)N@odSw#=kK!vG4!nY7@|9-lD zMQdACq2cDkEoV?@%vf@B^qqnEF}wMVGRy<08mhsetoSm!ryONtVI8PVfbYp$)-qBm zTzh@Kys+v%`P7~9V72fgXNeQ$>YDD$_T1?|hED%l#8~NW8uGEOoR+7357TnpQG4@8 zJ+ciWR5Ximapru|^~XNBZTNwOo5?bkau4$~1>-d6h=2cEM4u8( zAG|56{Qj=nZ%SqmJ8xYRGdsD~XSRXlPbg`I8}`4=?D z2ACXrDV%lW0o&4`{^&se*#4W(U3ZziPJg`K#Ugv2R?0D>F}UxslPkx;@r1IKbxlMN6Z0x)aK$d8C(9=Q%&_KcjM8|(0`&aPZ8b4;>}8*CV;8*0sh`#-?pFAj zl&OxAk~k6@dp)rx|QVs3UR#G%Wu$KiOV^ zI*|+xhdS5+_DM`3OOg3(g&UCWfc%%K@quG=JOf$FpRlcAQpA$_#3?Qwg{t9MJKGJ{ z;0G1p(@#}u(=A8`f!00L&%aScS(Nuxzp>E5a?=U6=2NzZU79}UdRi_`e7|mLAXZko zg$V*p7jAM8=Armo4gdk-$4E4OvbcQgpn7E3v*|NJ4I2gnN1&F&jM82q?G)hoN9{kY zggJ|f3YwPX4JhpD{~;5vkgw4fZM|Uan2O>IvG`YgHT&}Md4e>b6(D@X$BCiSeNjq6 zeSaSb0o4@`NU^xr>5x~*QRZN&)sk8EmicN?Dft1uWQvH!EbeSzV)TUz_S&OZw`#$E zOwI|#g->jmTE8N?Bu3g++SP)pp;@)bCKr%YUC>H}I1~k`b^zSkU9-y|ZTLl|8YkkL zOd?Y@Ji<>CrE@=jNdnuxcIC>Mu7k&~z zvO5cum$b;%h?Ig%RQ90`ZOU!VfAL~UkZj{Ab~Q(0YF}x-&~qU38oa7}D@9rNx zg?RWiTDt28#kBng^vtcyax~wR%d?@_U=G{$fy;kM9gCwFS$hV-fY*ye%XmpSY7nh! z=b&WA!t9h-32oNGcVFBdufS<`^{2M;Yg`02b5k14*;(&REKcL zFFSw4tRKds_90`_XMx6mE>*+XDEw!i5De?A(fdQunbu?O!k9E4#?mkYdLtUnbqD88 zI7gN}@}O0L1l7X=eqlm(9k9$3tl{UVP$X*as}dI;EseJ5P4&c8m@n{G-SV)+tBQ`p zuru(d4@GQ@CqU2p82+f2?2+u>6=BkfR9^@ww%w62DV!|PFDNpfj0xn2xuum~tr>G|j6-7neX#KLGzgqsY;k;PmlQQWdb(Z@4XVTCJ1-T6!G1}; zQnvEXr2`@xo*$H-XjUge;kV&gF4h-PGQ5jrb4SFN-^k=|jTiOtR}_y3i>AN`xlTL! z9HyQkPrsFok7Yk#R;Cr$^eo&bd3;p~h{R!pki@`TDygnFq$G_dK?l^3j4`X4* z*}?h@|29reBuC28x`C53RJ36ymPX3@(DxEw>&}};04Kx4j{r;}z#0)f!2jH!#>`$B zXEE@SVqqYxs%BpoekW>vGToM}7{#qUjJq2^C@X~tAk#|}?gbPSm#YML!Y%Asf^CUe z8fTd1Po=QdeXt4igJ>qxo$JP~mbO^K0pHqG-Z|m!XTvKJ$R>DYb!|G%2wDq!$@&wM z$H`EBWAkCJ{%ax}X>Rs-~P=#0cD+3r{L$Oyw z!Y)&-7oL57hV<-H>TLfYv0C?V3?Lm7A7~EodB>gp3oHxt58=rt=(VL1qjUJf337YpyH@jtd|E?Ngq(_0Pb*AmP<0;HDzx<3%N@L zS${R}tF24qELyNFtrT4=fuda?yQPRM|JDa%{wW#?Ra7F#FRt8MA52|%#5Vjhd$nW2 z1u@kCDGmO|iB&6z<}?4(A4Mh5ob=|Z)!_Fr6{*&2RyrTL-y8U6Uc#Yhv&Al&$47GF zrQDUrd~qzMJzn`X}amF{}G=7;d(+fV-{=a=EA<&zb|JZU4C+<5w*@97vHl;@iS^M+Lzgl zAV`_fBkG_RiU@eTZlU95@INVVHnOi6pA^w&(@=anV zzr)hISZIvJQc?56YT(>_FBkiI<6f7Z_{oowZWq!QBA@6cRxY98opVobD&1<5F%x~5 zbKSqxAQZ0E)h@WQTW3%c2kNMUeU*hWyYHq{k3c?mD?X**c-&box1QUyPIE(JDz3p? z_=#pD|10M~^dkeM#E+Gy|Lrqv6cr4L3YlxmTU**7BJ8(eNf)P!HJSS_{9!(>aU6s# zN*vf74=&Z^;yMagT{kT&9Y31G=uP3`U?&hAGoPhbV(m>WM~za2p3`4Rt0_1O^QQx4 zql!1fVIB%J&Pl1$TdBbqWcHWq1ps<{PJ4*iWwTetvvqy*=zE5g05FqARk# zHcRNIGluqh4MWmypAnFOPJ{+KDPPl=YR+v%|iyn zv^HFXg$r|06$EcNpY}U{PD_uXofS1*?^=G1jA-%y30)89z4PveNshgS6h6q4#(j zC7)64d(z%|#*enG&xL^8L)<(GM2O|iH*?ZUw?~Gj$tEJN1Yg>*(awOn-zLpvwOX7$ zXoV89&^DM**jLE6-ML3tB=%ut_vp2=-e5e?2!34yL2T5EhBFEZ*xxI7GWzsp;s;T- zx0b8=(9mG6U0;q?OoicqIWMO*4B=i0L$4$K=>(XK-xR+RqKBGCOtJV+#?ai8KlA@+ z?VKxWYN{dTa<0 z64j$qNU4E>IdBXCox1^A&cn|&+ z;cuWE>SrOxKB2q^HtZEK)<`i&Dq_qf6S|?xxzI5nxMb@W=EvhSK02n6tHh5A9}>)? z`b6--Cpqt9D@=wbSLEHO|Mx&LyADup>}3kRLOub3Vj6mrv?1+_|5 zjUSV9R^Ts^Cje)~L)oDeD~8_@tr<?AP z-4k}`XAIRSmo%`={IF=0E=Nc6>vdr}Wa0g8+mnmLS&XQ8^=Z}Q6!C@r_4SvT#f?Xs zXRPh#2ksp92^u2oet1Jq&)Q!fK{BsUqg&OYOx%gA*U0tuJz21-)n$rq;T-x$;(&i4`F`R2QGyxE6)D4V7brqIC zrg?G+Gy3Wg42H4=HmmyqDtJ*E;~z7B>oGK2-xb!yce!OXG!&>r8Fwh_$vtQ#?g4=g zXH85mrG8mK^!4}z5G}*_KAE3yt!cPToqbPOd(@xph-;l$EgDnTES$dhfYIMZ$Ld@Y zwavATCM*;NL?T!+G9>ilGXO0{N&I9%Moy+8$mwyK^?Du7(49|4fChj#XCQkJMwO~Z zI9;29Je)1g8t+DY%6Nrh0RXG^O5zcQSGwED{|lNgr-WY}{VpPq0dlo<7k>Tx6+)8- z=h3VybX|@7;E@9JTsN3l9X<&c+~Sv^0>IHqdsB_^Uf)1FIFg#UTNND#@c5LC3g>3K zEL&RZlA41k>6NWC^?3_>5}Bi1q}$$?w*!+l92mw>0r=>p39lApz(3Fe_GJ$C4zsk5nSd8zxZY4$!Uz;r)<9up z+f)3MQN`j_Eh|riEht1o@b~jSgiZ)kFq3w6PDY(Gyaktw;XBA$nAo2>Zd?D<#;>O= zZznqrKY9;PG`eaTEfa59(>Qkz*@DCZ{rV3#HNSrWxw1%n1riO8J%}BJJ5YctYfPM! zhR;|X#=+zofh>(r=sN#OTJ6k1B(B>Nf2Z$oD6P4@H56nIBfRd1X8G@+3^0s6FmwD8 zko;Y4t@(ZgWW;f{O$M}Z4qsc(-A^uo4zQfr~#$xM&7gVuI2FGCN*zrEHDOFFbS;(;+!3!LE`2muYCu)Y!t>F{uZj- zE0V7t&cG~d5D;gyxb@ReD>sm@rSd+%b&MYZTW$LB;I6( z@ppN^#R~uL)dnpj7ZA<;0ZQm>W8u^-RZTSwz>IfuL3$*7pMI`3vv2I(;`y=c9vXx@ z5D1jKaKEhFMAUv9qXK$dHF~awZ~D+~4|uC*%jp`TpbMi9{>~r1bc4lq-;I?i=8dMx z;`2s`<1%s-N8fo8B6n@ zwo|ikfliq4RglU6NS!y9=*#z#vc9J-q=~Xyz1q?Aa`Cx@p*3%}(r9ism18^p7Gscg z8T-(FR;Tv;n+=%az8cKD9S6m;f~fIS+kNCw4&T_Py7aq({qBp<(Y8&g-*ywojYC>HQfBd2a2~c#@zZ9kj~{cp&jJ04W*xo|z5f-7vm`|GXo6mY1P_;b`CN z-mGqVkbR60q&q)rX(G!PMK?pZi@X9A6G()E!7pN=m5PTy8u!1YawJ1;ZHuI*x>wGX zwF~b%z0DY^IY(~6Muer)^80(cys*WS#Sf;8fl!gG5~v;Cm+R~MMyPKUS{cJA23;LO zUb0ydD3&TYPmYJ*Uy-!dfx|HUC^jPI4M4^2wp$MjBwBKw{MW)fpU*AmXjGZ`p@s#L zXuU+EBmr_XIQ;y{H=nsE1f)8ina=odh4DDd4pRTGAfiqbOQzA@Ed$JB`9GqFTHHYSYG z{pzNC?r;TnG!$8pLd9|IRAmm>=69XnPzVd#>!Iz_MwP8LudX(90*x@;6U!}Z-d!fy z##{!zc=K{GCk8NB(6>=d;*&+6Ie!p{VM4FJ4iSEm^RtZ#gXWtGTSZKlHY3W^E7Y4~ zpMIEhbYBx*$9>B0h6+V^f0rny+os3YlsB%oH3L!=cYN7sU<&LX@o7~64R(7%H$D-h zrZ_t1b{;IC*hL8cl6_;(ru?#UrBmAeIK(jD@AId6+Z?X3L&bFzv-F~ zF|xO8VN38g3}`BCUYD%5UsAtX7!XW8Co>C_sWys#?L}OHrECMjLqJBp?}gP1ePEL3~1%e-Hp(T7Gv`-iGXh)yV=0S0Ub9POkTDnG<)VbkMSpG1B-9R zlT)xJ#N%Zw*~JkS^R^Hvwv_5#__K-WWkZIigDTWX%OW2gr3ib;NTck{?&-9n^dJkZ#Kv00)+}@gpuVmjSG^A| zPQ$+ZPuztpaAOu^jbi2IGySNF@#qwCjFBvZx_c4t(LvdUJ_)uk#pue7{IUY-t!b)Y z7Xrb$QgyqZEh;?aR~Qa)nd8P&<~ve=6LAvBc}KF=_ZGpGjFqw0hc( z!l#~-w{zN3L@W#FI$rIm)+W`_Gs@l)g@l^X!^@OYM(3=|lf5uVa1_ct!!`A?`gcV+ zIe+ty)3D=G#QY_?5qG4J+m(~Vr&lJ@N!mS9m>rZKy3Gdkh_1u!j7WKt{3SyY%tn3l z)|$I99a$GL!081b&MFCOIl5|t&}7_Q(cHwlzkbGJS}~+Lj!zD1uLI?uHvfRRIQ?#W z)<=&Ch0?N8_kA%G40s656&bhBt>Gz#oFo8}>K+)Zm(WMD({8_%ks{R3>+D%J)&@zZ z^I{I?<5foAGL2Vb_z+!MQhO;Ip&U%H;(~>^uZ~hW9}b_@QSG{*1c|HR)biPZ5R==q z_v^AHq&aU~R4Bf4OP%l%>7q}OrwatALv2@%8>buh&GA1&++n7n*`*{L_v;ikR_g4E zxkG-!Y0JtDY)sspruw*sk>+!Q4>(?y^klu1vc$Sf1+o*bPXiY(^T_dQrp+&AYi3sP zAPL*(3w>NesUw)iH+MbDs1p&bI!%p}5f_v)D2i`$81NT)f zx*G82`mz~j29qpzWQP(2I)%$!8keVUn8tr8H%6;i5P9x>c}uIUjk)Xo^5)z3?vb0= z2cCLNJ`59IA;)KE`euu-s?@jYAWq5_iqv(tIaCZY*7dUM))F+eJs#mWS)=mn!5!4* z*Y+4UYg^+h^5n53`!A2_*4~yTbU6D--2^*dL-OHccx4)NMUa~+YKomk%E2O?_D;J? z6HSFp#_7RRpY#-#i5k$ebL-RqvK5SKOWWBFoN3KVq7VX!vNA%qNViRob=lvE|LA-q z?BXc&2_C?A9CrME`j&FVo|c~YBIzjvyEjT=&7fkpr){DbX9<{_)jg9$!1$azfk-~7 z)z89gKApcndjNuY{C%7)dj}dxHSvRV9g=ssCR;Nvqi@uiY{R3>4SJmy<@t4+^6+@?bsVdxDzm4TbzurPGqvmfy1+4fdYLefqk&QaKUsH z;KqJWEmJK^oqxjcDtsQ)O}z#fUy`6G3k6#{+%~41btv^Fq%sCVX|VnG(zj;((m6J2 z_~@ndM_!y9(?D)Ombx$F1QXT~JYSJcSIlWczvOpx4{Cj#D_f+zuX~i|99emZM;PRZ zzG?I4d^5d4f#nyL`d$p-YN|D*a`QrG+<8M(p&qAE&BW~sTdl^d0qKQjvg>zqY251AW?Th!Zuaw;dJGr4j8`DmFHsm#`1G) zbnm}K7uPMgmIzl+Zx)|7lzeKayd7QKM=@RIErMc-8$WaTfVmxS-;Mc$+r5UKR3v(z zn+GApT&y}F{?LUIWMB|sgc_I)uaO_if$KqEXx={;mM#0R_VrD2zB}?Rl6y}mmb3na zb@fkL1=q4mAK*G;PI(m>CZ+h~fcIYsjI1}5S_3SXD$0TveF}b**#wn73$lzbM0$D4y4#%`6*e?ry9-{4TD=Gp$K{?3rid zRo}YjTzDsfg`^|ylTUWVO&9TJR{m#t>orwSuNwccqb-jRlG*RifP0>|UNzLa#;hbN z>kneJrQ*2pTO?sQkMv)@AV2xbD@bsSoY|1fP?$8s%I8nD(w}@J2xFIW&`-7+^#*-@ zhZLNo7~mBQZq*ExHd&y6;%?#1!!Qz7Ne`xl52!;hGYFomob=%{{PL}n$lYl2X|d}5 zsh$6&RQ#5(3cZZB+IcMW_;{ASxLJhP4Xoy5lJHnHsFKk8IfmO&HHkIaJ+uP4@Y?+s zPw%CeM=(WqKDYDP$DHA}&veVTPSUEuAxs@_p4-o}ws7K(?_{Y*hK`SYG0*bL8z``t zO7*bUAA|%sDo3NMeY)Wf)o6d-gN0?&2mODT@<(D2f`)C4{5{& zQ|z9KKR#)ibnC=l1*&28rAy$|0t5_~%%r9kG0h=Gi+P4Zj0XAucR z%D*$>b2`8mmefoVzmCKc4}aOhe~COp?bju-n#H$AQT*U65hP|7&w1Dp0S?fO+3Bcm znG5d$_yYu@3C6~On)h=m7`IhK8gD8N@F4MD(vVgdX7Be7yVClMGq&KqM@TjBEyoV- zsgR*({u7v~{wYK}ikCfd8+Lka-G^k=P63)FF{q;sD$Qi^(OKN&;0^Ik`x_xo94d&D zVlR%Y{bM5}6{0=H(KLirr$2%L7RmZ!;@qFV{ZKKoT7Xfu1<^>YP$KlO z1d#axcq==8E2D`gJ_NWLCjGv)5~EXs?1{(1MWr5?_Tr`pSiwS=ov?xIp=jH? zn;gprCUSvYe6)pV+)R+}Mhn2cpzJRchIxX2E{jLu8gceD=~3KaV;5M$rQSH8LqHc& zHY)O$G41x?^1lYR?-QFo*X(V=!C{@zS3zMkP48nhp7CupsYB96dDe##Un+0cJ^of} zt=?GpON;s*D(_mvU$97w?#BMU4zMPY2Yw|2bUse_S17yZguRa)nbmP(GPa;qF$G`X zsr_Mai2!qrj(SuF#ii>BU5i*&GAy*%sOEQ>s_4Ch!Y|2 zOD_P-<)vEtwWruby5l^!yTcs+-RZ3l_wKR?kWKj-k;$vtsS)Ll=>=`qfOQ3cM?9T> zM`fSh5odhgl+qUpeh?!VS-ATayt(x4NzVGui$skCQGA$jWg6{HRsGfKTQ{X04XLr` zXb#SmeC%X#`&3AivY6xR)$xV9ec({QM~Gxq&ewbL_N{OoNG1e0*k=u6%i6d zgd%fTF_Ei*%iq^ID*@QaD_t|RL3c)Fz4U+IIK_@~imLapdb?X9Va`iLPp?$_p7Zd+ zlU>BA9H66D74&GgO@zdJ*MpQG!ztlkarfI8$&9B$t4h3OdWot8exbvtiWzv$X;t`3bp|6K*F0|1j>R$a5ENug;yeAhms zuy7Yj=lF6Lnc952$T}^C^ox>-9(e3dqak!B36mLJZ}Iq-=-d}-nG^2zB$Lst8yaPO z{j;63p;n;+2{EEN%;s9M{OA6W~k+gJ{B-8yt93;#?*RPx;ry0SozN~j`qMr=HUA| ziI5KB?W&ba8<|1*1fu zjtIEV^$(Y_S57txvTW~jc~_4)NPhf3lFmC2>i>`9XG>;9vbUU_WJF|U=eWb!o3qJC zbVRb1oxR7Mts|6?O}0ZME8%1n*>}H>@9+NQ_UGR3*ZcK)J|EBi;A{577J^Z@hE`oN zg>=-@1k7g8u7=jNaN8@kA>wxmDE6 zOuv8c=XumC>1@4^v9-Xz)G18H*lpAVrKg=2HH}uB&_qg*~}5jhJ)8xj@{46Nmh(}cTN z3Uu$Z)`!lIJ)4$n$uu#4^Q%dwMfzsMVbI;f<_|cuFt45CY~zFpl=|(;uL{=M6d$fp zw=#|G*;LT4rAFvjlgIC|8v7|7)DYV7>s*;|-p7EpN-B+#r$m1_+kj3oA^2kIwr9BK zI$*dZZye~kjE{brrG*g*TgxzrjJAKB%>-UiwZnKJzWIpNFA+$k3hxoSyI;2uvw}P` zX3%f^`)5Nb8uX@-xR$Jc4mc{1-sA-N;tg;b{jMuFl>TZj?jf(P6*9shqu{dtP`eXa z0JI#$t)m*+fSaEC>QQiKuA`O_d$&rHDbXWRaAr7h8HkA#i8b^(A2Z``;q^=(A-)y5Q88kVbC~<+0IukUeofqhbKh zSvG&jz{N#*^NjxCeJ!_pHWaM1!{_ccE$5FrNQpl(X1!!UDs}u7T;b{zr#A{T79RWb zk?!}_SVyrJOD}IPQQCth)sTTMLVxAe7WWz%HaoVxarF0t(v}-xPWA_bQmo@yVT9zW zYs{b`$tChsQ_GqG89HglGCj_z;PN{kPe@))Vx-_Uh(AwK-!=TLRJHYgq;kfgi{zU{ zg*_ipL+5I>%Zq|se#(NUYu$ao$%QOP^e;k6F~MMot4GpGjjOOTyeRkomB_>fuW zS@nE>70~b^7f+J0<)d^2_rCmNL?J|q(Y)P8Lt99{76fXg?aAx_kqOLh&cJR&;<@)ZmnwkuBo0_lbS`OM z5%;S~R8T%K@3DOkwD}$SnEeB8{&XV!V}7}-hY|h|!Bn=SQt1*zeCNk6pYcK2Ao|Am ziS4eDl2KfN^47vass?p z5tG6YTzca#%kJI6KE6|Ce~w!Ab607F{$qen6FfAIh?k7VU_2XiE*LL9mPa;Nvl_hz zn3R`>DOPQ70Z8r(+dFM02~*_-es}Fr4@)0v-~D&V5RaCt7 z-K4lBXCG}pNRk&sDVFqvTksKCKd)P?{}*|p4iqdh1@^GCVc<^p*Zs1|FG-puZ`WoO z<+g*2pZUO>v3oJ{mQ|{qS`2w>7-6w>ijud2PPA;peABRE9ZE@RjWaK~O5k)FhCGAX zLIqo1b&K@A^v!4QT$dF6Ao$My^GOf><`>hM*v)3a5DH{|W4)~Z1$h9bAs0-FR4jWf zz~XL_0y{63|NOd*=%tQv&T`U}SHW$Wf497ZluBMdY75)dvh$s}MH#>Ni**SLCw^SfBgIHx~+_N(IOR~PF~7(+*z;xMoUELXqo4={dgy)|?nIDPlHqrq>PoE+O@pJhSrRm1CwK-MoYYz=F* zft?3cw~S)BdmcDocCO{)7~eoP{wn!vK^b^!Ct13ZtVQ3j!O1%5I0TtMiLY1Pl2N7q zmhX|)SJ%uuAG?k@ITlY>4WQ!+$9zM)x!dzAwEuPjux_NR=MwNtAp0-!3-5tGyEhIhCS$y%aHG|<-=8KgE+)s34| z8vZ27>M|NsVl}FLj?7uwN`wO4_=`G;GCMsg2o@08qnBn1xqseH2Yh~b!7ESKDIwbF z##{H`ZA0@6)0el0!miu@wN}og@d1$1yJ7296!C0m@IcmC8AK`M`HxAv29(@Ue+-<2 zP#PwfA>lt>dQ2xN|RZW?`^0TI<}*tsc%ljhO40SXA0~B zdJ&<7f1|ICwh#5Za2t{zaSM)y^Dda&Ic&u}%hLH>L`=pK^1=EB@WD?3F&I5cAHkcX zruvp>-2@JKiJMHjtx@fZTlSLRC$D4}I1G z_L`cjPJOxmky&f2MCb|c%!c7|XUFl>-3wK^HMAX~`gMoCnRDh;r@bS4pn2}2$y9j! z*E#xYneEOWP|E zblQ~BkdXLj({D5)X+Lnvrl*@2k$r)(P4%m(>?7s6e|hVX(v=~Q7r!NmuK`G=uRn>H zO}|Z^0*m^zLb<=D#Ccjb#t{|Y^*2lB-AIPmD=XKU^d*&xax8ON?#t{CCBupI(Cz-E z^LW8$Dce7ZVLUv;RSu06z(1SYf14!*S=WJbqKqw7L<6hE?5ZX-!&dsIgYLD1o5|X0 z)1=KA4Kw(&Vb`Kc!nbCx%xZv3B{ZzV7+|C;YMAL7Un643tYBC_PeEi+aK5-+`9KIB zML!kvvwj+-Ep7HltM>aE=aH2V_xhwd^dkH{d<|^^{`fwC)orhDsQT!;Hj9A>|Lk}5 zUH~mEf@&-Km6&!1{SyuRBaN5+&tLVBH}w!H^Mvk#^khF?>Img@p^HDH|2I~j=42fh zTs~#cnsqc|ih+H&UgEgKv-9Nr+1Ur7kBD2$4EEZU-5*jv)!AFLG>652eAp^);*(3^ z4FexP`bihe;~Ys*%c=5*RJLTj3(`R+0Flsx^&G^sR|{DDp4c(JnO8J?)?BEF;aCA1IP4oyR2LqvA80ITIwpZ zT~E>{qRJH82xvuZ<>b76g}F@UZ_QYZQL4L7v~51(BqoAZCmnS z^6_-^T^FXi;$Vaa^X@N^Ys6js4lw77onm+fAT?%Ugo@8}>PDh(AvseF=}J|OiF*pn z3Q9$`>p8YNk*2hT=@BPB`Io?6)uS{*xBGrbs}dj~w^*pnf9s|@27>K@a8LifAuUlFM{VP^bu3GO3B$6{Z zlczrQRWD54_{+*(=c1{hD!RYu$C_9Af2z|nLurfeHYe{%f5gOgc80GD)HG$eSq%g4 z`kO&*pq8rF2a=aAra&D+LwMFol=#D|rDEW*e~X3q?64yv1s9w@wm4fw&i0$Q)TRfV zM_iSQ={7x2DIZ*vCPH8Alq5;=smT_XTJO!m)^#M`q1o(z?`zWEOoKLas;-Wr$Swm) zctdjYV1Oj9;+6(g%dr!27+?#JK}9CGoQOY9OTx*=v5?%m`D@g6D$prE4I#Q7M$L8? zF(hA>p59tjY8>=5!$hml98I`V%nISJ?XiU=Dwq79os4xzYorQ)F2Sr9iBf>okFtIv zdKZyk)|0vonVH;gmn6v~qAc_(U*BSxR!^gpF#BVaI6Zvio2y3Vq~Fz8-9m;7VVV5R zhhZ~)C}KC{Ux_iEd>P1gLSoEh4}JOkkt8;0Oxsy0nVoRk5O=i74w868pJwZgU3)(K znFU=I?rS*%2e(asmjDH5q3P0qp?%A~K~O6ZIjhKTKvP>Yi$?iRP|{~guFe(dCthL= z)8K23P0`V0Pdn*_)?scQ6^A#(AoU)U4)@>1b6UdqQA0x6kXp-WN3-B2ur_vH7GY|J z6Cl}7j;$C-Cm*U#zfQqlx5d}w&cL$r9D2l8$vHcdq^%A$$1H$v_JAHZZSW}yVS|_2 z*hy(j>}&ih8H1fiPnib8UkU-mpL4g;VsqFxGHUE>EU%A1};FnQ98eK@l2pHc0TS|BJ{Qz zOpQ9m*NrsS!fxDSAze(xy5^UrXxM*kn8?7BWBn0F_Wh0+n28@qmeeD6|FJ1`xaBWZ zmHaL=Sdy1KF^1)u;H!zR5AT^d{p$RxK=n9;g9u{^O-7|$DXejoRP+Kk7S@snoXc$a zRJb!ZUz@g&4AYrFb9*>N_I=PkGtKAXFiB^APk+m{5-{o%Os*#3tIhbqKVVbeI4<$h z$GGV$+5K1a#pUyC=<{b-Y2NdIx4?1|kEAm4>~aF@O%5NO1%$S>Th_iJBpXel^G8G% zZ0w}OC19Pd8;$y|k<2{yZ@)F z)u?U@^E`e~Dk*uN+a{XYb4A5KAPJfrt*HAj4k4NS9?@(JMa4k@{gX$ZYNWInJFX3U z_I6YR98fws{P4^ouLQ?)vvuGJ)U%%`lofTu+rCWXzpd}it9KY$>~&xPE6RgTmJ zSL=NyiHh_byY+SZJdB86cW?XCH)kwRS99Zq^xvTTK--Q0PKZCv^mbH^JO0 zG1GRFS=T`UG^O1Bu}lVny!SPy&S07&onM)=hQS9d_1cV*`yH7u@mU=MG_kh~Kvtf1 zD$1koH1+bs{iYiVxhWrWZu))spkG-G-BX^H^|{`v9R*!MzVJI?1+wMkKg`2{viY2< zViz>Y)&5leJ@e>8pOu;?g5=68d{^hT-X%^@1Ode;j;h2KbbcputroTde zDnjA;0UM8=$hlIOeT?XsO^L3UE8k@`fWspUf;pkDjN^AWSM<{JkF?(vOd{ zqe}QU_=@sO?7I2xK6Wu8{(be*02hzC63H+1&xgaYG2l~98x$t(N&#)CEr$>~CI@v< zbM=ZmF1`2$(a*WKrzuMY?TN?sx++TEZvpc{>m9I1dQ!^Ut$dYGK;r^61~)Kyq3Ng# z=n%j7H?sU7hSjI&4P=#u4jU?Ns!Su3;>4WzV>V(`E5KbMCPh9LZ-x*Jpm7lRC=-@5 z#nUj8V=kailv^L{Bxd&Drbf;q{|um<8q~ zcl*7G6iL|_OKY(F;=}bK{)3`YJfxrnsU&aIluC4Ut3$PEnDk=`8Ir8up0uY0{mS|*l?=vdx8R)6Fo+abd@-`p_$D?aOdA)2DY3>< zxkV9x4|^sUf#Gm2WzsQKq$P4Ly+PU&4WgVlNzuoRV||~ypbyGY-AeEM?sN>3J_I|l{V+3o4f#Y9 z6;PKqKOACQJqE1?U&M9i1H3qog=1B9sPno)r{xDQc^wev;N~GwW;qe1poq`Z7W#2M zlIsoZDK`lpim5{hnl`(9`D%^_gf_vRWfG4cl>R(!syLr?L{4{<&CdV z4!E>s+j{4CDd^N<6*82oiW1LF@J;6fWQ~*L8 zKwbRfE9mh0=0TQBSWGQ?;39C3Vw(x{!`Kgr+^ha|49si+AM3X2mmrp8rvO=9yk55ejgJ_if(>oR}*oVWC8zFtD{xt7E6KEfJ*tq3y2NqoP z!*k*Me9(9y!uIXs2*D!AK3l_M5w67b9k#!DBP3^SjJlTDqbPTJi!#I{C6p1ru+#K; z$=Fz!Sn&)LWiFRq%J#aNfz=sicxp+M6YZb20;>`* zdtPZQsNaK{CQwb1cTPak+VY@)#`}d5JJ`XIK6h>xU23G&k_suL1El-OQ7`MXp@4wL zD!A8afU4cO_zg6r;#&(?(b#K2FSOICOgEjjUXJM!gj zs5fJ_&3k_PSxaXVb>r;`CDhf$(e@toE4{a3&LdrB4B{(RTE__`0*#Le^D!_&IEv2Z zCoQkU(Nin=cwdu(`_#&maRz@mgN<7J&6-DIF1Q!0^? zdvfANgERSPF=94yo8LobxQ=v!Tw-X42apfH0|1!;nBo@k6Cg@2uo=Za8>NjJPe@Av zzDnI`W*UP1Va5^j_&M0x!iufYQyJvI;D7fBxYLeoDK_XqSYEwOS`)7OJdP_ z$(sz3sZka4*L4E_TbphW4v)7?F}Y-bG0^N^rz!f?4+4PkKHmvg04Ua8Z3f|IOn%RH z_$B+Y^iPBA3_1;w3kD&&x#ROaw`y1?k>qK>5;P7%RfJriFkPdrWmZie&O> zU1P8JS&V8(Lm?i|pc+C}>Z4z|%W{q_62FlEP8#p*Bi%}Q0?{N?W)um)q4ySkqbS+< zQwdcFZOn~UEvOL+uIwN{E4g7m){E_Ob_OVpF$V@nB?Q2Jex~Xih<7W4T-F#7+MZp9lj?f*c%#1^%?oW>tpKAw1Bh48 zmbg~@BwEKC*WRR=FBL#GY<{iE4UsX{Re!^9urVZB`9%6m)fdFLM>eo|jOmGuO z#lXHa4M*KgJWcT)(lJeSMG@UMAgob$CNYc3t>@UWXjcQS2bkA_*a&o^o~E5s(oM3j zFCbKoL0N;6l38~6E@Dt;>m)z8u)2*8KGs*PK0b1$lmjdMuqxb1N8O3)s+J6fA4#Hb z@NR|##S4`t58JJIS36gQ@4Z-Y*7C4t$H01^i$B!S+0d%XxxUX3$pSET|JYbs<#Jr} zn|-qT#45!x$A9iRAQPJqCKD#v`rcQ&UR=bzx*YstX$c@<17!IgV(rjqg6_QuJKgBH zG%C+3c|YoC=83tAGl`#q)o)d=oOKgSGc%KV{y|}Y>$?n%D}H_wHf`rXy(3$g1@?e z0#F}tOHz(dYG_JH7#3AP&Y|m!n176Kn8L%2q>J6v)0s(s1_} zo7V(CJyIF?26|QVYOkTM9u71E;f$pmr8B|ugwviy4I&}>Y)PO$c^*T=a7QRI6M<@B zx!8zJa}0mRJ2G5JbVqB*<+eDpvFQy+&qRg=Z0z^KRjN{7F#H{OaXuxmBUxQqQ(Qi-*CLs z+7$Yg$H_VC;T=$LX;#H!Gn5Y!A^h5NVGbT1BUvH;`zZ9*K!|J< zy}b^POYSCcXaHWOG#*Z-Ja!-PsRG!Wn05$ro_lBg)p2mm>wE@oU*}dCHK=^(Q9-C1 z_Q_aXdb!ACTvYSh@h(Wis?-EMmXrBK)}t`3pHo0z+Ji6@ zT@_Ikasx|O^YtP@6<|b3NkdV$vFg}rhWzKhS}P`TUBA=kb2d3MpOE(uy_1Zb%c(eg zEV%riO!jgIRlrk&1|y8rh0kBQe})zMr~NQ1%u!D%X^ox>ngC@F$x(78RD`iSrX0A7 zqU$j<`5>dYG8*bRa)?6ST~Ht)jw?COfh_{nT&xv({_?c8Ef-56Vbi#1;Hi}oGsH#2 z#zm@JW@cRW$bKVwJF4P~NOzcfktYncZ3P8PVb;5;;B8BWJ9pqR7Up-*v;{Gc(r*^J zVMR45UTQzv0)Ug26n?8VhR&|+mUDN<@8$`@QM+r?pSyydWO=j24U}=;GkVTMC+#be zpI#>XXn(d;c)5Qmdsn|hDRSr5{> zlpx~@opoi#^Po&{WqJiuALmI7dx-%7aHByGzKrE@N3WDnd9N9I_sV^EMJVYo{^$|G zQluqSICS=M!J*1-!JYQ-2a)cVM0KsspSKNF1;Mv2jI=9XhegwTDAc;G`p!H_-b3@* z@qH(!SYIJDr zw^a#%eY*MVF@V>w-b-m`f3IlfJD5UfS`89DAf3@sfE@%4P1I=|rcZ7{ed0x+#U^Ua503vs*~7Djw(J>K_OzrcRYy{NJ& zdgqoy5=&B1`uiUI(ui--zv}B>Xb6|k0M|FZN)zFpoWBhl$!^o?f5_IDztpMyR@K@W zy&kt4RZ3`|H9^zktT1(VqDLe2i=WF*6{-&1P$GZxcO+k5|ssto(z zP6WHUc96Vy=Qt-1vd zDW16)l8x)yJIV8ZH1D9s+I}xrs9S>iT9f2&@qCX1PMmTF2D~H7MeK+y+<2q50eWq& zsW?ShPpv{B4SIQ6o<&CW{Z{CY=;$g+(lvD`=LDHg&yF>fLa zy;c=TcJ@p5?b!+M*W1c@1~`re2tnP53NU{8u>r|;rU{7it_ z=)CmpGnpl~#-hIMMxOHLeKU57=b}JR`mU?+B zItTryac_OUgjn$6lnwf=YY3!UXm(8_l_e6jFPm68ofYFSf6u+GDezw-V|f8}a8sjn zyGxZexN-=d=po>i#ex-#9t-WqtGd3dVOCsWVI4-LC{uXNA)7e?>- z_=_~seA%vFM~jF+U|3(>9J-$JiwTyth-@{rh6}N8)>Ct9A;o> z`(`HRO`tnZ2P|0@l29t}1#&|`&Rl}S^ zOWQxLg|VC{!EoOwp1G=mozSJJ-&qE+@_nInTRj8e&N{%r*ZrjYvcwosEn4peC4U^t zCC++%*!8A%5}xCSNe&$s^?f_#Xmy939+aqLT$RrfB)-s^O!LQ=9Y?om7E2G>cn4b# zO>6a|zYZPupq7e(e~7?{Xny~xb_+v$b8Q|9S#-3V844v?`A(Z9qR)wyY~kMGLIvJF^w2$`RWJPVP}$1 zXR7Et)K5Cc9m4_#(K}&x0M&Ct)eQW;S+~b__f6I6wI?v0^=1dSw6mkqy%Sk4&r3!; zok*vZ=(K6MrcQp;9D-`krCW+9)VxUwI|DzN=m38+RrE$-nqd03=;>BF%#8E=x?U+) zVA6d>$jv-PVZ4BA5oh*7=<+}M%gcW(rLO+|Q`l*Qb)5o}FzhxybPje}NG3rd)dZU| zU36Dp2jPfU6D~}LKB=_k6O_~rn26*vjVC86Rrasp8_B#ulcRd>bQ~TV2xA|k+ce5aXSGNY&mOK z?EC?RlW@(97J8EDd0&2q6H%6sht&)U%@{3B$7UDb{A-QT3zTeJ*r#+h-s*~xe|r|) z#0tB_KmdL8phdKHR+;8Qa~jFMeWT6$;|G=xDaI=>CmF3$hNvE_{~2naHshiJ^0`d~ z@dJhqNRyDtqI21MWR*(?m>;f`&~$VpF4Hc0_U>jXo${nRcNibymHG+pM9P$5PXydC zZlz?f0x_-&_eTxH?@xWRzn#=FM@*nADiEk#{lq~G>W>L6G~enmsIx%(>F4=A0jE10 zaohlndM;2ptJ8=XMPqtu!gMOa>1#`1v8Z&QBTfUgv z9t2e_ZHaf#B!myTVjfI}$By#+-tukbNrjNer7_ zmq{8flFZ>*2z`Lto#1^q%}1cx@qFBAtgG6ah5hDlpVkIygCj?)7? zpMx6e|Hn2H>i?mz;&<34w3Q6h#$yO+9b%R2Ci*j7RDz}J0ruY<%2bxFx+`MxGAs{yK96cY1G@g0i^~B#z?`cyY zkuS!eP6SD)Cn8sGN`&K1rJKx(|W z)e&Bd+|yy~xgJdxMAC^if8%h79Zj`3?9V-mMlQknsf6Q5*ln{jc)9*0d?xDQ%wne6 zG_}=SVAq?byLi$1Okn{;=dKL?m#a6WT+@G_2KZ354h;4{)OFu|s`EGnnww~q{vLO2 z+!QKV7ctKDH0S?{QpD9kKNeXpKKT1!Yen~w;pH2p@6Od~8Aj}E?9k8j@FntL^73Z0 z(lVHo|7+)I?8{>`M2uC6h|CS|VgOcmf5S@(Oa+011gnH{9yLfqD#FFs$9MJ?exz{; zuJZiHM_GUOr5LKGSqTxVy?&y_&T7q?^)I$x6lHnM<0>rvN{H?b<3@juQL-+`ZkjFz z|B%{{H~!WJ#q(lTKlBZ4E)6ySiLGg6>7z8q|IGzt=r2}2IMVYS%Ex5ss+dV3KmBD<(f>oSvJjei}h%3E@cnU$B%@Yg1@x>?e<9K6CPWv z7eU9=Ui^Et86Y*d#6eAeJ97!NxbEF>PDs?(d>GR~9B}h#TLY5V$S>DA8%_lad*DG-LVaE574Ctej;jLU(mQN%d{6m~sQ;_LJ!6{vepaIAyD zVysT_jNv^z0%*Y|bS#PN>)7D43y|v+9<^V^;~8I;zsF_!{tv_ z)aWQ^zfQEOl+-cyE^Mislvi;GlYBV$NTXT){&hbo7i9?_E>2{ftx~qw zrHL>&?&apCRhQx-mRp_1_$~l{CI&{pwhpw+@Lp$X7fToC`4-S zg&cx2&~rYCEf)CoXKt|)-hOQR%_VvI6=tb%TF7kI2Upk9kf)6fa9vsYZzH#~MGS%- z`O}g=Be+e_6Ha>_CS5w0??B5r7;MNtc7F1-73)87ZgFk;M%>PSKabZoGk^EZmh-z0 zgov(%dV_wr=D+Fj8hP6jsl|Vhht%17JM7Bcs~6M%ACfpfK6fEL3(PBl8Y0anf5b~@ zRE{Po6^9(L-d_2>{W@{4=lb;%=RSF!4$m@A+UkdBt<2P%iV#6?PW+GR|BCRGli}rHP-F7>PI^6gS+Keafj&y%&OK z*L*D~Kt}PM-v7x>r0(D4Ncj_6y>FD9pD|yPFKzip?AmR9d-?DX4!dRQR(mLcixpS| zkx6BR&!@j7qGUH`HV32#H_FEQLQ5$sMeR`xjhm;Oi*d(B^OnT#I&POH`er@)p*$7j z?~7LZWVNgH80d8Y`bDqJVOt7=K|sHKR<}Y*Ji@W;_GMMBP(D@u&fF(d;&OlYqIX^? z`PqB~y}hm-@#SWBJ#Gb!ID+@CP});-msRL+oYiZS5H#CXIu90=H=OtO6z-te@JsYZ zVtCg64n-w-^PT+~6LUJZuB&-GP>&`R3$4be>psXIe5i>mk~LdFe<^Z`Ks+pi#^&+I%*EekWljj;$Cu2eeBNw;R|QF5_Im!YaZs?Dz_S$Y?#Hs)16U1{xW5@d3@ z700-qBqprikAQQCzTpFsYWV2rqpGxYZJMx4hNvH3`}LsHxG&h3yXP;6Jt-siY|cb~GVolj)6mr7WyGDj=T7M~JHPi*bU9!&pphc-ISl*G-qmHhdIsEQ81 zLvd#(Fjao7Hs3u<)c+&6H7DH@Xfs_g2yMW-jpkjT9~o4PRQqh~9oWSMzb>tIdzWrO6LxmG$R~6qNG!Bq@NT1M<^MU&i`u z-hWoQY7-39$Dyn_xp>r+NVM&K9NvMj*Z&_%`Zm6pO1Y7YbfjuC&e@Nso{aidSYP{_ zvB=zm*-~1ck4#cT@1VyMlz5+cV@Ww-b69U9#ef`u4^pv79vQTjnn_9nBhubHq$_CX z>sTAC9vxX%MKu^ywdV>7G0nFC`ML0f- zdD+8~VS!(wFV%NQZX>mE6QfSc)$nDLQWdYFn&xGMd#6204qrO@0e8FTZV;_G?eVgd zVa-%f9{sml1d9#R4nAlh%Q>JU;-!6Tl)mwhj*F+KW9@m0qU`+nOe@)d{wd&|hnUlV zlpkYKmXCOIR+Wrh)2DIn|-uv_=UIM0Nap&3iipa%dYJ;rV4H307>J?z5#HL>VpLm zqcmG+)(i%xF&Iv05RuESzGtJF_RO@C0@3^M;)Uw#WsJyh9cc0I9OA?cFhB1XosGph zSO3ak3O%)eAS8(%DD!RZ;`~bWPXe`4G~H)#ET2tsov+3c{P?YVoK^Q9Jif<+f@c77 z7T=#1Zit4xg3u9SKo}jdcu9&hM9+ey+u>xqUrWdX>KcB)+}zY`|AC&%r6;?2R5v@_ z3-KfpydcqFgyywT!hev^V_<7^)wVB8q|L)3KeSec&fywFp3C`;N=vhl{LVKAkd)OH zGLK$48XCSav0P!3&%GvO8<*6$4 z)el;OPc;9Y_E<_j_>y#vQuH>{uBI7JFsHjdo+q*VW;3D|Qke};$A5R6_(%ncu6Qj& zN3b`vi~(ML)LY1;7s4z(S(uy0t{(P2g-ij4TH$<}mgPG%Tm&}F7$kY>CR*xEOPKJp z0#*!Y z1Q4J4Dl1}>V4R!Kr=J;lqFh{Js^@Rqd3QKU%n^q>NLqun)R?JbRo?bNX~bvxh`J|t zGo)R;M-nI*BqA&NIyi0kLP`F8rmMg&8;g5(=DHRY@JhFF>peGqH9 z`fMCnduLHh6%u6~HS?e>w*Gdfp7E*p0h*}_Lgh1hxri(S)_Qt^nqN6N358RchFZ8} zU1XS>kfso$N)C$@$W*H8_mk|sMCASk(gf%D*`n2o-;~Iys8{a^9ZSVu-8;mpb!GJf zzp4Pxw7uwSi9`*F$VM^{0fm>ULGoPPK@9b+loS2k=7>fbKoq?ff{%cEGAAKwm9L!< zcT;Y8Y)c^}O!xjMT^9d=r_oE0+6XilvNk)P_dtOzh|qxnUC~gl~UK@r>bhU+mSNC1flVRwnz4z|v59Pv+0?jn-7)J6Ye{ z5*DaN-8^|Z{ll;v?_epuy^d2)AR-E@=-x|30p@ui9LMN=;2U9zdw!HXwoZm3V$}2B3G5C>iUp+h zQ%=;nx;LtuaxyUKR3#_+#ULF zWu3R!u-^z#RqNy8wUzwUG!18!PpqGU^ZB~vm(xGN(pQk9}s0ktEq@*uh= z%u{9OYsK8cAjO^}F%LyTm2vg0FQGRpFEq_no~a&%UUy+G=0sGY=PI@3*+6`n7{26W zDVU7TD)T%PLM>TZIs@lc%U26%D9SjjGCnpeO(axJ`wz-#2Y%DFk*TmfzXwKqyE5`+Y$t;@$pAC&Jc3W5HmvTtO|p@{r3vF9Y6C?k{B^?U%Uxl< zkyMm@p2O2hi2xE)_?m<)>;9+0ggU`+=Dkf^XDO-`q6cWF8^oMI^d4*u`;U@0D#<=g zq3Cg_iWX5!xDog(y6kH`PktJ%0m=duALe=Fe;3M6GZ5bu{ZAi$L;*CWHh@f(XJ{(6 zBpYRAN>CU##4};M{!5DwMYGRlj%Dvn$2R$}!fBaus1L}tNX#{_w1E>{Xz(afQuGHF zC^h9EY0DJM-4T2et+KN5`qlo@xu^;jA|44+R*Urq145}M*Ot`;SQUw?TEf0lvfs|r z2n)@KzkA{t%}#;;1%fbLyaAwFcGN>2!a1A*&i4J6r~h%z2tHNihVa-|{~mx|<-+14 zsA$S_D-;5x3mi;N-a*b!YvKRts=b(qP_S;B2-o?yGZlW=h+da3#*Ezsq4aNFGv9Ee zz3H=UoJ?lJNB&EwD>j0Zg#-?R@?g=?X96d+I{HFf#H6hCGfcHJ(OIUQ5+M7}*K+ZC z)lm1ePG%83V-C0MJ|>Z+wlnl!B$Iz8m^dO}M79(~cQtXH2AWNNGMwy_iIQdXs;T2d zst$QAGnXGCNbiOger2ov$S}@q&jAm80RW{fYfXBsTb0j5WTEVbF4c`f&#PJF+-_)T zwhQ)<(y95UZYo#Iay~24=PmerJ|c3Y$KELc-Q2+%>p>bp;<#MM&do2KlHH%YyH}_4 z&Qt^*Y`i{3BhF6q`!%uZmWNi=`!c}=0AETO*w#e>QAB9+OvMl+RtQ3WkAeEyW!iNo zlDEroqg!w!KCTJ^vJ#)&KH3?HL(QV}$qdb_G1z(3HFOi=lcqLm&T&U!B)rl5N2I-~ zn?Z19Nn^#-2UIC^=EzstY{VF2i(U4DI_?y~sa>q0Y!?2x#abdqtVKBZ9fi{E z^1mHp9U{BM!GMf-_GlHMEK(inKY%j&aQ}n=oN+{fz;Nas3JpP(cQ3wRyd>pCQ|z7G z)sC6GvtS=U`+(79>4z~I*dTtnYx6@M>!cBaFvQ0;((*;}{m|FZ%n2;(&!Cw6<=s@H zJMLL9=?iDW3~BRgPv#gA)!!kw1YNc zfd_tPQZ9yi0^iOo&ZqrvJV1B`iiY^z!{25N`R(zhyh8ihdpL8?#!DL1L*p0Zo2%+a zCvhd18Hmmmp+yIc%|)jW@nuiluvM=ge@#tytgGU(tN+{!G*feL|F)L;^aSaUU42=#H!SsBr)ZRSiN3=`u_{L#u) z;K?ZXZya2f%~s!>X!?P8wz0ZU7B^@mG*Tfp zOW6eMN(*$s{;n$VfHVag1}f}4-rwbot_!8K*kyd6m{Go!SlFdiM7>L||5S6Rn=;>kB%+W}xUVS3& zM)UC@NVLkdkqYGP=6C}tqTC?7K|k)KJ(>Ks;b+6oQpchBXRMa`BVcLW9yUAwe5?c7 zk1nXeAx##&eS~1=Dw_r;bFANe^9|u#>CXTxa)2f8GG9YD@k~Vgx!DXPz4ZgOHI-Y% zE12U#lDfXf*nnc&WuE%4LocxB24B3kO-6i6T9Zw*m-&pUy8j&XECudrXqeGTSswfI zRhQo)F(ZV`>{%f)G7ru<4jG4#oh^iOtn7q?P*%n{ zzuV{g>(QU?Gv4=o-uHE1*X#9s#cDjSG^%Ldlcl<3HgpZfj{>)o!^@CqV(=&d?0-|% zG4emu(l}4ldq*36>~)cTpb05g;XS@dErf)-Vv4LCaUu$A;tk^Yb*2VjKER{yU)4|e?iC8V`hAX>Cbf16q5nA_zyx@jX?uUrc-WN3axtrnmN}G%xHu4eziuP{x_^!t)%|pD?}2 zp~@#HlvpR5Cip%`RE>RHlMBh;XW*gYKN>Lc-JCh_NiEvH8PkN_=>v(@z1Py^>^?wf zM=DtSw7Mqz@Z|0OesldF?59rXij$3IQ8NU4bO&eJ-vR(q4e@~~?}N@lYJl<^Aflx) zK!sRKd(c@M^#JS!jO z!WR^hN4;b%eHnL{bc9xyd$}tt=L7q`GJ~N`2FwAOcv+;XEI?UjP{mmy!+N$G2kqWjIO7eGnVihWRz=%VsYbl z6_Y1_kNGXdG*?F8wI32d_rac{C8xM2g&lF1AbJ1sthdhU6zdTe^Zz57aFbsI<%1SO z$Nm|AL!)WFhO0$=S@jgNvz)-&phZs)0f_avP&j)SJm%eG5A8m40KB|GL|U4@Bvb>hihjxkWIp$s)M>rCeW9*m zBT$v>s=`DDuJ5_}vm@Ze!xsf>dfSQsgMb3vS_Jfw1XGT0vYydCpl!+ELv+Pfe zd%YtcG%xMG-cb5ZtrqX4XDSv;=rt;?2N&dHOZ@mUSg6OH4qTGXVK)}hkVc1p3NgK=h zLP?g*-at|C-%Ju~qxJo2@R_NIUhrj>Po6>l^sZ?piHV}nw4voP>WN_>3Sssd1A|vh zY~2Uy78r(l7)#85Z?T0OKXAEbkOTxq_x)9Sc4kgzui=0~V@zA|^kX7hSTL3^<}uoH zyB;{rGk$56vK_#MA$ht5^1{B6Lw5I7Z8Cn*PM<0JtKv;t?MA9fqiwI zAFJ>@F|09donuf=Q-b4(&o{F#1Ok}xt;xgxmll86G4|z+ekRmC$t%7)dJn|FrvYl~ zWd)eqKUXYT6q&x)zb_I%BXiOk`A?pw5o^FHF+%V}?9<;l4DEM5`lP+puS2b`_($8D zUQUOn9&P&tnPWY&(_k&~Ok=!@`PHx6L7mbOalDRT#f+7vPAM7I8ys&mvAW1(zaT*m zU$x|-5G5v{t2(t~yd`@P?~r)!A?J^Ns$b=)us+%^`lJJe;%}LH3k?M_Po>CL7~ZRcOqN)F*7A`cvG8Gf8$tnZYgSn7i^)&F*Yb(JH_3(-pHnrulSmFOPD-`sb6kO*|_ zV?qn#oFggqjQ2GmqpPi%rc2LiqovPOMc(~@1dORfbgGdfq#xYSwO*)YHO*KJ)(T(E< zS}Ux_enn3L`ok5Pt8VAF))@dv7rTL_6^h{w&ER0qM4)2sLJiBoq-zCx43t@s=oIuuSz-=_+bn1DaGOM=+7+VyCap*|$oN=2o|Y9&V94=UOZJk~Ss_ zZ?+|HysaP}hJKq>$rXyb?iB*MdSB~_(pW{qFbOPd(+smIaiXUZR=1}83W+K6qZ)Vf zy^IS2Y1?Qj>!~)PFB+oyZyDw{3XSJl8xd|xbb}V=$PHp7Fe(KKMU|!i9pXF7d8h9= zv@aTjG1H>n6@k}I>{;JlFq}S0!aoEJ@|Agr)G-l96^0ur-y3%yXes-_}~Zy9Iy_2H^(L61g-CdEP^Fvd4p z)$wEEGUuPY zeU?hRa85T#2(4~4xt6c)2(5O6UPovG)-fgM3w$!|>1g0jQu*WtxTnQaO>4;gCiIf} z_%|0NlDir2w9#~c3AT3{zI?OmEf)xM#lM1n;b>rgGMYuZDD2)raO>F*14uU2;l%|F z6D`bU)XGORBIznJ&GbJSa<#5>^bYXXei_AKPeDKk$l;7(iF%z)HnbS|)tHcbj$F(+ zx}jqEWA240wVNXpMH3b&wr$!kqRE71dQ24%blK{2Fy`8Q4b7a0%f3x#fI^~)`BtOw zw||hW!U%pq8T+S~Ec@8pC{Lao)^dh+c%D42)!C!D>p;|1$=!_RH!w6UPLZ9f0$$%ms!DAgSuz%FB(|PH>!~VvKKH?Xx8(md9!g4M=VX!vO*Dq#uvR=;_6doOe-X?samHWntgxz2*;t zL+Aa9V2Xz6GHSh$4>dB3nOy2@Cf6R0UTsdZc1-g%(McaoHruo`oxLhiz_WPiQ3?L5 zIbh)dOof^j`=6`)LW&8Klditj=2eHe6W23{tQQl%v(&%Jhv*O8z0{&K>bDOFf`eO| z{e^p_QHNz#D}caeId5nR>Pb;9pe~vY^t8PJzHeM6n+E-t!?W%&rT3|N%5gsm4fC-C z)Bfk1-qjvGA!&|JVIy*VDL@hlCDo$^`6#GCq|0!CWwJLA*vXg}E3|nXYojvo_Ytyj zry2K2kFr6|ru-8i+up1{n{<9kBXQ$z&%0@1#5R>1M*CgCv8f)x%8ve}0rlONl@Pn9 zYtUkjM%Xv(?~}P1we3u~qdVtHS((!@i?<=p6!+vY5AOYW4#@Zj*Lf0H2e=&7;1W}g zHkSOsP21(c48`fDFKpVM^G%~<$NG0-QKGYje=PvDugfo8EAdAU@dqmwKUKx7FE1*X zR_+!y>D+H^O@3j{oMCqeW~Ojz?x=FfC&tWM|Jn#I`{9Wlmw=+%_}<;zS9otK!$xIC z&vwY#ZNKx0)d*CclN#CBdDY`uP?hnk*}fG`}St9Ao>$#X6;j zEod3CHUSG7{+@}4Gfr@CRm1nKp_Yf8N1naj_?@FV1W!o?zDF7Ggu^u?$n!{uJjfQJ+931IO>^nQz4Cafvrj~hj_aHLFHV0kA@BXKbrjP|EI ztG~qVYU}#Gn~xEuqUUrIABz(RJjVryk)EdQ{N$2m@2++VSo~Am7d~e3zaPw z$1`S)!7be*ce$qcKh?-WbIC`%Bs46EQlt}|ipQbh`rDyT<@+;IX)M0^Gmzn~i!sUO z3M`NKkUupk7I#)}!^1cRH8y9YbV}b(B4S^5Di5Pu*>g$}14n2B%pZdQhh2?8N%DXo z&&GdLj$k*6@ar>S*r}yoZ=q|eXm}gnr_V)tS}S+&+8AQfO;cK`KNARu{dc6L)6gdr zSM}3fZDW2U^dl&wyRWD`+L(fL+NKthT(XeW2PwA8>|1%*7M8Oag(*l>^7{D<6M5OUAsJ}?v&#B!jOK7_k zOpaZKq_aNClP_bDJI&TsSChim(DVvFEH0^2Qh{${q`SF2w~7gKMWcptBoU87SKoWs z8<60Ct3IdBjCN__m{_8K%2T;-R*S|t%Xk+lTS+{|DZ!cL5&u4tkt{GyR-?xP+ZGFH zy#T)Tc?tB;a2SernEcbObzAj4I z8SL_#jf)GH9A*8Q?J>Fkw2#MwN~h20+j{;VK9(X8283S}^dj>2D}?1oW4IlI{09e> z9G!eb;L|`tObT3)Xg1Ix7K7nLWfC$ zpUw=~pv3E^2M@ zE@1xP@X9zqfWE$CobqH##r`+9e}@u_UAeJuPao)&InA!EV_(jT$a*D%wwjNx_8$fR ztmqDenxP`Pzk=o{jfVOKF;t<{I6SFrtfKMuvf3^67>yiPH!2dkb9%%Qy{pDuG6!h0 zzqCxf*WIS**74;^6Xe7T>H}{Xuw%@Ua6iymD1q+k;wzpVf0hv!2$4(m4`|1n$p$9i z3{7?3S9CglZ#v8sHpp>HahlHr8LKqT8v;xmX;&$JLOj- z+5kUJlbI3l@(7qj2fWma03W!oKz=ABxMa>a?bP)A)*20Dxl@8h%x>XCf22IFbfmDR z_sx6xQ^%SnG--$R=kXYBnpP{QROJ|Lx=jhI0R_MES%GU)JC(*sZ~6|I?+@z13u$Zu zX=Hs6&AKc8JBNjSEcw#T?%$@s|By<6uy-M?(o{Eqf5YpkJ~UI+3vH`M32gJir(>P} zqP}yDI;Dpp<@_7gTtzezr3u*Ww}1XI9NC|YKwty`TKm-EUv~vRlZI`@@pR=Oe6>k} zA0~c2x8>BWn|39@)r6lG?)hh;Fqv7Am^0^R`->J^l@e{dQ%XL}az%IuqM^zJ90^iu zcP0!{0r=+1-vVj;dZI2&Hjb8Cchkr+I^#ioTt(j zgy?d^D3Mk(mryfCs{hWraJGnngRF3( z4Hb-sP~YjhM}1>m@W$SWioNy=*9=}d{9T4KcUlHR&nx@L&|~Y$#2fZ5e359pJ*BT; z(m-HM$K6HCzddM^0^I$PkO`B2T4tR7$U5SU;=a7eIZM>7pk`NUwo_mKge>&o&7aTE zAc2&y&@PK9?ibqY4RVe#*}0t2tVg+9b<@i26Rt7yQ)m?zQrSJ-S^x7AZ@WkBte=uE zC_Jm}GyVG0^%g?$?-utD;XB_7wlz8JQRK-eqDEtX<;~1s{rcV3ivk?OrU{?Sb9nD7 zi0n0G+rX#TY7c&;%wJ-~@+Bqxj~=6t)9((N!d9n>=Xx+ayP>{yR+4WE|*t`gF@j!k* zqUYy<{33)u>vVj(EA+6g|Mab>sB;v>ThGnF_Hgsc-3mzK>s|$1W~lxkaNUN79{VOo zE#8ta*5^DmR_1eLvzB|fJ8S%BDckwaR>n+#ow0}>&U^*V6q4`DYyF8V@ZS(5x8nF;Jr9*e_GY2HNa$ily|>!Y5l89Hwg6@p z#?!EE{hiU;c#^xcS`TxD7roQhr9jHtO+J#)ZqjC~<5L`+Os@}1Easo5#7XUwc&;A_j2Hmm(jzk_%{+KMzK z)+>2dt`jtaQLN5Mjpe|AOZ!Zx@^2jHB*9v!>* z9@hICU_~!-8aut}aXo#=OpeN{CsU>TyT*95L+32P6 zQ}uKvW&TEpjuSn(InP#A`83q(UggXYXH7dfegR#QOK}r^hjSbgcBurRqqCOuON!h;Rab4aH(XRfR z!ZO~cNws8Q8(jU6_9-Dh-oC=;r21`bOmdew`Z39w68fq*$*k$~L8#M=JhKl)z#n|Y zuzSd~U_9{3^8ov~qiWjZO47LfSEx@9hjn)>-o_^){dOr^l}Yf~*)u#pMn1Q&gXmSS z{W4Wntl?xH759K>dpxsm-}xtW!t&emec7DR8^4U1YYOYX94(Asf;2IX4{p88+YcH7 zPHxRYUW-?`A2xZ>vlaXG?$squ8`5fIOCLg*Xcg1cNo;Dw4N2l%={)B zk?u&7mi=a?ac7y+n06%Q=2@8!_s?4%XF?V$gcsR`$tn7u z?vQ2swk;*L+2>h@J9bOsZ}6w|0DgZZ+NVEJNgr2D4%ckN^OZ33jTqp@N_jd~Dsn;J zEOPf~E%TvhmZ?K@{ccQ$?5cW5$UXYqf5osk+0ZfA>TaCZal2|P;l`ubrIh1d0!^iAB(~Pq08?Vt+XCq(!{AB-&czXAEH1pJhA+mjtz2^q{aNbD!g!uzdJ_1bK z%cbDYcdQQ3Q+{&F73&K5R%iW#wJA|Iw5 zT$I%n79Sel*ZNNZyWrX#-*U{je429bGL@tKc}Xo7nPex&k;J^OeO*OGFF0=!ZFPLA zWwKffCtn)DtNI5NEv|u6ksi_Z3+i^ zX=J8pkJ?@h8b{zI`@$PHxp+4E&~WCpC%!$hmSqP}$1W6kzy*2>Xf~Y|-o0j2vZuCq zjcV<*d~>32s;I;XOQdLdNP{U`8^b` zH%Um<9o>v1Eh!>79MM%#>ft)}2NPUgmE$eN2yXLul(M*7*U@3=M!~+h?adin7M?K^ z_PW+#*$-nopOn5*xL?fUcTqh=d4IJI*xsgq6TYA} zeYIOU(`QI=WqcSs^S8U2yuaw7FcU_AZ-l4cyY1Yii|=P@lePG$2HoHxFUUZ}kU8G4 zzwzx7#+c~P&*D@KXa0b#t!0yCA$u@=`3{EGrcJyWcwzepVR(?Bszu$xs5#bLRofP~t<)M}64j%)Jx!oB zF;Vu0+RI;Ezalx;yRmHcUyD5^jQOxRdhT88c$imyNAv zG~ZT7OKa1*(YH~Dhw@MX<1YZf+;cRrp0~%9){_DV$Hw8v`n5vzeW+@ku52LzydF$r z5|N(sS6K9sDMorGY~B18XnSWOQm?-JTvF@IOu5 z&fGI^EokT(A7M=CP7|(jMSX-7TfD1_rdAVI4TX*K;t7pdZ5C5qR9u-t17`Rf-UwV2 zc}tat46pS$3Ix<~{SzEZxu(k5_x8h(@~NCqNWf#U26?2iXQ8?1(F<9Q(%+tW$o^EP zmFwd35X_=Pl%lzB7VTIVehNX~H{N(lXQ)B+of?iJ>oGR?jQx4l?a`#O#~*TxR%#xc zpy4ga6+t^!?%r^8hfGZ&^!fx|0uttHP%!B9_#A+oc2ufk$VoWnF*7*Zk13a=yxy`0 zcU?Ob_acZNy4u;{5&inVtK|=_6&SQzw%HWnbs=49U1_PB=>q_sm{qO})+wi2J08?5 zZ)$~e1mMy8tX3j9Wd*-C)htx#iO--PndxRwP ztBR^w0V7|a?2iv^yawexni0`@0>Mfu4s_{xgk`WU>z1yqr^VEo+S-d*7R$P!ArY)U z(5YPXr8m9aKx6p^aQyTzo)r{UX1|T*odFxY>!)vWvH*w&YlV`oVo27; zVz;J43KkwutRcV7QAUQ74dGk?+~{W_N0;N!dKz2-sUeij>3#en0Q`A`asS=)@%^az zbDG12U1R?*;ESP9B5(kpbKC&5k!i5FmvKJnOSN~j$)m7@hnfPd!^nzWSWH6TUXy{b;Di=kBreYHwp_eHFBa4sF_TH@$h8a1HsokT$*90(iz`? z#})w6dbsMaWyx;I=*-q5nd`Y%st&m4pL zGe+?CYw)H4XM23V{`)&nO-|fX9}u;oLDTqbOMO88ASs$E#Y=7i5ClV#7t3K!?H*h( zSL5IWCiV4@@cFbXJXXdmmb>K7cx_6Z?M-)!7$NQ`=IG2nrgS8G__uF%aHmMRbB(dy z>6o>}mQO!Fc>ug-3&?Suwsh@8^^#o{J@6-Aze^p>EA}>ktKZ|iW2L`(DM7lZ1%5yJ zi$~jin;tTS0$6oCg&jl4$Hwg-KMa_!5S;00i$P+O zGPAhkbH#?%$XzN6Z#_1OFel7A$D7=v$Gtkb>2fC#iKEP-2={WiCwro8f9DR@J?So_ z8ACsqHkD;;07Bu5C7d#rQN{1tO`yuU>4r%SO<~h*-xq}j2R!N3F5!F-E=WC9wC?zT z6!c;;=culdI5f_dj$Gq3;D`u7rArUD=6bqwZMQ1Z&HcuP4 zIy!97#8fm{H|`!(dhp4+J2b-;2EjK{BDWB|{O=Z;(phqU0}pEV)sA z{Q@R`*h0}V?n4d*^{cBcmvIu<`{zr5a%2@?FY>Szi%vX47jHf+#lO5~D ze$1oiKt$~C4Ik*sVG!TGE*6{Br`dpOdRJi^7eWDaZsTJBH@m-uF09CK`erHGP8lYs zUm(8+uKAHnR*ZI`rs9#(?AK)Nj0Mg9))%nK*{Le)Gxn_{1l&;yxS{&s;hLGGe^YT7JYU@(hYWy=AZsUS9szLV+ZbyQ);5azR1(3d zMB`veML&&HOx5MlVT>OC_f+L>DtkJ8F5I{TZMQ?s09wX{n+)e0;-ejRxquHfLV5;zXF zI)YItpoA?=)$K+*+%nw#BIH-=c<27J@Q1XWrxh=9u3${7sYh0}zG=#xVi~{G!3W;p zFT#*0{I86)GNQMU;>vr6c)lTJ^VMtVnL@SCO|rH4*F|m<_qTw5J^Stpo`(4jB}N$S z;^zQIZbYt@SL4-Zgx~b_nD&l69p9bv*>5X9NC79lvUy285Y0f6p&`JrxV=2M0k+uk zsxkEAujhXVrty5+)V?z_nTnIy?88Z{K8{XeXOFHQF9TE z_NRXNH@bX{+t=o+pQ~9^y5K^rn)PTD)>*mJfs6~2VcM_-tNvQDO$a{6>Q^NF)ABo~ znwp8<1$0=>2`#{=T(6}{3C{i*2SvedUv`I4Dv}2tCl~RZm?SShvb4fyb2bg?G4)0F z|Ed1zWP^AdNur>5@q?}V56mVo;=7I-bp_i}WGL@lbqq)%K>*!0PJEtL#3zhUgkbQt z=Bv9CPof(M$hx}8ooArT4mIM@L@G_<$0RngCH0y}bHzq=@ka;-3Rqvz*!VIP+m)w! z%KAEw2^Hf~m(*sdkGUEw9Igzdlp1~hubS&8z~uNRc_~_KkZ5nq_c^BOc27L8e1NX1 zCuFlnD(rc=5v|=VcNq*oHg-v(qKyO;y=Tu-IbEfZe;#6su>FQGNkTcH2CW#C94ZDJ zcJf^di@dmG?g`3;JcvO9xfn^0eurKCjmYY_bZSAly!bImNhAD#iJCW`6OF_~ai zCX<`R_eU6Ao9=|gK^|z@TrLRxJ~ZM}2F3(E&>ccPs>cJ5CW)c&-=dh{){g8w=xzO> zQ8efWTw-O?qY2^n2c&_K@{xfepBL_)p9@<*1$0nA|0y^?{}l1b15;721}f zsZ1;ilqd{HuuCc_VuZD_F22bAQ-8aWNZ8ILK14*-~8;~Ee%MGPz`bmjEmBgFnzWVhYrwmD>+ zqTulX%yQJ~YR6!Cx=78OJJbwMMfL_#w}a*Z9l+u8&6r=9U(@hIjLRoqK8zIp*|v_l zHq$K48*iPSob9D5?(iYK0-{1j;k^MJ2OzO)5`ZB&@+=g6BP>5x z-x_#cjx^1Kgn3YG!s_o&`wSt+M~N@s?Wf~LlsHK=bPN5Sg4Irud>A|v;xV%W+CLrF z8NAdJ$cKzLz+UZbXAo*AL(BCB4N;~inD@|0ajElO7!Pbd5c3Kp)t_UV0bQo3p319> z&wf*HTo6a1#|x$>6IvZ8eqGugeeHObxW@WbM ze|-u(1{L~;t=wd`Ido=ajQyytRAy@YVP)(D*v(Z>$7a^1@45KmYxIb%!U*VTeE}h) z+~E7PEL=2Zzv>q95&d6Y#V23s9TzPJ^Cl77(=Q@@fyW3;Ale?SiK8b0R|>Fzh0tb- zg9=}}E3YTMm>MX?VTu1L>#_o$;SU$pFPS8?TIFgi{541F+5CBg4u%4#;QE=ZV9hX6 zi+mcO!@|5rFhF$=&t!QGs8*o;IU0zox9y-^8;7t<3B`+w&mN0tpM(M z_8i;WjG9Y%-OcR?)^!lM=-4H*@Cnp}eK4qqDO>KCE|S}rTDy`RS)*Os`0Ol(Ewjt) z!s!ekJUeh~u>g)!@`Uz3L6_-DXfx@a*j+Fi3g8LB9dqye2PYx@u^cg>gK|yciLDW> zvyh+8GjoUDX+#X?ZwJMR_j*G(Ssm!rC_R37qOf>O8bEw@8o#gR5|ySJ3w;8uVE8~5 zMnhHb?oDy6)7!t%rMEt}TBY~f^V$LtmtjKjs0V|9mL^#2P<5w*6SlFSN8wV-OHXf5 zEQo(ZeAvv~qJgbFDfIZnM7&rsVvWhG z8M?@NG|x#6o~I?jKRF?!%=D6;o+2$(wBM{h5~Y7UuH@{t*mAk$ZU@?Q>=_i( zQ?S$83b5kiril%%sa2Ode%7E-C15CSiUOogUpTIrKOgUU`@~6n6R@!WnS8?mUwfzP zPKdBvayc%`biRx7x~<|WLg$lAihJ!O{m30j=8#{kAj+?a(=!1U7SQ8`_nH`gfvXZW zgtQ*-E_lLAa4JNfu$(4(*NF(b8^z_|Cda0rt8B=xK~GQrwr0y-@x5vCaW9p6o2tWm zJmt^Ip9j!Kz+L0Oij0Pg%j~!Bivg7vp3BPdwoB=|U)M`6@kAy(Rg}wAdv-o2OgsX7 zRhfS#D{fuU_l4XaBA)@8>iM~*avjBZeelO*ya$Cu_ayhWc6!(c#r1~wQeSz^#2V6Y zpY4#rtdv>By5=xU8Fn>v4)|tu?x};sWwsiWXH6IP1!(|ed;Ks~eYG=v5h*zqJ7q)9 zeJ1ddy-pvzFL(#<&j>pNJUa(3zYa40#sS3WMsz>+3!5HutbD&@YMDp|G7XXYNs#}1 z>t+Th5IzXGk{naPAMf$;GGxnhUjI29`3xWdx6BsPL-hB*I+X@In~8fU@zl6ci3O?y zt*1_C=sohoS;jt%vg?_Bcbpbrs*zMz1>c>AjLf(Pa9pQx@S_zM$5!u z+2zNW-)$V3ubawcW{5SbH?YyChg(T|A{iHxT=Z1Z7w2(-Gxf9mP!)2gHgPWC7b~Sb zeR%sWWUz?%v^8;*PN9b~M1@Z$-czwqBjPSM*L-K#wfbT@1LtGlc+(=ZVnyCK_%M7Mi82eWW?x z+dHmB<8Qv6suyR*ZBU8AP2UTeX}IX-FMb2;XM!XbWfPld0Kw-gE?+MP8R4k51=_a7 zdyqa|I?FCNbSSWAe$pME`76!YXmSREx`aNcB-Z(wx#PaAs^zUKyU5^OK-RrK**9(m z<+D2a6pqpbpRXMN*H#;ggmUMU1wIlo!{|JRra#A4AJff#9}Jfi!V{jvOL@M*eb5^N z>rvgMRZmWW|IR7=sQ4EP0L$0F~$ZmG)adWsI{a$T)Vk5S}i0Lblu% zxg6==X0hW+Gww|efJB8!)h1O z#hV%ET+5w%K>m*1bbxL3Zb=xO(vvr4$%{j4+O5Yw4C)bm2QC?NQa&P@TNCqj- z#id~fzzXkk^$~6F9X>eZyl7I4GmxN?F~Q29y#E$@cvJ1+jiXb`dY32^)L#dbF4dYv zh%EOzOokWgO&vGJHCz^e>Om2Yrk|C-Hd^>gAbB8DbG(r`loOiH5KCbI**Q&_Ce6M` zgKBo=&jdiRdv@GwfGBz?FXVH12g;yxu>F#Hz5BdJy&(kL@=qguHvW1I=z*fe zss^+SsXMHlCUx6;q~0#eTYjNh4_qeQe@9~kg+3EKvn`@}BDkBEh3>I6Z!Gwvd=2D4 z)dOAWX{CRV$joMPkAhA#c~&v;&pTV&E1TphoN;UxG=mEV^aaP^+l=ZSt9(VLl%TKC zb>oCDV4qY};L1FWeY_phJ!%!XE#S!p4pa{}BkYVrcy-J^St1= zLOx_%KS+=Kt<#ZBD3iM=rd5>P0V=d;=$%B=j(N!H+{9vSx!Y*l>hrSW8rQMJn?>YZ zb`zfa@0%lxp;Jef`$Eb%9@uG4@iPizJiMFclU8xyt8X_$T>yLA=Vlg@`fpumYzo1+ zu(~F^>fV?H{*p;Lw5QOqn%VmX)+kZ^yiyEZq~gX{&nmGk#WqvEas$FEwnVY`l5tbx zJ9d8q_o|}a^sQMxrR>!nSAetKwR@J77$$TSRkNxcE2cA`$?aC&$aiw^w{G@HwS%Bu z?q76@)>guLsUZ%GaJ?#0%EiitV!$#FMIKbJJ$;W)Po(^G0f=>(aF0`9>OaLH{ zFYkNsOr@056i;~U<*();%4LvIzHzo%tB1h|~|(qq9Cc1;+l zKk}l9m=p!f>tz1L1uhzqgo#3Emo(u(9q6GCC$xT|Od(U**&O$Z+1)OWr|G?qlx4+D zD%~s{Gpmj0Pw5uEWdL_VBvF%dypt}0aibrYU(n^MQQt5*c^hNth=;TEm6ISxboN@S z2_?uA(@16ZtJSQ+yPlz7yuHJrFIU;*`JZbXl<`zj$ho;LWS}wrMKEp% zqiTx28QrFtPR7~%iKy1smr`d!<-Y2ApATGDoSGvscacOXC!4mN;>z59Y+R zn@?1AzWM_!lv9;U{1x-AcNq-ywCeN z%5GB;;fGDne8raSuubm^G*b4wR4b0 z>$xlsv;;bMFaHpg%uyNmuW|m?(CPX1AY%L(KPDZ)W)C<758NmI>F7)`kjfksZrlJg zp{o>9zyD2M-Wa|5Kr=D?;%tB$`;doF)36`$+vX)3SKrIN^K~CS9lSCNzKTY0Wj?rK zh>s>$-99nZ6Ki0jQD>6&5V;u7m}3yuQ|&rxI4$r&CTBlt*RC%Ul zf$JBQVABAmukh5Ha+*Kfv%lC>^|%7%Di8&Q-)T3Yuj|fO<5O4cPJx2Y&QeOb-srZz zbf!>o)Jt@^7l6Wb_X4-hd&#Ul-<9Ov819(4N?3PhQleHA+~~eO&r!7dt>5NThNcsz zqG6`mVtM^0`4!(u;k%OFTfvxmmB8dCokDaGBF)VXsUH}tta7mY@i7Sq^(@413mbaPPtg@yGg|QyZ57v_-@!P8_7&L77jX)y}%x` zCTMMSZ4}93KacbE8%NHT1Ra{-M5fxZ0KU!>#bk##qU~~MCFCkMMvmK2Oy~DAy!O|t za}1sZS6nkk$AF)}WpZu9cnUQ*X-@O-8YPE4;9k!$LK|yt@!QW*UUzt6*JeRyH;O`4 zN3)`=!dNPfllmEIB)&|!$z_+JM}-sKyJp9xt9}KrywgpEtF3zQz(=}+z&_ah>8;#w zQ4?-109`lvBFQaW&{Oi=dwI5v-Qb63D4Mmr=&It2@8k9A3M9WI&}cG+0K=nmH_x20 z?-b!kLJ8Y1yjJx00-iwBq&O5K1zNRxP=xOdGhzwv2CDXKdXtKq7#tJddN?zF79Xf3 zblCP8n-k#ie5G}kRRq-Ef9$8mT=@-lw6ydGoRcOtG1zVn&rV9pkEJ%sB-cdg!WpVc z{rn_QYaqktw`h_DDwSq!#vW6ZybTqRvr1az*MjVQ$pO}fhrCzgZzd&&fc%S*W1E+V zmlY#iMu0NhaBk`?un|H~Z5tkTVa{PVGbXV9`O3f_s zPGx`T5r7w_yyK(kCX?rpLZCewQMC1th~nOM!!HBYs>c0FbZb2> zPmBgcg2YQot@N(+@S7HWjJ+M#-y3GYO+TrbK572Qbr#4TJ&hYn@_pzvq5HnVtr1X4(b$ zON$KO(6@!5>p_qFqY!RqRYm1YuM2)RM-{+aOKFHL#7lH8NpWzriR{4?i!to4mPw2w zGe=oeNj2}&Q5ZoD6R9ShbN0Vk=vk#Ph1}7=AD%MhHwgE~r9}lxn4>(2Fe%mp%=ecs z-8F9wvJFvM>-5U*+&c0P5#fYxqK+fcdEbvK%0Qc%IA?zcp2f0608d?$q`$yn&i=Mj zkS#2_>?~W!Ff~L6QbX~yq|L_k8eYcaqy&D-qM1hgPyptuA5 zmywRBh8J-*nn*c6md7f}5A+<|dG!pm?y20H9~4F+V*kx;ZzljJ_2gC8sn8_>f!y!pc|bJ@$~{k5}7>)JH4!=yu5(ob}HSD zMR0|^c0f&~@?U`2l2{!kr{i*)@4m(6KGj)9+<^u76Mcw9K};lbC^j~_aXGw%p`eZc+Kf<0EFq7LC6 z$WksZU+$oX(-l$vt>5tv+@iAA?6;MH-WQ?W48$r^-%Af4oqx@4B(hbf7tflKMZAk6 zy5VWO5w^0fP!`y~f0o8hS4AxC&oI35DYcfB}5CIC}>0BX{{}CDh z8|JZEI%miW%1!~V{bX0As8Em74NPJ3N2%2Gz^%9+>CAhdhyy$u7fX#kW$yk2@$ppe zn|-$|oo7woO>RyuKN}?F1lZ%~YwFEzaF=ID&2_PB^eP`3;z}!tL3&X9@#6l)Ebjqd zT~5a;(EJ5}POP|Vtd{=rr#rn;1h*siu5WxQto5YxrAOND9r1?E1r#S?qR}b_w|4VP zT}YApY}4^4n)%gGq&L%iJ@lt#<=HT6_xquN3d${PcAol22o`8ccMP> z)m8Z`YAc{5B@^5onC445od%#=>;k-{h-%=*UX=XJuVbpz4*q4+^hetaxA+tDIgr0o z>{NFl*MuFE?RL=j4ZJhj?XmZ^J=TKJj*=(-?)cy4UrAGeq`xC5`be)@BN!bMqo3c) z|6}RA1F8Q0KYp*B?2tr8QTED=$ll{}FRnea_b4*T$OzdZd*0#NmynSevRxU8jI2c2 z^ZwpG-=Dv6&wZcsKIgn%ujlLW)T5X!C_DJtBP6_^2KrCbKr?c;AC=DqN4ws)b@)~<<_vx6 z^HF+h1Xw$Oa0En-obRGUm#aQaF>D=Uih-L!HVnQQ_$)~!8AwlN`qHy z!OOC(W30>q0TAHpkWo5jYq-adm1QG_ma##cJqMmtKQLFdZ&=qE&o8ZnZ@Jv>>}%{? z2as*c1rpZgm%$X}_V^Y0^S7|hX0T&AaXFXGHOgDG2|JGbXR|ayM63$DIuHh>@n)u}XV0aiAT2WcvB-*U_*b*Bxc6)CMXmaS|z}|3dYm#5Ec=x!uvj6P*$-G_;R8T>>V! zq{ag1q|Yw(dGqm+o1~X?WJ@rzr!Nxl$5S&mhmyjkSg$KE31Q*iMz_}w_Z#GSey%^f zm3Y~WufACNbm?#rDJxPX&)BRcy*ElOoB#I2fd5H`VsFgeXV;643f0_3@6aV@k^do= zF#%d$!>qErB1a<|SEp>mch!)qxk1>2M}&ku8>{!4Y6l>f?iAwN@YJZ#krfCKs_M=Wq9p~E-8Egb0|_36Gvx`nSa zPNts)&xwCl&7;qK+KC?44gOppaKHC5U~5SA%5+j@ZIRCRh|_ZByl&*g40PMjw0Yh* zXzyn{zDTs+VsiV)XnOxdlPdVmq&@Jzf$B&yHYRD1MCzDI`t@m&ootT=@~3&BunWDw zM;HCxZt1;JLodZ>-^?#;Y)D~Oj<0f$x(S5|6-J~m^FM6{4QmCo`&<+*k*!;G(arK} z&rGSku>S!x0hHPgW2)?70jG!c-!|pFvYC%>eK_4?mOi!?CG6k+}&a< zGl7NW5O$|H?CY1Zfw3_PFSCu6@?;=wZKR&W;Zi3~_$hKPipj#=^bhE>+s1nUGNu%; zce+ufWFjI*?1@>2MOF6L z77;YG2SqJzce-DyE)c;PB`KCIZ6;8`Kli?vhFI0U*gE|EKFZg#0~-rA|4c~;q%0s8 zlhm!or}!@sSq|Dys6DvlDnp+*8oZ`_CBGo&ZG_xfo_|b*GDLlsUOZUnk8mU1VzU5O zOzpBGykzMk;uMKfPK6|?wz$Rm5Un5BCGYEpT>R3(hUs6HP)@QH89kN_S>2OJA_T%1 zz{xF%7A190C2GKE&X}C!V$w+XOiHET-dm4{88QYInhzDpSkHaNQMIhSR|s#Wqt30n zXDYWV6P0i=TO^*-Q!_eWw;h!8zq{i`o5^-AFMj+YI!O|GE%9t!;iijKzEnomPfEQ= z_W#z?L10BBXFxpt!S|aS(M&6!$}|Z1k`K4(R-6*=MN)Gj45LPh-XZ^_{drV0qxXLM zLm-B?+Tx>-K!~Z)Cu)L-e@GR*zY>6n#y2mu8{(0yC4UI7X4-IjEQ|Hf+%4yB2<78h z*iY_rBD)iBy&FgWu|vbCA?hWM3S)D{qnXwW#(SMDriE_C2i`{0=u4%S7|2p5eyf^J zV|%ZdpLNHJKULo>L6Da?Piogy(zK%pCJTM0pT2bkYWrAQ z@j~mAL*QVR*iVlxwZ2@z`nSa57@w{H!+n}Wk)AAD`Q}lorGHtBbE#iyEnhv%Q(dn< z32uy=6!2QjbR7Hb;{L>+>?%n6=GFD(n2i)f`e3QSH@y0EcLY(IeZB&jP~&PfN>M+vFz|K(T$r~~ z4K?dhsA`2u3sp0djFrX8PyaiV^VbMhPem#8LOytPM(I5&U`^* zRrglv*9w8vN`aRqaOxSBVAw)GiAllOQIR*<}+cG8N1C(isp=1_IQ$ z`MSBsAB?RIwGx=|ho?b+Fl02U!w4J=ziZr4CeoJ$(Uq?FUdC123C`+te}q?_u7}_V z`13VQ^PdLD>7A(g-3zyYhfk|oJM_C8<+yWDXmq4Bs%DXQ+N>I#+4%;-Jh&FNcKel# zY?mcWK?=tpX@}a1EC4`k4FEz<;u*sy%it~tITy4dZ($3%gM;$R!cf_=X&`;T24tT( z_<%@sR+q08vr(^%74KstAOsV07c?wYFSA|zEm%|Ixakq%a^X5CGwceFf!{a7Fdeky z^sV^BS{M~7L?j()6<$7#R-h!rDx-kyS`flw7QgyR(QH)ORZ>b(B|_9UL-lp>sl7 z4HY+hrsFwnfxNg+tV0onqy;j|smqH`!2YIot_cg8Hr2R$X2}y8hVNA?t<+u<;(zEB z?N$6fC!yuV|^sRwUz_dj-ed;4s8lgO5ggs z8;X*DVam9RR$w&mP)R^p$d3XAy*_!1vsKaN@TizCqI|RYfP&e_mH#{kv~&}gzW2pi z2zxoBOb1${84ELh$EG(g+bQ3&(o$R<SAPm8V*e#7UIVs_(CB|HPdJ7Di8 zKL=QqA(_W1!&)TP?2G2=-aQloIvW`9j8%gA;DdKV6-%0umGwd4X#iMl+RjwdlwX)nwL5m@+mPKo+3u>y)GdZwSJ0#=>y}2a$yBG@y78k|3GSWuesbui{f=df) zpX}`&Dqc9ZX{G~$#7 zFB80dkXJ>(uo&^H5pl^>#j48B>J*q@qpaZ;6LzM8*=#`$N;=f;8XBXVnarNPFN*o~Q+g&^Aib0-)(}>6W~rjsOU} zKre)g-Ef>}k1&nlgA7Q`7dT)nt|&F~#7mNKYHTYJy&y6abfGgbw8q;dDE$!X(%nlL zU7)d)hajV1G4Zf>Ix8!kbT07hH&icnL(k3K2ZpS#bDPT?XXmGYc43ln3UDaBt|A05V18zhPE!0?=~} ztR}shWP(rEk9Zetop6fRCgut;Q|WWU8>=~iFW$XAdw8ghv@qM>!7*O zQF1gu=5il!uyIzePfhc&-l>V<`BpX!(KyfZUZU1Fjf952d@7PojotdKR=0u&P>FzQ z`f!;QBwW4}`;+CEPY+40X2ANQ^tF%0G@Q3}>Z>p)K!a~IB+LgSV~~9IH;e(G!g2Bv z>|595IIyBko|}S`BNAqN(zYSf6MO9aMFQ5_%=jG!a+LqPAPI|aa)t@{v`wg%*e#%6w?bxq1vu9F9AatC!9hz3zq!U#K8G0U@wYvwF=N5I~88J(157 zL7^~5oC$VN;$#L|rQ+|*&mLwn`3*Ie#f}-nP7Q8mS%5MfHZ#t+1()gsrOU#}+*ZXC z3sjA6MqCV^X^dkVh+ZaV!7X`h-f^hDQpXOwK5ZBp9>uLVT>`@Mw4k;Zq}JaVWQoeM zEB^f#ZijYFoqm5w{Uk3Z7sh{};}JqgE#VAW5@8Nf1KYh#XF+ez`3HrxnhRy(MtDh- zg?%*hPCW8hTW;Zs?ZQ~nfi}|w_MQzSWB-0-Qv?e5k?$F27_z(rK zovUh}1?^uyMnEZs2Z!yCzkc(K`;lO0nv8(w#$A#-Am~i;jU2U3TQ36~Fu&~s4)8`z#->0;O7qYDMAVgEpHP#NtZc9B`uKoTlOv0eG)YFp= zoM=K!lQrWg3wOtErz3Y!8%oUe2!MsM^6692sDlQIo3m&XxA)%92g-;JGO(~z`RU2& z;;v+Lg8;0b#nytzg9^FO$@RS3Q1105<*`nBG^!aji_AgOJzJEy;vtC?A(v4uQ^mAi zXIK+DSU@&3>36{ICT?&LM3*{KED|msb6^!#rv?qLp07e57@1z9mw~G7O9puh92|q0 zhNDfwQyGd}vGp1~Bisx$ASK17=j6W*-8;kS$`AcVe%GU>O_I%x4g*4hfuB~mk;6%9 z9IC5vZj(p-X~Y$@_wzWtVH|xkpa1WgXB~yzdzrG|Ni}=ftB(G~^9a#`NZsY9jFK-dMBWea?m3s)dR8)Gs>t#4AG#N%ZYoy* zJC7CyaGOLLth5uLo`gS+p-ww_5WTN!^pS5O2COd?8@JyOB#8qw>mkAC-XX#JC$6`h zTrDT&Tmr$JRYi|Wb;p-C?IVA=z!$d=(ph(ud`-EYN=|RS72fNZG4^k^41%*3@zafiFs*@GkKzWoiocS^+ERd9YR103J7m!5d4`z%a{JjxG5m(Qn8Jz?t3Nck zVG5}8VPkAb*OjQj3%^S=;Xb;YTQ9jpwcpT1N^mLY7E@h49N)rqp(J(CsX>48LIZt2p* z;;?jh`~oKH#ve-d^@f;A@%4;YLxty+`Pmwb^^vp{ItyG2(Ba}a$}w%|Klx50S3Yc- zPg+Tu&lKbjCFso^g*ekUny2WpN=@v&J5t2Bo_D~<$ca2|=Dm-rwZ|Rmh$(EhJU~KY zA;hneu`kR*yskAk5*OzT3Kazv&vvFoUnfv=kNOL{#4*_=g=STLk9$rPLvMI*nA~%s zM)+Tc!j$p{01&mJe-nXqg_hu!1`9;-A6c|aGg|cj#G8MwNlm3!#6aECM@CTJ+!`$W zEh^j&TLh*1AN3YYD;HTzZ7HYIcp0Ug8FLj2`VR<*aT0;%#ge>GlZr8z7JH0IxcFyM zNy41pBu*)ozb{BZR@@j|9MAuPx^D-jb`Jfxak=7GX1yruaB3s&S9Z z5Gf8duCj5acpGb?amIkDu|27le&`@j0o$%+JIiENsEzDhq0z^-RF+MZ(GvB*d@S}P}l9GZ>0!>O`kKx`NOPu=!t^(2H= z(ygoaiAFota@fpMgk}Mnqp+((>H-PCjvE1l+2ssCd6ez6XyAogWY$$;C731?5r!kT zuCg?Ft`0st`KikyTxF($;^o{b{KsFBaNl$jc`$Jm!fwLIGH*lyoG5ubb zUvf(79*^{{TD$AFTicsX2}d{$Xo#uL^qG($-`b4lc`X`3%|_s24iwX_w{35R_)_JB zp*j{CEC{04<+6MP7eXwM7Av2qmV zzcFO}C|0=2Ro(AXS@yVQm6sqM9*j41#n6_E>HpLAqGCULc^E$&K6}b$gyh_6%?gj< zMKWZXB1q=_AYe6I(IpB%>{e{#1jOy%>&IWbb z4WDL92gli!#Vv0@XWzCck$(7!m<3Zf+1$c11dpSD#uJjo*n<1KloDcu*IpeU>d@GJ z=raxLsj3Wzwl5}8YhkQc0IG0)n13nj6>f@L#)2So%_MQ<5v6-6H-RXp@@3__B%#vW zA%o}m^|YYps58^NvyLnmK=*ijdDKp6@RpGM+jXhK^zS#qCH`$67Bvn@2tzMHiq!&< z$n#(q^wf8-)CelnXp;reRh<%tz~BIJHULmvAErYP635P&m-XXmZk{yfHE0Hs2>W1{jkOFe%6e7P$T_H<#VU^Ud-GcwE#9nN z+}cA8wU}4kzAU$gmGK?U90OS1i+>OJ%s%Q%F=wEW;pP~b=2|_)rL!DH2HoF zZoD7wX!7CLk|sC1Pu8^uPHx564i3q| zswe`UL$@cmaHYIjm=+dW-THue}>gKstnU>&?DT(SbRo?ekc`&lIAF~ zw-P!NJ*p&z8;M-F3iJy;#|5-re*X#vBw=i%lpBH>T$vD9R$m7YRNn8af~voq&0I)f zA0ZAO{U+5l2R()QUEPm^WIsU60rKjdMtAwZT@X^_P%$fh`ga$+1zmT~Ox+Qjdq}_p zvCQy*;hZ~PcE;8OSZGi9_jMpDS{6KjzmoS0m;GxEY)nrtqG&g?=Xi}&igsOzKwhM1 zBPe8Ks67jO;zexU%WdK~Fa!N|ir_>YZfLe$qn`;U;l)b8v&%sT`m$BOd7@%V?-J=0PU&srw5{J`Ok`TO5T<0JgIc^+5b-N9G9;1{!*WH z9pAd)Kc8eLA)K5gB%62qmIp4YNHzu4`Q z&^DUIF9m$LkxDhg&WDEVBi&n@x&i_OnyiC1i_1mu$kWC7orMS2-+hP$^+P8MSd5An zmtbq$(lnDC<}3>uL+h6&-IoR#aH#c0Op9f79=DCz*?H?CJ^4$f<5-|gfyw^$ab;}t zUX%KvJ(ayoAAas4T_Vql^ep-x%og`jd&oTo$=f!rruoJ>Kf(gt5%4zm*2l(q{Sqht z{r=tg!$W1z9S(NuCH@iwmr&VC*j7~2pA;v#CrOOYB)9Qijl58HD{$z4_YK7h!w(v& z@8R#>{gg#JrD}>e`P}cz@?o35P~o#@i_sp~dHv_)nzeoo$jm1F%98L;v)+1$ls~%_ z%nVv#N>6N$%0i`LUlMM}8uopk;2P%XZLg`r=A(iDd349Zk0$q9H}wtJM!U`EXKb}& zlrT$uTVnm;F|-K@gU*MOuo(?BfQ>gX(~`vf1Sdi?v#U@^zkUK4&bwDFcho7v2_Wne zc7XNb@hw75Q3#m`VCE)%GRkmsh~Vb1GDIS>l-SJLgAfl4517oO%l8`k7}UpR9eD>$ zCPnsaXzN|nQ8A~jlL(atwq+{IoYBMsme~cRG=}cYsFJOyntCvi1@WRof*rQ1+p2^26p;vVmCM7wD$WKBi~UX>%9w(P zqs7pQi{6@P4jXevB29CV4v0pHW8>x`Vi7Qx*JO*RuRHdh3Saul#bVM5qO4J-H%!)3 zMM>G7{1$SM2_^c*F^{^R#@0=P2y-jsuCTVO7%vJkOgv9uWhUTYY{&{AyF zjE<^b{VzX6a;u$8fkavO3Qra3!o1oSZN(!tnr2t)tt zv~;|#hCXMiqxu8<=Ppsqd7 z^P2@x4MD=KWfBk1NV!wM-hl4{hr~nPkg*%TlqqYC)0?3Thm8wA4!>bOxUdS;$nHej z8S>tbk|Oh$g(ip$xNwt+jAM z1Fd^QWl9NxOPbGjqo@iE?q^ejRYkyYExPTr$-8qbP_J6BF?6~-4hG!jf-sufylwT0 z(&!~!qd%WsgzK!a5WSyD*Q6m39jv$=^StcL^2H6~=Vc@%IV6T#xiEm0DSLXHoSL+^t1Kf! zvrz1zS!ut%K+)OVwvg@ip`z(lZ~a{Rbd_cV6dN*sG*9C{*^na>o^89Odo8Ao4wIP$ z6M!0DcRb`R?0aN*8(d@qnhiu~M^|j6m|;CWeD}+H^V2HvGGN;xy=iz)K7sG=La049 zypw2#qRX@*p>T0ID{YdKVnpShR<6wb+|(&c)_$YjbAA*Hs%-jm)Y?i%s_qT_Pw8f$ zXBI|}HQo~EK9Jk(ySc$T2%_krHDy*f0@@{{4f;<#xt4+rfv%B;lL~LjsWRkLQ_B-w zhcaF=crO^_Rw=1sX4~WIf9Gw(#zyH4PI#^D;t$UzovR7dpiQbnPf7Iz8Yd7?QX6*G zgP$NP=2p9ttSz~!GQcTs`M0_U%j3~^XFBSy3;A(j|Gp`JK;+s_ zA>K+jMdMY6gB<`9nhWjWqq~kwEFCG_75W6R}LxLlkffUg==6kPU1DkxA%rl&c^rr_^6p3DbWvVTY&eQ1fK@Y?$UTFpyoIh z<*e;wu;c|zVCE$arn^mpaH_Ib4>BeJOr1gp5evN7w3V6Tkn?6|!$T&_LGsqy;(*&@ zuaNLmmuHBbR1tW3IlsA;5$`S9iXWID;C{XfNa=I0T6HoS_w9Fh2mlsK>B+;uDuVmM z`c_3x(G0dDFK2xR#J2|b-*{8lm~aLJg$2hfytE!?rGd(JwgcydMJq}UOAzK)sd=W6 zSw2!1fK?m;j3jkUEff%Mf0@2ZVtq=Fb1JpBNRWVK5o>3_L+sXsln+x98(OGvY|`sF zoE)6dC9^bF#>!m9ebR*h(SJ~|>B%B}Ztwn!8;y?d*que>;jG>^7mSu1x-wGy}EXN{$#Y%Cs902fR*c zY-fw>VSY_&LopwnGIPpX5b;4k0};!7fFB(qmMpWPuH`nggTJs7gH~?8K_(g!ns$Gk z`M#JfnT-&95j($>2RUbsosOAqhod#E1^CA#)d+C?RyOURe;U^X5-R*Etdt5HXSuCj zX)W{5`}dGHi&m`{S0t0fZ%WJ0nbNt1+ARmt=5AI+G#N()Ay59ObD1l~juvg@5liKH zsY|o<*9!GRT-*f)>0$g$g?We{@aF~^(yNcod1$18+DRKrzR_|dp3tDTRup9D1bdU5 z#7hQU13m>Qeruq?eI~$t_jkKL9nbNN>qG`Ok>F;epAz5vLlP)g|F?6`lD&E7-tfW(&1#HUDQyk2Kf&q`RY_#ccxlB^X)7isrVdRhb*KzGPFVKeFn$zpjOUQRgv;Bp$f-G( zV$g(L$1d`mPs>8C&It8t{58Y9VrKn*)~190>lYPrkxFg!=T&$#A4kR%n}E7XDuZWR zFkM9TY1Xl|i!ugxQVvg#UE=^~E*W<4npgSgj~y7)Z5oY!xIfvMJuA{fJkTuRyjFjrh%@xB12l-XljTf3YbWG{6Q?SiHq12f=^Wklkmk}=O?TC3(i8%p@8DX z>8_+I=&XsX7YaxS>eACtFL#(4<@pqxW9zPn(I1KA{-|iM$sb>~66-Zx934#8qlu^eH z=H{1#pZz`u4$oWw?8YBX_&&g+*j7Ix+1#K7=o7cU-#(NWtS6dNF$`3~G?tPp7 za>Nh<4qJ2qOn_k>l$_3oX}pf;1%fFUVGE7^x!_%$lc8QzN3d0;iN(pWjoHpy#!iZ_ zGjJU{|9XaWLd4A&$$xe{`{bLUSNQjtwPbq~fD_A;+8@uaG0&0Lg8*s#QUsap)y?%u zq>A@2b?X%wD)a~bW>F}aUbYI#1&bM`enjcQxjHQW8J`4Vg94+bK4D-xyf%)H*~Wf^ ze3H2v9yj;*HsH3`YnpcNiu%{qsVifl#=1U(aHI05{|ako56B-7fhZopac}4QxE=4@i|0_i7@Y}xoP#m1q{C0+1gt-6 zrVknY{iJrk;@|c@euRLAW`Mtw9$lEh9cXR|8yD^T* zM_ze3s*-W^(AZK0hq!^1l`o80=Di-9xUZDCmWW&)_G(@8ZhGWH0OEBLG|@0UVW(t7!x3h;3#wcM7?G4o z0O<~jO-@Am31$n0U4xM+@PM;4S>U{%`DW2VY@AL7M`SqubrCMPT0Knf!`;gd$KR_< zqB*nhS!=8XQ`mthfdpHlqIW&hGC9C`8jJp2r3{u(!X^2rivID>2U-tPVdbFCBBo zO6CVbEOJa;l*quxK~|*EY&L$eo5WeHQdNh+|4@sxKxy+DnRy##?{7%gO<-c0EnY^a zF96b~Q~&za?d0aq6xDG4(^%ibe&@q@O@Oi<)CSR7^Y*xVllh~HI|otalf=2Wn3glH z)cf$mBwH)U>3gC-hJkBt{xw}lnN_7RrZM~8wj6{63Aw!D!|2Dwa&Ph9%LACtPRncC z_wY?C1y9`@N$d;mm+2Z+Qh_eLqsVrDJ5#^5W3)B-EzZ8-`%dg+rTm}&-fkA>!#0^K zV_%qD3A5t7&5yB0-hZ5YC+5SOrjsbt?oDKqtJK|Ro^P;icM(Wb+bs8uQ}+{z$4|c; z^-T?HbDux40>G~8KpQz^y0+`fvVz*ouhScZoQpGe6)uWmi^3Bb0vasFl`lIlf_Yir z34^HJ6nGo+bjBYWCpj~_HGdpVSb)W{6;WT@we(NPt^m{jJJ=0DM)L}?{~5juvRshi zFR2bFUoDpZGi7{ne$9!r0=nw`SGzpKjjU+w%nC`N%{0u5BtrxhP1)^JRU${Iv+0tm zf30w+;9W8BX2{dmBwn0G?r-l$n*8|)8|||ng`Q*sDXO*t!h;EE~F`1S-iS zim4Ta_}97NXrN`tvRhSa10d_i^)adp4+C{B?>_^~y=^-<;uhen)lC!HMxVZJ1-v^s zdK3T)(=*!F0dS*up?>)g9_#E#Im|N_`>o;z3w$Zldg|}LlV^v!NftP!-+%hi42oK0;tmlZsmq7A5r1cfJCA^9-+s;cStXv%(UXg+=ur`W)uhaBzV~hh{=_-#5g1&a^;>z)db65-6{bS^ z@>A?!Z2sg(UWFn4_C?jFt;&nZh4yHUl1WS2H;Fj+KRtiB?=kRI;Jf7k%yS-qc~;@e zk3D!rUOYei0RKW*!CW?VmR;&7Q#16_*-yO%(AV$lSL8Dsu=ujU@BwngeO>MIyx)U! zcvjQ(#Npbj&(73^5+L*M9X+uxQ}p&`tiouFfy5agD*dM7F93f94_j&=0hzN$a+IG7 zfi6e~COwIa%M+9E?8!*+80TMJA!$V0cX_Wq_$)e;EJW)rslH#w`=kVcy7v}^fjA4V zdeI#idCuTxk$+UHC?9eXriEt1(_DtZ0bX@L#ji!Rt>ZP|g1p;>ccl$~(oGu{5)>tT zv#a9;7C%BA`g1lb^iuv*DE{OnWav`q>doy-2K^4cb>QLhU2XEw9bNun9R6wX1!st% zpow_*{GI)JLp0`X^wGyKSli6)j#mv;eX*=72XyP1yO3gs2K>v5kVLn+i^r}szQ!oS zqsx&;sg0yDU&7n~5e~lXq(ZOv15vlZwDiLE3j!H_?!>Uy&2CfmEabPZ>esIx9p(@h~mvZ8MM zps-f4<;ml#gSF6??880d-fo-mX34gFX6#L{fc&LJ{ zJiql$13uz&$6LLAxZ2UwSmihk-k>M$KF0#vy^*%O5=DymeZwj**}z0y>N=+^{DrNI z4V&-_)di#)Q5INGxOzTo$otCFBgefl-17!dgE9pMufMf%MBzb&WP=&CAe@ZPvs&c0 z*i7&Q*v7 zj^5adacZm1xLfxrx&cP_-*33vmwDZ6b6YR@cPwv7Jg$zj>vPTe_4#3fm{-M`^t07t zzsp}1*As9Le{d6nx-qi?y{-E9OP89HV%hKMED>Q5-3Y6`7cLi-lj9E$_`h@n(>5sU z-1e`*`A1hgoxicY)^nr3b05*#jQX4VdGy4rQRd&%{1Tt(=({;hv?8wz`&YVVL{5p% zO#LrPZbJ#ScjtjESx`XCS1#hK6>N>9?5Tj%CfrDveQ$Kqqsp9PNiWBj?lQLprRvE_JCV>$mR7?HBM&l+lSeY$Ub^IO+oV= z<4@C0A(IFDCKMu#O7mRFdE&8!D*-A#$t#Pr{%0tntH*xK)*o@IgLr^zyt-QHwP9ov zm%pfs^zB%!0>1F=t4*;BwQ|lr8oLR$CVA7B>SkJD{jn7d&-ro$#JDY{1ihh}Jlx{o zHl)Z3d&%V+xO}G-hgN+Vm9a|Z|A!e-}}^vJoow!-^p`VN(Sytr)hKAeY}$ zyq@{C+AqoOzIg#+(cbMa+J4uuZ^Y}QYdRJ8Stuy=vYM^>?a}rs!QkY-_t<#yJ-}v^ zI@g7%PV?i|^rwdDi>v;6Qm4AQuI6wla2ogRF^$kLE?^Bm@0JfSK>j<)-Tw{t3xQue z0T?nbO-n3Iv+k&%S+CL{+TyCk3gGYo&x$K;3^f&W9#LNCufoom`xoyd=hqZon9U%5 z;U>u582*}IZ02}R#lSl)1FC;)uttxA=~N`(o;ia>jA=5w??4?1{DRawv)qaz&cPUr*1`A)Hi;=DrOPjMLR=( zQiiFlgIGL&(-Y_yfbT)Nx=NOzPm8*IR<$H=k6yAAXgz^?DzKhr>r$&i z!Y)2-1Mc{Z0%G#VCl%)!TyMiX5j3AN9PTg{2H!KI3`=-g4ytm03E^|?EUx>Nn~}Sp z;yc1s#(2w`Q27m@sV|;ZI|Jp?NYZ}95M`ux`i}#Si?>$FN{?Zs$rkW#gI|bu<3ah z_k>4Zf$CvD#0P^poHmcPDgXiT5iQ>D59;6fy%l=2z>uvVyXv@03FD8EhYTaiy>iw? zg0Am)%2e0@Wj9~!I?a#n>XY!qFok_1iwS_f{b}`6X)a`=J&ti5=~(PyO%m{s@6cDg zG;Ok2Djni#x8QpdVLUy`-F@#Tn_b-OA0%pu%ioO4C827)@T}D6b`2!{YYd`&kT0FE z_))sGNhfJ*70*0 zD=PQcV(1>;{AgE{zeoM(lAdQREmH+rBbZ8BI1ShQ$#PY+{wctW&(&3cWETt&unDbm z1M2j>7{{dN|HN-ewM-dKy_ox1;H$|)bc$lOkh=h+?h;)|=PrMw(&2Xt?>4b97HSfQ zo*V*~L?C&PeeQs|G*Q4-nPO2<>ViF=<+31FGM~z!~jig`PnPB-3i_@-Nk1 z^E&__)Qu~iUk`*2*$zR>Xqu|3isipU`v{AMK8O`%ZG1}xWY+)51~pjt6*GJ`i?`1b zByO;VPxi&VfogJ$G#3PCeWiTB9OC*0U(3I3plq~~_5S%|3QoQ-X4IR0t9M}*g2j;Y zN^SI~9;RR2=gE*2C#(A@jKPP{wMS(V3jsUXMY@c8{~4_TcaVFyu)agRv>;QV%y^f8 z&3;}IE2)#MM46xx9Sk`Y2R!8H|1}qe1$)ZUM)n$Vy}hwMx}+&p+?51 zRRFENwbJKL_mR>RsV4EA!(W~BNMeZ&-uAdO7eVc(krv_`O4o*rycf6fH#CEH12r#C zIxGdhZLbA2^KaL!mLBtd4Y<2pD;e-U{WzTNolZ7z@6u?P@UfhRGe?S0%bewb!fQMc zvc!r%jC*NVq0qp!LIWkDUo7 ziA+DaK2aZDZB7v|_ien@Y>CVLHpI`XYJ!7sj!7&%6Gy6Ht?e$!& zsg9xsU+9-?RElM5DL7k%X`Gp|N=4>QCqGjEDNV!=f=tbg3UzK9hj$D*9;y_RpH(~}aus3=K)UvInvD1W#Um* z=~Cgs2Hzf!LS`3SHd2LbwBwIQ<4CT2OL;#F2}hHjZUNhEd|1~wA@8JS1~gs$f6C}8 z&o}s%RRB|}P($nL{?{b|5lc)#C_J4ywz+5oPfqV-qQErA_0N^p5)T{;h6Y@X7E>9j zSdUfR*!j2hz(3JQ?u9{_JrUV23==8CJzX>S%ik^v166w^NvHz+8MMt}vFe*-JP0}^ zJY)R{u|?Qv>t}wH9bkNXnt3 z5o7ac-Sv#hQf+gp*M#QyM6$Q6#_o1wWn?q$@m%5)CY#BXfEul2pC5vvYO?2xu9?HVHQC zmyU~r%6P9-(d+wi9NaJ1Q6)X3ifx9)*S!Rj%buj+A7Bj)g8&K~kVWqd$D%0XNy5-r z9Nf$u8ii#VYhWfdHeGk3yQ$86r+Lpa2IcComsXWGf`5t!hS_dW{cStQTbw;(x=O|# z$D_%7u^Ulnx~46nLaxXi!BN!V^VtnV_i9|)VtX{K7Rm#vzCGzI32lP~@{*$ZShL7)cm{)mvx_~l=>KysNH(G&O$28mp zqhxZkatZ_pz=!a6h*{M45n7JtM+UUWB1VSkFj%LYNkSrG#2d=N zsanz}Z2{eR`0)wXBH#5h`&m#tiNf+xT=y~f7#(u!Gd|N2D4T+`)Qo_>o2Em)_D!Vt zXz|2Z>cuC27Vxl*9xIU8&4|hd;;(Sdjil`lj4*!+ZElz=MkPwz4j^b08!=27EFLsY z8XA>FY70;-pQj?E*i$LSdOx803v{NcaBw(gq*|Bcy_>FHWIFZDmiIq@=4+4*FYxSM zvzvjApSa_bep>KRy^DqMPkxYfmCm@38V_KTv+V}5JJDFO@)^-$C@!a`vFE;}SAlAG zcft@`nJ&LQQS$|HZsW9}%zUqrjc_#COL}o|a$1bWZusZeoF`gJBh5$ztjlMmq(v#3 zsYNN99~3gInucO1^d_XgD47aX9|=v~r@6&coKL|ny&TWAo^y*U&bel!`*B9~tbK#Q z@#(b-2(a{t<#)o}2$4uwF)GirryQ8=N#oe;TQowDJfLF~go{VfI0Zx{k>PxH(Y+ zRT{e;r6r&0hE4Bx*dT0vJ8DU0JJY{8%PhU?%$As)7RPspC}V7`(dDpa5p$4UcaYBD z5^7$fV505(W#I*X@J#!ichBbI+3S$KSKgr-C)E&SiY0J6alMuq0c@;9yW4F6rt6Uo zS%^{KPlwuZ7V(7*=s-%60 z)7f-u0=5l#OV+j-vez%%LuV+*T3O;O_BB;J6DrS})FG@r!H=wm=E#CtG};p+C?Moyz2b>5LwWcSYc+PoIr2{Drg~>I!iL&+0CgK@9SLl|mi(Bc zhF{Bc8h+z!u5L2_uSx`2VqV-tla` zZv#&3)mpWwqV_IowWz&EBth&=6s;C5C0eytYu6~XC1Qu7_G&5G5?X54iW;rjBk%G3 zy`PUiPn?tIoIK}#?&rF%JK0zDz*W&;#HO-_aJ|bUBfes=H4|ZAH~|LnA?;Qweo$)~ z!gRJq1b?@|RO>R^NRX7V6DS7uHz|Bb|9rtwtj3;7ccyRBt zQ$N=LFAG50JDtk{bal4aoB?Wn?4*oFMfphtEPVh*W21EqtM+-`=-p&M-Q`+F~dW!Nld0C4VYa4tpa>%=d^>K)ZAj0)2lQiAU$9h zF@z9Z_cI{VeMz>K#KzZh)Qo{?hjmrewRTJgF3|r3yla>4Y=WCj8dwRz!GU(`OJ2x(n&_xiqJyACOymXwwOZfR$)dk=!Eirs+w;VA9==egLBRC{U6hKtM zxrj_$I)oEp}ECXf2Gaqd-Z}s||&O>+y%fWxkFDXmmmlP33{-hf*V$&9ahOTF^YJ{b*HG$N25D`Cu`u9$pE>aPV)6H&(K>{UiXrtQ{O&_gG$zbJssQG5!&`#fN zYX!ly&htQbvDGwKHvhq(K&y*d41s;;v~j2 z9Qa00s=l)f((ozQOe<|4znW=f>Go2&tZ)&%*f_BUtJGHsXF$cyEpUM0`84Ra5TS;p zums7VYlh>lVlR?>!_Tj;j4}gnwpOb^-x*|VoXtbAgH!4tM%bFV0tU^>KzVWP@7vCO zu*7C-s}ZoS@7TF?Fo*j!)ST>7nkQ>^jL&iV1lXg!^w(@BIqad@0tIs&kx!^SX*U>GTJv)}YUrs=#~U(c z*L-`JrCA_wdcd2#bNZ+MrnN!KFNgwk3!y-}aFe3TqDkkO^re+Sbk__OW=>)AvZj$9 zwy|3J z|MuUq{#3e3!8ag+#Un1hKBZmw{FBOc)CD~+8i_?u=6!})h{gWb0o*_|915*E-Dy%4m7MWB+YR87$R_nrmfP6gJIoRPD?Z{bq)39 zq~@w-bRPsZqIVG_pqBl25te{qzoGeMI)|HPhc3^KD7cZQ_O7nd<^jwhJX4nLXnAa_l}o3f6%;Pv$xIC#USn` zhsoXZZ=UJkIdvdpA)I{-lVIN%&L~>^zzr4iUQt^`)8^PEX-Ega^ZU*xhZX!GR#OZrJ!$VOjsQBRXFY++ByS|29QEWsc}VrNphbg3cem{Lk7;2IQ+pm$4*jFW^m9^ zuqQL#Z0uoh6;sYyY3pUU#UOa?l{VI(^du;hcaRXyt#`g6W6W$}6PL9LHeX;T0qI6F zxEcyd5B?o-&hEfn$5&qr8*+h1LXyEbe!|%XOlMAqvSTK&2foB5M3U|@1a79THHl*4` zsCKNzJHd4}^xm1Ga4tPP-vD&apP3(&b`5qD&2(=B3^c#PJIBLxTj} z2?%ni(yiNSSVZO~m|f zswruqF|z8s0jYpWVcG=rhzr0Suquh|f*RfZP?2K|JJZu#oC7@;po@xSKJ9gaK`c`5 zD|IorBy{S;i)dw(CZE;l!dStEZ8@~)qwZ=Bn%_Z)u52RHJ+{>ex+se_Bxt?&DKtY=7)!wy!;Z?jwg+s1aH2#>!;KT@J_3C@tikK& z2h~qm`a2D`dwJ09F5R;r5+^MQ$lKp}@e{zLKsj6?q_Hv6_|eG(cKQxWaG20C$*72f zoO-;M5*3%cP1^Lsa-jd(J{ZwRx)uWrQWB%QDAME6XC$`2uPJi|o z_9j{^tXugxt1NY(aGFHw`H2e|X#2b%cc~`z#2OZcH>eC_RN%i6(;@QhA?#JRvn@dPME6nF%k(-^AzZ@git*MhNj=Of)(99=>1q^^y*5$yiF2d$57rin~E7`4BWBM5Hf)OZVt`} z+;oSj5}#JXTD|%w$7Fw0Yd@wlciZF54`a#Qew{A3t=>Fu}=$k5NB zJhSxnrFU^dT6Gith)CEcAv|?)W-ddEO$vLAwA3KflZnTwf6S)xbmNToP#x$^IrDm> zorv>Q%r|z-F(+QTgzBh+8jZv4zI<)Bm)=BL-;ijMrv&@}c3CT;#9u z2}3t+&4I3-kfcl3aJ_+S^zQKf$Dphdz$gxsa2?K=lxZ~C=fX-(yUpPFI$NpVtTVQ( zS>iY?^p&$rS?qDecobh$Svv`j>0Q)EpCdE?D%mm&L4J!w3 zgegg0g`Y|ZAG$XIZyZ))`p@vA*ZL*8*^!JGpg^&olKm_u*XnVWyqdJ>&M-%yWQ&y; zvqf)u*0W!Ekc8)qq^wgfqt3&g&bg@rN*<43b^-V~2; z$c@z+HBt*OfZ1u>A1rj%JSwfw)=rOlD zYEZiHe!eyLtUyLdCVrRuZFJ4v;cHqJKVB{BxWbY^hhg)FhrWu){BfwL+#M!ZUWQFq zBPc$W^Bz;Fl;9M*yZwYkY2%t&RYM6djEuy-G{eBfP#@3JtrOJ~V>00<{^ebtzhLw~A;kNw-qamX?8-It2p0iR z9G>)JNyw;?L$a%=G9!?Ve}338AA%SRI4Mzy1FB%Kr2uf<-{4m?5oe<{<%Z%HlL3+Y zSz3>1XCLHct%&GJiQ>9q*?UaCO;0sEqgSu|kLl-MKg@c)U9rgY+o6=NP(o7a_7&*a zZrbp(&m^ujlv-1mGT!WLSSqf55CJs%3bM;lcb;jH9I1>J7_FhSCf0Bjh`aB31ZZd$5Z~a<@ ze#m#N!(tVeC=OIKr6LAs2>{GD?AN@X6)&8VdnR2_yzyR_~!~m2w&$6?eJ5J zZGQZij{6m?=$D^}l#P!kHgS6i5=onXlwADtXwRo`eR$wp`na_)?!dBn0$qVqEte$kP@uy1uV z_D+G+xloPFG*MPi5G^{0K#pjGL9%_d7V?H23zuD00e*Q#^vnkv#jE15ct_)5`U__A zm%Rm@USNpKl{$Yd?tBElPwbQ4wlo+@Sjh~Z`3}(f7l@ikgzFElppFGxS6lixXM1cUc@=*SRpiuYR}5L64%IT|vRny3bQ-CqJDa_8Y|7 ziTOW7P^PR-3Pd2&nTQJMWL7v+SnVd zFIWa-!Nqnq?3E@d*Bf$z5Q}Dc%NgD>wo)*Rdx9suInVw(TH^HylsXl5f~4{8%|^H- z_QYuioURx7dnkTUUTdyhj2QbcvoTX840hY>R!YQ$W~B$Xo{Fim5^I_=d5Q$gzP74i z@OHfwU_}8a3gkG&VmYZV{_?iLF=$+?x64w^BI++Vz#W{lfNTcmi#xy4esGPJ{eF3} zR<&w`QR3I{_-Jg8wPP87^a659j0TBT zRA5!g#P}!Cfw8jD1(GM*EH8m)1K|ATH>`P%1S+bSu>Z6IOGG>3iM1e}mIr#ST)S30 zlhLD)Bx1MBzSI-ah5eBx;}Ck8^bpo`UKSXy(R66fPg#fZ!CXq2$@11^H&6#A`lzPP z%~YkwmPcs8#=nCA4IwmBa{Q@sY3yabltc=(eSeE;TK|N_TJZLhx{gW#o+B^lYBb$|0PCGJS!EXZ; z+@}mzl?Woflv-(o+HIQ9bVhcvHHCtblzn_m;eU-_0HNC@>R6ikS7q|u=gjH!qCbD$ zos`~u^~h?Jd_A(=VUXlK13~T2$CUS+7o3ZHANQM4Fkkb=;Azafg&66BW>DC(M=K$^ zWbWm0vcCgItpIVP3t&yqtihIb_8QywP29BGP}joH4~n5As}ybpt};FCcrDg|#J^8p z5I*`G{}j~m;TS&>S%2>-ebPH`3CuYfqIa6JiQolqnn%8mcvw5ox_=j<=18t?T#3X~ z-Ie$|J#n^5J4HMWEG4jGJgdF!S#BZ%e|GXMz(Yq6n|i=h$wK^xYJZ;-kev*yhkwOQ zjG@Z$(#t!y?!9H0eu255tQA19^6sXUCuBT+UBgFbi#s(NFHZSrk8x7M)EE+~s|aLt zsB_NyRN+b(hCa%~RGbdx$hG$O66`e3gahou9Ei>-cPz?_h-ZC7+a;W}%W)1YSng8L3X>Si{4?4=nPN#X4 zhNQOc#EU)0yH&|^ze5IQYs~5?zMrf5W8&D`0xRzEi7Y&?Ewq|Cr>AOmlA?fi^{9fxh= zq*G-B>F3}mOZ@Ahoh5af2-lhiW`LY-{nT%QWFX=;Lx=Le{K{6vpf_R*^c!NG{Boz6 z;VCoUiC_7m+m|M&{sB28z-qiw8~{~XUgZtim{?(aw&x4;*-+48x}2-ApsSaOQuDN+mZS)D&cZx?rcY7W}@ z795D?>6$CExg5uM)Co1{^TtT>#Q5y1fddL(OR|f8vhP{$I6>XP(4u4;#I+KeiV#88_ zABa1q;=$aUzF&+$@Ikh^`MQ#g*--ZAz2<@|j7|-xf~+*(yaI)A+Cm-oa9Qc_2@I@}FJkb_DzBq>ycFk-X{s zV}z@hy0rMO`MXs|*X)yKnSLG`ZvJH^Bm2H$HpKCTIJk{jI4e;GpLsff22NECR4-ib zaq&kWK-GoB9%+B~CpRj=#F=2&S{1h`&uw;Xyco7Rt)fTx@V_^^YM)4U@zil&Jk2wH z(Nnz%f-0}d3s)PAK@}gq`(t)tS~XvCzK9g%^p#=3hbTo@6f5>csL1?U_rqrj5g;fB z2V3?$sQa)Ulo-Hi+(J-qp7e8;Ve83L5L*biw5)iXSyF%9VGCh^ytgFqUVHr5bDxPS zuZ7S~(gNxuQ<__mxJ_@g8!C1@RvqzHp+VefDeX=i*%wX?H4d3kskAl^;@1?KGex|@ z=`KCPL$3b`iJ=!muStmc1!D!OjG6-1a-LfJ5oiuLY$hf)pZuy5j;r{by38xGNFUZ+ zbD;=qTs>OeVT*i5){EMq?A%lNBhJ}Nj9+kkhGdfnr^FLG#gs1nIs=YDq~S#q9g7TV z@z;npHfD#d;Y%)wmO}d%ev159$^_{PJEhUZ199Zrc#ZL_K6T^z*C!kK|1%fH8U zbpw|)4iLv79fQaE=;FRgs&;P_T-?L8rY3Ki?6>)>Fr|IFUs1j}4o&7TVC%b=`_J|p zN0>^_27TAK&gA*>(y-p$T#9gxX4DQb-ZTbxQvY_*r)9m?8Ueg%(v}J)MVDRmw!izU z(8M^=_CFB$rzr2^e?Ng{46RWhu;Kmr=V9?)<=`VJE{;1#j!M?2Z~`DouGj)q2V+)r zGik>>{8e^+3Q%%GTAhDK)MvByTLE_*<$u@w7-yxVAjqz{KF!ec18Mtdv0GczvW6w(QN`ltKd&I8V` zglq=dInVRA4L!p?f)01yNvkKtCJ|0|^f05?>BsRjnBH^FDAR8x zzqT)nLx7^OgZBc%?STb>kP~wJ??d`>%(WMrHDHZrS&^CdLNao>S)G z-`B@^N5!3vyq@^z(Inn_HfB&-2A8wIojh2p3fHRh^6Pu>x1l}u0nw%(!$k>vhpMA; z-sWdF=8YqScI;}pWBI7%aqyv?k$7&qwZRf^#Bm=9ko3wIl*Nd^|hiSSMx1k zTMBrCJD&Hgc;|2?8QEnPtfS$y)$fN-+@F38HZAB%(X@&WasIQk8`94#dXqHpKrXtj z@ZMD8sE_-i;s=&F7W9jUVl%u7H*TUq?O*xR0G20k?MqPnMwra~jcF#iXoW}~L_WM2q;9fzbzG!aM zoL>f_{ps=!3iO4i%pFDWJTzYDJ{_M>=t(Wi_TL02F%Z$Z#cG4`#Mg*9?e6?c@vb-D z!VJV)60=UxeI(Z!_YU%OH&8u#l>Z6JP`^O(Y78V#X1=(X8D8K?s2L)D&DWA8Axj+oc z&ujR!pWO$wTE2+ozY%n133m>(5pcF5SV?q=Y^^&#MKci>r(Irmjb>ui8vI}Xqfwjdl|ZGZ@oxVz&Dhp-@kw!J zMnfemGGh`^Y?N58Gg<61TVdfkBODV{U6vOxinLJB?dbxEZL&!?EvQdBMRR>Fi&?(| z6nR=7bgKQl;>op(@VQ$%$u_o81%3Uv@n!hEN~u#-Q5c@VmCo;}gTh&_J;!Aq)}@`R z5#!$EoYf`iFsw}@r>M-0ZZ=7$0d=Q!_g8#gqssjy2s~yT89e&Hic+W?o&?sSs*Ai? z^A>UcH3vvUhWTQe4<tJ z4Wa_P0vY>S;kgUE#bfZo;#5Ujced(QE8eD|h%4ai8@tkBpwY+`Fg7pb(5uawQ2}|@ zi;wn2XRQ5dMdD|Wqr+|(YOWh#@{=6;2S=xqDHkvsH>}S_9KvU@#)5-pA>i^7Scs3e z8g9sMNtSkTTCIElM7F;^q}xG|mE#4`pGaPHMt4qHQk*5|b49&{msqDZMU$zEt6?$_ z#nXspypqOHOUZ;bH18*V5P5f5UEAb3F;CLky=+pfU55>J1$|OyQpAXZ8|+|WnW_#H z#{_Yl4eiTT5}=*ksh5ami6~<&jTmAqy8ORs;To~A)m!F@bzT+UP0P)CZom?te=$Mf z6@oihF5FGu*-i_SVX<+GJxp_nDRP?uM6$>Fm6kQHJpF zkOBN+!-{1NlKlu)?%nktJ2OQ8Z(83S=Nf1Ou(dYS!kop*?E%}=y&R}3=(FcyD)s?P zHZY8`ZUOYqgvYI^`yswbpZX(6%OF1PylMH83pd4jcrF6YgoVqe(Fve9>GnxZ6(UVu zr}*|%SJw%w^usSdOwzf&h~)#;UF%9MXuo=YdYX2|aJ|GPgTlHMOZA_3msA}PBNEQK z%gJcTGke8WB#xv@p*`cILbU8|BGo28R#2|7_im1exmo!bBFKw$l@XC{Q=%bg*C`J{ z^2x#}SlIEUa`}ld0w2n|pxqzp(!!wq&FwZG*ZNC>O}qu{pqVI~KUx&zlX1dn^l3<& z#a5828{6vr{6Rd)PKL^|bkS-fovJ-NLw@=SL(QWiWbFTvUO_nxNiw$4Q6ivNM3$?~ zCC5Nz4{4e`jfhDspDarhI2#6frEj|11J>dZaxznDhCwC3CS)bVioIcZ2wHTm81ta z3?mG^8G4VS6nckT$b+rlP?NqtKKs4LOge<73? z&WMrEAkYL?FQ9FRv=wXpH6^B^V8Ip$>dMpHEq%OP;l^7Ucq0DQ`+iQnY0IkIMRGe# zFe)WOg+276w&kKJJowV02|pWG8=!t+`?ZT9HU`jR%I`)dY%fu1 zM1-aB&s&Mxqw5Qx|2d8?{mVLcB35DYNUq{}=HpFLes7I|u9sk~n-)HTk0ZZWLRd+Y z6!v%Zq4}0OM~?7&jzISX8D_!3#s|%Wuq7|{vmNffns{s`E+AI6??C|Cg@k@;@E8fJ z^$7E<6hps^Ab@1Z)y?MF^x9x7|Ha#n_jC~45~UF$_LEB_NK(C+Xc=jBz7I2SwF0PD z28{)p8w${rA;2Bgf?iE#Nlh+0Kl7s)TxF#o^oggHBtt95 zkX}cMH`^WH)Nn?SbrVuKI^G$p_BRkg{^rPIy8z~jUcOAmY26^cJj%h4=um1Dl-Bg$ua`Z>~i3de( zJT^MwvqinC1H=q=Mea?>I2vHe;L+twNTC*h^<^G5|K?EWtR) zyRq*k2={=?sK~Ql{oHd2mF&#ftwBNe`W9oK48KP6>8n9j;XA=mDbDWY&|czJNZ4jk z&>8qA1CG^HXj_r0y#R_b2qJY8lE)bj0o%GGgE-WnF=n2ffVLz;>Eb%q{tT<}Dj2bJs_XFF@msqL#XUsn1XNh5sep=%}3@ zBL?Pc+fOM-E9+vnsHCHqGdRye36*XIaR=-Var(36j7N2#pM z+>2H_lS*)4{+!p}9mB1;|If10zbyOM+Q?(`nwsl}SVjj~pMVy|ip%81Qe)MZuj;>^ z6KD3fwoJ_MRdqv!lIZiSzvB#J(EYF9Z@N`!TMkCB|Jd<+-UfRV(LFoCzB5Mr3hfyB zLp%s#0cPV7BpOt;m5;;2F~(mN^(txKzLVT{%r~adBJ1R*`2dd9;^~$SEj?)osYFW~ zHA7F)nXsJA?yqn>yd4r(sdpJ@uoC}u-f)IB&Uu=FSTr@dUa8Dita)%gHRIPTpG>Pb z`6oqK5etD?WaSm#mZ(QJPC_t-Gk5}>27AsO9a^2M6-5-yT!cnO>Q`R%5tK(U>&zPd z{`MFj>8k0+Fdp?0?R|;|Il_B=R#ensLtB1X*UV}8H|=b5%3%=01bYx{a&y0ai2Tb5 zOg{rz1b;VvL+I`$j0RM6qj1N3D(Lz=OVgIpZ2p+NQT(yKpa+$&-NX~b54CSbIpABA zy9Wo$C%1Q-h0GZnCLn!eXhzXIxi@yy=Ijf9kCdnOavF;y{Y8u{1;HL{Tq9)$+2Su7XRf>AX8;UUOWGNm>2HcxJa|5eg&;o5r z+8}yW_5U!2Q{F^bTu>2m1RS{3`#ZYP){vX@h1}%0rZ)e;BtmJ*#}r>$A+Rd|E-6PZ zNLxB#M^afW#l2ZSTH?!}=6%4W^AD=E2upJHye)4DhB=w@#KeHGSW80%iB86ON{Uok-nSMxssa(y$m1Vq{LFiQ%?a zl!hMuzO){v8iE32gH_=dd zHG*`q3TgNad~X7&43Jv~0KBy<660V!oewrHVd5tMm~lc-^jj^M%`B5)fK*f~wg~fb z<~f#eVFrd4%X0rDN=~M`ItehRr(bopMu&`txgzQsKUD?UOqMEKWoIA(Y5oIxOiGqP zuZO{lv7;uFQj=Aa4Fu(=WR&P>2QTm`Z0Xe{N7UueUfPy5=m}oTuM9yA)i@^o4o%4$ zSKznp1sGGWqof)+DIP3E5UezOF|Us3*`HSeyVa+@Gdj&4pb+@}+YC9Y$auu$5WAK} zVr(M&x9_(;8DlM$vnR^me?cqueC9SUc3d$zL1zhidc(ke*vx?Y&GrhaPb2=oxhgDA01z}N3 zXaGa~eR?o1QTWpvR2F~f{^(UdfA&%0LDisznAbeX3+^h0)C4pl^23^hdCL=b5hsZV zd8Uf}kS)uFi!*;fGuPn@Ajo&R79VLm61Fz87KN2wg$+2IvCuAbe8{K(@R_wmY|f_4 zp&8Fj^BkKEL;PL=Lh=$CIk@P+G>DZR^$k@rf9?TV>?FHNW}KAJJf?MOZoTsD?IWqu zOt`Q=`JSaJ^+fBsRjK{{Ef9&s8WGSmkNI+4FML}7ECb&B$@10-1I4_L=l4@X@B(@F47{GrlBZoxUp|J-Yf-E|y%| z>;_MOnk`G+Uc{q;pbZ}JacI;d*vuBd=lW`4unhoIP2R@{zMU+F z4u{`$U%BI?^adT^qkkc+WF-j5Obc>P(L!uWhf_dSP33HS#QIfjxrPq>E)h04p@r$7 zCx?u-%s-E&1zl)kFox*-c!{4#9CyqDs;xbR>dX8T(5MxALz#CNGD#tO6(>{|@!cPj zr0I~kLH6&wREBYq4vVh1RfRsX>PK-Q_s6)uPRk>NV)pAKO3kVEIF(s&(jO9znQgc1 z+mdVjY^DNXW(vj`6|CLFpf=x`F%t(z|A=-Up8s_&^bTGw1g3DqRO`52>pD6mi7r2J zhDW2rby>ylX^m1Qt2~SS?=y|22HL|W*cmyV+n9&BZh<| zFlv2M)9*=tPj#p0?aoft@Q=v{h%=845n;$_3v5 zRL$Bmv7cn66U6ZI&)o;AR5jY*Yl1a%Kx(51f2sHd&U4ycIrm?eI>2TeE*3d7i-uku zKT16Uf#@u>qq6tvxlbY6V_j`rwfOG-|w(X)km1#JNlh=dsMIm_* zNiG;}?THoQ)LaHmF<==1D*5G*dP+ojRZZ(Js5$XjH~kDcyo38?u$mjES}FkQCm&WW zzIt&_yy_tHg9uy>;3Z_6Jyh}aQmu}@{@dtsiKB61$~v)ixsS@i5WDr>uAeevpelfz zn-NqxIUN3@=2R5qLfQ4m_fy3}Qnk##jQxrwU0`Kts~9=da+R|j|AA(5vRXr1xIc7D z>Yf;z^FT;t)@IO1zk3RdA^T~eUcZpnLRdxpp=E{CX4CIS6xQEbPD-Nih|q3h=(`?? z1B3yK^E-remh%L(?)7)@D0so#QJ0uiLEO^E!tJN1@SiE2b;C}G7Uzz(VlQv7h+!wE zpB{AH7SAG4;SrsZ(xIk$Wrv!`?Ey^Jw z7`nKA5w9V#SO$i8JG&V7&g+A7b2H!w8QsrPtl$$XlMwQs$*zvgf>o`^!2Rr#LYK$P zYkNrPb|YQRszHEGkBRRq6wQ* zo>how6yt_Av9P6p<~FY%o%EJU5_`KcNoEzPqlxSEH6zJ$E_45I>mPv9`Seu4!}|fo zQnWM|3QN6XVMs2bHdL*ZHn{Tsy564Y$5602!M#Epub*^E4hPu!F=Bw~8qvyU z({KTj5YbZF4h|Tpf>@!JZ|U{H6(xt{$JSfH$q@?1=oc9jIK7KC58x)lj=971}qwEtI7?h}uC*1SjsQ!+NI)m>sMfTHKtz)r&(gT5CH zz3Du0gKw9bYCM!_Ot@C~de#sB>UyMyiy8?#WjxK7Sxd)XZ<*ha!#NSkX=_#p73r_z0=t*84La zEg`GpaB^TA8;L&jZ)M3`Zt_3xvy@2@Q?=GE`DTR0+J`(!1@?`MHic>c8Ie?Y15n(%>=Xf8mRUqz69AW}|4;g7^Zh|rrY1|AN9FUm16}Dy73S=0pU~wkZ#P`adMCWmLTSaLr zoeRCZ3E~DZ<^Z+Q7bg;HeDf7W7hnVz#Zz#wVC#!av|TNEp1dmJ7|W|~e*VDcE%6FQ z%12`hJGR-Gx54$42Ja2B^B%)|7g3eX_{I$Fu%x_QrmREQR;&npiPniY?K~Wi`;?fs z&P5(YDpt~lG)BWJ<+B}ptnz$;cI<&pipUr7VA!d8Plcj7Ce?Q|>q`dN-eSe&NlN71!W3uqD=IRyXRcBs+u?v0$gRj&{~ z-<=G4v@Dt=_GRneTNw*FE8YQD%BV45{7-3-pEKbl=%M%Ur{@XBS`{zsoZzqa-yl#J zbFTNVL8XsPb+_KIhB7>%FDb6O_VTn&k7%WVo14D9->I*A@{ibiGH(icQKTBd@Hkt6 zMc4%VRibR2O+e~%{?k`UX4pcwO?_uxxBZ8U2JC55^>7Fmen=95;oo1>_ibR(xOY$E z=7Wk?$Gq6{wiywOrp~QExQ^;46Sq~dBJG44a{I-(?>d8^Pb98I`> zEE9v?Qwk-MAq=SaqYWiTniPw!@5L)Y7DYWtMKNv~`4$djAIk_$I!P)j8ch!`8qD(( zfsqtA?ev(Tc@f{PX$m_!V#BCG1^Mqmx2!dw_nt0xGl?>frA~Q&HmEw0ZpXRKXU()C z8veFcZ&ZDAZA1vom$S1k#Th^i-f?2$3zEOH{Q6iOBhkXZq*JRAKl)W$4b$X5yrkc5 z-M&+Ku4AfSPTWXM-_Uz^FxpNl3I#Ffl7!U>m3aDo&=&E9NwTn)`a$&x;zvM{| zq#*Ygf>g5Jfa>LfMIHJCOXhyOgEVclFnSDqrIkxf!SkLrbx2`21Ahz{DXiglI4>{k zFN&qJ=DqU|HL8I67cGzU#9w7hXm#DVd?K{yL;mv6Ua{Ku%F|r-s6RZFI3&b4R!XZ}H3GUP8EoIVm zh6!jWiWNWiiW$BqnA<@Ki~6PiWEvV373#T9EOkJ9CG}E&@uNq6eNL8;XSn^Ot^w)O z<}v9wavoXBuY{T3h5<8iEpbyLs*q%CgCW}+Uw7I1El3%;6-D;i%iq01W~r9%gR(XW zt&!*Tt@u`8T$+A9e%GpHEx2=MtQdBKBk(2q)$M%86eOSF`|%siEwnHuMJ~rrwjptC*1sb>&Jy|#j&SPa_<5Nt-O21T}FjmqGh zpJ{^6c$GJ~!CKG+4nmoE&ER+I84q`_lkXL*+dPg%_P8xWIMC*!-cOiS>FEqM#DV1P z1xkr%^~RVlsIf>b2R%zX4VtYUe8`z_Q--87u z<|hxSjOOP+4Ret&wUO#2uQ%jFf`v`+H@Jr^idJcd;*&?yOLu?$K9&mS_-45E#cB3% z_T6YY_Ej@l?%N|W{b_LR{j9LIkgRA;$-Xvkhtrp;9>tmgw87g)SU`9g(Td$Pwhry6 z>>&N$&(%ba#nzfU`#1Y~0Xg(=xWj)cY`zAILufbpKm1|1sx;hg`qW@$PSuLndVSi~ zTYa$3|I{|*xUy#H8Jg2$KLbA&p#d|%191KItnER(rq37IQgQehCQh?H_!$0;LZ)!L z%?iDDzH@bA!Yz&e#;sKe>%lbO3=lHL%Ji`ETTPR}Nd)eI{EqQ#5(`XD{XqC?#M2p) z-GK_=Y0Dw{RD;*3y7bkkXftYG1gmGSlltc3Op{yPAS!0MTT=v5qbB%nmoO~xwu(sDeJTX$ur-+jTQ=C3fK=%niBfoR3vEa$L; zw4ddSl>!N&Ba^#I;BNlSl15xcN7{gxPnD1&k3~gLQ+((S5qQWpi6O!yRl(hpsJj#` z9=1ODI%MF=Ylt7Y<+8Jgxhm*6VVng`vSXf9yr^`dT_xn70_D|>2;I z_+lbZcw@z4>h;OjNaPwX^7J7|q8>qe&@*`;(t@cTpVbaoR$jI!t$)rkJP8qor6*1$=e`0 zVrM#pMu_K08a#;PU#94SF8Z$@lUFk6H0Zmncdujgge&gL6&5iMV4Ws4({?8E2~877 zuT}|$sV$;_zCPNZy0Gi}lgYz%j#{*B4M9`RqNTe5$q|a~Epw{pmV1wVeYORxChz+a zo)H($Q71&%bH^P$(2H38d%sl*?(}^Lbt=^?m1u{E`qz38_Y(G1qF^yit@NmTR=g@j z7#H|O-|f(QVQ@dQx_GEzw^h~L6BA2NEz`#!zrW+TALpsMXPzTJHm%-Ha_rits?D%_ zeSNSta!`y3A{sUReBYSQd0Yy6W~u*@&Cwc~rV(>_8^ zUo~X%pv}E%naZo*@sQXEPl}+4j#m347Iq(h4J?w_cjGE-OifQUIp|+3EGoW6Y1Z1a zWplJ0L(Ff84&6B^Vb#ThtL3XML`z;Zi)-+h_zK4)$vKIh>jpXS)a9f<4c%FB^n9To z3j0v<(DeNG%>{?F!9SZw@iBo}Vtd-nQHs?q5!pj3d}`lnA z505g0Xd>w6f!NbYy2c;eVEIeEgUJ1-O8pNrTEOF;VTRWeTOfdmg zi{u`N;kzj{spsP@@o&7;X>xW7#GD!Yc=+P=!EHXZVADo7D@Wb0ua$8)1iBp5pS2wxBzVHsR1UDedQUVHEz zOXaY*eHElGDysM?f3WV^MrlCFoL7wZHHoM!GoP#O$KM<`{6G_^FoDSLU?H}oxt$s< zU}5Tf=BSU+yjx7Z%XVBX*L>!*oC&x&=n zmg`?R&r&ieblWXij_5sj-cVgX(j$^B>XWSL{pllKcoRcqKR%HlbM?dRIiVVml5|97 z+?6p+CRWf2aVQpsvSTo~`ZnGo=!9mXoX9&s+>czL(J-fnFFDDbV_t%|S@=VIcPJvB zoxX;7MA{au#C&qYk8uRXN35$P>>&?#UH-_|MuED;6Dbnp^T^dd{oFBdat^jo$5m>- z55J1mFh#hh^6Ym``rL&_7F9R0%wLH(kv5rt^&C5&K;jfpPn37k%z-8EP2yBrJM9Vi z*Mav6Z(RS?AZ+uFuEjmrvP8n_hKwM7-+JSvtCWv(u_xZd6snIeYah08!Wha^$ zh&SBfcO1!y=5}t(p1uDnUYCz*-E@q|afQ7%<1LIT<-_P`Pu%{D4?Im?nNQ3Y1i9JNh|+YBFTQ4${cZ!Wji+_d0&x8@W0eqU3r58o z3|=*WAMpzx6q890gC#AhJ_7!`0jnLXV8pNKBScO-)rg4LcNn~SP%4_aBU)wF?ub6s zG3@@kvHJA?-|qkVnMKvO61)M*jbV!IlRnel#^mDV#pTeFdQ*~_7^KI;puc6+TwNI_ zR!Kb@AyqM)3`?SJ{5}N-;w&>YTEC_Qu>4-EX0~l-e@-2n8L1hs9b?9}e#aBnZiK4( zh|N_X^2giN)Bk_H|LZ4ifk|@J5<-JnWa55CL^!&mx?TatuTO-CL&D7pm2j6>?Tc1U zoJ*4RpI|0bMu1gZz~x&;iis*BS#}nG{(}?X*sLI1Au%?mAW&es2Y{~slesZ*TBO9! zEUEkTx9aKtNAZ9C%qEa+D!FEBApnqgA&-vsWV5NWnfpL9NdCTFs1*3~D_|H^lF!D$ zDle{p2(noc`6b8kV9L%v$*_MI1>&vp88D|RSh6QL)UM>$Qi_RopqYL05YOKn{&f3) z`v33ufBnoA@MK;8KTaTTU`z}G8Up~-ODwz2Y&cfc^sLwce%Je>Y>kpQ*Ff?SNb(*K zy!WyL0KheWlgty5q99{T5%33)-!(&ir`*j50H_f_1qS@-o5*;z&M_*#eN%n<|L^yI z{pIayv^=>L=9@*@0c`DT9k5hLT+u@lG2blVF7xxB-dcm`X+rB5Fq{p$0A6Q-Nywtu z5}fmIzW4tEYI+||&(5z60s{0dP_AL*X$P3T_uzfEbM>EXS5N={-TtRfLzSVx^5zG# z2n0w!c={#&etzdqj)MRnc+9q=)@0_zeJ7s&{5|?&P`qb2;G++9h`Sv8f1LV%`v0-~ Z|Npy+>o*+G{AU0F002ovPDHLkV1oJ}{3ZYZ literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Chevron.png b/resources/textures/displacement/Chevron.png new file mode 100644 index 0000000000000000000000000000000000000000..0a387439e2b32dd5836977da53d3f6d46b444512 GIT binary patch literal 21202 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%0DScy@6wo`&-~2K9O+al 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

v0X+V<*G_NZ_b(;QVx+*E?bck*sV5;PTB80GDqA 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

yPyI{rz;HjW(HomU%L5+{ThdcCVUS7 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�^mPu0dT(mDa$W~2h+*! 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%0D261}e9($_Xp5&7t*)vj;=1|t6fLCsScMm{9 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

2Jph7$PjsMZ``_k5j^;gf6%jA2_N!kw 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

%Z1t>UU_ObufT}q=hb!Liq-gC$U0VTa z8US0#+5I)e!W;L+4J&1kB{m2z@ty9gM_0zohIPQiQe62KNTx5QbBq`lNUe=$J!5?^ zBJf%teD*R=Wh+6Qr=Fb(oqCb>idVFZdS&5(PuAw;=FHU6%%ke&e7})BA7(8mv`|ZO zS}rMeD4vE<@Xg|I^l^5LlbcB78vUMo-xRv&y@(Q`49v`J z(K@5O#>j1mK;$a})?(6)nj?`mk1|v}%b};Eym<2Mmd9)FgQC3JTB6I8lAZk}#wniUfbq3QDN;?-u$92?dH~wMBu*%tEfCAlG(3%vn5~d=(03iMz zeUEXH3_f%0u7A68Rj>^{d5CghWG3%rq0QH(F^e`p`G~W42{fHE43mM;k`Yah1JrWRWn^-) z_$zDFBXmxF`g&G!o-e!8l`MYbY&H(P5Yzw1W=WIGZ9I)D2M74S=&O?l$V&#*gHV(4N0A;|89 z2=EXR^B!WryR3c-Z7OXvb~lmL{n5}CFb*-KZnCizA|vf>r=CHPJP+l-G8Gjq@dS&t zgPFaNh&D2>t$uo@_i$~3DP1v@Lu0&FP?AnL!6O(`-?*cEQxqC6UGrsp{q>mO`rrEt zwgZCyg&*MCzr}A#q+bGbIW_YCifrWNe?R*@`0i)Ssj!0fg0|sR#XYAw`rpeb+>ko{ z)`H~MmhnsfcC#Vj@2#}&T~z$upUuDg+!8L0-?OvuTWWEeCl_zcqc*?N{EuKK@a7v| zFTc22zx7~j3n0jjov-CVN;)&XWr_2*F4^zgxP0v}-zJc@I*BiRmsh<)R-bQ^Bm3H} z;qRu6{&w~Ct>?D!oBmFI{S0~K0en;8`Az^=K&Zd=6?w_B*G(t_u$s)zEKV2Mn75+% zTe4$?;x->>I#;xD5Nap)Fr%b{6Wmpk#^+Hz=+R@bX^c1pP%HmVT?SsWqb&V3>Ti1-iO% zjLhyPAg^u5OQs7Pc=Z@X@jAh31kO6!M6NxPWA=spUyeh)FBzIFlr%5${8#Bh--hSO zaPKdry(lx#^hp3rN;;XDhQ@{F<$i*Tke$E{y7)FkvCF@+Zvz3K!iAV7=XPo$y!Nya z-80eEJc)n&rRy6#GX&17>z(X0oYtrJNRuXN0JOu~@jo zh~%L;MlAI?62$vRRorCE(L}Ua1nW4L6%GJh_F|fia8vLIlNJpEQfBsJ*3Fe1N;(=Y zR#}jwg5G)a_Gj*&u`;f0fLAh=i5PQwbn*h)3ERUauCO3JNRL0r@#2f9Eq@bph%(cm za25RED=L^JoNFf!JR6WeWH?BE7JB5FN*8f6ezBLWz*Nf$_fG>t_$0HSpCtM)5H@+E z;b>YC_=6p0lw(P{e;4-y6lg=)TrHL&XVRl$lIYF9F??9li^fR*^IH|rC=9S=wo?FZQTS z=FMdEphD}CXQ7dNI<~t1)}smO(?ddXdn>)q!7 zhJ0+7&l|&?sJ82baA?YXC=na6n?;*fn{N>I0d%^GFlM%%k|8p?$OO>A|D|YE)Ja=r z!pXD>4N@oI4JX>QQTl+2DijNjq=+i{B>p?zO11Ydj9r3KWRJmx=`@H<{uqVRRuqb1 zE4B$SHa$X=xAuMb4wWy-gyP<+qWgBmcp$ihhuS0)YH{Cy-CVDxtjz58?1LfBK(YzR zB~;N@a_W(oG9D3xPq__1i1_oS0k|?f7Q8QNfUpMIb%hKgu^V7D00|t#adn~5`#a~o zzW(AWH73hQAbT}vK4wt^x60rNGQ=${(Y%)yR&!MC&2W<_i+l@Fokhc1@}+wTeBtz@6yfdKLHsEaLe!@2B{uwT>^*#77N zoU{`}83VgPWkFbUakZ(TpEm7q!>%DVgaZE2bfa^VTt?O}7B1n-=@4Zc&uIt6FhpU+ z5%Hzb(?<5rE@SFCWG(?~NM2!ZnHWmuyZWyygQgO2~=eNH$r?+6Qryj)(UvM;GBH-3e= zmPOo1w(z~4ysQ4l!5yZ{*Tr^TidR)V_682#%60Su;FIE!hCq;Fy8$25)q8yMHBO^N zv9wGkhnK(-vuNp<-^6IZV4StZ@Li8j&^*q;9RrjUxrzM>A(kg6G5WC0;jHX%8jsO){$bsjoxLY8!~nl7Vxf9i3|XdU`@rlTXVjt?F=|` zhTAGny%4-vMPojV%Ho)+xR5PS!(t>4C%DnBs2^BI3;ApqoH{4|we;;mK+4sNpbwp) zu|WauqZhFUBZXW6`GYz$P~H0sp*T>T{@@M8&;W9nzH~7YEa&H(j*@KScZT@voB&!Z zgDx$R9iviAT;t)yDI##XW%|-EoR;ps-wn>0Fu%rN*}pZcI+pr!j?T;Lywmq&!2YkA z(KDPWh@k!!k3rWWOpHx;r*53_As)_92sO0SGV}I+nPuvtYBY%8EaE;IMl`}1E1&`j zJ@MMdA-kRda4^yV_j^Mh_^dI-SKH|%p{oD%K<1*ORI@B3LWQB$a9Akcm%O7aRsUQ_ zog1$TJ&M2@nY#{AoNl{+=4JJ1Ggls?rw69cEA>(|@g#UgzdA1Bzf7<)G< zC0ssM?q^`@(EIKqxiYt7NyNArW9uyYWpNd)P{bk3?;4`fOew)2G2(`xi&c$HE%$hC zc;U^F<2CAz1DCNZXT~nu*y}*2`2!JMD3n^Wdy|-LE({Mc-3MQGhNcZ4C$?uM1 zR`>)cHe7a^@(lJI8xheYI@p?@?e95W(L`!OG&!=PzKvUc$*dGT3UE`bnohuKL2@o@IsUKv@dURe z(bf8amzbA789d+VqrNnD7DIYutXr)cJ@lynOak_}e8qv9B#K~=!2TCW zPLomz7k|u6iWU|MnQH+q%WecRer>@ zWRzjQ=jj*Q1nB0pZvtjUJ(d&Sk_QJ2Yj3pFOVsd<4v~dpb37m>T1@^wf>;Ms@!l0$ z4Yo5tUfbP6IrDId$@xivYYUf{-V%&9@bc_tP3&s1ijA+}rCE`2Xjp{AC6x|6 zAy@f>Ed@!ODj08Ii^{zp$al#>{9_D!?=>eDFqX(GgH7;wbS?1T!nb*pL zv-*kZ-9h{JmWKjy=lERo;x04mA3P+##Gu*6w!qW!t0(}7Y^lcd28$TFyUyf7r_+Rj zqg>trb-}`$bxhtzJW+;T>3eH07{{Fj3$|ImEg$s?Vx4 z$f$Gk_bku@OG8Oj{R4SVA@8t@Ny3)Dcg3{f5oJhrS48>zXI1hg?IQA^E~Da5M2U2M z`ty0Gebt1!;<0Y~)(s4Eyjh>+OGIIjl*jr!CIE~{e^kPhT*xlT+;nDvuv;|Du=)ev z;=ccCN;lL#K|Sd(hxW%$kF!D`$yLUvWjF?LYAam`f{^~=0>5(B04S6F0W+xZ?{*#3 zig`*#^OrZN*}wyg1|(>j0ww(YaYK0|SiZ{iHoMU))dOI{Hzpeo<89-c%zI|z5_os^ z)#@~BVbPYLP+m5y@-nO)^zYKMN!<|o$mV5`md&jSEf^DX*t{inDrpI_4ekd*p~+!* z=M;lM;0QHm^9Ud05sKERH#%s07rYV#bQLjZIpN5(IhSe61i#%A;fq=UMJj;^hnB?G!t%!ek{QJjv?~B18E^ib$JHor~!_K906qBLhe9# z72AV?xy2s%WN^}`<@o&=u_PdHLMKlTHcPAan);#QMik zCq`T70;jS~dLAOAt4Zk*Xz|R2`MYCBX^*|#BvoJ>vF};;nXw|IaAuV-C@?mmXM&&oPi&=xVTY@;wpTatgc0#KwQ1>*)ql#0vh zJhjXnh3Vd-*pQkt&B?21V6NJmJ0!55IVU4$goB3U*R<>ReoSuNVxkqN{n@vT9#Ih@ z+H{HQ#nd`x%yt8&L|K#=WmLcOR|d1FJEY`b9ZL$RBC#8K9tAb&6QgFB<;0 z9{p6Ti=B;g0LJ(TCKF7aIRNGML(y?83a^HdluA-A5P*EzV3C#3DB-WSW_yTZGBYWu zz`61s@DqYEDqCWy!sDLEqdc*l7`4Dfj)T(6x+oAcFn20sPo68iD8`k2v#!~(K{?C& zdgZ|Ty$tXz>oIf=GR0d7k-uq)3<+^i>VS zXlux~f&-&%4FNFO7yV5NTQJ$+&w_!YRsrwwl3e4;>}TIw~0~C#9~iW zB9|L~FMF~kIf%t1%bh6~_i5$o)lo!=>j!b>LVAQLlCR*g+~f zHi@@qzH>b8SxRWbIv1WUy|>^H=;eu=RzPTu;&TF!#bM}iN-V@42pVPalxaK^z~t7^ z&MclwJAktYWz_iTkmAeARPKW$=vYQ62*yVe_*ocKd;2*4ZEqtDIN~>EWf8^Isj2f&Y zi#s|;Ie-?&4A+2gmG>DvC9hi>vqFkG^;;q0(<3fT(L%ZJ>8!}JwdDHcYK6eDBo{nu z$P8U3zs1!g$z5+Ka2A>wa&GJqal)qApvh4qthejwa-hcoD5G_8<@2_+D3K$Z4t4Ww zsCFDpI78PWsfw7b&F0>V1eG=wA;%paz!v-^4hA6M*%|E4N-9T0DCV%d=#l;uwqAsn zK_d6tKwp%KNfcu!;g!_O-W^%W5&{*CZK_nN{-Go#GtLW>_hVx#$NM3mpRG=Jn_! z!m6~~e5Gj(1H&3`MEZ>}?(fB@n!%pA_W@mNd^fx8u8VK`2xytS%!5Mg@2>ES+kR6o zcHi}3rnJuC60JIRJB}A<9AK36r{2&@7gTkZg|08;3WzGF4P9mR$!-jH)99Akec~L> zfilfeM4qMWa(Ov%rm9Lld{-hS_G_$@+nwiliFFMTy~)0w<2+Uqfdeyt^-=KIipcBZ zq9YKWL$H|1O;48<2P+%X;$lp!bdaGR%XnL`EkzBKR8&`HdM1%QRpz4=%9&lTb)u$d zGOFj2)CPzdv8?ZrJ>nAi-r_=y(S{MYN5mES(MGJg>g^( zRzRnr2*Xd_TClsWzfS{qP5}CXn&T3O1vW`(v*UhbPvC=!_Hc*xJqN%m@SC1*MQH~t z8~nT+eUQb^-ft5qix!d|)XZq#Og?{2hWD4hX`ct8Ea1A(NXS`{7a%x1ggnL%8mmIF-EhnzN6ddw#o<2CH<{ zP^)^Yi*_(~)QF(<#;GU+jWopsV`U!=H;P~E%-sXAaj_~d&hW3OSusIdQ3x<>8QbJ> zHhRyrP_9-R%GiKdaRU;0I#?v*G%mnx8iWd0GEKzpdr# zLhwSu04-(w@dp(2@NW$d`h0u14QwPwvx6tnmCbtCdcqGh?J$VlN!|Z+`dvWMp;*Tt zChp28L1MIWp25M$g#l-nJDO7SuKqDRa4;A68M(@T=vV^QJA@{mBKpIbG5Om?-s_q> ziD=MP=B$3@4Jt}4GBcrq zByyuQhd@im#;xt!#aXpG^SKkT5|S>?lYMaSw81s{EYb0{B$``(G^{PI>374ab0#? z1q~PSPj*HRduH=+qd0PzbIU@Nn(O))V2yOtvS+R%{Vnh3wB!X956JsQ{1wOw_dc0s=X8M9zx(ai@53;bTm`Z+c)dF z{J{An&`)7+4;;w?M@xeY)jzh<#PWnI0d|~@+cQaoj2d z;O3NF?aIu-j96;?wS%GhL-MtE1ORNvyjVaFRILtbeEB*=Ob$O-o`~#!lPtIbhqT_g z(y64$?K5oK%ZOf%PVV?2W_X3&P!*j4z^0=S7iKK;1l(vd!RXW^C!sYoep3t`>uz5k zX9sjD1nT-Uk&6|fw35Tp(LZeH6fcj=A#Z>_>+7<-y#E;nmRU!%bfGU~XjCUUP7hDW zz-ATXC19Xx(#5rRO$IVi=4aToz;PoP3sc~J>yWA>y`|IeHWXxY8lwz1R%-9^3Xo0N z=aX|d6LT+BQm&d5jGwYF{Y@N+d$+yP%!-0z-4F1XAPBr7wFyt3cLy;vs^qemRbGO~ zjA&aAQSoZ+e&FvufN0}V76VfmWLHCVB%Akc4BEhg-q(irr?<+&a3rU0ZiAfOrUW}J zc~JtItDw)onejA21t7AMNWo8LGZ2G&u7DK{IDIVS$R$fMRBU7(S6$@swBDn^8B0a( z@`t0gDWq1NhX?4AkaJMZ2ouq1&^#3aW94OYVn66X0w^;;Qp@+&pwOnyfYRwNR{Rj; zxndE3b%qIyFg&i0%Urmz%eWmQ3wD0d#46&Dal1P0fOu6v%%|z5?OBWGdOudH7q@a6 zUs`N5&TLgF^cQCw`5RO{RqOqnHSvw@MZI) z*P4UCXdu^tXEh2yMw*agN?ja7u06$QiuH@^;$;d=U`lgq|3b`-_22-~O#m2WuVuSA zw5X7RqT=X_v&(^vmpigOZY&QRMV#|d9_jrtI~c;dxEAu&8EK4}WqF>$?e|{6Ip$_k zsB%E243%LbQcn)CAJCep+Xx~^UM|6A6IqF0BZIP0Ccdx2nh8+ZH6_!R@3zu?idx0lkBo`K%Low#zmjeE0wk+=` zYG-~kU|d9IGQ*&Y>Jj#m=xY*Lea76XnJ1FpS5sk}?I|{8!R`jS>%ZRim+T>}`;$w% zek*>joKRy&6#^Dr*Uiv!tbOrwpbZ6t>LbW(n$vGw3!P5a+cQuXj*wwy*V*Kd1nwU} z^FT%sJKu*DoGvG9tObs_ORd$83jjh>*bRO>kcY`vyu>k_97XDWBfBbu!pbCkouvf} zg#HuRw1I0fx*?|_VJ`191ePXNnSz-3M0a7O^TYpI9CS(8W8zG2ycY1a!lt7(#}hpO zJcY3g#1S%}wXeYh$e#hUD~j&k@jlpYgUcx@5F48G2?ZyIK{XjrsEjuNJ>ho7!B#04 zDG$L-j0k{B!0tN&g@P~6i*9`^Q5f)i@r5^5wcU>vdkF)?N)X(oSY7ACJKT_UX!XXp z$eGA8+W?VmTtkYI_Ob-bnm-!KqE>xsa*Vc_k)H+SJ7-LR**h5&Ghk|ykFKtp1vVse zZ4nFNJ7b-K5#u9a!cQ7ylEDqGRG2`yX#*f_@!OI6zFEZFW*)0P9>w&A8yI;ZOxL5^ zjgw0s_KxpQnXp@`~(S- zO*A&E;|HTaQ!rP<9F7@?7h?hG=$2%lTwWzy0(IECO1x|2PTwenX~Fb*C;%lMi(oDop3@2jI^1tWv(++s~K-y#4S9>}agV{vj0v?8>I05*XIk$s#i+CUdp z;y(@@SJTV6(QDA>Tf?UE8_&F~YU!O42*SYgEdS^HYHXbC^fSQ2!3o}gLz%mGo{x$E z4Xh9OwcU-qq!hJXNlhZ>?29wdW!Aj-A-MQ(zx|6>3(a*cc%uZ0f)<=x*oZ*KP zqxBb^3e9ZiYt3ACcW+;chGx@PF4_(=~(VHKo95<|(Y1q8Mzq&r} zeS|GgpZ5CGz(m2m`lr}iI~_Lh1xNp{WW3G)HRS=e6K}KsfvGV$P9s$IBR*f+Uijh* zfW1~mKgQsNi-LlYD1tu43BmPp_q!mvF-)?g5NChOGN|XA3DVo_j{qI~uYW8;yw|IX zAJwR0I)Qcm0+@Hidz2YV@_mt|aG`np@w)=QC2wp9BOh-fu3Fqn(=#=P4%o((|EE0EQ z`bmNb$(JrCtcp2A2n_kCfFciL`#KE80E0AkfQ7s^KblS$Scgw<5(1N}1WOot%Pcl5 zyHLFFG1PQETf!$vJ=TXIi)7GWby&b!^Hfv!F1clwjF$>xOmoC-C$D^Fe;A({X8YYC znR_?|-3uMpW;mm}PefX5p=XYc;h^SnJ9zF*+Su0Y^DkGuEdHMOQZJq`K>&-23-$^b zIOKAlkF$GAt?>^Ron|&uhWpvTqMw|McB5{XuXa&4HEt);o_*jpiwy+L-r1cAF-^Ls ziZwgmxjs{Ct|Q1)npSRISvKM z?I8c45qP@k&v1%&%H{+`v(b-21d}Snm&N`RXcAkr!lBxSUFybNj*KdRu*^}v8RjgE zCZ8hSku8|aRUm8gaT9T(&S3Rx2S8l3WXOB`;K)VuX0UjC7;VFWvC_S{H+rOJ)H4mr znT%M`dmeid5vO}8sO2_zEi_H>YDw-8s+jPN@088U`s2!jJGD`C6eM1hl3v?WBQ?Yv zqbrUO^!0*_FBi{M4YXnHb%Jb-Du$Z5L{5z7CxQ~=i!i;#RNpO)JE!3TNc#FkkbExG zJVios^W+WvxX9(({SQ(`6Q)88%$P0+M@MV(FQl>75U!${D3=|bb@)runmCzZRplXO z4i-P7Pn3VgS{yPKjOp_UtIqq3cKHi?=V4%nrIjKw}R3IZN2y?jTnAf(VsH3KC9$TNa06 z7u<9aP0=irZt-7gwIQp_eY}}Pv`J-7ny$2V_{dD`F1p4`Z^zbuaFdqf>;1;8yt>z} z877#XyrkI991?C?HcG>5?*x-}&s$x!+-~$ayC~IvELfwSPk|cDv-w~h#RxVc;>XRD zfH4M>@svwic}fpowva^X?kM8rXOf=5XJj$}*`P9cBfn^&6$$5vf78Yki%rm;^%|k* ze=_K^?y1d#MB4ZeOp7n>CW&$wvnb&gRB|{PS}ONIMGC&5L`u7`Fw{^+MtT!^bee&F zo`d!PnJBB%A?%K3;jWGljk6Z^$hYM-)48x{?y80mpCT{uU6a+${ysT>LYWk!_&UQyLp*?~ywJ>Jbo5`UDVALz#1`M_v1cc-j)rF2RvHv?5wVeStvVO5G>a^eq zeB=;cJC89h|4rRvZ*fJ$ORDN<;|MNb485}lZPQ8wwy#?et`frj#D&ml4*=+&_FKD( zd!EPdBHmjn4HD0eSQdddsQ=`^+((B7tEu&f|2UP(6Df@pT1802{m@rxV<2 z#Tg8JIZeBq_%_TeDbw0;WgLRCI6+>}30)ALW2?}pjrGSC%tKQNE`tVX!Ot}A;9Blv zMIQ^!t%m@Z(x9hY&7l^iP-pf>8&c|Lu|)!EWs|9V10P7a1WV4wo%ibx0VF?0GRbPW zeT9P>?i1M^8;X49tak4Wu(GMXAhlYbghfR&3cUn#Ioiptrr zCB^N$HpFh&IINvx9(k#hJr>t|f3RaV!N?F0wnXZ&y~%F~HxpP=bUe~bMMm%yMR3N4 zh&4bLlvBUZ^#BC-$PJ+>n^m0G|#euwvL@{U3tWTd}yUEXl;O^#;hxIB3CFap9n z2hbPCWFTPbv_V8(x+E;g68FOlPjO5#gJYvU3{SAng0hKcM>o^k^VO*?ZKDI|0mU4K z$fki|RShBAgHGa!BK0Bm7_|3!;JiLoxE|voDJZT2>WK6!qBXCQOd}r52n0P@KPe)( z27kMD%^6>|2?(?=9oE0$Kfo@M9uw>zDg(0VEYYUz;)-UmCJq^>Xsd;YN_8)be^WwR zxr#F3#pF(w{xUbi90P=$c~)>tZpwzH?n`JY#syOk(+;$jgNauyz^vTg?75>1KIuSh zIk9kxXf*4vJ!k6(Y(Nf!xX^2dh7g%mxHdRN{0C?tEOo?lf{g6B!HW7%8lFP0#y}do zkvU3-dfgKwsD+)3$)hBkzv#pqHR-+EM7eY7+js@D)sgZvOS{W!+v(m$4|3JVXcO1n zI>L+ARqz?uSbvLY;xFo0K+PY zW(@M=2Usf}Y)p(X``8xby}?VKTp58a1IIexPSJPh zXz|P^i@O+$M2oYG$9p=dxI^I`vw`S|@v?N+65tsuCplfdbmUFXOe7I52Cv^Q9RS+^ zV4@XHiHVD}2a5;>!U&)ySP8@6G;L#&N~GZCFVuig#uh<(W7a#bD?k*}gc>;~WDfE> zXKCN)X313|&(5+_-q8yY4r_E61~q7&-jl=Cqeh+O(d7my4hxqJ|5R9Klg4@?DK;KQ*@8+{k%HXFx3f0WRIX;^rhzy}}c z)elmEOoFiD?U9-XDESIhz8BZ&6wB&%i}V0Xv>$|`->tmlvB9en%vwSRk^t>!) zs0qTKZwhB%X8s9@j|36?g=bm_aLTR4opeG)>j?9(>o?tx;3%Lor3Ta#H#Ghrgo zLB{4ho*+h%Z&byl3Q?mZ21Y7&OzJQ%JuC zm^WFofjE6f7Gc+s^P2L*nvtbc&Sf?N6nQM(#86;1B7I0AjsEOoUfL}j!UAwXbr*u4 zL$|8#wS~$-Zmb9V$77k0Y@3LAT^U;ybftL&u5jkU#*83xhUMU#Xu{SyZ9xh*k#N?g zpgXbY;Hl#ku{K0TcRae`$oAB1%W~_K;5X*yG2JUQAfIh^Gs=y@$PJH~^p?gQ(-Lm_bmg74lcqT=J!PIx{JA$|Ar}h#ez|m9P(5VA(jNSlONP4(u&u~%)#>}_V~k-eda;n0?k`Z z{c;0?;PQLHKmu4|{_4-aon&y=_pZ05nc2I0ew_Qz_r(o67n(s?yNr}zb zT?6*I2FYzcwdf|O2qg5|lN6Z27JA%#H?FL{h-~NY=>lid>f6Lq$SrroY0B|UDhT5E zH2R^T+S|mxPh*zRG6CM}avXE?5pU$QA;g4&w8z)nNO`+C#ONGFTCu$tWJ2W-MF54I z%YH4|yA)~-(A^14V+Kz5aMJij$_LW)639%KL5p|k(<)Bj)Dxdw1BT3fh>v4t4vo(O z_0Wq9={%$4i$UNhf-I>dVPO}fG!WN+TwoR&Ge)3|ad?3c&r$^WRma8#?S}s! z2n;9y55*`xP=};Gs~F~ai<~qu*2$jG08pgkTti_8sh~xZ5qnx(^B8Q0A2=fVpcw)o zVjK?NueYAWJMQ21jwVp4yFR(+pmHR+E1kQfoM|5zD7#rhHLz)LkPx4q-<=^r_*9;idg!kp>l*WayhGh15$ab!0XWYm01`!^+;QjeR?pnUpEP zK=o{x8mJ5H-B!RXS=>t_g>I&&VzlWvVb_3wSry4KO9FIZ2YoV9CL4LaUYb*_w-PFs zRNLv4KRHg{o9%b#u%=cjf+AAT`m%pO4)C!Gw@uTQ=>vfH1@6Iz11l97K?TUoZa1E`SoB?_khL2@v znl%sB#U+a*Qevo7uL@F%wE*k@zl%kz&Wz>eW-xb$sT{VG4j7U!ZqWK}I`ZD|icXNhe?L-R`IC8<~s2M1?}Q5y+zC`>nkBm(7u& z;<`K!&A%}3Fo6NvtQ8CsH`w3N#8(3rztdIGL`G@*=3fpWQ-u_d-$d$RGI-%S|+GXsQf04A6oT4(pp? z97nJ=yEyZ6u%X>zQ>QO99n4i2XyE|ar#(I~K?bk;-OfM^C} z3kh%LxO#mJxFn^lNzq$&X4JtJTO?ATW&tEZmwJ%QM8(i9c6t`L=#Q9PfLZg?9F z?yd6&Afq2hqK-g7?^vRt6QUs9NED@TD=wO|=-_#$hiC)k(jj6I;6Mkqj~Aw_JkT%= zXtp%yWu-E6+GGx+XkgwSN8NcTbWFMkNn?IfVN9F-Oj_R0^IVUmAcdDGVoY@*;89xj zat7-4uBXnrduVMb*CAT11v9-3mF1PgFUn$IS{3wT_Z#QF(*BmxWXGxWG9I!4i~?t) z(VDJ-5pls|Hi<+`=^p0~!^oInx(RRM$fj}1hJc*WO4&@|(j=nIQ1J_3+~QdSFo6!k zbc0yxbwW~_(xDBPsTFovGXdB-5$E;=%~bIB8&_;YMIOtc#zZ%8laPgoTYUz{-9ooeJ-B zo;P9^m#V6TBpFf*$VJ5i0Kns-4q@`VQvkaV%V5zXMrx2u=|Gx>Gb?Q9BDn6kVR`{u z!pZHKw>L;ZWBOU5!e*JMunb;0sVJoVP3>jr;A2KYWaXi5c0MdDRSR|hjc7V*K7=j< zv+;ND*q=DUrNblxWQ#?$?zoAVH6`3r;J9(h)?6J~R)tw!FA{zOZCjiisd7e7u~|5k zO!#tcD2FJRTe-G&YXj|Ji@6*p|GlAmM{FZHbOk?4a!UH5<@h!|!Em{R)xC=tJ!#2) z&RHcaLsU~@V3$LYGMIx!I;*%}tOT+eBXrz?`{*ee-Fk99x8S5<>!aTpJmr^v5JlP-Nz!PWeDSa7&lS4vNN-%P!_Gr= z1kHXtlNI=Nd~Qa$Enp#t2qCBTEcXXyjA~+KBw|j~gmw$nn+02&DWcG^tZ66QA5s3ZsWZA&&~mT6gsd9!$vRycdLupTU^B9p)BEyiCj!B_kSA6N^lq& z192o-u+O9~Ljd@nNS7##x9wQ^o_#PDz+B{o5|94KI7ej+t^{hml&!@Nu zZQC?98dWufT!Kh5Cty=^)k%a^zC)@-cu(eUb5_sFgNU58&Bl+h?oV^)ZJ>A-LW03L zBNFi6M59cFpsXgoG1LPzdz@CV%WLd{RE?veQvQ&)%>JrXbcj-$U(%P7>Ao=)gGviJ zIV+Y&*;00~1=@e#fjW6u89Q~LwRafD$>4&!*uPGNi{o41#58LErGT(XiD$ znPhJ`%**_Qb$JJ4`y%jU*zQmcw3yu0JtLedMpKlc_8J!v;kX!=!UImMP+XV*ZR9cT z_ddF$(zUr2=@>4cMW4y!z9XFgfgrUGnOkFO~eE*|Z98@<7<2B~@ zoLVSWF|4v#=Q7En`NP9I0tk!Ma}OZHZoe82K{i!jD)4VwHRJYDx)7-M-HE$VuwF>?w`tW zF!#NY?z*0y2^YSzysSujlFtjuL1;d`)xi~K-!YX`hXC&&PPdYa;M=x^dwwR+C!l-z z0oh+mHvvFg4amkXyZ$6-b0siWHb-SJ)C2G1GeT?a|G;z35mGc?9aCMw_8X&xjsD@9 zn1L3OVJct@H=pB3eD_pRR5yF(KvD)G+;CEN9$#3_xi7m$BsbxsJ$?|?i%BM)28`CC zu2=QOXy(YnE#UfnPv!wgG8nL%g+B9L)km~{&J0HP_W;hM<6YH@Xzhmj`d$>#(PGJ6 z&7?C84w(aNRChq82X!IF9kTbUmGc&$Rz660Oz^+tFRCrisWql=!cgHxz+gsmr|3KZ|tny{ChN-Eo ze?uN=1g4khS9lOLr`mc*l73bcIlWN9UUHEoAyq~BMD^c%*>jTv=pBSGm$h~nQZ~WR zE+ZZGxnRvj!T=SxGj?6D0iqNwwYH-y0e~;cC(-hYJ&$j4e4|>`vqOffWvJLJNAOB< zN)@3<9(@O! z(ZPr|2vg+>_rME_mJLu+A5CW}bxuohz z3`-}<4e462&1J;Dfg#^7a&2s=(cu0l)nSWFqMVb~w?+Y}v6)Q$F`a2Yg5!f|A#S*u zi0i+$Ao08DE_H7rrDZmGssl0-7XF0{+k{tDy&6jKeTaIWD~90 zSYn45*U9omoS#x$qPgiyXs-VJYL?~x~lyZHlei>;LM%Kh5&%C+7pa7 z5%ePk0ELo>`pT(TG17z-oWs=%agKquvlY69>FTMzNJw+NT{|z@#7MLZ;3E8XVU}5( zWN=*k1p~?_9k5OFKtHRo>L6aM(5d>{Y?@PkPeNojeEku;rx&7=W*$N%LxQ#=Yj?L0 zBQ#b?4KWYP)AdwBK_cIsvp!|sAw*I~UAG7p6P~;#*h)s~;+2~!nhdE@8<8bv%`3a7 zG}o<*s8eas#W&eJI4H1RTnmB%gkSw(5>;bj0!~;LASg4?pjX)%fMX6%tB4g;)M(5> z=AGF$)RWOk8PjUuxgpsQ zaaUqLvt2|^$S}FEzmEAH785*W4c}c_ReCOM9y$-+=;~Aqun+b9-tkZaCsC3~aOY1~ zrN^bZe7HG}wW%$YQp06BjPJ^P=ls69heB7$=%2=-vSpJ8naBn&*>k215Peky(^cG9 zwR3P$8MmN~Hd#ZcbpoKZ2HBVQWT=o^F^)qEMJH4WyYvDfu<4xRsG#(mmTQTizlkJn z%tZ3Ayk7%u{haxlfmGxVi<6AXNXYe{6n-?XfWt?Ls+`1 zh=x@KsY87{VkZi5$qf@quUAkgCeaP0x#8?Siah`qxJ-(Bv&<+!d2vZ~;}htrgIP#N zj&|rh?bvQqQ63S8R)RuEeUYLvb7S8_KA=?NgG3MSu;XM<_Eun4&l`e(Fn!tBWt{3nhR3lamzKr$-tTV1x7o^}1sr z%_dbEO$;-k?t4++h6%f4U?2cPK)k=qv?Fy2bKsjHJdb+na%F5xzDcUhL}Ps2coRt< zb0}QOQOVo{XAi(q9?;?3;#>QGa)t+oN;S3hGZucyslN}5y81$L|3|YyjW;xeYARAY^O?Jg=Tc0k?jx!ltwe(meJd7a3YJ|UB3=)*~_Gd1~)3SCLYyWGL4?)3hBt*@Aiah zYW}k&%o0}9uvfT0*&vYes*W=h!yTBG_aMF6k_QKXQV)P=<siUtv?^k=f9}))ybFQ zULp!Dn|pd?t^a4>hCl<|cm%D%H&u*C%4lz*a~m)u?1+i-7;ZgZR5is2(ndakxpXHa zuCLvA4;GLTvzO?+cV0N?Bm)rl5s^!30Le#Ro=d23_i3m4Lg(!R7B|@mxXW^|mi`F5 zEs?)FDa4cGAFA|;kTrUILh1n?FvGc() zhK9(kJ8%o)LGSQJo9bCTu>pC@59y<@GzLPNCe=6>kFnk&;G06^ zM)ZI%-DjyzZwx3TM0o^|&joSMpk=(9$GlX7rdcmn5>_xG)>J3!eJ8 zJ?B_ff|wYhl?!$(-x5x2pjql6`aEY!?TwXVv>?-yFwAm99fN@q-pbLvLN)n=^HbWmYt$X z@D{ug6Vaq7VXy=#C8XC8peiAtvFJgZv8L|HTNfn;tri+lb9FdXT4CE&gMIoYIpY8@ zs@1v9`<_ae*k#@V*IV6lq4-bVho4T~rEM?NgaFY+Iez>8LBV+Xlo@RaMZxf=D0e*7g0py0U zNeXObM#OrY#v=|&1Lm&*_N)g^jRALKjXkk`)%(RD}!sQWm)@hYapc!#*j}=|5 zX2a(JKt9bywnXN_lk%2vs?FM4GfWO#{dQ-QEx5Y~s~5esc#zND zO|>!9`cJo&zCDSWCqXj%oU+58Iv0mP>3q!)g(%vsH8G@}h17t|LjyOnK8w z{P&c{uXF%u_MK%7i=f1X)hOE23oq5)6L}`^Rj;FJZ&xXO?X$v{iwjlzPXfZ4NWy#( z@4a+PLd!ORoSomu?{J~o`3ZcXa(hX3hTmMA8(*Wt@;T+&c2r*LA~Nj-Cd_j3wtx1T ziQYn^Ke}@V`cKl7zu7tx%aDhtmHqvbml=WHUmCkxSv3h$O2$jn3Z`<6FEI%5D&Dl+ zw{zz$;MEf+uOnc3EUY|8wlI4(D%3rIrhDU98Gp^4;CC*d$;_M+)52ze!^!S=ZT3;7 zhNBPXH{3YbH#Z9qDkl$zV)8;Ibf3Xfuw<9Q@4IdLFkc~VnUve<5xqzH-*OjgCY?jh z;9=rU#)YnXt`ZoUdYuXrd{+&F~M(IJ809m#cnK3h~RWGCIGXq|H&C8=q z&8lz&Q+OM41Z>(7Xv6e~@~Di=0U!lwzg=EKB zuDU0PobIjuy(0RtQ{Tt%{0dAzA7-A?{+nu(-jH`kC0ogSB`xGiSJF+pg}xDTND!k9 z7!?MAbQpG(I)VjFrg=E;G9X8@v1nmCDNqfFPg8N$+;m4LfF-V1w)@=7xt!2Ph94rl zt%4U2hlFDH-l-F`siPo#*{6+BamktlCmuR^++nv;>JDx^p^`a)6&03oj!)&foZDb# znm6Lxl-l=NL3YYSsi5F;24ddTjYHtWGsXfwPj@v;OGC)(#sYfKq4k@_hyY`BmG+dg zcHWHL5|POb7{R9iD;G;L_+BwEC#!_r-jfEX;Up>fd%U#j(WdbVZi>=oC`(PHPRS0J z7lKzs%)khdf`sfBx$MZ$hSc|CyLR>70u;~kA%muHyjLd|Y42K;2x=6&1oRV9uEA5)5&NjAYs*L+!;7hu!4;^iN-AsE*cN9iQS7qB&WoT?(SC>+vQT+ zfaT7!#kp|U%wLrujiLvJk|U^`xWX3hj`SLc$ySe6lsiK^#-wE7v1=hzhbqHZ{F6;O z9>Vf)r9YsZEvVT#7+K0;jLZchF4N<0t?k4tYOh#is>A~ zp!K{Ev33AUEKsfXRY?xneWTZeF--ti;64=WHu(VNbKjYs9waTi&WM~Vw-`%*(UP>@N9DJ}PseYW~u z6lrqRgrFlJqq7ze?o=f-9yL3Y5!a+WP=a)WSaYD^2?*%YNNNs-Tt#T&OrdIJuF9u0 zD5wd${tQHs(_36%`ruQ-k_qwmVR$s~R!nr91@Jg~w5DacA~JjSr94O2MRU*r?TqCD zt49(R5Ec3|+6`&(s ze{a1vM`%6d0Vh0d-H!zfLO2pDmUdOme5(OMweA>HXgy%j2XIuT(PsJS%qyQ^xSMch zv>B#@yAS6N6_YO8BoN~+V=S8b+e#eOA-nIXT|*8tL*hqK2DqZ0k0I5kUfcS!lDCm? zmljH9mMRL}qF5}z2NJ`*m8Nif#)CXZkAIAl* zXh*PYupJ2=bT(WT7Y6zkNU|n6Z#{Zcq7w3y0KLs3D^tmo@Iv7Rge>MOvroRNT|(SY zQF8L7OQ3xM6h`UU{g2t#D_o;2X9_F_VQfsXXQqqifDT&%HK4kv4q!7mnBKgICBtMU zwpOIFi3hH{E!cH#AqNe+qHxUMXKmNmil_rnDix9Jhf5LcU_oG2?DV)(LXIqeO-S=L zmxl|tM330LI6&i$r7{XhlAiI%JmRQxB~+v09T7Z+yB;F_#-iUGHIxIBhW7&<&m~Q% zl3fDzWgR!=h*QQJkARfQj(40yXeDzur+EQwQkHw$E|YOEUai#oGns{RLb zMsSIM+)PZ2);th&8^R4Tk%8Cxi9=Y5J{I~s;126LM$+Yw`s-wg`am+@v9$1i_Y+Io=0(^AlT_Up-jsk>o?I5$z1%aM3 zVqv7I2`BAI`$k(TqlCp~oO`$#68b5vy>7GERsU!>^p%&kUEyU<-n16E#$kb8en)3+ z)A=N`M;1|l|5t?7F};4z?8W7(X1}rAa5OD4OGY8KQgWMuxEVntVioX3F%Y@N4vTlm z(ox<@N?LwEl~3!Y$PJ}}Js`Pt1@NAth6*z}l(v2Hi@d3Af1EWU!@lfz^>usGXlV8Z zk~h$O@~oIGN&8!D{#jF8ML32Fy=Yo+W=1C9cLXrQsutv1RZ!-tstCjKxz*p4)Z5 zxamMW7nS3$*6+8smu2DH%9Vzy`{+$CZBnLUJFEk!%X=$M#$>&IvU|E1iOD`>Fv~Ze znYwVlo@Ell&D31^a>+lC{?gexCL4+ozj&CFuj+3zt}%05h2LViWTodC^rf+sf4Hpk zXoccj@S79V-b)??8j`Zzp1jbZULuRcSVTO#k~Js?4cLzPdUuxF8Y83vGkm7c;BjE< zGcZdm77!3N#4#PeoPx7331@9oVy(;MKxfdmd}#oVh3P_|`Q6M>B{L74gA03877-NA zY}k9)=s7X2iNr-D^y8h;HOoeVwoW-Pi$NULA_iZqFKId95^SO$eIP(2SF=xo*ZK2d zzhJ}4DFeMYOPvD^qqdRPYPK#mQX~*$#uq(_nk&8&XI!d11JvS5>7wWw=_oMqvCj9z zwSzpv0Z3K;6}dR%%z|A+3LX}64kNuX74e|K-QV=h(mX9eYDj2G(osn-lI+f$B4@BC zMd)owtZvVA1cBhR1cX=TGyWC+HHDCLAM%1PwZ14bSVq%@I6^g(D5Z6K5)Y=hI&7A& z&O==%AEV~SEA1&~3Q?bASZV{f7_kCRrCePMX9R=U0}%kOJjh*4r!u%jXpIN{8<%pQ zE2m1>DkA4#0))E{xqTiLmI7q3VJ;6-)T?+AfJQk}5tBt#86QYI~4f+;B#DvSUR#}umEc!Gy}p+r_Sjnh@8pq zCO~`p@CyZ;l_QR=>04)iSDC>_S^PL+M7^X8D^Wo~!7Z4&T>K3Hg=syiz1wJrCj+pf z0r}Zl3(UJSD>H`s&KrA8Y-A%ktwO2Tk`yv~p~S-O_JoT0%?%$NqL3>@6e3M+sPQAm zDdocD+4Z%ITb?}=Po7S=z2q3eW5^)+yUx9%#9?I*S;L*UKf`k)4u1?u?;dtr}QsK_(~ zgayQ#ho1ZlIh|RM)p3yIEJjw02;3b)SY`k1slp;H0vg8BeGSV3kn<9dNG1&=l75_;qLD`YymDzoLH z4+q#>_fv7!J*IF={W`#2niy3+&AT-t;h}R-)8WUzit?^qIKu!6i8fp6u0GX0C z4ofZ!^=HG|P{%twxdvW9TeBcdDKBv+pr}Fe=!TBtNbj?auiK0i*>F|D8oPovDPpjr zeG*TFWAPSeY(5IhjELuF>)4A8kiP}8$mFT<}z z3qul?wr+exmG8os<#M0Jev5)^`5$cBSp5Vz(5-H1R2q*JOK7So0;TJ;+6nO=;`APOV$IP`d7_kZr@3CEG7<}J&zhK&x72n3Q z@`Q8)?5lq=BmJa`%)|*9>5=( z5J6_Fe(KP^j-FDsxf@y6Cy^3O?IdQE0)%~L zT66RPiHBEZulGjI8VKL|u&nK=au)V-QDu9MiaUd7qvi)g^#I4_`AD08ndin8KqFpXYr7!3{x1B$yvYS3^M$lK-kx8cV1a_d?{wG|Hhb=v_cOq zBx|+>MN{!jldC8dDHTeez5m2eEji7Xq6{+xKL&^MtL)baUx-6R6_)HU%G3n|&lF!& z{}21Li0Tc$zJokIUtXsFUr>@GckuK2LG_+hWvzbeH0?D<%jc9166$x_VP zx?e*H>Bk|o8|>4%ixTuA6HzItrbr@QV2LNWi*ySeO4)mWV_rlKuo|CJ zt_%!YubP@J_yuT%Xe`NWwr3cU56AzMeIn939jUKXUdXC%$w4t9B1K#@5v~@*shqMR$HVF-hrUV_frGuv11+dsG*_wfa#K{%vw(e)J6R3{F zZC<1eC@MJ=g^L}Y&@A8pO0p~!R^*9uQb8DMtZHQx=XfZs^97`FOzS)>$IiYC&RoT+ zFRangR0_i7rNh`5F){N(Oop|3pBT2L9~?Qvzyd%aElOIy%D>P+gP@E<=DvpIvMl}4 zM6qd4AuX?Uyosk4e%SJ*Y+ zd6Kx0X!fE*5rUiT|4~n&2`{%YyXMqz#$i`l45dsT9#D4(0QV~;?(=0t^&f@+J@ppd z)8Hw}hh?Qo@Ud!xpEZiXX$oP2rP8@qS82x(<2aBeWF*d$l7-?D%}j`~0|3$Ay6@G{ z)UTdva#TXgbq2NoR2<+t#3JX1%{uOz>yD!l)89 zoG&|3;O!qAokQ5^lKt=Cya&L-dgyWU`@p$vGgAsCI|k?JBl4^y_4242ZLscyQdRmw zKoznx_t=qGl*M!=MdN;%qFXd=!^A$po0R=d>sXZ-lQePTR70=PN2fkWg=K-pP-4)W@kI0q~;>K z2t}yG({4_Vy_A)lNQ+xYf$Rvpm!h$b1fI4x)am8}j9DVUjHC$TVl=M)N+0Venow0V*7V9EvZo`CSu_y8jKW+alU z5oj@R=)FGB%1S20DNG&gZh2N%q*DvI=xN>in#nz2P1QC4jh#UqRb+;Py z)H8Yc9~4LTr_d3B5V7_w{>BRu^XSm!PXPGam%Vt>Sxn~yYizRQqUz@Oe@|x`C^--} z(}!`Q!ofp_JF953Tre&%beLex>J-Y3-q}r~hXy(aGJeZ1_IBxa()X9W8&Iv#bAsvCfy4pm-Y_745n)uzxs%g%# z4B4d@s<9=k-DPB#%Ho4xSxys5+Ov+*4Zvs=GGn+^3JKF2?>~^||>Zl8cd(Vk3Jsa4H@!vu* z7hlAiOu0l;h^UzCIu<6il z*O5(Y&Uorz!n~b}KyEMTB_!b;(EYdaMME-D4OXX02@vj#%lm@h!|XxHh`OCXiAssU zj1WvkNiUBm!;iBnoblUX#cj2y&ka2Cd~lryeKUN|X*kS=EcA*1LX+RFqglGjP^E_? zAEn#2Z|ZfVmm-r7QXGiwgf?)ChP^l@=-9G%AAoG@O54s}Xy(8ofMqhBxS(;M-}_ph zT8$s8Z~es&g_>TLt|_hATS?Cc&44HQxc+qwrZ0D6bfZ8~?`)eCCz*6t%d+wU*jNZk z%QwXf&$}rL@s;m>qxVhN@m=@B=K%N);gTNRL10u`pfuuZvSrCRw#bx3)VD4WroDnv zfrQzyjLKte$$6|KcO*bav)!f*7|K9Z{nPB-E~D1KRzqe=Q2IAZOyw>YpuSmn7)Ut? zyqoag^`#NfJi(bovTz3+BKO$*y>KUIr`YYtVA3b6p#TlgAqkkAEwhzD|0ITKHnq2; zaie9(0K@F7G(XzBp0dBWoCK`T9b>tzza-J_`U4iJIVOJ`mnB6Ch;#9K6Bh3jzv@t1 zzH-|)T%+LP-y2^7;$(No$1rpEy)~5=liXAzZo;1=dovKMWQ54=4v?HsUYZLra8zm% zYG$)ULM^48g{zxgj=?nnFTo<$EjaGN9r(G+lR`lnmmM<$gM$0{TsI1KFP22Z<;nv( zSRGVPJLtuxguqeFkj!H(tl<=`C0c?8{L+T{ZgfKi|J;RSp9%L`*6C+THq3E|T1!JugLN4m6Z8?g-Iw+YqBWo_jhok6wQ!Y)h_96) z4XN_{YO^~1O@cr$(xFyjtu6C~nUBu~ZVIwqk6WWKt#$1nF|$Hi#gB?F)X4xu6iZ10 z|58ukq70mc-fa-08A=#6P;X_#rAut(Pv?=)_84k&%H}$HD$k8724Kp(rYhgi`)xL* zZy3hLVNUC2P{Q9P4CY3|dXp;e#ye)9+8tXy@zw2tn^d;Cj~(5&x)nH~L0Hc$pQC7< z;LW9IldpN80k)apIRSb?OxIDnBiL#{0Y3~)3(fE}dt5-|&7?<^^`a>`oWN#_HP>Lk zX}}(IP%PfBrpPe$l1i;5Mbm*){g1~G8P2Uq48(Z$dL{@a2lWpkq_5qfSDxodvnLrW zeVTd|nqeq)7GX|0p&oc}=Wg(U5}RH9B${LuktEPeiUi|G1#e9lD2~}PoXp=*ph5P2 z8;ocN?-`6syQZ7Or6oC+K`@_CWbULxA4@%$W{Rl?gPiT9>K~Swshhvw({C9?(mN-D z9P8h+bs&mkVrNoQW790MBNMYF1v8mHE&+gZ2t|-9+=#vdZhe{|TnP8oU^uF`NM|a>=rASrm8M3pZ*ZmyPAzU? zcJ~13StUrcc1rP?G`h5Uei9E{9R1r-#Elkjb0;~jtf!=BbxtD)lT6|~Rz0WM`7cMs z^=pP4EBxJ}U%e#m`_op3$8&eiQ zS0fkBs#7yGITBsRF_& zacC+M9Tx6hgGi-9UYWAwd)i;}DqTtKQ<#w-wly5I8xGru8bxnF%uH1!8y!3bWCnAQ z4yqsf;;Nq?nAGyW-gCJzhU8w)hREhei6l%$TB!CL(ae-^l&R~~x_Q!w0FO_$%~2bO z=WOBFQI-I~sy>h36T>R#KJ#wGrVVZy5p=(yC1e(u87X))F2`j8s-FT_FplSwS)Jv1 zN-nyA?sy2IVM&-1k)Rx3Qi%Ib%OGT~fvt&d4GC6mA1 z@&&|#i2@7_+L}DAQx8C=whbsHe9xNP`ocv_t05_@dDNlN=_$#~*(+oGx@f`{uxZn@5A+CO04Z#bl8!wWTJS|ytZs45nM zj>Q@E-?9%c7O5A%nfS*31@px+sP|_W35cD*F~OgL`pY;USB_+ionB=U)oOq$hL3`c z!d8J$7veB9Ycy}`l4NFg1UZnvYM(O10ApDeuWsInHfv`pz7z+?q@Tqh8vK#^c;cFm+1Hq^{v=6lEI>d3%BlbbAkR&=yi# z2xYr@%>V${OUg3Dk9>Dcm*{S5943-^RU}(Rf8pg*cc7)ADpwKeoHv|4%GL zu=&GS4oJ3$o)|}2i)c$~6jP5zYP?&wRFg0^msUNgQpiIGawAy1iJ^6xEO$jSS?spC z1%pZXFGb06{(4?Ndaa!oKb$$N1@i+-H-D21TFG*@o&eyTC^3m*Ysz8-G!cf}wu+hG z{K28_w`>7|GGKwKeg;3d*MW}?sN90rP!UxG92!HkxxC`|p|$^Ioq*n2d~_7j`vXo5 z6)4O~RMre9(}*aEw0sZ&M>t>XbubnaNS8T*Azv zd*~Ie+p`?jz8Q@tdnc|!mqOIT4%x|hf+C3`IwDp|>e<0HdDFG0bAWqcc^)>MiU{6P z(0PWlg9)x4;F?09Mn&MlaEq_mJx9dKbvJsQ0g|E(Y&yVi?9b?K=cSv)qDNCa8crRX z>feQkmGWtnTahiZ&$VuoA=SD+3dQGjeM45w9M=JyNp>OvrS~enniVHsGfdwSnvi(T zoBn3l3aNXE5j>%8XIF%ri^M}k5Bu$3cmZ#F@wd|cx?g<#hyPBpB){?b$j8rbkdP)R z80}@mmEZOO{?b424Ihnbr+K`#S+K)Rc~Oyy|I%~%&-{XYgK`?Lq@90tRdQA8?fxiy zWjVZ>nvQQeNxtDMd3F;dA|WshlaoLkt>kFRSU% zx!>3Bc*seR_bgkA@tH*H)FT2@ebt1mEFrx`j$=r->;#=Hub{}6Swq1nEe?k*FuruV zO!(X%{X0q(SwzVVRvv7+*mdfeO(N8 z%L5T;1{jQ|uSrIly@HE((j`kw#Mw&d#jo-9x^y<{DKzxsVx?0@`QpjtXp*J|O; zO+$-FHbW~K6_=vOe3>xpIkAG|4sSsP%Or^jU%N_D(jsqEBCF={9WXE=J9Jq^305A_ zIv39ECS3-zVsLC~nzLbAp%-IVcrj#%8wB;6jTb*sI2!g%L}6b_#MRSnv?2~>@>_6g z=}i#=i^ioF0kFpq*dV))MyUmpA?b$x!LW!_$zFa9LeD+wNEPxiTIamuD$9^0Qbj?aAmXgb-tyTFsE{|EU&Hnd0(U0*9iKk> z?G3*(*Z^ep&-n=(89v8~3E^V#E5Gj!-a;NSWadQjT4JN|M$`Y^IdB3S3HGZRVM>uN z6O4I|C!pUUzk!(s{T%N+ziqayn6ru1{=8HcF=bA(s2J z=nbklJi@Pd5A;777`YG`pkj70gwBy{KYNOFk>QHi;6~MmJiI*f@yd-tJYW(rATNpk zzr8PAkK|KhlYB*lSrlRlMlucg_|Gy57wpLmX5( zA9RFo-WGZL`XdwCY2#rP_d{ZuD!YoXCm{1qg;WyOH*_<+%)ZJiV^rYzfqf()7w^$6TkTF7Ik{ZIom^fL(X6x-HvshO^W&$F} zR-+CiAC=1XXNGjp;*WR8D>gg_1%zQ1`}wYq^Mt3eqjBc_yh}Vr&kzBP^Riif9pB?U z^PkD2&2S1Px*uethT#_~?mZa`)JWV29voWn0SUb zBs4YnRs(!wmI{l3mN{6+sbqvK9LBR894sh6P>sTlIU}rRbCP*RE3RoB+k-iKQbC(8 zBvT$@k6oMotHX@?!(T?3VB;|yEA1e6XkN8Viigr`oU(LJYa6ut70~8J zOB^jH*j}#l9&V#dqOW@K`UvHCCG7<{kN7Kn(`}U!>`<~aZ*4Jzd}7cQ zcb(jrgiu^3JaM0?t}O?;OCW9FJY23P6wixEmeT|Me>C79$?I^X?U0*Vu^wh#qo~Zj zSg9a5+irt9LG{$Vuo#naL6O!wAl6nDP7b3F0!M% zV%rW%t-DO*F@>ob}k)*TvRtp0hV2$-z1JGR9du&a~x~Be0C%*d|l zr3J#}9Lb{a=KW+|xckd~gpbW^Q6 zS&)4E$S5_gwGW^e5oCV|8LPMT1j+Npp2T=13uCz&Sx-%z!$uOQ%)Qw?YITaNoDid- zlTLP_txH(cLs&V48h>byBQk!nj)I3Etno`^879ZF%Tjp7Fl01602PX@aRp|YGECDd zfIH_&;+|%F#jg(yHDG0e-K1=L*no-Joh-+LSQI4_>%GKtBYXk`;G7Mn5|x=aCKU~q z>k)4#G&XaFedTfF?9ZVq(R}MOzFfS%m3wy-Fy21RFLBzo{_Suw}ebXJ2Obz;|j1*q9{o(GeYSYd)4ur!C< z5)L+~xLWLRcCjp^B5-xpb=BaBK-%n1uUdv*Ru3k`W&G{f9&HRPoTWFXT| zfWV|L8pHC!zJf(3PIKG`_v!{zr&E5{&f2O!0wSgJ{SbWtdXR-_4sO4?m=mV4V0AL( zA|cXQv)Bq(cdCP{4V%^FAFC3R)5L^+pqND&<16_^&hNWg|1V3`(loZmM*-NZs0gWa z!WglNu}cQTnjYp=5TQ8MG&^?#Gltx((Jo`t>4td7IY`NaQ#`Le6{&i@L*DHUz`Xgy zPW1PP&~mgM0KE~Kkt-tq0t+B~l9X4m|I-)E-Tlm|0SD0&sFqo8Cd{HBE?&yMyMX552n&8^+Alj)SqL8O%1pvOU zpn;zOqZZ`kuiBAny9szFK$jgx05vZnJkH}-J)zyYzOVT#1o?{ zB3xyd8CoH&gJs*U9kP#JEh?~B<#!CVHmh|w*DN4IF zW^@n~_GsFqVW7_nF^U>QXjx+bAsttl>{0Q8Ob@`F zF41!CfvkYK5syGF5*1I1_tNXdatJLY8m(Zrqe3MTLhL{o$sPn8-^9~g+g*jZ4?tmC zGzUaja5&~iG((gd10+Mosp7$!MpML`o&zMQy285AqS90??Rn(ne+i z@u^Po=3;ZW0DUeVq(t?aZ!vGTrnunNmqNTujCBeDPLIqZ?zrMB@ zI><*f(WsXnXSptwJE+9!qB+U|dD=G+$jGGk$RaZ@7B}ENu`Y*8cL1#YVW^P>i}Y`Q zL=lGwn%h56R(pLqUXV%LDB&VV=}IRZzEKkTna_|>V?k^T_pk?YM8CgF6a<;%G&7%X z8`IBRK2D_EIm=geA;yvtSyvQC_$P^c0>HTW`)!V#E`n^rsKpzR>s9&MZH0)nGZ@9R z!=CeWX_Q8Q!Hh`H%2_Tna4IGlRQ&&Z%M5-93nlchH&@^6ARpGZ7wgO`j;Xyun(3!Z zx0ImJE{n0s(ZWvWe{XV5Y9}QI8X{Eyk**I9b`5|pp=mAisiNOKsOM@h>}Jwo<&8SJ z9(bkDNI0I}@8qQCgy3`(0QNs*Qfz4R7Hp|q3%L?0A6g*dq_HfHa#{JeB=i_---{aD z1R_v#AqyJ8s|3YoVK}=b93M3=urD>K_)8&p;iZ=&F_Mk1AHd2P;si-y7Ux}mQUT~&`4VC75;ayPLIkKJm$SyMm25{EP z)j9!80t;X|JSVAuE5AB?lG-vxl7@o5gbkEo7DaSW4m)E;QHn{7ZGj6xDN*&9D1=H# z4K+nbb^CJh#`(u!GJ$eN(Y@xZ-6!pFq?6`Qm=U>O zd}O>?*-=6xsiDgp_=E{_`8vmGPZfCqk;6LyumU8x=SUG4HC}vg>p4**w{U}bmy^?x|0R8GVv6D*vXNt#5;vh;4$ z7r5*hV!O+aG(phI$1=CA) z8qJf+a2rE$IMH6?4830qClgLvg>IAsn&@%T1GjCz`1-grNHMd(yGS5m+(%cT>h23$ zwixEe$NoI9Q`~sJa?(zl(6#J#Ntnrv#kL5rg|jq~GJzP&xLi)5n9yH@uw+#TH-X5l z=+mY-&8@u{{WYjv3?1JAi;Swm!`}FU1zbcsk5FC_=SO;ltO zxa1K@={0^iu{F#e))a=+bD}bPyaQ~dcWU1)tOxw~8dE<2CHwE#0U>$+TGjsT<9;BJ zdGZbtfmUICP@xR$5S^}#A67ym=R7eI3HQKyK30}CjNwJ=m@+5#c_2uof$n3Kxn=LX>9muPl$d3l8NR7EA0+B*90$;+QQ-0PKVY5W6o0-unM176m+kP48Tc@M zPYKpABlYF_AIfo-ZkTC*XhAV$TmvOa;LI#CFCMzbOY6D4)*NmTAU92#X%kQ44G}t& z-gz=79M0@rATD;slA)_a5nh`_6m*`=%}`|sO4RGGhNLj*M(9m-K$;nabaY-Z34Bh_ z0<*1Na2^k=qSQmrkbW`d+H2E+a3up9aSC8CWNat4O5*}CiLJH(P^CcOqBNwI@6zSK zxR7#KjnbpKHw%86QJQgKGG5t6mzTmioQf3hq?6FwQbH8`_+5NNEGp8naX~PPdJKpG zFm*w#TC5t4qyiD* z!!${BuEU6^3F6=pT4jJ1hhfuCx)4N28lj_m<&l6@$s@ERt)*BJvPeDbMPC{M09#3m zI16m!a$Qyb$i%8Pmm% zwy2ahBNwW1W(;rT_2tZLyv3x$6kx`}kXu)ruKq2d7SXoO97|(4AlURlD*HDRYR)3r zKvLpHx?yjSKFdX^MgNxwIPx5-+^dJC@go3tnWVz-2q_{sXzgH& z_(~M6XV%itf(k*I59Zm6Al;E^{HM1hO}ccf#y4*G0a?*NDfW2tXP+-{Ek53i2OHd{ z2jwiSAVZhQL?PpYizK@`5Yh)Ash=sPNz70NHam&&^{*pI2b^-*H8&edz(43SV4R5R zTGWioB__UNE>~iCg%1;-d*Zv)@`zLS{|rhzYr*C9E&Qa?7zT$-;0Vr&-nsu`a~2 zsDZ7u0DWL*Zlq@34JV;7)9sH3m}==<4l%Nbv=TUh9%U!$USzf#GY`#Rr>IzjE5S_h z^>KMej^-*(kR9xxrxJQX<2d@&1E89Ep`#NXpUd->T)0 zM9mo{gRJ3Sy-1LayKR)F^~9T`k6*G~&8isFyrvL(fQO<0u^8oWqh(_O`^|_z-7Tnl zhq5$|)OPsYD=7J>zWyku|A-(6;9WD7*k)4uz|IyXonpm5Bh&p0olOB4Jh&>x5ZtFx zc*S;QQ5&;_=d+U$15vh&-EzE_(U_+W=XU;5%+^&&ge)JJHrva$%9&Ys7{6(o`N-o% zeb9~BpCMY%8{E+t{w}bQgrduM*&zx0wlMzy)k!7Tlc6O1N7wGo#l*2fv#5 zJ`~> zW0-Ju#`yz$JQ!jJdQf|zlOaPpC{ zX3Y@`W>O(!KaAxn#l&_%u{C;Z0U3fMX8G8R7CVLPpe&KT0{-F3y{Y;yC`!8P% zJaHbtP?l3obXh0;#YYbm3LF4SC{bsB_6}67a%Yp0_W&LeDyPMWT+@i)$XDh=E*ls$<3)Ty5Q$;7)`hjq3K6Xc;=W{9a(R8zYa#Ko9lncnon)HV4(G+5 z*<)8DI*zZG)OyTVb}3_oX4+ufw^lw*UF4;)LAzpc9xVh8T-r8AeMAwtjJ@?Pk3D`=)kH zpEoEa@Ax_u(GOk8hZ&j@jE#VtlLskNiZ*a8%)MU_3@aY0#68$_7cfVrdzAxQ$?|`l z(1$C-3zkJ#@)cRfoM{ILs45E?_HMsf|1*@jZ_LjHuGRktVO7;8hb0o^^xP&a+`OsW z#RfEWM-W+(fyp?P<-xWQOqs-4a2SK@VsYH}K*e#+a%E!8;Sy0#LTQ|R5uJ|u28C_x zgE&FcNt3h}m{65QUc!W(sj0r&PEk!Q@F3@2a`vHlzr&!PrhcW~x}1o0iQfoG)UK-j zr_O^huibCXg`To-64c(Sz3OL=2@xSTY!F9{$?Jai_Gw4ze5gbq{>(8dAc7?r3h z2iSIA4Np8bBZZIwNzzplA27pU>@yQZJiimRPy_|gX3=1;s6qf=tmU#d1}GX8xO-@uKBuaM>-c8H0_EgD zt%*G0nSIn@M1Fi=4X0Dogk)mK1uFdmAgRR}yO^~WdNkezi=scRz#m&y~2{M!^ssoIO*(RuK~C1M-XgOYU(h#f^Q2c?QV8XE@U`Z4cXI)Q*sr}k0tu{2fN!Fmk8cXk5 zSRv!Y?b^$?YZJ$yZXz>q~JA-2!zLvZG|OB+xi&Ql~s zU1&$*`t=nt6pJ_keH$e)(Xd#&a1x znTA5}uzr~^pSeJPb@euxfhY_Sr2t2nxT$F@Bu~PY=xS%PV7;#{VtU>TM6nIV;D0h4 z=aeLV$iPk4^F)sRF_C%=2QX7)^tj)_)@78LAsD@H4%!n3%iXQZ`g{cQ8nDCv<{}Oi z{(K*%?DoW|B=|mY?J$GtqR$a@SP?y7Kx8elCjIh&(Xu zCwqcSnqqSJ4cxe_qJ;`3&)?#wNI3M9x5=ptCFPeEYYtUUUU*;HWTVM2d35w9K6zg~ z9Jn`x8<|tW1c`Z^aAZL83nZP&29#;pAHggX%kTV?ACA(jOtF&t z0JZxCA=*>k+by)Sz!)sjuJG9KVl^p2;*O#X9<;JvDz#*U0%ejtnvw?sP^KIlGmAzK zHbD3w4sc*#*}y&N--g$3xoZ=y<)!vxCAMO*QyGy=|$p-t@-(trrcPh zAz`N=eTOXdDzaq&vv&>nggb6f44XVODCt?qADN1wxiFHVpDdM1y!1i(yAayNCsFty zM;|Xp3Yiz=Ed3DKT*FoBWQsVjNe1C*1MLH_Obdy0TgWO$nVG)4PeNM1=Ql#8kfc5e z{C>g1#dQaZSZ>(J@YcvnEGRe1L3y+}04o_uakzFN?`gN$MZK(zeG%_XCTTN8aR!-U zx0MN)P(CkTZW(?DWmefv&lk~V}(z2*(VbnX1;C z&_zOM@^+3ejR&yckYZx<;6hE9;NTp7J2vM*2X8p_4s2DX=m4=pW9lR3X}!rPnaqrm zQ1<{RhC~R7QaK2SlwAy&@<@&IQs;^A-tJ5Iz@_kb*7hek4QJb2a9e1NUl-bg&?u%d zC)smKeXNj(bkNTH>UTFR z)sse5esQsYG4}mX@dMRP!6Fx@xGZj+Xu&keK3PCh;a$7GD&5A|Zu=ycrB5~OeDl^- zjM>#oqw%UOiQEIm?I&Du+aOkgJ9p6NhZpM!Zz>M-y0?kTH;X zFh}k-8@$PB4b6=s2Vxfz$1KIY!`aS7x*OJ2E0#$#$x)_gQ5)+A8_`(|n@cugSf)J= z8BhS&)Q%crV|aYV5rB3pz?M5qkuC0J_wBfktsy_kw7D&jg2EDs8?1E%qF}UHp3M4? z2v|032Dld+DKK^@DG?2_E!If1Hi~De?6)#YGTI1vqYSP_Y(*Q?2Awg= zUgs4UPq#>1lF}NPNetcuFHEah1br~9&9ySyMg&bKaTTpUq!QsIf;^@lH}hHBII~$B z0Sz8spX)WZdozv4oF0QaB=liUOHU)-N>XNYACnc-hlJv53=p#jtb6pN+ zi7AK|ONiDG6OoUBUCa$DesW!W2$5K&xr>go`k$DDE1}3HLn45d8AY=MZAm$(XvIm~ zwX$LmoyzQO7}zr85S~R*PX=oNEg6Dy)5V01EhqweR9`4BN>IN6G2-?K^3c~(uiqP;qdh>n<@aS;qQb9 zL8ap8*~X{IhA7W?nX({UiLM29ra-|B8UUSH0}ELNr6_3D^L#FfQGxY{R21Z)v7S}< z$n*p&><88WQFC`uocx*p<^mgrWV{5(axGQf;_{>OO&c{4IvH4{_*6-+{pq_XP#bt- z`VAR))vAWhaG8cBA01qzq(1sAX{@8HnhNVp>7QoawcR~(liCCfMBM|_@srd<(`pwZEB6X3_1D z9}5etWUVd`j9^tk#+0scJmP`NlXtkgdbg!g)0IaWF(2V#WdIc}2x2BUx@bS52SFS%`c57tG$NL~8F-dLw(^-wjnu_s2D8QSKDnbGr@1oCI3 zDXzssAa7RYNVC{iCh1YbHq0dtOlF0!HUvyumg0R!%Ask8&BFGOGgAUm7zAg7khOpo zV&RFA$&$0ap+*4UokD4s83jRv|FPTn(DtL4fjx?bTnKeN|8GbI2-6BVNfbdLZ3V9 zcoGac8{~uvDc3h*ma=3Q%^j53qyb=C?S>q5VQr)0EDyNk2#i)mi*|l~LRZDeZ#|Gtb<`rVvAa^lV|*&zm9K$g)rxvKxWcGR4=Hlc4LOC3A?#Fa-Iv2ofml|&iRpZKcbxJy!g+-$os$nP+oo0cfN)s=v zFf`)AJmk(+|LXc=rU|cI?Ax{qE|<alujL^f(tq2STmHNNtxXdAbucJ#4+PIYQ8}!M=u9`qTM70(xl$gO@V|>W|(pB|um;FKLctkeP)%|de^+7Ko zhh}uL;@)7RZ)e3yz}lY0%>V}x-x&FOql{x;6h!TLM6!*0)^5yj`wI1TV&S~mOL}mwtwPMO(+SYp zo$dA+H_Hvj7&)6sc9R4I`YGy959@xLq`*o^fir);4M8~e0$?qEUEHt9g4J}Uklz;0 z1kHbQU4e1DeJC1Fv}T8o&I|pgo{`e0@lof(IGH^&vHg&*xQRz-B)E2siU&SwLMAq3 ziCEFk4HqJOaCaPXWE~@2e3ThrwG^FFCqF4$(mwh1!dS*(RUcmJk`Xb=cM`8NyP=2K zc^Z~t7K3GOf|Nvae41ZtWwi}s78A?u)8!II{&2EPFu%f)3SL55Fq#=wNrOU^HjP#s zwFm$2TVS1a+v6ImoX0tZ@&L#*&V#8692v$4By7Ox2V2{~>{&=^8Lq9~k<1^mEP*i6 z3?T9B6EI8hp{Iy;=UlAMvz5qaX{;A=kSfY6G!Y53SEzPR9TC2o2dA>(?C=Uc-FX(B z_YL16*gRQR(@)_4ouMeKq7NM8u#bsG*96)27*P^HYMnvX$Yx}l`D17f%`*-C1!pxZ zYhkFKoAAR#--YyVZHb*;53nOg6bZ_eJyB&Q#hR@cx0=qOIKP7(=9pmiSg)rODAM!* z(3n$XGuAKh(fxnm@uZ~{zaPD(;X8;ny*U)06BALj?pbh$$+4b#&u`3m)}%br)6#4+ z<|yo_um^>Q!(Igm^Nq=r29|BCYJ+Rval(&*V{l!b-Wc{`v0yn54XvXKYD+7Hhxl}R zx53Q_-h)g!LC6UzXsO{n8Z@Iewr_uk4=Vu)e=?ki>;NODME z$Ts>I)K*{32LIsM#1Roc;bia)5qX%`m-j!kG7K|RVIZ?7L{y7nVA_*28S^3wD)ul+ zD^09Z4O97ey9@gwIlmw?zhR_YPd9#m_`o( zv*h4ol!8kYRyDG^_tX^whWu<=xQZo(H3Cm@gQvEJO#R*fzspQ$F?Ek+V|>8opCRt{ zj2gMQ!i3gbA?Aue_H_6I_JUXs42sG1Wi41D%Q}Y=!wE{kx`Qn3mV}87MGVVD=AvnN3YX@rPRKtEW|CVm z6cXTbF+ng6_y-dEmuwcD-RU&vSLXgSkbWlU6L&1SkGEkS(=4iM~KSjv}@^v;9s0HJjUULFvc2!y8dWlH#wURSaQ7sueYRai_+ zT=F}ZuB=Mr9dzU+q+Z53M^LLN;!zwqct#aNWBh`w`<;NI`7bM53Acb4D3=TqPqq^I z5IL9yAzvYwP0m`SK^Hl3=tXxO+Q8b*yVV3{T$f&O42e=r1M6LBT-{$x_zd#Fc@F@8 zl2bDdniznN2~z}fc2tE^6Fim#Os5gRBcu&Rz=gFm`7D_p4V*OqoKE&kvbqv9n88I! zkBR!>u4hG*lAD3(smko`rH=vI7b0*fo6}%C#j6>Cti7pwl1Wk_f}M#PL31{L30o6^ zf{tde7E~woXC9XX%uMMV%d>r&C#Y2cmI~~V0JAjp%B>G?P*V)(J)Bp=f zWNDwe8Wv}!%tvjymuzfu-Zqs)Sc`dh{yo2_y@?Gms_G@@FHsx-sRl?p|ASi}X4LNW+tn{98U5A!B!zcpD1_Qpc8%eJ~ zBV6WR;Vl74r0sC-Q;l6ksr(!*m8IM%+$H#*-{KKX(2TTS}Kfv zfWsvtlLI9ZZm7nfGt#@IQkiv?u05s|1T-DAm$`AjqAq1nS$KrkAA~l(q`QDJ?4Dt+w1Md?eSz|z}5Q!6C)PcVde!yK~b6)?Ta8^*KA+_7;71TLmSnC>0YctOVdq#>BSVgten$KhnH zSOTRVS3j~I$R{jFQwVb(2&Dc5w1Nict_=P+8QNz0aHhmL5HPbs%qPC>s>+=r#fES- zVj^()Agw)lLKg&?o*m03Eh!io+zX9t%)OC`M2OLgpel+uj{)E$CkKfg#)OLRaLmCB zr3nJFG|B41^T7h69OwFNcv1P<{bu5?&z0~u-ucZa0I=_TUfHnPQEvMICU% zs2kB*i)EH4qB%N3DTa{9q~i<>y9jd&_MiDDTX}`;j^=agR%86<%(MY!P?lvZ^FDiP zGr)Bj3WSL$(FyTk6brW<#|PY!ogEoS#wB-cE(KlXs*1AkcO9pz{?(>a9Zg-dp@KCaS5i;W!{@>oF`YT@pn=zCVKf}`-R~(F5Y1o z7#)WTMcV9UW_caVi3VZfPvkz-nmkDqY&}=~tI(;AeIbJmRh!4ss-NpDJf@sk*)!rp z7>DSFkE|0yXe>mj6;OT9lY@~z9|kgUr}ST6xmh_3m39D5t94niFALMj5K3Y+bwIQ5 z8mnaRptk}}zkid`VgJ@Y9eNWmW7J(Cq0>2|Ro)k&555U+&Km&(H%)1S(rICXk*$Qupo`DeNEowg zA9Gg#1V*XOJxY6(sc$-+W{KtyVdi=f0773YLs?dHMjcNEuM65QQgh$rDm$jNX#jiR zY#Fq3mhKNZHfm~ev5>MOvs;-IA~Sz{0hkomLC+=NmRsCB(yBs|}}4AcWLL7}+Q&!+MZsvVTReP9;6#KDA4 zTn$LZCTip{=|EF5vOiV~z|u=JoPnxIV*N*Nqx=rwwX!3e<*e!N*)!R(%QIXj0Xl>)1$2d+}F7|D9b z9X&1w2zB_|8`B^@vJwo?5|C);02Rr3Bqjyn7dEZ%vsJG}v)GQky4m?rY}AceF21U`>N++=!w>?naY3{K_Iro#+e@(Oj*vJ5zoTY4!RE%#|fr4?8`M%@MKow z-Z0N(y_PulE(bcUs=tZdedA|^LwS*u{=K!O9grbawIZvk+uP;L?4PN{-e#tT=zJ4E zqW4ldlZH5R$S4(b(k>L@-KlRfy<2&P0mB0J6N6-9PZ`Q}nCWVuJvkDywJYT`!A7gh z`-|sdFahUskQRSTJ?5)hRu&4;jLoamI7VWaz+CZsgXA;5L)CJ|*Gn?)_9|>HGphx* znkIAx9fXqkSuO4oY|Y9ciQbn=h8u6SB5i$!bu<=!VxO;n=%L6lNPKz2ryA%5#KHvW z@yY-q?8kPv5Ns!sA(M+g1}aAxC}lx3hZP7;;D48T;X_O`o(1b@Fd&~632*fhfRlY= z0uF@+zR|8Ww>HH1ZiUKkt`la5f3u0`ur6iw9gt;04fe0H6=F}=G#0mtOajY}#ELLM zKmBKwRE=Qgp+NpdhElz{o24*Ib=S>@x4Imu2qUN@?PmJ!xQlH;nyWoOlU6L5NYot{_7VN+S*R0?0s6`Fqx>8K^3jzbC+)Su)iKG%Y^p^T z@P9({oulD9zf;)hvzqw3X8>k~222?|G&w7V_9ym)_180QG;lOpA?3oJMkLz8g&C;p z=UH$kRI4dFy?2cEbn{%=-}so{!6r|v^fFz}2nCjWrqj|EVclsga@8aV1n$ z2=$m-v}e>Rq0cMM;erb#WwvnJ5ssbw5u_Ikl#7UnBVU}T4%LFBr?tgEcHy@OwrIyW zfi!3GFq|AJo*v?Y1<=mK#46!x&lxdU22>&?il%w>{F)VBeT$ik%&I$4p}_dIC$SeS z8TI{e_|VmVCo+blp_u~b$V8%au3r+VyotJ>RCt3saG~7qGGE)NRG&1r5E8>Ia;C3p ztkc*HwoQ&h@n%xK$C6OqvtW7w&Wz0KXj<_#gO+fj03lCXJl)ecvaY;cM$nkboWR2l zEtw9SL*%-OLPRcR(6JB3bV6eG0y|zcF|r$od*eOjdE!e;GuxE~;-L*klXL^j7z(ha z1Utzpj=IeiEWw)S57SUB4p2-*`J7yUC^FLLP>uDvR=jZ9{Su0MX7c-dlrb!xs2mW2 zpet}!tng`)lF4!akmOT}G1J5izdg+Cy5p>0$U{3RGIH2p#(U%J=%dGxN6NXXDL@QW z@S+xYTEVqeC$s<1G}LK8R2j2MdHiFQ!$9>nKr&^K*s0e-0zRA=BB_#zw;HEr=5GAX zH6@jifkUhKgwaDM2_%@Ay-e0?qtUx$2ai8cyi1rdI@W%Rg)JP#s6`x+Yf}9Wd;T_w z5+r^ADKiRH&{eJTW^M+%irfx>1*W(RvQ|@Ug1GVN>IoalZa)jeWa<-@N70)EAp+&f zWF@6z-Xapw+)L(aEHb&(&eIFUY|-Qh1@|o!5I!Kg?a@RR6b=U39-XTQ;tyIEi$ed= zTccc&NsixP4fjIU!yPT3>K6%G@jeOnO=3PHF@x!lcrmgFXMtb7P`T@lxK>VHPF0bDXXZqVit^%XOwd)ZPjPc_Q;7_59Jam&25z<0U2 z&oCDeAiDZ0gG(k;BTnrNVrWBQV`BihTBO+be>a_X{#%GBN%Kzb|$EcSLgy) z7s{Mj$ihf5Gs5@Df5Aj6kuz2kS5F^y{|4WOA#-^S8b8dHV#u&QpH;u+--*i&wMQdJ}|1al$vDoOp`D9 zQI{^k=s=ZL0ajtAujlRyLq2Wsk+4CAR3D={%QHh($@%+m8S-|xi4~HSC7@Gy;~CV% z2_{`O>ZKPVc-+?kM$Cwj0{Eam>oI0iX{Zz6SLGd+n9QtPJR=tNN7Y+d{eQI?Tfexv}oX@Q}$btrQt zG|4eHb9TmlUa*>0SCd;-@y{Vr(7K9q*$J2d5v(UtkkM~?qiF`Sr3+INe>FvZL^64_ zj7R45>Usd<4`(x>7KZ9U_1is|n61f#j@5rGf~On}7>`UK9=?~>kb<#sEM3L!z_!a$ z49v1UsQ|Kg)*y8Vh>p6VBQ+I$xTh)W^@oZkPwzGne7*Yc8CV>2r-Rf6#)lY0<@4Gn z#BImaP}o)T!AQIZkp6XZKvPzb<%C;3+b%ZpGlsqGowmt(i$j1JQh_NgUh2%p5?US9 zN5vu;mZOs(8VhS)>_HVpm9$1Bw;mR^C5rr`63jp$T0_NQCod0x=>CV`1j96CH8%`p z=;bwY-lW`9xG7=_h6Gfu6RxJbmw$Sl5`WQuhpD`HUjyTCIC19oywCqy-15!P@ zvLM%^#j{dSo((Wb>0~)??Ozi*IZro}5lBv_%9D-`R@jA<|FXusm*xXb57#5}fVmY3 zt9fX0{X6&U>nJ2+q)wQ%%u8Tol1H1|idYUIxYsk-66osR@wKM;eUw_Rof0}2RV!Er zdR)+#^TM;?6jB)2Ey+JF*qcdAg$b+jk!ISZ@`|j_`6jAO?0QlwGL|(=*kAog2c*bB z=xn^0d7=d&*5DZ42#(>5u8gTHz(0B5nE2hS#7=XKdu|-Al9;q3yr|&P8N7llg-^9x z-2?);xE5Ygpn!qow8xG12T*BfirGn+x{yY_l{{UPfiR=5VP(n;+0*FnX30~FQ&+nD zV?{d6(RR2np_NIx0?sPC-k$34P$})s(*u+6y+j@cMTJFf0tW&I zTPH;B&h{m#!R;QW_l0C)YGiNxnJzRbFrxJWr>$BNYoo4bD$LOJzaLCp{$R%wV>+5EiFP zpB=-U1y;n`N{idK0t<6b$&eVuA?>r=qG!mCOcOo0F@v5utgb@EbqIP48?kAobYvxD zT%bm(BUC&v_XtbS;BooaDl2a6cwcALn z(?`Kvt|Jtwnenqvp4p~TGMvV#ORe=*rflNeCN5glfKV4V-YZhkPYo zgSk}$>9eD2{m4kXnk-Q2U8b7WPDe9*`Q$A5RUtE#74_Y^l;5+q!V z%J>P;H)W5nW9rq@F?$^|fkCfK)9j+GkV-!5oIsATT&U~Ufmk6@-B2i&Z3_|bYbK)^ z6}U5T0=Xa%P)$N%2At8?{7$0)B%Rpx!K{C9H0f*q<8YamS^a_}&aD4vr01rC3E7@% znWw|0S{IOL2t)Azp}1-OVd{ZFCp+l+Z-gntBwW)Rv@D{C7(x)Jcz|F&;n#lTM7%*^ zTI)cIq_s?De5buasHu%KKM05Q2}6k7jfMF2kkwV)N$0e;tA+X7(UhI1F`AA0E-o|= z|6BliV{+NYz>jgd+kIt)sUtp+xe`OsX!BNQLp4F>cBuB)^h@HhC{UJ=A$ol%2LU`N z0v-e)kM8tPTzKub)C~ts*a79#C0~og{9$^$?VvnhWJOf=VSWi7i?H!b$Rs#V=KiRac1w^IrmU-saT#%ka4VBUScU+N>;`P39^sMh+2+p9 z6j|p;rI1E<# zlxRtA{_$FBWj%DvOzP@Bq2TUYJ6G@TY)mCS-kA~pSw}Q{?b3HX+C}Sjg|WkT9)dlR zzWwf-@{UIUg*eZ2908#@VdRIeBUOFOb@{UI{CG$Ect>-^9)sP`RX7u8QBFI6|01HlQ3n3m!x4U@NYj?gXqvR5uAZzx z!TFY%G@Pe8@DO>39M^qttu-R&-R6^*j<>?61G4*-pggZ(oHIll;gE7Dhc0Wv2{sn@({4T0FHA3MA>x0bq0_{Sv!xGZY|ru4`x@M>ayU~%uvj`Fz07lG>p4^V8#!O zh_XF~8m#QQ-<3Tn?~VP2G+hi&t+Y#|tLSLLB`KIlUb zi-6u;93pS;se)kA6Vhsy3?Nk`8Ant)E$4OY9|J=%Jph}RIYl0U0&7lV^jq_){?8lq zFo&~U{Jj2u#Z|i&?YS@d^FP|>)IR1Q9+1a1z%x`Ls_TrKc$?3XH{v7EvwC5R5(Z11 z^Axx~f2IfPPx!DMA50P@ZCM6WLBp7gL2(&5uTafr&?wwAj=qML^Xh2gT2VG*_SryNR0(T9X+4YoU8x{~Dl^#=!brFZKgJQq2cmVZ|b zkWTxUaWSIj?7*#*R|6dasQ%lJH{s%=)rz@BDCY|~^uedzB2*;As5uIu-rE+z=Ki~W zSBWvHYc=r7_%?U69o|x7S4#NRZhe*^d>tx5tN5V{*7!RCqw@15)RSk_a+$;@R9*JXZy`DIndV|{dqIFYELm68*s^2deYs=L0!JSaYn2j5Q&ORx#7zu*QHP}{o z+Wv9u{{>Kkjd`Ms?zeYwGlH@@Ai$884)wS)GgD}_y{aLuyRzNWXpI3PLUw-lcZ(vu zeD|2slY9^!0i3lXyyd^TPN*?iTVq8Jb=naC+m<|6W<}&<(N1(+=%7FS^!5K1d5qA| z&Tt;^A6(C`GD<7{PkS=r#M7y6oxQ^D(Jo>R_OX})?bFOq4Q3_>aGpXhURMBvi2gX? zA!MJ2tw6Lr80`~2-%-DV4?oj~x}GrQ5d^B32@SdWjnY{nJlj?-pxtpP0EBb>Kf*-u zQTmHS7aceV11yEX3?Ps`h*Sl+Q|KD-Jsuh|0_<_V$E5ied5Qe84ZJJzJXy_o$P`{0 zj@oeq8Un1Q6Y6=zhM$69eKa#K(*w+F{j;op-VHc_ODIxnyJy{W8I&vj4n40RXWA9@ zArACfE&(!H`KJIBX8M<`HaLr`2QG-`2vf3t`n@Q>5a31Xnll6?z<&`$_gqFsG3bLs z%`4{c__a_badbT=%oFZyYJ8IY2zsdk*v#$(5dm~Q(qsw!W0gNnUoc;P_~S`iPy2D_ zwKX29EH>rEIS|o^cprl@aol`TUhP14z{Al_K*NJoVD>>g{Phs}yunUs=mj<6NST}H zacE@K0Xk|Ce+Je1%ZQv%@Md`d^!u;lE{C?~@tZu)r9iK?IZ{X<^3cFluqRiS{lHz+ zkO$UDeb77>UKbp^qyBkb>I#elkhf{piyi}U=5OlJ=(Udkm`TL%CGb^rv~+vsSyB%; zxd~uWz}|xE0x&F~vogS2@X|qkvy?P!nuyGvq+Ytiyj_(0MFi-l! zJh%B^yFme;FUm{3DbP#a0_kYb>-FiRyH+yI1cVbffS%(qo&rr1)p`H}pu0g|l0MXW zfVcSmUtRxrkM~?axb10m z{^9dGz7?6UZ~tEZbssY8$*?vTGo5+Nt6{Pnh0#tw4+6I8|15!m$TqOq za+wfQskolwg&crq)w=bu4ka*2^cslWs;VG$G8%=g0f;4_IsxHS+fdO;F+g!raBT!01EY`A$V*=?8#fvg7W9FOHF@5Og_Al)KodyRzu5kyF zYo4W<>1l{_zTyA@;7z>9r(8nnl&6O@IYZmGV;tAaVkClw%_NDasp(k)_IHt}MiJ~j zpx`%X@LL@|&$9-~;E$JF2cQ(>kU>x(a_cz)fyo&nYV?eUChi~)Bw_~Y#|b|?&d4k+(HoVcqu1=RDf3OA$pUEL7x@-> z#k4ef3*bJ{NCHe!x4L}W+v@`!XN>Y;tl%eRp8aYdj^ir-5X--_`TsoR7yPx$!nB=4 zX`VHaJrqC)Gjk=F{uAmnLn)@emJ<<mF& zD)v&)r0!KC5ucV2LafVb7A9(0uIw_Kjy?$-+fcVC~^ z$?)XNHyo9HE}_-INyOy%;&aOl=#i->>l5YYpzRXr33Z|x0F8l-_K)(SeA4?;pQq>v zg`97*RsTqBF~`g{Gp18_0UaMe0%4{IZ+A}ia4vpBB%mHhuV32vk2r!j!O<7-fZwE8 zS-zObC-Xre>TEPuV5#b#w9r$Pbmpd}B3S@7MR2u$jt5A!0JIDgj7Il`l%GbM0hP(iL{*ufhmqkBu4gp3R5I_p1#4j512 zCodhFbumXt`A;T-+BdPwPpj%b{2+z>_raNCQjw|7t(*XyU-cmXSlpz zsiI!67j#G_v?xd!>%od<3e5oS>)%#C?E*O0KSHcCE5&^^5S`x~w(^6Eu1Lt-NF4pD zfUwLtqsJCKiR%gs>;Fl7o^de5GByE{8jWR+n3H@;_}J}H{80{J{z;fE<`S5elCmO)|DZw4h{_yKY@&A{lY0c6P@hA*c$gh+EmO!UO(S^wB8 z;Ozg;um4NOEY#uhj)ET%syG3#1lBlKFcSczUI+|Xa*R=rG5cYBfATs2v-;zx+1&Go0AX9pZ8K>hnaSX@{EzyN119Al1I_#HSa7$+Fg zpvOax9Z*d8ljmLWpOoLkuL7Pyp6xTy z;4w_PG3g&1^`A*JXAa0Q#P(SP4!Ussq&$hYnW|k7_*3MQ-UO!h^VM11Ic6OFVtiOE zW7VLNjG4sWO2|*`j{hmyZeZcOC~%*|t2z1Dw~tTqL7j0q&YXi6D=A@GatO^QGcg3q z+Uz;2C&{9lWt9~Gu`1#A=gq9+sbrvHDge9$=LFcr3CYT`7OOzO#RR}-A46)lM|B^K z{&3tTRJoJcD|xmV$Pu>xZ^|FXse(&*D`1(M(XJs_7#qqx#%$GtGEY8-wE~Dc-MApy zAtb|bXmH(u6l*R$RDNn(t@L>%2Ad_^X{99md&1VVm;ZQ@;21ITQa@9PF#4G5- z@!4o#mH^4~1o(YYptH3~$xowfqU9Zspm5~G)->Q9A`Uq>=ESIDceQ5>GK0$BM?=8v zub=(@r{JT8t)oL7S4m1TA^zf=0NB2;x_cY|2_G44eD|t29ub_c`E{|ttpW`4OoU1W zcS)AKC`^)iQ2Z90Z-ZVpu~wLps?!VRkP85C6u>v}BtI!1r;{BMhdrk9va0lhkp*i; zEaJyc7(NNu&kFgj6~Tr{B@mI{lut{6IAv#VoHGy{jXs_!4Aq7OGGz-gNssG%7Nu*F zYlv0C9j99m+6Ak1fc7W8FK`s|nlFhDjwGy97=vh=Bz_DMd=JZX&k%8lRY!3CytBr2K9gn2pTMm878*%GLy%EG$eWI6@K^4O=RykrPEkevb(NT>JM){_Y|W z?&ZV=jN2;V>LCDf8aOR0lIe7eiQjSH8ktw$2{R|s=>VY40RDZFL6Nv{Uj0%A?Xh6W zMt3j!)uzjg>yZn*kD?nWxvY~*SN%ymX(qtZ8GyKE;7)tP0=7&ctym*ENDDIwYh<#K zR^p;kfItHdBAyjK%=?-E<8A#|-|g-H(FL;cb_VkJPH6lirC0P zD05XVavyYkeV+H)HR5$v>?p&cmSfYzUP0zDR@?O^MK83%VM&5QAz@5=_k z@W^>h0P!@=H_7bcu7ue_R;W5iD&Z^Ve;=RI_(gtFeo-EE)>zGe zV>Gr-pu5Z04@F5)WBkGOLgq4Ytv<4tapQnOTVWoT0SJ7GvP`MM zC=m0wR^MP_63jO~*IPUVv=hKSDZd#Si~&`$g6)~H(#0}Z3;|jo)g$&C3M2t+>F;GC85|3O|TaFujelUeg+_`S2XvlH*MnB%0?nvH)k*-CAlKa~+8Z3E2A@uZ)DC=6IW1aF6V!3HgG!ePys;Rs0| z5xeKq61!3tV}Iw%6J z*w}FheGX#9PD(rfpK1W!4d32^r^rJdFm)@!guz)0X464BOjx}gEV^ratL_1`DukHa(ovOVrk1@}qznNA30=55L_x6B*P3Bp)-Ea1uk_#KR} z4$2#N3V)ZXUZfQvrq>qs$eF3BpdkdsXT$XclrD)wGbL_;mD9mo!`kN-P7R~S=D ziP)eDNzLO&D7=57Z`}eK6BwQx0Af0r9Cd2)>451v41`G*Rse%3&Vtb5ND>pP9!sZT zGdoLE@Q_dTTZBw~Hqv=CR{T&V)mfNG-%ALRPYye)F4b&+CJnSk4cFFdOHh^VE4Hem;JYa8F5XR~s1+v6; z{TCGg&^R=ci3e^rU7av6@p*J=*9uFGU~ssNhKxRa0#C}*ihwTx4EYK9CHO^7@q}Xc zAOq}U z2W^B+)&GA`e$}wo+7fYib{qMGPH8A|=$W~iBEcG~nLtXU32XRG`Q41LbOCTN;6;x5 zC$XRdFcxo!ZdqxM3Bbhx^Bz!*0fa{@qm0O(2l0^XAG=UFCOBiYYnmI?r8DnOyg#~(D2&aAYOENZ*3 z!K#eOUI6`8Brs(Iho{KrS%OX)TN`tJ528z#N z>y1^f2jQ~xOPnzZD_=(OcM-FI1%Oo69M?~I5lRGUH-O+F@~{r;OFoaM$k^faQ!wn=>!9_V(O_O_B`yxsXDCKb?K&u8I<0~7J*mO8kIGiT+6&C)J zVLZva-;c-6`?LX|@qy74phNL@=h}lH`}1#+H}gL*W7O9yA?*{r;(}f_AYv#G%>seX z5&VThcb#c;He87}N5@RBNM=_JZW{qyDsaf_V!#fk#Df-{*a$3lTfV!p?QmZ9(d9>z*h@owP6OtQ^(63eFJtE0f4;S<-{s>ebb?C zB`Ek5kANHv7yf;i)aw}uhkFC<1l7%ja79LV_R8bFpqV>Cas{xoB;3!#bKsAw0N{hC z3%sB2Er}*5Fmo2M-2HF6C_qsazNQ~A_CuK3swfp~=zz5gl5aMckKQPZH_#R|y^*JY z+DA5e-Rv+d`f;xZKr|XqezM8m3qRVMXFQ5MiF^d+vqdn!cOi99Yu=B6&F$nwfV>9o zbSy=A-*2=5kPX+@or7$an7GjbReyQ+!Hgz=Z;>Z(a838eSC{u<27>G;Cdi2H@!e@gzR&^@e5JC%7q`dYxpQ`0eY!dZCk+5NwH!FH-nV|p#36h^o+9WP zxb^B=TmTYdxlvx9Qv>hR%>~?hu&p_PnE>blh;rbX0Q{o7=!nH+wIwMv5lHGk`-SZT z%ptb zu+M}AB^4W|bHWI5lCy51h7FR8%%f9)XhrZMPmy25dkDue1svD>NN^f|iL}DO(o!W- zQh2xW5EBKf%ef(eI5w|oO#eWU00$3hhZ3k%!EqEBdH2^fv2N7h((7KtXGntUF2JW_ zdw;^jW0wmO8vww{=AS8C5u`$S>iO>J zh!S17MI&)q$x|nNroAL1Fp*z^0|a*#KE8N~asK%o|LlvWaC6`eV6u*&osod&o0~Z? z+F+hxz`6i-1HhWjf-hDoD0Cxj0&urz;1`dcLnQ$_5zLDO@Fr)f|JtT)QNHuu35s<$ zaQ8?imEq%I(Z>TkB_Id!wI4MRCPqtRq&B|6Nb63AvwTQUPT0tT!0=Q6SnL6~dYM># z&Y%4UnZRVd{zcx>Rj^J4p%Mh;iww(e5;QkwBa@K=R!{uhoj@faK^`;T50*j@%I;8_ zJKvmNXbWlUkBXrDX0*X>Es%0DVE81c2V9eK5kEgkTI&iiWeYq{3Mfr%>Pzq>A%g;M~$MTd^H=Wkb`+LjsJ|Z>Z`XFMYgN|<-yWMKuD7iUzE>72sHuT z^{&}+2X-Pc-t3~dr+MY-JUzM%F2_M})jvWI56P_1N!~;H@C6PWN?-&sabK)tF&SXm zragGm$gWNR^g(%Z9#g-yk!KLbhNli|Cy8~>Lzx74bhSgmftS-6g80);fZPS>H#Z(0 zb_+hPcGRhNd7Ua)FN44pzvo3?BHOn@zV7jDdN;qaf07%4C)wjHfQ$(2-L!G|@+bE~ zMn;Cm4t0}-_=w**eMhqweb7PS(Hzh#sES+7$Y1eKUgILSor>G*J@Ai_`+Y!O#2#0F zw)ePg8L)HbeQPRG^bALfL5NYH5cyS1-njY({KjIy2ACd2g1GVQ$M(FUpsbShEPx;& zoDvN`qDe_}(|Lyw0=X-GFU3NyYXFkips{6^^HzmACPLV_f4v=iXu zsAO#qZggL_6#aL;%x)1h6&!Z}RB!)l0Z`sw>H2QaF*dKad`47wqF~z?oXzpsr9hnZ z&#UkNyv5nO-2i+ZSVfF14%VBPLc#$!86dbkK=~u{&F?%aENNDolu{-a?lF>pJpwAk zfv-xsei-5Vk!_gNQ|AfzwgHW;`u83#D_;hUxy=_^Gwy5D*Xlkw+XS@~drOxtZ+Pv0ANaY|KPk#;}<6p=_kQmU=3d#K{NCK3|HV4#T5$RucF!b6V?fSVjJaO|4mT% zKt4GB>x)S+K->Bs#QBg4uSDNqr7p6p&x(S7m^!J z^WXSuC`hiqwlyB|S1sv#e;I%7ue$8N+yv}R5#aD*e9UTA$jUKumHqu~9C@QTO<2$k z0a?~R_>Cjh_?nw|D>Y#C-;as|EmUA z?kk}9B}cBm8Am#j1%N|Hs}1}9XXQWg>oW+tPA4dGW&)WG>%Mu1@y{HgPrm-F{@HY@ z#`i=me-6oHryM!q&LcEM8wwcw?227|S!~`2xbvgq`G2ea`4bv|zwQDx8=i?nBJlII zDE$Q2pG3!9vP1@jvtR6N6Nm?hb!H28{3ic$8>rw*D^` z`2WK8j|brS_U|8m*d-$0_1586T*a2?ac8|JMevm*z<=iZ`q`buYyq4eXmTfP{{M@m zUY!83{<*=-;XmY=@ZOX%D*(p%5BX=}U)(g4z%CE|pHTmtANJ$Tf5gUMemLV_d{bj?CZ+@fG;O{+wzjXe8*?{t24mSKN*Ux`X;NSf5_}|t<807*qoM6N<$f=D&Sga7~l literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Cracked Earth.png b/resources/textures/displacement/Cracked Earth.png new file mode 100644 index 0000000000000000000000000000000000000000..98765003186ec9d0ec0573e401d1dde8fd69604c GIT binary patch literal 259705 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

eTZ1tZav5 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%0Am%H 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 bUIP9PI6i7uV@ps#00000NkvXXu0mjfWif6g literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Diamond Plate.png b/resources/textures/displacement/Diamond Plate.png new file mode 100644 index 0000000000000000000000000000000000000000..0b474a69212fe662d5589c0441788dfe6e0d75c9 GIT binary patch literal 153635 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%0A0~4#*2xE z=VGwo#F;gr76DY?s>%|EXVOUrQcP640t6pJPZ&Fh`L&4r5Aq&0)VeN`QG+RDg{pc& zd}j#Z%Gmj?ni`_qg?@{>AWx7|jNmGog;=B$c^g@lyfjiB?XrlJ8u6&FMY<0bwQ-t3 zR}UuQY8Bz3;6qTy;EH9@!Zo_Q5foGo3%w?v1Z@2= zN3H;?>>?f_I}`a2eh|g2psxk-93Ghlj1vTC12E>6kVQciWZpw430I)@!BHO*#W!12 zNwoOQ2~^3W*F&-OGKRS{2i?*%msY9TGNJ+iomsz{II3orj156;NiWnmd{^cf*^Hi{ zrsZs_vl9OvzmTfzlZ8`|npMH(PDuS&b8Z>?IY2k)%3diA%eqJCp1}z)EJl9tQK6GX zq%F@-k$%fs8>}#~XBD(IpR!tDLHdnd`UDp>5uoe?GwBu7j_7Tm2M2=@Bbm0_nFtPM z#HeFvQQT-IR7lf1xI_vBUD!e8X)-BY`6Xgb=1HuX%rcavs-kNKF7!mBC=%w<2)9v3 z@6>}MCz2nmv2j#@8gv>W>33V?cLIzpMIYkk@q{AD$Vi}SHY;LS^t2|xdLB&dq>z+5P*dd!t5+^Nn6PFpUEA-5r7UonpL22 zx=zVVJ}Vq#67e`{pa)ztQokR!DNN}A0ldICC}X(^;Kcy5-v{eHRRP~HZLaD#DkW2? zIL1*!yCJxEr!0|B^_Sv5TI823Dotf`Kn06tmMoCp3*H|34QRM0LP;~CH$+{OaBKj0 zuvx;l+bGv?nywciibu=sf)?urhWZKeHj)-wElo$7MWjhc!{lkji6|qzta2P>noy&Q z0Am4Qo(dyrhgmksqD22J!RN#=-9Dvi=#fOg9xBE_6PQ7UYMe64@P(}i%vv35_-%h3 zCCFO$+M+}?a->zgfR*CWJ52ys7#2gPXVcN19UIZ!jL4X!5(YdMM%-mOMRv0Sqx^7U zmoLAUx%9z+TB8Ivq=*4_!>XcgPX$BaeHJ`KO)ULD_`ch;8)oLkq;t?JfW~GI zgQ@849%4ene_M0A>1u|&WQ$>sct906MGK*%J0Gg=K7wH( z>p9o#4|JfGET##d69K$(OX28D>@w9b1o4;_eixl3_Ii=nQajfwIMO2>7>oq|#4t$1 ziYrAB)ox?3ERb{TFnuBYpH%}&4rKl#2-XeLX;>;jNB6|+KWh6Pp2}QBc*KMRK!V6G znr+gkN{C8#&XQtAboVY+*E?+y27U=&{BQ*DoJYnGAX0!52?E;9IF`^8js~-3xZc#( zFJm+qveuI`*=DWcE^HVNgNeO{<5i}ZM$-~sVd$Tqwe%LEMtP49lyJyOqez>4q>n`X zm@9S z5}GT^R88U`fK34#QK5Ox;X<~$uBe9MGlUQpi`7%;pWgt3LJs5VOYp~W(2o>;rZGq+ ze6AwY$FnqBuGZo6e7!MC7Jct2miSi5PC_<#i&u+D^Ar!?KCF{Aib`?J7wf0+alYKm z#&C5y4F6{q$PiT0dg@efu#RBiIF*jc)@-qQTKx9*K7@d3V!BHTXIB7!7maFyiX15E+rH&SUo*&w!6iDJG95s+xy4m za{p?6O9yoQNE@gMnjLu=YQr0P`iMhpnJriA=k0daG@)&er!E9g9pE_5sipxIMG63c zn&KC9KTz#h=kJzij}6Da*@r`WJe@=qSe#Ur4N1Xd&aM^Hu(CFxDK|8~1;_RULpJ+$rdtqXxv)K(EIsgwg->SK$kU-AVW%wx3{ zY6h>EE&Ai%?DwH*yCd*1uD_jx)!zT`awwJ;P84nr2ug#Z?q|0t-rXESRN0jk|;!MuyFdywle zVOj_k%oeMsr|ovvH2eL29}b7+&>qG@Fm3_Eu{fqSFo!K%f^>mcTM{tlWx7thFgM09 z)ah#9F8sah!~XR!hJbGU3mUwjfvi@B2dGz#tzkHN#&Rr{NK^9PXMg+se!uu{&Hf;c z!1Z1kwH>f7ij;tdM(8vEL0L1h%6@1*EdPGL-|zQL=pTPb1n*xr*D}x{1HBQ3WPABC zPaH2{iTVXsTn^&)ACCX<{7qj7p5x~?;2Ch!-5R@BuweHY^Wg?{Be?RPy){R^{lqYC zDt?CN@Am(Y1x@WxI7vC<9Dqr*u=s?CN*vH~jtA>p7$580%HCIZ{k`m){o?G$=|+dcH8H$-8D@+WI&zi-6zU#1Y?>L*7^D0pTx_0y32zcl-1w|#zIh3Dt(uIaO&>WPSM{9#KX zg-kt!c7F$ohJdyOl!glpJ;HJBzuk5(pxHOgcDs3Ahv(<*cDL)Zz(czJ3~p!70^L5U z8|k7>VikP|6As(`LIJ7GMenESj{oLq9iBGZ?XGRa`hTAFLNF>on4vUnu-MNR!;T;$ z&30>S$UGE(jR=36=QZALm(TaNw`1G3hxTx2+tcyw<3oQ1OvY2l>)oQ6sMoB4CAH1$ z=hYbIN_hgz7R#ab?UVoA^YeN+54ZFA{rz-mTXhRS5#zJuriZkU{eKcAg2{7A^M+Kh z&CFg5Vx2z|EFi9^?$n6eui5XLrrB;cPfyE5xL!Wr-%jmOJpaeXB^iq8Xg1VN&7sN( zTa(Z6&zXrnz!IQ?wCB`5;V}0fc7L$9NPZ*`z!z&p}k*U zIp>9JKork^fuO>8j2s;fOEM0g^!r%+K6T0e{$=0nce~x@X}y{+X0!Qn^|aaSn&bYk zKOEY_v2O?6>O3j4m2`Fe3%w41zFe=nEcnn}f#odVp+p9j_klUpt51(u zISpPcmQT-{?G*oZUHAtUA0f>AyOFi;=6SuE z&w9(mdDj`+O}&7ADEM#Q$N#D4pC%topZsh#PwV9Z2m$P_fak6k^m^F$?ck3r1pLZZ z@tJYx4G(FV^cJ`ZZDX8(mXp^X2;KdAnvy}|JgvI*@8*lM<$Sq%+HAK?+qJ{TEXd+WcsSjf`~|uO^dsM^ z_5<|E|E|CO`r@z4eu#jNA*;MyK0n^yj>j$n9FE7+DW-{NhH(#wY*g6M=e;aMeGa(B z7J6AV4nF$YcQ@YtiNpBq^7;OL>Yjf(0@oX>5N1g^grS&bHUXj@0b;N*L#9BJ zNkfjsN&5VK@zZ600=nHl|7q-nrB^_{!UM8wTw2>l7!R>KslFd*-5%y|H*QeRCGMly zH@n^T`Kil(yU+YFevcspbZ_7g9RZ7>RkE?eBln>U02wqgm8_(u5|aIzO~=1`{J!-y zQ(7~;4O`&Qj#*HD1W*|Ox&{stJNs-ztojn`##_@ht#(CpF;B*G->LR(cH8Ht)pCwV zyR%o`6i47Nq2sQ1=Kk4;F#B)>G}V3%btqpJ0JIqq)gGyeyo2`qUGbyX@!tFOKelb3 z1sr7-z_dk2xjx7p#!;E!=@z(od_+2DVELQPht^+T`$plfLq@%em)C`W)3F_1K`U+n zZN>%Lv!H-e2t_UzUBPSCa8w7(`>tz!^|jx4`)4)Cum##<9|GGk3qn-@=ueXPpXkA$ zyYIqi<5I|>WWIE^_+12g9x&f8MPgcUeH9B@r7{#{s~QRY%F9V_ZY(BIwZg@@@ba zvfrWi)%Sj;;~zrk0>G3HUaw;!+;&G`2mv(d57htt!2kdY)}C!E+8zW3=6x7^JwLCO zin5J6nvmb%^-tTwAK!g()@>6DV% zkkYvQ;U9kqCZ&4IK_8Y|0YDVL`({5R|3mRJ<|*$f2(O!qACE&KINXAFPxc>*+#JKB zyh#+A25th7Y{De@-^I0`ou0*8;BYv$9baDH0#ZP7&;mddrjTEfbd@a90Fg`G(?9>+ z?f<;n?e-JuvFm-9rX}n{;Q8aNfBx4V9r!t90WdrW(=Do^Ts?ttOH3iSXD5%J4j)+TvvX?wh}tJgeuA$gG!#M;G)8a)kd)n= zN>dR4;wFw!BGWzpu?Zz|ky`5Ugrf7i-R(xqcYpgKS{T)kjuoCo*odAvh@jQe0@4{cLvA zHPbe8NP2+cG z{)VZJd7ZU>V>Y59Z=K*mM!kTW2$~FaegerA zNAJByxn!vZ$k87X^ZznDzu5oT1LHjddBh>$G!_Dfp%*~qlPf)HY5~V=>>>uru-NHV zf5mDi*74UI{_=I0=HiFLVGvFmn1aZGkIct6EIU>y9f6t=hgMDuQlHP;MjrpC^=dJ* zt9NP_DnbbN>o63aa3Ld2jGjp+GI1;&x06ZL>xM{ri$L{lpD_Q;%YNVNcm45)+KH#v zljhV8ADNOTiH@Y>DU)hlz^nnNNe|Jz4q&%qN4bx__}O)or#Z}&6d!p}3po_)65+$) zaCjYug14Fgz)%VRNgoX%$x-9!-Mxt8zkt_JK(-%)9E#~oLA9;RgcfNj@>&6a zp07rMX5J6})`IWfp&on8yR9#527sX#G~z3Os3_kYv$x8Hn*3`J^20HnyK=?G}(P{n!- zIcSYRh4u>Tto)1{Wf484-z59%v!6MjrcJbP7!bp*Aq0*?(RM;dKDIlJJCgYbG2Q#L z0)T)<#-R5`7C&R_PlZm#0U(W%6d3t79*SM=ay{zF&wUX%Bz7L@HUBLEcb%r(tZ7BDl=Qr2kO%;&3q?%koDnytWmE0ZH?(<801a23#? z_AsDBr(O_D_1!$0Q?cEI=TuyDIvU@@xc*b|H!>be4l8hk?|2<2La*^q*pj!1cu{$W zE5GNHwtiOsCjW>y2k2!0*03S0i~vbr*ns*#4+xOkfBpQt z6^;M?`Saw;jsVcxl-&_{8?t~ymIaiqyJ@del4nY)4}}OI+lX@g$KvmKH4Z*KHC@jK zIGP9@4~J|>-9AKF@T<^{ylHJ*Ru1nujS-;9Ynh;F82~yQ&MX@IO?nSA6`1D+0H~MLNE4A! za5R*f_7^I|O5!3bEyRHcL{sVs3Au}j@uyHeV)ZzvBS`m$+(igzyP2pUi&D=39O?qf zWdc2kqXhFV)Lxp7@+5E%NczSas77m1ge(yj0O?dj+jyZ!D*y%)H&4$DFD4Nx$A1@v z{~`H>uNcKp;oA+_4nMRq1W1mEzv(qB_f!HProtBkKtw)Fh2Ep=PYnRk_3@!$+my)^ zuc8<5DgeS6UE3Jf(r`^l6M*PEsYdph+`}d#Jm#R!=>0k56_cec$+GPsS5VFpfP7=q zx$;=j6Vcg`Lg#*zdq@VK5mj9qGek$hf^Wwd`Mw?esTTrT!-0v0p6_ylYUSzp_>0=V zo`#Qt;%OKs0#Pp@;)s;W803aBj762<$bH4KEYW{@xsPol8Goy3@ELMb?AXJ=2>{7Q zxOcDM)V7D$ktUKb#o&Rt=weyM0H9_pbO?`Iar&ylI;S04FP> z910!>d~CM`F2hg^CAc0PR=|{~GFYiIb;4t#${kqM8H^GDCFi${EGrDf#1)r&I9A~q z>PVFFhaQsuvGXaGKbgxX0YD~X`gNl<&+oU(#1wr!92DWO#2y4OiM}!bDBD+shbD7> z|M;h42GN681O?O&ID~+)7tre3nXGUPrUqy7pzt~MDCRsg5#PT;ETsP??;4c}mSf}n zcKLjtqTomtz3G{q%mB)71@`zyi3t zup z7p*@yO8WT}5cXqFA>exXe1AV4Q-Ju5Wv&X+8<<)r8eSJI!qLu?@ZUs~x4HTDdw{O47qQipwjkphQIf7%6$}$J4iz{RXdT|q%%3; zTD1ZF-NXj(n<4xO&^NM%8;#74QBz2j6;+{AykCbbSmv?Ibkjiqm#>0vlza^{eY3eTx$JAwO@EN0Oa=8)B}M0 z3dU|i5lu=3^;P+brehD7Hedbi-$chht^M5qfOMIe9}?uieZ;*0aRu}pag>^iN{a6p zY06JE7{+m&eDv$zW8TN)Kk4dI+kz>Sy&?ci)2aLI+(U!fo+c);V2$|9;BpUXstTzG zcrv&jJ^tkSGoMO&0pKk_m#qsrwQni}##eASNF8<9GS@6ITv5oF$Rzo1_M-D2Y+4pA zz)@~*pOP_R)mw<6kfaHXi7;KmHm^8j2!`7omCpBeIQ`??8}RzZnuk5KwP8@rtrM%c z6dgJ!2piK)r~$x0AO}~qFJJ%e_%Dzv%VdU$S{FbK0H#D;_j>MP|G%bgLMi5!ri`PG zNQOC*;Z*#}>@QOLq0M&G+KPA!NuSs@bIS{Xktvo)MnK_S(?#SlsGN+vH9uR*!ADPd zSgq!8d6|hzp8y=C*LxlS^dr;LL`IraPZpJA>4GCH^GlfJ_Q`w^C4bZM!+{pX0RR>z zmQJq*DTpD<0*|?&7_~uvrYxLYJ20S9l(3h*T>mNjLpe#b`m7Aeh@@x;&%{SyDg;iG zd?>ZRmuH5sni1v)pu~N24!6C>;n4eGern+&&v2TGGMI=!rgInuj*=0XYD`C1(BvvI zv%@UVCDOU+Hk~LR%{cf-G0#R5Fs2#aj ztEd|RK%T^?`nI*Uv_Zm@1y3B2(?={ErmGzK5x^6F3G>ASdwWS?Z_IiwbVU`}2LOpI z$^rmF>W41W9i}Z1hX4|}fHXY<1~ouN)-aKT-)11Zt|ru9S++b109J}CC0XF)rUOSH zrAes(+1}&YT3tGWI0i%OO{(2w`Z}hmghvdlhyb~df=GmqlYFM$_j0Ce2sUjW-SJOc zZU)Aq?D*L>3YlC}K7=lw|M)uZLqNENN^#MYL0d6E&W_O3Geg{w%b_f zdNr4-GG7b;HfPdRCXsGRotxBAk<3vFU<6yAC?$Hz*6;c`5jzvGCnl3Z)&Wm((gPrL zMW^gdPPf2lsC0UJ|GY&0>U1TfGpQ2oxDBnJ3G=*7Yz|E0lI%K57FxY?Qo43}Ur+^z zzV^L<&qO#4*)zjX5-`U_Ddj_ep5ch>s+BPmGis{lBIOb$;)ooiWCnwC@tgYDa9p0A zq};_c`I*?vSpYytm`JKHj!1x0T?mt+%}g?<=ZLg}l?*iyBMsDH)-g$@)579Y;vNL- z$rcgFI)TyFtPKE(VG$)?en^A{TgA7x4=I_!OcE;>=Ad&vl+ORI_qdxy2VI9ds{z2; zlAOMk767J+03&pm07#Dj*&9vW`W$yeU+`pz{=4q+_mc7g(^e=`70ymZ5p|YTtH^8` zrgm_Qk-t?r2c=~ga-WG&>U6u?O)gJU_Qz?S&n(PEhAmDmK9NyF^cQyD2X?iiEuRV)^tmNPfj7fR_OzMXvk+ z^c#Pacd(uTC?&v%2)ISJXjXEWNvl}$iZNy82R zRB^{?01&SwN`GJTCLqlO7{Tz#HngefX@w&JlwE6zQVA}AY9bwSLNG#scp&yj z8~}#nAL%5;)h`pRflwqp($ufZL%?tg!fWVu3@R+-^5d_O(CyhG zycpg;|5mEr_aG?MLKo%~pe6tmSdByV|2|xQhgLuS3eU@Nibc*3rbB;^#^6@?H&N$# z08o%GRIVz9&>^BfQY_%d`O2VapwdABvoZk4W>_NxKvwe@>WGpsOKkv10fbQikb1$d zQ}TPc)1O8ZOycO-Atts@`zD$Mn@FGpmnDaoB{w0)!qsm(C~dm@1)~E2syqD8bNg5EGj$%QUNDU>W`5EfD8ewQznX< z&QSm;k0_>m>V7+azNa9dkFNI*O$0ffzQF5CIJEMyA3XE|A|^m8Qb}F?4;Hvh=dUYU z6cZFQ>V1C-LXcUBKw~Kt?P{2Nzq|+{(9G)_L6PBjECRsr%{fIlz&QIs;fZQffaud@ z3V71|!-xQB0Dvh70RIDs%j`a_zwVe|||eqPuJ-e?q};s1aqH_gc5N@wGU>AW5K+c69ntf`Z=`O=!>@N zW5O)bTtJOPQT&M%!gw)+P|N@T>jG9Z&4O*^nz$vT@>q-kmekx$@IV_N0!v6dXa|T9 zaPWlk5v)#2dLb)}I9STul-fXpLS$#5u1clr&}drXn931}$gHUZ{w`y~6yi5iBw$9M zC`EvQ5#3HKfYaz2H?>odxG&`v? zZ9>4eaqGw9^s-=d4o$3{|;2(_Hbxl2kfgbO-=7ECcQKTACI6zlX9pg zqC&8dSl&qYCW^b3q=`<=;-Sx5w>-i z98NA0VbIsO^~I7A7%RPSk{6}5lsh?enFz~UFbe?j`Z^gv>0^zOXopc1@njtTv=~G3 zexcHYqeF{$0yVKwF=YYKL}US^8J{9D3#Gn8@HD!^q>34910UPYGx=shIH&{OXl!`s z>R2gzVU|t=rilncshBDT0MkoV>Z10#u-)Ltn6n{hW;xC$a~ni)q7c7=ECh`DF~R6^ zJfhboobt}^?{BGV{8;@4vnTrC`5b%`f;%pK-G$u}cumb>HKQqX=_XzuG zCm|5({sk}t0J4e;ZVYBfM3crWU_agg(hM|m&&@dirZ$afqez+k9;HXnXI%U+|NF4J z>DGQnW*Zk&JG|X49SQ=4VS^F?;u{+Vaa*FkUSk~`I3Ol1p+p5*5~&kzRw<0HAnJ&U zb|3}$0#0>P1x=>n43J2Hd4`$HA~XH55K=qw1dhQ;7JQrt$X-Yi02GuZ^vxM8MY>TI zToiwC?GLT0NqQfQd(Ei=k?CB(1c!pD5E`Yi#w-Xbk|LfGX|xpEMv<8yu5$gA-S7;H z17sH@EQR1m8FH-+1pyY%zX$+KwNRjA?Q!T&_e70Kqat5BXvgAj6SO%8&@} znj9d>I0Hzal@A91j-)3!_Z>m6)TD=U7)mNtNIM?FG6$cCS<@ESN1n)nBbSx6aLB!G zA#@+aP*e6lGesq;N3c(2gswl`b_>QR0ttJWX z1OP(M;V-_zYkVc#2x6U1q0YMD_u?aUV z7$mFEdubz$A-)ACjQn&2XygBM%>kWv!7#Oc(z)-QDLh)&w4r*h(aDJ105CqB6bOBh z=(yp_QtSh4JPsW_@<{yRRTMv&sE=a`0~HEJ8*?w!Eva$qF!A>O8E8_^t1$#*){QD20Q3aOLn41QQGOg-@lbdqD1p${6Q(Lqi%uf@>3P5h zE4>hPqtOHd22CS!a07^(0f*v;2qhz2=<^JeLqHKnhe0KCC*1-2UWh){mKPF^dECox zis<;O>UT8(0G0-1K@1Z)aWFytj_A0(tc8|y#20c54u^WinCmq6(oe(kx7yq{EdX4f z5w$4*Q;E}HqfPeGT^6)WEy5PVPzcJ5H$RNjy2p&Ozc#`>C}tp)0U!gq(3y^=w?5OU z5@yTw)AMH6jJ=Qq8Uf)y=|3W!3R2T>fScS%J8M5GQ zJ`sXRlpdCQ;eLKXV!qpP?tL;qj|g>u$M97OHK^iJ?$`6soZau+K`m~T(nOHq38Duz ztPH2QZ`%|8GDkS32Cg=afZBA|fP`dz1$wW9xBK<-`F=Vb+rtn7V~^l3F9(>2e;(Sg z^?Un;Iu6hV5QHTt8>PkI?S4If^b*yr?i7LQ-IVRF%nQggMg5|N1LcjzX~_txSrN7U z|M7O(9NYHLt0neok>ww*WB>~2wXc0e?K2u;Dx_~zTE7-E0D9Q01UZnmLpINXESYq_ z=zmHn>KwH`d%|Zv_@OuG%q71W*S;v^$a(0HHNcPste>|7I<9GiSB#GGhAU7X+hXTF zK&kdEel7!uO$UN*3~C6>cF#Xwte;}J^l2yzn|r|=dlQ!cg9%7`u=S3tcm_4Zr5ONK z*>rIMP{9LhR&i^gm$8r9p^EgwsCRV?N|PvYf{XlcQ}ug~&{W&p_OUkxJ1FS~$Pmy> zweT<(PE|-zaI&00ubJG%r|t)9Yd%tC{4<9}S{>NrV2J`?(opEzVZ#;>Zh;of10`|V zSoAbqe@Ee>5CEWRFzVWNrf*3|$=YKtph-gjQd(W`o%zHYnLxjt8LEIi@sS%J37%kj zX0wb=>A-FP5V(llgpSP(bEx(J413fb_A)xc3*~nY^kOK!(BXrzCWk^O7|#t+aJ&Vj zYEEJF7gFnXz4wvf9@IMvt_F)DO{q0rg$Ri?1W1NM<3wcFOoc!v8xFMth{70P4F7WO zKdIfQ?zT*_ZKUQP%1y&UkDf{rpp?vgJ9MN;3E6BEw8By-U@wt+z4oZr_VsXh?bV)! z+MjyE&jWSDxf}iCV~)Sgv5^qYeUZw6GhLiO4*;6+L(cds27ttVx2!Myg#eJ}iJ~3Y zWB}QkIvAz+a z5I|&q5$f%7r!}00c`DM5xGo4`pyV96k~n`KLtry{<_=`x3cUpJ&+KpibA)j)%jMiAvDdMN-P(aY$v zAQ=L{q$9-Yq&~kA0F(>|DbA0EF)rOK zafCYz_SsFMow!K0DVS9+>Ex?bv>sDy;nez!xwS3R06nJULSa}PE6njwDS** z?LA3mJWXWj1Ra=&g2k>c@!)&#I(qwV47bbWY)oK8%q1pr$zX3TP^OvkD zP!#~OooogG9R}={0I_`Nw%LzNx!6ifA0|<#{fyhcKmN!CgK7i7K)!)x55%lJT`*8h z+KU+nbv~ja8>yYRfS18S(fL0f-`+kV&tpCe`k^_)sR#gCvjJ`y%`pqyMLyD_WC#^s zfIfJQeD6r@Ca(QqPW@==%E#+trT|LcfWCCE69S}@`pB17th5>99^UiX_j;$JyUZfV zFn%-CWS$g*cq9fHi~%!%lH}tT`Xh z%~7aZca%DSISC!}4i_OML-axrS@;{XpJR@CWDVEBP5`19$3nN;JuZ&oXQ0f2_i+e7 z762f&zYx1>Q>jP$lfn`vkIn|H!G9hB;{KO|WOY?_=TR0QXhIM;T!swxbBv@uFM{t) z!$`xA0IF*{jn+@f-Df@yqH85Gy?U7lzNB{INE?8%_-m8ihiYdWqC6HQ9H0{bB(*_T z%3(R{O4(k+P%yd$M=KHM_zT>NV)yNt3+Sr!l&r!y$zfsK1*6}HqU3Kv7Juo7;+v<{ zNR#WBCa3XR#$s2k?($;cR4bebpy}aK!8{=Gi~K^DEo36f6HO>)3d-X@f?iw6__|a3 z*j5J&`Rzq-oTd4O>N|lcl++MXl6z~|0{cifYcRYAOhtMash|N%nusvw4e*+9 z)Akq(fnGgyv=qk(*uqI3|4HrUpzEAdV@$g`=u(3cRshJu8tSGCcB>^3QEmiTQqp8H z)MHotw8G}Y(1@hV-la8O4+d^H4*)ReGWisVN}S+`i&zbO`LP`~P6Uvu9cie~+{lf& z%3~=2P%&dbrua+SmAE{0M2?+6j}F~Ny`Y36qUaqQ+NkxLGWKZC5>>zvLT;@0KmcG) zGlip>DGS72FJrJ(e(BMXBTbG0Z;8-#dpNWc=KYr6ttcz&F+YyNeK4{bQ_=5GwewIN z1(=E`i=kjdlS${^^7fyEzM<=d(;4H?h+lD%i}V7uFxeq@6Q^M-nR$LkWZ@T?1t4O) zNxg$n?dY!1o3d0L707NX^lPLc%?_(Ir~@?s1cXsCBvXwV_gQN5qT^l^Ag9rtr{?k+ zo0PgaBvtb`ht+Q&Hqp$CUl$(U4$~T-73A+VM z^=A_!S+ZwzbUR?w(NB{9LFrbn{j6{UgGQWuVr}jVT?Vzx-UaL0%@J9;J?vY73mDOH zvDV$Fckrnz{pwGINq$Y6lF<|i3ZA7mO3#auUpvOXwT)$1DLWB5ODxAb}m(-pAajT4bB z{3pl%7zus<4AuYfwfZ-lh&}|Kj>oobn?ykyN7?#297dai8$F>clyNT%8Q3ECYlYg| zQ0cS|{yE25=7sreU<&rGq60uONQ39s#N&-tap#r*8ib#^MG#f!&!Z&(6#>FDHx=4} z12pLQdA*zmx&4LS!JSsu*De8yJsTKPo&gy{*Q!ZxnKP)49?8F8wpd31@c|$lZkO!% zYlQEh<=L6oZJ`7ZOnh4x9I>oKv=0%2KPq6}X^QkBq=#Ahm4AxvcB+kOUzkL4+g0}pU@}kPsvy`79@q;i-AunZdOspehVI$?003apUH3i|#(;EMu@%C?Hn*+z>*;AIy@vcbGnygY0 zKrm&I3tk?k!%*ZY*wci<+sQ^fmhvq+0uZ_hRM!1au+j*s9H25YgJDFW27n0~BW*=P zqV+d!e=Rywon?4D_$tDiq{vf64QrT92WxH&%SQQF!4V1XBK%M5k65V(2^vco?3hxx zOsBK4!HKr-nFRoP(>FB$C=kPxDZATkpYi`nU=Np?+Rv1%u>(Md?4!uHINcLE*P=fR;pw6Cp5e(48yDuI^O>X(`WZ7UlvV(cyKpQ7$MtXP!+(|l!c&8fQh=Wc08HfFDT~?-FkUhUE3*s;;eH;5g3V#S-#4K&_jWTeI96gt>@t+yz~reOANjzk4acVI^>+FE z=!c@+`X{hARQKDNE93C7!GJ#zplMWv3O7sWMlZKpHxb)*1F`+$zaK>B*UPi)(33>~ zCrRc*6CNV%YJ;ZPrm9tvr`_ycLC5%LyA7Z!y%NT>AFR)Lp?FXj@>wY8s?*y&eXv6a zgz^Ig5_1>|osR8c-|R!-_~&n!>fi;nR;$dPF#{BU72gV}*I(U*(yjkn-}>3(wT-jC z;>M(w3w==#A0F#0t1uzs+euriORV1eSu2Ya=E&_7@?B>GT zwmAf6?hW&vXEKh7N%qkAoJm;E(#A1vLF;cgEhI!uI;49}xel(k%lY%|bUHR|8z$5% zt5%v{nEbnlD0th9wLg+Na$LJd1|Narv273dzaLMhkA(745b!J(@U)~G-M(SLHJBP4 z*HKQg^fV^nu&3wkb{BfS_bpFOpy+MD?m^H>nDohfX|w_}wcWgjbi?9^rKPwYb;m;+Ab9^ zh%TP9r;b@jz6=w z0(Itq>@B0RC9Vt(eL6UTVGR`=Y8))dNn}|0ZYUa5u{W6R9U9@2Hp~aP(jPJ=fUR23 z*g29%ILqL$l4@)B$YYZ5Qxq0Tw#mCM+6zfv8iYP;jIg-Vno;Ud4K4t5} zczgs?J73k8q)qHr@qT^;sM6xmZUvW|-_OOv$N6$&J~@Lmbc|6*)B^ytFl zDQ{U_Qzl~qDFGJUi?iiw9pc-6%FI!K7kIAK$xCDcXMuf+6Q@;bEFV)S2L*@s^aals ztEVs(KdjNPRdJm~!Rw@P_&@+){z_5NXeo3rI1UBzoDF`QAwTsC7iv44s?jzyWB|RY zqx$^w0WT2@I{M*qBel`Fo&jile=7hW)C2&veO!`-D)j1@AV`(--_4e1%Mek%Qpqkj z=h8+(BIo-`AEfwGpk#ayrM`4hAb8$nRJZti5w5pfVh$AnfZ1iB>3apulq)b1Q>oxu zWzQJRdcG6@LcnZ+Ig6^KS^#j2;Y`ZB5~|@qccX_>yMZXNrjo-&4K_lFtu>7dh_dJ# z70Yek%c!80ZV4@+AeYW0h0d%I0< z0ll{gmh^^lVhm#h5Op!~j7QAi`kWLd0C>2tEA(^;yYXlGP=Fm202~MfFcNyVmk1L9 zguKN=4I_fAj7x8Rs(S=<2IWHen4m@^^BQ7=+1=GU=URYD0T^IHJYw~D46cQsr8hae zZ5WnTkKhhCD}d<9FN{HGe?5O@o?+rTyq$I0J1BT1q7P!@QhH>#C60;H1&0lUze-15RHsgFkE5%$mz88Fc^1zP~GDC&ebB(Us}{>^P39dKDgT(s(e;j2rt~?o%u#|j~a`5Sw0s4bp+h63CYkX z?*lj#&2ISyTl*%TjH7a6+v6~3l35P`DWUrCAVuBz2Nzk8ppD+ZPn;GV#%4_V)U%rO z$77`smY*|66@9Trb=2Beee_xEsllAfv*OYkDcp;)zMR2uKmwe11Uf1JrZ#U(0bS4( z1#;lV%zJFP@Ge*mD=hdvp-2-hBS9&Etf+``OgOPdaKxfe+^+H8}dY1 z50D~x#23#TbV)vR6l~3q&fvMQ`pyT7%+JIUm1evD=ds4+zYo)ia25+E8P%}X3Dhe2 zsj*X0u=;k7Vy{^a4vY(s_FTw{J;Fdn zO-^X-_zHtL)YidB3i0g1vqlZ$Bk|F_&*Z0zpDjMbRP0B;iPW9KdT>3&lWGspl*34s zp!w5Pg*`rIzVpT=Kn6aIHr?EterLkn(&sIFQ5D$fdB7+hOv`w+O9%zFC}es?-OPsjs@sax+u{0Gi&l$IMfgwK!{k=oS9@6B5VT7t{ilA zSG@im9LqIWye#EuVKmWg_aLiRh!Aw!SX*}vx~)R)oiVo!UX)2V>gtoK^KSr~RDb3Hk_I0Y8O#eep_siw|+U=W%2_KEt66<^SmXSTFZ4hm6>xA~aN zH`mSfR|am4iIo|0&cQzO*Qw$YMF@j7hD)i}3AqoZ#{Q9`E#=Dx~;hQeo zd&LnY{oWOoU5dp6$N%xH#h(mA88tEl;EO2~n9!9_j`GwuPC?|;VolQwE^Y+_wR6Q- zSU4{S2piPPlALG!`lC)E?6w7%u~*bOj`$TmI?5olHhE*wayL6&UryXnC|*iEGuwJhCoJt)0@KwD+(E@50wS!UPL9!REyFvohytaF!b0A3 z$RB`)Y$7e_J)oeuP|3L=>bCdr8?&G7SkNP3i?CJbod(S0SM-F{NK3<5Ac3`O-`UrCp>*dAk?59EF}E{jJOK%50B;n>BR@F z;&RguNfA?@WMNXCQR=Ii(Z#+2;B$Q7Ub}G8uaeDfStu~S7WsvAH7=})qH3r|i(-=a zLrL6AmYG=#-;&@*glK?$tfLV=Bg=VM3Asc|*Lt0m~2;)MCQ zXuICiq-*9?3I3*{c}UGL$`;u6OQGyYWe70%NhNc9+rzfGV11lW6K+1DRx8_}tO~sk zOe&zX*7%qdK8w=oxh9$^HEl;s3<3(Gu}Y~2po^{4rBP=np6bpk_f( z!cLe|QOcsOQt???G=VP$zWBJaf7f#H@ecei*&Y7|fAvQt+W##dg}+LL;6L#?|AXy> zQs)+5`dIuw!gD_I`OHtc&_D%nyn#^PeTQ>J@~7Uft$CS-6O6O z>S*E1-8#aU6Hg6<(p-cdm<AV)?cnF;JOM zHXMe&1$dXAcu|w9Cs@P^5H<13(>j#q%F-0KDD3iBh(P4gTIt6@tISm za@3vu9lxT5lDTtGj{2E={L9Io?l$e}htu!{t_2D43*93+vfK|G4O-S>zA_xF$_g2F zinV1Gio#2*X_bHN{1a4d?v1h%4=P60G*#kxI-#P9;3eG&mWzs~v%OdFl}0EgP*{qf zLw=wHCul+f(!6_6P>_fxHFT}SYv@E}A)?X%DKa)Kc^%SW$U;M_r3yF+@tfISjMG*= za3~Z5G|l(knma^q8+^DjuR28rwhRxDy?n|n9VsUI+AXNS#-|bh2A(E|Tf2n8iL>uuj=Li*5M^kbR3Mr4azEbk#DzMjxzB60AI9hNBYoGk^qmrn&l@&vXx` z?e7RXVMKmm;6WNUvTp@NPLV|sm8^Nh7}gDIIhcJOTi_F+h#h54E-E-O`&n zpa6ekrZEPq`$=`*P?;DPLQbSJ1hWFAdKf5W$Y^ZkNAzbbr^ZkL6@=X@jqIbn0N_s2 z9P4ct~8gWHl#0|0_AJDRjnVMTmTf&liB%Mx>`CrRrrL*Y2tVqZ|L77LbV zCebkp0Ebi}vwB%S790{xIsM$Lr%>;_g1nuFWFt4b4B(rMPX_A}h^YN|L zFAG*J8gs~-gI9Ma{_Xf!7;qTW4;7d;rf3bGtqfD|PczvHES*Ux1Scw|C()+riq-m# zTGcAB)B`{=1EG~`R18VFmrl*EE^kauvXdb*-DTt^@uhC52cV>lWOVee2!(s<&Z}vW zkJKCjX-0~lg6HdEWe*a+`17ZpD7xR!^l3L{#7>x^BKJhQy#%Y~K{nE|suX@EbETtI!11AZb@x*oopnGIPaA-j?rx+(36YZS7Lg7qDQT4MyrVm$ zJC*KKi6bNw5Rj4v>25gS;=A8>fA8+h&CJg3&b;sQP{%fukBrybG^4$k_}xB0=4^=j zq6y~;KXd0cY^TI1ytB$u-H5rYs1(gMYAxsSLU+z|zJG*;wHSMLfD4lyLukrab6%U= z1Z_(9W{rCYQC-Yr`kho5W-8n)$hbAYmI&@etpYU*68N`t6=ugJiC879N+)%ya4a;^ejA%dn|TT9Q_Ba z_W))`G>FDVg1gT4_fEovve2$UooCu5d6pN4ZpX#BDc4VynEtQ)=jnZcq#e$&c~4%9 z|0b`=G(9PMAkbFQjKF;#dO;-{ugHkLDtKT-@Gh2ua3S|zA(BD2)}X{zt4fCwyfk0( zZ@Nqrth@jDT$kA3Ev`g1{?;be3wm!PzkQ8R_-O2Q&onmUg|%RcQwpnDQ7QNB)KmH3 zq~lW&w9-oQ^Rv+wYCRco2CZW@hQLgxoIhbz2P$hGJ6; zzNUN5(CObdl@4R0brf3qN3^~EU;nsq@a#q$xB|L%XRj7`S~l8cPA75a)Yh+}UFz5kUx930=+&ZnIrK6DUf z2URsPJH`{w=o9-!9u#_RpvCgDm0%lJwq)IRyB>)3p>tTtFCft3{SZPs=t#{ z%-uk$F8{*-wI1GdRp6NUMN55?%2isTyn{?@TkvQ zXc)LKtWPSY&bW>5rT2=aC81WPBX!NK_H3F&Cjlp=P8jIXrPG+# z{^itWM^ypw)y9Bu3@)x-v6)DsuC=avuu=3`tnHaR6&ekHZx8}>oRxsUwSC_KzPGm8 zgtmotQ@sAO5KTU5aW!vG^h;K=0cF*AJck+VLDV7yx0u~THl?xsOn7Lf!`^4ck`j2isYDEzZ67DssxxRd&NtMiX9!U{Vsvd}lho`8M zQc!KZ;%fZ(-Fvro&*kd!4g|Y(S(k%|ZJ)aB%HrnW`qS1KVU9$2;{$4^N>yX=Wiv(r zT$gX9H6&<>nKnvO5`AbL1ylwd?@xKZXiMsjWP2HxlgTp8V@WU~FYfnR za~)S>yGG;txQr>9t9tb#mLqX4p-#Td5JACi)rss60~!rv4e zr|G9g<4cnx^~^3#BmCnHgM>WJV}aYp(Mzy*{dTuJlV#N|GrT-eI z3lqecnv@IgSuSIvQFyAfCxN>=#&>@YzjIsfg)f{z#EvQ}=K@iuQ70^p=UVjdzRjDf zzn>gsuqkmM|K2IMQOr_xfj)C_R)mD~hCKF|$kiLIlBKq@^zL@;S$q&!{MQ2F2_k$; z0PNh$C8GVKMSQy$muNJ!`V(>Jkk$1?4 zTg&xOB)rpp6>jQd0tndkwI$Q?*rV&$LNL$c;$PanmX63e?wi22qLG`lxqLiXKD)fn zR-#UYNe^jE9bWdGCgs;i-mg$%xoF8@ou7kH`s*GD90Er?%K0AMJXAxl>~H}FZX)pj zI?_-)Tf96b{40S;|I70HYohYTp~HY>*vT0Zda?{f?S55SWAN^p^RxWLPHUSVsWUQU zSfF(HP6#wSWKYXQ=6*3f2#-Y|Z%!b+aKy440?}e3Q$O_FnD9hiQuL{N`oHTu)z>+^ zu1li|3O9~#RYGj1epx?~bRyj#@E4(oq4$C@lUd5T9n+`XgR$C8=1NtnX5znKw~kC# zb2IjG&yfFb3P{h#`4G$;MR_agj?zZUthh{k(5%^HY>cfdUDm%Xz|S`Z+HKgIt~$C~&* z9c!%DDenZ8AWwxtsH`Qs1c`Su{dYbpy+z#etfsI2#X8OiMebs%_}Qht_(ub3U7%e9`?SdjVyg(k6hw_tj z&K^(1Lj?!*bh*?`yHu``r~}Q^BV9XDR2rizcd6p_N7BDB;LbagZQbIJG*}C!A_#zA zw~X?LQ&P<7Qz~QA`Q_}PL>j8c$W_-zSht1mBl5)ilFll$n~;xqY(ki`0m)v)s_8n+e9dG=`8H-j+o`X zny{mhxosZ(3p|Dg76>{kMxLl9^b{}n3 zO47J`6`FQo*{Mha{@s1(-4U|Qg3q3He7DxHn3ge`$o@+9j&W z(8HFCTyOQ~$bT3m*ZfeV-=|{GMVAW5sN9 zd4~&MvcyGRSpGL6x(*vbh=%dviQR;o8T+o~lRTxFIkd{o#xvxv^kqwaV{Ax>^@mgy zatD39z8}G8ttvE|c_?ziNk{(Pji+F~kjG_UuJh(X19jk|fn`6YN_l>`l;RMVwStBe zVWcNfYRA&|Wym@L^`A7ywC`c&Z2sa5#3!ZNk&Sm7S%AhH;?Ef(t(Z+;%~kP9n>A-R zjGw1-8uVvuuH8aQC)sz z(;8sfxSM99l{!=EA=n6oZEH^}xGVMMKWY zwGj`?mZD_QFWkkuiQ7}@(J`qrT`7M55HPrI(pMOq{rT;Qi{wzCFBfKvr4Bco zwU4d^-dfYc<^m$7k-kE2S$*NNCx!cj-MK;Rm|w1zmD`y;N7qrw&oKXd$+7@kfT$>| zq4Oh`2^XW6v3aQTnu}{D!)mKJbot}s5@`Ln!{)MBFzKxGYL@uKtv@B1cP#vj+Vh^Z z(ar3~1+5d15vg>9Cd4MaHv$q_P@MYzh2-hXm`w4MYFGMFW@mu zC-8w%tk5urD97#+QW(grDF%J#uZY)e@!Oogia#=gRNgYTdR~i(*pYmg5}eiBiO$mh z@tf)02m}K6v$_3X;=9^7s1v*&^@EkgRR!;Z6}J7~v!M zzJfA>@%Z$QUo~Q}V+BQ`*Q&zm_o3R0>njonM!Fh)%xKc7T}F@ZzqfB^TGii5EOTDk zaRg#I!`CzawAvNJha=8hFRgQYg)rBi^4a5~!LLTB(P#6Qni!l){43tWff+KUAr^e# ziuL}rGbES%Gr5$5F2Nn+w5Su#qKp8)ypuEDQ;&dG4l&;p+r;g*ZqL%!Lr)>#LOBig zj0PH^9`F_|dT@Ovk5P6FF_$J!zxSi_MbL^_$jNdHgzu5g+47D8RW{!qiqf}rF~wxj zWg)*n#-Hp`vU5^}hIncAs>&?_J4dpIYj_LdW>L|<=lqFOlPXrA#1yqy)4T9|(ox=mB znxg|0`7pI7XmK%4t+N@Ba|}hXbF@Gog8De0_3(u;*2%?hxsOJ>l*Dx_fgZop=@sH` z&gj=yH$H@2o*tUAcV3B8o-T#yzgi&y4)z^U^UDc5paDCL3z@vTA~439P4-n+udC9+iMhr$Pr#zHWV&!qd0uSEP-Bu zHR(>;%R+W~HS}<~^74@pjfVa7i&9VW;+Z(qYpEk6XRDMngB2Ccq`|6Id|nzPY)GvNU@01o;Y~hnt?SE>*X4IBW!O;_AHOGC|uwtLm%>{B>ccLjY{W6Gp6u-b+nA{(TJ7{-(XOe7>#WgzIttsj%+waaDP!2QDbGsg?e zsyKruf5iyB&Z=({#C z&pI^MVX>ygRZq2&CI`RL2;7E0+)Sn|ic?ga@qal#?sO`7BkiyOCwiOv(H2;--IZa=99 zs^E0aCF`mHX~}x;75{)`lwA7zD`9ucwC5Ya2dj})&ojBn`Z|U9=z4X_*s6L}e*GL7 zLwxB%{Me3&PJSA3=*b6MD)ArROfe;U;FgqrDh`$Inw%3uW9^!H3&^{lPm=BIsHj5d zC0QKYw9qUUX*q6vAqhE7V$E%QBMN`U>k{@E9_ zWm@#WMA(J@v67X4EVP1INps*+|GsOWkd@6v>k(cGUPk+i}fYb<-OG49$d}iU3#; zPkT*x7v-{a8VJ6NT;D-Y*=(z_6I#`=MUO?XSg)yThjk7vIm^xeCcTo8^AZPXz-2lA zj#!+p8ZjQ@l>YeDGOk31-c`C0Bu4pB1(!D(7+e3DI@dr1=zOQKxt$S~+1CaawKK^u zd0ZY(dyIHloNx7uQTyx*Y02pg2QX#OV-wG$_~ zxYhdi{j1XGeIn0o{t$;gv1X0A{i})TDy-sBKKW|zg&!KgHW^mQUE=6MH0`!b*wnb{ zdv(nru+F<+-t-m?!=&Xyj5p(jh17}HHm;73XUT9@?Q)2&CU(9H)5R(lDjL@juH*CP z@(r*(QAkEJ^@-!bRHtNXwu{)8#)vs$#`}Al1MmJGVs)cdSALQ>szVg}`fIBGvU!Az z{99?cmS~e&fBzChRg_K>{M30!{t;}VGv#8Ba^p4kF-|b9^^tR ztMsoqfAf3Qw({)>C7eF&-eSU`s;I}AL5ic=7?UV~(IPB|fc>}2%ut{-kkR>+YCPyi zfR*!NUD_C>il*JU07p2Vk$iLSmB z^mjLoP9h-sSFXx5ZuYzEDyVGDs1w<#t<8Qonf1D1*MpFvG?|@34tekCz>WTu+>I2i z520S;V%zVZ=1DVYeu=Ti5~gY|uM%V*uKG?Y5voR=g5R?g=2_I0!B%Ra2CXzrUh{*9}2Vrkrs8AS?QfpFl(@GI8-diTW zcQe1_rvnXLS-!8|gMcUoJY4wMkP@Sn1MWnxnnF(g48FGU``q35>AWX&&DD=$vkQV~ z3B|d3Y~OGw;VMg`F!@^fn(@D@FXS_$ORsU97gT5vHYxnMBvi>ks)jRd;)obz05yVx z=lIEjs_){}l#0>i!X0LG`kHjzCQ7d4e0s^8(dCC&I3A&X9+;kU19Mz&Dip?ZoWcpV zJa)N1;aYh!pU%xAQ4&w3#W7tyU|hiWO`4z$HOnnXAw4{7X)a&EzfF|m%$? zoKNuWf-g{1!DQ#eR%>j4=Sl~E>l3-3nRo93-98<46ur6iT*kfvJw7U-v{Q{DUlB~$ zCB(}8aHqMYKjG#^J-n)9o=8wMiFHWwkoa?oye!I~9J(9+dgzTT4L|Vbxch4EY7#q5D8{g=r#S9XIbWt`>`+*Z zEc|FFj{Ey+kEU>SqtaX4@EW~RzGU(mHsEBImopf$o%pJcnyQ5%(OD&qz9}CWYk-@= z@F_%;@~)2C2jiO4h4dc|`12KWJEG`#nK}Lg4q^L=UQ7En@em;3SZOz)`$s1}Pz)uo zXdlezK;_1MW5^7|8Cqe!mU(%&5})PI{tKOE@trij0}80X$lVd~=KMp)K6g*-CP?OW zp}pi?g_;8h5cnCu^67FT8HY1vbu$-)69pkc^prVAU|YAJVbN)5lEoxW&#Mc z44wmtNHL7jvo&95scr_00o|A&Jty2pSduFJ=tG^&AvDp8}4fXT)2>@JFha|7<>S{ zHC#<>Z2z5BP!hih#B)rdi+@Av->_TajRAh`ucDukTGZ5oo}yqF@rCA-BU^5)@3XRZ z&G?iqFw<#E=GbgWq|IJXJ9i2yV|dD7NagQ&z12x%wQ@u4)bHi9wRH;N=o@=QKfs;g zs!FX;HN8~=v~e59L9FoUkJ^!(x02r@e8lKDc4i0DQ6K2CCH;z1eJ4-@cT6MTeHBN? zg0t@4{hWr!K5W_ifcrozwDyYmyzaI9bnMW@g;?{=(=^Ci06gV>3wTyN%ns$##;}$9 zE;UrVRwHM_Z}fc$sMWc4+}A9L<-H52=1<>F2pprOwRwxBao?XyP?Y(el(%9`R!7;la3S-~z|w{TM-kkWL7wa& zZk%=X!@Lyw=b$(~Kd;r=(o~X=H?Dd#^cIIp_sr<>D!#KKjoVvpZ*`BW2Jp)&e4VrA z`@=?Ap=eeN@rg?TUF3|f;w-@&Nq^DgyNM~)A(zlSy~-%x*e}p8Mt`S%_*0tkt9j=P z&@@hm)qT}a_g69G(`yxfVWq8|lF~6HmamNUSA_hCO7Z8wqnlztt$+6(r&>K`MLuHL8x&7t5RaQ8YfsISA zSUYN-(Ud$SZ;beQaFS=Wp7D8(^$*UxQNDZJcc{i4sg~TBv^r`LoVic&I#WEVe@jwc z!_7KGXWh2Nt%OOb$oN!pN{4j*`#Gs5;2jt8Wo>e1YY={Eze$@kExfIuL@o67)?sn? zwNgJf0qI+|YfR-*-;g$-A@!Oi_@ZE37h^@R|b&JWwxuIRD+sp>t#nx zD!gD;Q!>&Lp84KBWTeS2jaN)o5lEE&0Rh5b5#HeR+p0MKLmob)HO)esZHZ*=_C(147=)dw4 zGQ%&0DH}T>m)ta~c4iQqAy7DqE-uIH@M|t~`ScRhAh+GgObrT=YpQR*1^s)59mrgA zRJgXX7$&71&%bwd;VsQTg7Ra#i!*8OUG12%G<}j~=A|etG6|b`Hv4uvP&TypEc8DD z=-$y$3bR}&0=DPUh2nKt{#qBU#MD{R&mKGQmexuEi5F?@jqOPC;gqCSOcg-Ny&P-o zfac5ZMJdH+%blQ5crg58Yb0f%7!um!a#tKPGw0ev#4q3C(32!9i`;aF#UFus-{ry? zu3hbGF|8;%O3Ygqt}eVFPl}N~rpU?0t2z5V9{M!bd$h|ySt5Rx6Xey+FGsIhjs?)& zKnu(9!r10j$ldK^<3jP)1M({95#d2w#7xgvL0h@9f@PM|u#ao`@3}Y=N+EFIEo;dq zh`BvOzvp-)+N&2A-7%QkNB;Rl?ig7thYVQm1tl&s4|yo)YVMhl%6laT^t}6BIpYx@ zHZt?yxs9y6Qvt~(yk%7?LXy;9N?tP=+x*zPhu-(X3mK81P`7nClIVzdaYe7XA^&d$ zl{+Xi$;c)#%b?;3+NdT#Npk$_&0Dx2`*buRs8P-Wl(dOllGf~%Q%@p?6lw3-P@^ZK zIzw@|9)lAo(Jm%2A~g2Fv>JOX%=ZjRY|-j!bTds^frV`WbQKa z1AgT6O6ktx*P;|l!TZOHhpoLvOGwO?<*|$ht09%?0FmL6w;XzL%UMn7xUV7U_91Lk+5H%s=FwK`Em6Ogt|ioDM1j# zea|w8LCYIEu12vSWWDqkqcZ=7w{&sEO^g%Vp(o&T+iX}geKc`FGV*A0;lE$8oXrXU zZLrU6!lXDRU`ZlRfVTLQR0;UF0T)v7@4G5v9Moi?Z3N1G-eEQcLHr)8Zw6Toi>D?i-9)ovc+dT% z8G7|t{YzsUDeUB?d>)SId1K}@+s{5|go51GW45%*>ysq!!4Qg48uZF$8gFT)Aw<*h%WIwLZXar$j)-WB zSc`vS-Prs2TdbMC!higbh9!;2`Gn@bKiwx$9;RZ+Mh1VgxS@D{suPL*F{d~1r@L^& z-k|X#Qf+9x*FW^Iz{@JQ(C4KdRj?TTCMA0Eh!ZkphH6N$4A*JjhZA2C?Dqp1GCwrA zw;3FTKcc`0eZORq0Tgc85}Nc`cVi7333&hTQDXDIgH*)gFZGQ!U%`V49+L`@|DbzO zkF#QANp9ocNlHnEh+_s91UoPWQ?ag1<=Re79gY2MM?n5CC`{?n=uVCY@&U%J6OID6 z5RbQjt+|%AJG^Vb{l7|cTOiDh7q{ym%Fg2XpqP2W1H-xo#lCygWYFphrA4Q5QTOiX ze|%OIdxZ3=Lg<~@&==*S_b)Z}e#_*=6@F!!>b2=lpb-Uj?kH1{J3Q9Mq`kytFqMN; z_OBm_-^mp;u65O8zuqNiHe%H_YjEOv@DaB+nLCqsKkyYlKF ztv#aW8*@p8Rbg?|-qv34C<5^_9^zRFo+1X`5cv+UJUSX4K_wL<-fLfbYn`N~S=mup zwo_^^`sk*9ZoWm0fGv>$3)%N4mUFoQa+rquui$hoD-k|2pO$?so*B#k7^YmZkncT1U$?96~EI$Y_?3~rixZc9kBOH484r}pM_Jq{ zWh#z|;tghCF|;&K#U@8q;76bwTcxYgFb8pj$K}PA;cRbV%z_*Omk~Nv$Cy*9H1^Fu z14t;g!6EQF=0>@et@W@Y0dm<}|7@jdzRJ>bTzonLG%dGJhgOiNcAYK^ zT?)w~cz17aFCtGTMff59hBPj%+Ou{0PH=~UkL3hh%~-|hT{A|gIHvTh13_H;h<2N1 zYUy-}XIHo@;oyt+w+|Dyk`!PiBEVRj<$~&Hfdk^IM(VC)`elESaw=n?d7Dex+a%Tq z3-H_FihnWn&qCUn4buX-$;cGYq?O#>^Ps6w_n{U^^2@{JPtVv)q}p)9ULO&ty4AQ& zYcxC&x)thQi$6Z|HoO6Uh~sm7H-EhUnKUk0~7x*KD?p@GN-c#@xX{mROyknN*`I|nY z@uQgSPkRP^GpRJyS8y{UesJv!7P}2o+B*4-coax+IzS(9G|-smgXcq2Rj^2HoUtgG zvHTzAQs9fzF#V`=^d7?%GB2z*(>(#*32$s8vl++gw!2|4so_1xk1*O}7-2MKmkK2vUD*l1sniL=J9mEi|sv?Nq-1Zt%}DnfSGb>i+%0 zydRyX_lBT-|H6ls)S2=)Y*Ly8%URW#ix%?rkl*0xM4uEYD7Gui24&}=nSDh-*G~wsI zZlfNOr|7tLf}c|Ru=a&S#uAfXqMVWB1lp*th^JL|@- zm||~m`g6CN5=)n$k7K+WIn$9=ue~Ps!!0Ib8bgj@y$I3>63sZ=mk(?7zE65zeE%Vl z$XddSonWS8_%2%ZQL#D4=A`R67c(SbYn@(dF7gR&lTk1;O=@!Ag}=0&aP`_eh9>fK zOz;C70lm1Y^bb{p_BIV%Bvn=MgL$V>0x zThfA6UR9&n=oJwm^ib0)ZE#^u!K`k6=hOm4VbX9l1vsAjIj15X3}7}lbwU1jg#7P}H>_YDdNgdZiZbk1!^^0bgy6?wO~|bqH{V7V zGM`S2AA&W^?rw*jB)i6*zHvlCg6sK*n9|nSCU} zXm5K?=|cY5=JfYSWB3K_j%?L|dH*~ixu2kSt@GYn(w}tRVE0fgLZc0IFLZDTwt4#6 z;86x#tA5?XmRQ&xTm-O>5%!fZ(}#HHzJ6Mfp&YZ=hH0tLkR=6$2dyBS=W$)E5*o+? z9MG*qbKg{a=8D80dpXYv{EAaDeC$O)7dibDaNbT@4KfBYJ@M_b&d`uxAie zl)a9B(IIQ;)y^JuMq-vQnQs_BaR?!wB#suKns*VhjQ29OB2W6B( zR@Tl>%ZCMs>0)s;;)(zqOR8`(yeA979ZJN4m<*h~(ltzsux7I#Fo(#IJ=Lo{-Zf*6o3!UE~60BF1xoyf1T~J`e^z7%h^}k}*iC+i zaQ0pqKgwq&KPSANx~m9MR@W6=(%kEFVoN2$jQ1sVIn}N+5_pER8&qx%XBfLzl^FA= z-LLp#F{F5Kje19xm_N@%-*J-fAy4p>KT)8;<~5`8W8(|(^|vD@~PY+)uzBwscs0NyHvADrPmy{gq?|DZGHq0#iE zK9ni(WF2koHq)3o zVP0jtB#GiO@W%>zOs@BJ|F)aE;g$sTVs03$Io#M&r#J&&38wV;Yd{A#p0aj4hb@eb zZ$R7ijq6I6cl0~5T5VpOV{gJ+FiYSXyH#o)cf`HRYh1+v3~mZZyoCBn9;2Zx=SVM| zF2NvXu6VDS7xZmGXfHsvFMymtvb|6C8ba}8&hfE9WO8l$3pvv6N{;xM_4%vE)w-vX_ADoq&jQJ+*_492$< z=3(HGn(Bk`Bc7G8RyuKgpSrfZT2TRqrY}vm+Z25aB-!1_AvK+)3!ZW17)vA|yD`8; z7ZCn6@?|pz4&w_U#{ZRoWuip2e@QOy5qJ7teu@s`&&{V;p2KpBzNhA4w>Y$1Zf=mW zl{cc$hPkYrW)&R;STg0zUyQ1YHl1J*m1vKt4l_RQuf_ie4eg{!t} zgG`BL-IoXjo7I$ zGp1dAp$W8zQ#f=t>I?mGv5@dY?M!ajri{?C#-|1_i0KG-GUV2LOTHJI0R4qFpjXJ6 zh5Dw!e0=@hlI|EMP?mUMsZ2;l1@CdNGqH)~0PBnUCP%K2E|!f{T?QdHH$&LqZOVw8 zdWeM?+>Gc*nIO6-ooGWfs3zhUG2GTrs-*yT8!spv9!&W)M&r2t^hBR3(2r~cpUoN< zn6fsk;(J%!Ty3B^EAUTGT%!ZePpKzJXN@0>BXaEfA>{}nTtGL zeIZqSGo}O^9GfhiWzwfZK=TDaZ;;sZVuST^4m_VC%{Q2o<6w;QDaK^9`G)PMQrqk4 zI3?K6mSzz?Wg|tBo>Dx$!~FqX_LLFZENUzk&?$6CBqb?duGO|_0WsW=Crjvy3O?06 zMPfeY(WabLU&#%>efl&?Wj-vOv4jlx;YeT}N)mN*-Da*nmt&)vH4ggM&K9htxGsz- zr9&l`wa0E?giEWA)o9v9%Z9J3LCa+*%J}sz`>_$vIq{Zlcw#u^Q zTiLqfZ>7HCf4jHpbe*2sh{N^*xm`p!wTCj&*~So|SyggRt~GA#q)y)qeT$JAP46g3)A^S!XU_s`bMg)MHjZZDQrd+e9myl=%hJ(S$h#BO19(%) z*a3Zb-x{YDig01u5TDW_my;8oUTHf^tg)~@5*f&tQ3UGgAsUf`SX$xI4PtSMwzR*+ z1~2wWw6pVYC{>h>qL1z&3jMQDC)OAf_&_O2WCRgSABU#$X7Hr31s>~{;u0mjQO zg|G-J_AygY_fiKURHdCc*2W1N7_W3s31_K)W>x*>uOA{au1}a!O8rFIBKheqNJ!$l zeHF#mmo+st5xR>p4Wi@^UDEcLsSVglxY8N47h@uriJl}#j^^6wje=u$esF?)xGOCA zd&C&iJ;%PJNiq?4C(BuK(OgXX~PtY#-qN3(JSLn%AHmm;~|!9o3LCY|!H z@MOzDwd3F(3%uC*^%S&gSniuuQB*8h^u};IxpJHog=vy4tMVdsSQ&rDqMzb)+K+#? zr<)f}?WT=l`4jf77WGBU%x41^M_V?KWtYw_l+Sh<0s%24R}xIPlg~X?YI7LUTfIK{ zp))K_QSMws*f{nZYRSaW;R=Zvx;#Z(90Z6+BkX%jWn^SlP)i?skB_09Pl`Zyh>HMm z)nStO$#H7ulo!eb5g@p3N09i-C*kc%u~66%s_eD8h1$xIkuG-_EEu$Y(y@#xeD!2q zP8Om?wcoe|{_;uMREbO&9Y{b{VMh$X%e?+o5t7fm{c5r%BP~60vtDO3IrsqTKJ)A9 zJh^p2sR!5A7WSyjw zd9Dt^2wd8rALfYCqk)}?CfTB1LJXY7o^#(`!$>A=sGCe07bZ7ln2>^x-}Vn_t53_B z0b`8t9jG=oKrNSEN(LBYy8qH2kpvIJAtr7XqWpsoBn#{AkDy>i?SAq;z1eZ=?Y0#1Nb ze$U#GavWetncfG<&s2OM<)%z6ERA&i3MU>>*zL{zH~8ZLd(b`Qee~hu*SnGkmn=cy zCmGzBD{HGHM{<PYy zU5o?k7v48XYC6~XN5dF%Zg!eE5x3}EJ8OX$;HWPZPlaOhcR~;_eW;YVvFHl-M0g+u zAoUEG+03H=#-GVk0sHral&ma2_CbQ?E~UY^@xo6$!%H7^^a$b#(ZB)T<-|o%&IEbr zeL3MHaih;Zj%-65Lv)|)>rVnYb_WJ&XfJ@Key8Wd;!{l`q>L{ypV()kK5G?OWRVBI zeDQ>Q8>>Q{2RHN3iwyMxuUpr7(K6WoQlU6f6DO`$-<}=_!rr|dT7dqHiNO76~6fNNJ510-Vm6)a&XphGLUIQ zmn)D#UR}c~y}rRk6#Td^RTyKGcC2rJKI*^yA9ihVgkmoj4$*cAXb8em?LddN80sj= z22t`*4>oOtsCk8~$44xnD#9R;XK+yY*xDF**I0UEW{%7#(_QSI#N?PF#Qu)9(IbgP z&X0n^3UnQ+bk>%(M>I214=<(6$3zROH{<-fq+h57%@UDS(G?}e*XgTpSK4kS8{-TM z63`Z}`;vO6dHP}lhX|#Q{|f(l@>8G~@Ye%TPY%pV4nIvUgN+QeAzSzO8$moF_dHC^H0`ducRi?;0mFAr zKB|mP@;;;`D3Sy38PmXK>H@<1D@?cvP$WnH72EL}rA^6PaQn zAKQ);d+;>4HN3g_TZ&_vNh!=8IRw?A@@{QadwX~LKH!eDV%t)}XnZlj4$z^VDo1ZI z03w-;4}1X!EPz_({nmuT5p!E8cZ8Ag#C9F6n?to8+jIa<wIeJ5u6e%;qlKHA^eSArSojk~WRTS<=- zVutocWD>LZ$8AaN--M}mJoltg`k5aGjV0PCydTs)%*gTD zH`UN0)|j!Xw~6?8~8035bhQlL#Lf|TAlE|A{=iK!mFeb>rc?_+t1NPUC)lFyycP}4^-E$NG%1`b0OI+4V9M+E67rGp-I9!3q? z-qEUk#in8=?;>ohEyv;H`Ob1`{3!+u!A7pk3(AT3*norf<0X?>i^%;*{l-G287k3g zvmm*aZ}-!>vCBz8W%>$VI8j*hpMp13(>J%ul0(D^H@-<4<2@%tQVu;q7>|g`Vl07n zK-pNkQh?4>FF=QCe#k$xy_T>|o5lJO+h-C87xFkDbN?folp0m3%;r zvivdRi_Y_v8P}xf3ZZu$y5lLXeE4ReJFfo&{Xhc0%?i(mKv|qtflqsOgJ4Yd=x0f= zBY?g)XIB7J-yfmh|K?*(O+SW~CJ979NXuc67?@!kY%C;a*{3S8iE|g+ z9aqtv>S4*hl33CjMIv;ms71mQwY@=qmI+NUxMTQBImyaK(D3pAcBH(RRXcWj)We%X z7)N@}QHIk?ht9(conkZ!9uZ~)5U^Spn??HpV+jRkfe0_>L&IHQZY8C8v!kg*ti$fL z%pCkKVk88PNI+JJ#|fUYeOpQ7j%5TZS zZPBwK`l^C3w3;4@hFQn1uqbteA<0q_GJA@Pfehr^X-mp?MWYVENJFwi0#~gT*3qZS zW`@lNfTDPzHw8=RAgn4~v{$P7n|ilDw6;@$t*%Y#V09MYb=j$7s4WG7+CfYGdNifT zn4efx%A_G-5`sl{L=NwQHF|)m8->Pg1V=T`8f3xYVh9r!Ex@23xxuP&n(+s&j-f;X5lu8!Kx4oroByqjG0YqcMhCbfC+~R0Xrp?XL?9Y{ z?zA3@D$oHgYGO9Rx@-HP`6Xy|67Yu~VNPAAk5%)jD|>5t*d8mY-?7XEVCgc<0`YW+ z%*?HP?LoJFjwIeIq#1Wj0V*s>3(wL&U*OtR6M~fpbT~A^FEG&pnb{zMe2d9{6eD?m zH!SNBXBUF7pLWm%UZWNjEvzRLC~xnQLm8BTS?kFID}jv>AUZz|a#%!LY94H@3+FsL z?1QOaS!gzxn72pLXc0Q@+WKHtOKe1X=br;XgH2#IGQ%9|f!_Xy(_AG$=ceO)EKxDI zH{^YwXSN&=!ig&zqaeBG|`bqF`d>rp48|j3e`mOUK!b*24zG0UHYA3<&ErXdU$;3d?=-_SIr?+ zkx;GTHdty`vS4xw5tKk6q6#>od#0Pe^pCZm3g@tg>DwAfb<_dW#zd_huToIIR~=j3 zB)Dr%NoKNWc`61*MP+tCz7%kVqHhRGF!;g_a>^k3IT%krATfHlwe0;*&6Nk z%>v=Q=o;c?C2(011#KDz{cb1bwmIS_UAjC^it@n9(cyF0(H+eY*nP~Dd#;aG=S2Xx zsMBwPj$p)$bB<&+8qkBkp zw=zjb&-u5z6fhN1VbM!rw6U`g*fFoK;xMRTDMAn0*UcH1b8oKBER_0m5y8(19i(2n zVK}Sh3g6sI?K7KM0IJy!17_ng;Q1g<5#SG;i#1mntB45fi*HIzOPHG+Y<^bC*2nnp zV5Qg|i|tC`;ZKJdgwl9{lLJ5ndY4`dMg*W%YolW^D8gfCLA$krj0&bxWnhtoEtD7fg|>O-Psvt5SR^m zA@qp>>1k37Kbrg=gAOK{ftjLM!7a#b%25GIdZ$F3)r9Y!uF_5(DlYPXqadu+srq}I z*w^eA17V8|IEQ)D8r>@*af-@qn5Ae)T^IT94!Y6)4*X4J1QWal4FLcuY__={2XqQ( z9j@NNPD6ffDe~CckVNNo%*NKd8|mCFn|Wdq`iRafhytG4L%H z{{pd(f7PG=89w8`6pRr5Isy1U`CIO0LI1_Kf?4PPPhJ=QGrZu%_ts-(<6a@udMU37 zn!%sHTF#Ph`8)d;zLtzN1&`u7{KH+$9yumIDI$aGn&Wn3VBHqXz7cp$6@l%ehq7Cdm#W%NXU9X*a3R(!a`gIr^gv^j&#WQpp$m3J$yRe!MPEk=0*J2^ zh9qXk@Haq@NYHjJx(0!8j7;K_AhxO}5Mz-P zd-mxVbp!?!<7TvEg`ju(iGIu%X0(gTbtAf<9?8rA&Cf-IFjFnA1LiqKA*R+WZ9Fl$ ze+zZFlFS?w4T!&`ThDJiJ?1qFj3uU>=|B@wx4H68=4UD2vz4+}IV#=OK;7y_y#Nv@ zQ}{Mj-h1=5qv0@QHK-wU$tZ0T8hNyilA%LjR|3(A<$`knVBZ^T z8v4?HZA}4dl<^#fi+cL*CDc|#10u%n+^B!lcY|P+z`3+D>WF*(St9SGBd{SN@~!v= z_De>i*Ox(e0nOa7I8)l%P@th*DX}69#i~i=4m6r|a5xk8p#d#b4=T8{rd}jENmicz zVy?|Ww?I&XW9whk|Hg1QUqlHNe20nqTUSY6?B~%h? zrY(_H;!wdc!#PGMP**je&mpu@0sOE}`p2lY+HNB@N1VPdPX7H%Sbk+l_r^deMq1h5ZF*ddrWuPU1wY-o*Xp#9&zp2<=**lA*e`LBgC zRK}l11VEeCrf7N6SBn%)K=hcTLZc3N!dEpUOA*CXI#~8{a0UyS9YV*mq8JkQ&sv`9 zmQ^^KRUKaP?vYYE;yb}j=Z&G&Yy=yPQLDj9{NWj4)x#&;PMys}HmLqfYSDz>QzAl} z=Vyw6bT$BsEr$Nj;8pR|Hy8#&N%-t%wiXw-hEGMjl1h?p-1i^zLz6i{rOz2%m64gUjZ@Uz#py?BWLd?FNY>pv47bkGe>UmZYevTM?j@ z!gp>N8bu;u43wCiG&qmNK7}|J-vvc+pKg-_)+>ivr_=DNE6;x&bkJvRfL7DrJFE2I zo}72Q=Jdm%?$`opzMEGXU{(HR-&hThqGear;6O6)zy?z^3(WTCNP2faVjZMWs0$ncl zbwh$Wnxd2tVpG4pBtsjj-^V_M`|%n>cem- z`l+g96IdGPPA&o|38yWO6ougE$kcS{(ihe{{rZea2MC=) z^@>_OjmA;p>5>igE_l5SAcNodC>ztEl}W;y*04yNg}}0M&r9_M!^xZRMk*mkFv6L$n}; ztU8nIgkV*CAnFDyO}G|eah~-eiv}Fbt>)Bbdo}g@8=4u)rP-CN!!k{`{0CvM?>z#O z6WYJgKcWrqLS;qPx-HoYhMgrDSs9eI2C!7TK9`>4t=a@At(y@Hczwr@Vv{9BgP=oA zUCfXfr?W$bZ(=dfXRt6iXzAvLF1UKEJnANaOpVzCZqR}!`b<;MO9Y&!SdWT(48^Lj zV}ONLtAkF2SB4;!a|`CwWIC#X&9*pa)PHPzH#!7_=hWp`-k+}2*!P-MPGEKDS*bwt z57O9(uz|AeP??kWsGx`tzQY?c=0qp=3V z^P}&}`xyDf$$~jR|3mx1lEc*?rxJl66u{QaGMulR*{G3sCjB1qcb-(yOYfBYVdt?M z*Z+38J{*Q4&}OK6WOBj;xz}fFmLR(8HUd_Bk`_2T!838Qyn6^=$Mc0Ldc_kdxli~* z15GsB8w`pYh`q*CzNO#q9t>EF&wpjh+}Is{Lbs7H=0TMGUZ#;%Crd)|r`gR-rwIS| z_u+6HuRzzBE?$O2!+z8_1TIuWH!)!!uz%J7uE@F;%e#l0|NG4i${>>r7lri^I>_WO zdl$0;BRbMF)NZB08(xe28aHgUuY2O5&xLIQRtDUEiVptsae2)awssS_5Zi0 zo5iB-5(8aQV3q(e=E;vLdmHoj8Y|t16#i}>p4b1j{J-q?@1N5xFniA7&1P*MD7L99 zMw1O5@(T268@bdp7R&qlr>Eyt*uQ^#ehs$(dM=N4n`Lf+K(m$v6;GVZ|CL6%Yurg$ zX90`l-TmX!^D6B3AD@SE{b6kYgf8kM4P|RslOCPk(_t@=%2DUr$5&D`0I;~dyMKCG zt-}8O<0H$0(=KjCc=ijoaqhUKM6p9`exvH6m4GYzSKRi9J*6Xm_4Iwav00>xronm#t*@6I;(&+%i1DgJ! zv$CZ{oObjz5N?)t50B5!>$v{o6%g2a`SDwJL;5t3%05Spp3urH*o`C4DsBw_>-Bp1 zzi;1m+x`2;=hx|cQ3Y}MO)c>6yf-3rSze%BN7-pcI{xd;Cakx+7y?P9a%M1sz#sdA z5OZE(S`Icd6~ZZJuvp$b499=H3ESOvzkC1qJRHv#F9Y{PYt2*768=+0Xx6Y*s-0vV zW0|yve1Vnvv)#n-zuAQKZoAv>;}u|UF|czh_bPN3ZzjxgpLM)9*~SqqZg1}&pH{2Y zX0uuT@7wiuz1{8O7NF+{J&&?r;$qpr7g*v1ZFR?v6}q{-eRzC&UadEq&1Ms}+wFF@ z+lgDSFt=^=T=B2eLemK#^!v`x+eZWUW-&hh@B+r?-@m_qjn7Y*=N~=o>I!{pq;-_~ zq6{XLMDLJ!rnpef@}QcvxV?KAZoik|`PbXscE|$CLZCH-Xb~Eme~-G|U5WX2*;i@9 zN;wQ;absa|I~0HG^=7l#EdRIldb{0jrw|Z3qJ~Ve%iKc`B$BSau!`45hj*=NOwdPy2%&9Fi(sCxRBt01sFoc`iyBPjo;`6VkLO2e^y^Tf@fV%(< zP_BQdFUF{RdNsRI#L@yc1B&Cn-Yox@)q1ntzOA=AH3W!h1dF?x=&{i#@(F%NN|{<) z3NwHe$dbSK_%ECFX0-~d^?EZt|2{rHLd#3k@?6I(b!7I7^+G-Wda0^FIEEi~laBvp zvstfKtFT&ccIgWEI-Rawe1|$v3?}=Al?Flhp5uC^3`vvqLqkb%I{jGO-aR~}@V{9< zhjrZlyD49qkhJbB7&sJ5`)@3|sL-v4>}S~jt0DVaZ`SM8^1lzY z;FJhvw*b+=1F(=ugfcU2D*IU5!1F8?st`0IKfsX;%1Q`5?S`Qj_Z$~hp&(K{cfivf(@IR;yEr5yzlqJCAi)1_xUZT=p$4UVTM`wM=dW#Tl4^- zmcFMH%#@A+#rcnezv}rv-}n3NEj#y0*;@i@FK{ z0k~P-Jv=?FR_o3BWqAJQr^own`22j|@3y<`_HDWXPGbnL^egD0uuBDdjed?Y1z-?b zwL(9F$`IP=CotE)EpQ*h@n283|7!L0^l*>&%jxhr9D!Yt2(nWpP%ph{TKmv(-AP7*-*ihsK=}tdQ4l` z6%CXR;G z&=|GDN0I4CGYh;G0&Z^a9v+`pt98o$o*wS+Zo=i5U4g^?|N1&j=tv6l71;wc9LMO% zfk4O;Cb8-&z(DFl)CDlAE>XEn66$f<|Lbx6A0O^-Z%7 zyO>`%ZmUMvL1C_pna4|K07)A}m8#EQZ&! zTJOhFFm}ZG_voYH@4W)fEG&oG=lOXkeKwnQcKkyK3*6j|gQ49<>4nT*mB0Sfe`vlm zCjYCU_Ysr7+vR{Y9j|~q1dP3q%Y3B6Um+d;J+=AaM~(Y??EFvR?`~Xw+zwaZ^J+ak z|NDF63YhyQzWYeT+?CfSU-uFBFtmP${f`g=K0^qB*UKrd1=B5Pa!N(qGhOW@rM0a9 z%4V2@s=8n1nXv_c#q$0>J^pa~AMb9L`48Zb2)>WKfSv4xh|(XvD+yF1I65-`G^PLu zo&%l+8B2|lY3?tRzeV=ZI2Ho?ED;{(;*d2LdfTAiEdfvi0iewLtgRyCxUd+H|7QI% z?EjSgr1d|a#zJ670<(5#;i~6laF|9B=<=|3Ag! z9|J%LF$9!HVCn^+S{xcOtq2aHk`486ufC9Sny>=^Q)gwdyuTkhzxnaMyOHs1*a9D8 z2;5DD(CJFibc8$gM8Q+-4FK@eA|W?66)GSqDVOmj-H6uT>N$qL;@4d-r^DCB`}?j8 z0jF!I8vq>|?@%zsRRExxJHu20kpX~h0y7rN>GppaJO5+$BaY4WGW3FWyWMt|*8hAF zCi&S*_Na&CAPXvstai+h36=xK3BVZoC3^6E24GuBJ8lH3I-O8mKleV>`tV7Ph+P z`*i&!(DUXc^}e#!#bJDeY$%3GRa}v{MmxPngD0)D$W>P-=mB6_r$s#e zsr8qRe+@|*Zo#w#QXv3+ybU$Ep$Gu+fA$zqqt#`oZc{4&U;@34o$qzl`Z3;koCs{E zUcme3;ds6(^+i|LQV9U8D2?JrqJ;;^qX0eoo7DQk9uOQ(93bH6PEL2q# zmsAZ+5CaZ~f?npMofa*T<}W2~ub=i4U_ zgx}}&dcAp>ir?7!gFa6j)_=MJrVua=MGGkt`}}vLv(=QgYHYAk2wSc+S$=i1ynT2W zuD_Sq`xuMgbQKjDz_!B3j|r{hnXyukD6@cFq< z(4a8{UahM=bSVWX0YJaLq4-`?4p_Sb&R481T@LtLj#~dy`{lVI{wgP^qGagVj}XM^b%ld<-0t8WC4IRMwZ(3 zKnrAQM1U+q6vsc!eW9t1mYjd`JVL{=iO|A;B$$H50i~+ZBzwI|(r!-nr4S3BRdtX3h1qDeL9YVttg+x5}~__FW(i@nFn6C|qL zY}PO1;HT*PvT-9-bMXlmv3uWte0`;%s3yVR@C!BP+tn+Cba*uWOK|`nIS=FdZ!*+h ze*DqAqKhl=EdxkX$?h6d(RRQM?x43R35p!`cbf*k>*4uh@2g7x5V8PJ-mnbUVJP&z z+wF!Rn1ukkTv=FNYCXE6^!?#oea4dUmbL!S<1Z;Oi|45pEYXoBO$rq^h`uN|lmGzF zg@eWrnnmj`8+@hWhjI#M?8h`~Pe)*U{oeyRx| z8Vi90jR+LB+4cGNIh*j10(4AJocn)F{+_br?c zUyDK(r02%YhQdQT{H>r#^#%Y{jyrf_z9?{P3RoI8=?jb7QR^|{KAs=P!6)O779n$- z0$5qe^>Um#qPwvce9w-6iBWC^022s6&%Dh&|0oB4#oWJF&atkz&!^+(=ZFq`+imp_ zU^o)c5dx)#1%c|X+Zh0|+kXVT$J&pQ&#FWis4*h!|Pxif*r-re!D3yMni`)M(9e;L+bEvHeRsiXJHW5*; zuNF|9HqTvl7HdQRO~zLwe7>a4@13~)X(8-jWhL zif>iM@C6qlWlz# z-d8ICFxO5?19Ij}u^|lZw%bfQ&k6ttjU3VK3exhYfN4EcAOE5A3&^|A>&h59yyZ|R zk!7VsX(Iqous_7EskK+3?GTzm-$Uzf6X)KKBkGNUA%-JrAl zm;)qDCJO;hKyL;BdBD*d0CL507?h9U_#^yz0LTE+JC!UwKLEg*e!?={WKdFo=~c+u zKNf#bRd|*AA9V)60vA!zq^c}X5vW2R%e@@q%7CZlfT+ZFOW4zTGZB6l9fr(P4`7-j zLv;cnFQ>!d<71o%OiZ~$yal0kH(2+$Xa)f4!!vu_svQ4KIrxBiYqBNXT0!1wn#?d4 zR#E`Aoh{3rKlP-p`uoBvfw{}g=<<(#+(e!KO+NR&v2?h#4+yOhm2JQ4#SH;)hDmx>2u~QUlC$(G)?xoY zXQf5Y6@ zE}Yq9^2j8Bm*erX*aE^wpgyi_k2#EHSR(-B$89?PtIZ^I{v5@wGVCz*GTaee5b=2| zgmR{6>WH)FM_F|(tQI%R09I*K7Cl0L|kc=iaDAj8b364ghGx+iR8uylo3B5pXd-en?hI=L#$gmC|npdVP+` ze=yiMw3WzU_2AOB$kA~VSzH=w=}2wI&9of=N)T{5O=Ih8Ee5|?@2AQ$P)-}*;PMLR zM#Ns&Fcf^th10I43WnB%(()|W$adQXI653_Vt1^y=`_3x@QKahG-l8iqEjA7(LXf<4lj&L~_J{(WEYEEl-Ps=k4RXBo& zE1FzhlFomed`Q(qDpGva)`DG}MS#N9nuyQ>ZZyw-ypU@zBgp)j`TeM5e@}(hAuV$U zsN*!af`df}+|_ysp$P@1ub7Rp2Vl@F#qp1*&+Pf3(p+f(9`Wh$&lRY3ss*=&UVvza zxdA{yfiW1O=%pKY1{5jgp5x<}QlFYGC4J}`RG?fD6sd)P{1(vV(5ZwT_}XXCXx^35 z?*REuQkRQ#qEJ*ktnN@423j_uU6TYs_k0|HIQ6dG3N(D}JA)K+*tebsJ4$1~~aA}Z)E zKL9}P$ZTPY<3CM49;V4h6#&W!>tLD^G@(W4gbb9)eBKVrL!fQyPS^7n`Sb}59W*Aw z>*LA-eSa?i=mn|BKnJGeXFe2u9}3|MLrbN@0>eUPd^9abGO5p4{Jl)sUqt8uP z090_NktrD4ffG9HN(Eq^&7jbgCK--5achs!zg7Fy;zTrB^w6PeMpvBUEs%+53)P(7 z08o^;3aJ{UGfZkDNsG_y_CxlqbpT)}1AwWuZh`w*Xh)VI;FJD#eMgo3_RN)23%@SIV8XsK~)p&^Vt z05JjAJo}J~E)4+5I1wnJAyC$jx&SDZN=4}btMP`SP-RuVeTmDgfGsD1>68)MpHTPa^q+Q3B8!$jbmgBR5UYQcOg4+ubg?=@_UB zZ?I+F$51*L6XSD$J+4*C7b^LQl28YGXaQ{&#q3jZl#h+;zZ=`(UxyJF;ZYjh@g5Ro z#}ot;#m{=Pnn2&MIXCta3{wEq!nP90C6Q_QP;4hc0F(gEx~IV!IZTTWgiP(`UYLAk z%MV{@kRrdOW;3Fp04h!!N3w7V0fn24uG2^x<)FB4K$1zu+V{S^{qyZ#kGrejSCeu= z6@SX&5U?Z6h_zfV~pEiI2e7BIX3Fed*6mkR@f4Pz3|a@Gt70#FMnWDSk$aj-d6LD}RL~^*Y7B zIQRma27{FxH>@I@I;2f=)gt*uqsWIp!o6w3ZI80cd{rX^LjD>EdbC{ zDX_e_Bwu_oRJzvZt|tJ{v0WJro+rZVy6k+ngsJEhW3Uh*T1r;VRHJ0ZR`rx?27q#b ztA1;NsKh9Cwq9?>@b@HoA4&i~W|ioTgBpdg=JSCG8a0N1-8OHB(Z@Ye`_Vd?c=}11 z!TEY1yjpq>!ABe$c@3nN9j&jWFjN~PL*BM;yRF#&wjL7baeNiwlpOW9u00N667Lwi z6aZfmU8~mU`tM*R8L=Bgq~lPqxHntNL=%QFnp{n&rjcsR%hbt{{tl9nNJI081QN zKcC8%Pse{fX1TnM34yB4M?i6dK^hT?DAx9!^`qFz9Qs9T}f`Ih3_P!gUq zgLfsRp{_~Bl4qg^0G)1c;KW$~M6+99(2*Bow<-WQ+VAB!V@iJF*mIK`e8W;1g$2i* z8vtlba~U!P*(n5Wvv$}w13-OQO~-$g5597~J1TNLLdFJUK8-Pt2fa&D4**F!Zv@Hj zw!2-P1t@2IUI1XG^t>LVF4ybT^OKaiv^+TxjI63c9l{JCo^EwQ2X2Q#V4v;(+OErv zQ?Q+~2C=W_=TYoO4nE=0?3F2S{-lfWpY~6O&(eSBubuG~Gyd>D&yV51^g;i_m+8CD zfd7~e*10D9+dmZ57!VGH(WpqXJ5iJX11Lx@0o+|mo$flhXyiVx8-e_X?nE}f1dy~~ z1x(EaU1fD*z^cN%VvtGyB+Kp)v4dXjaO0G8LIn*m&tWZduqWC>7Tc~tK~^p*fa z4Umn0Y5ItIEdZkgKtsG$E@4s)d`ld`di{%LJmv=u+$^Khq09kbb`XL_rxKmlFH@UT z%;bwVX*j=raCK#<`o-JMmBq2y;6UzX=tO>S_9!km_ zpeFz*vK9FU&?L``GDs+=A;XnaXdq|z=>Y&v6Ko29(Mj)=qXF2U+t=5O-UPgfemCn) z)&yLFEg|Jlqk(e2nQ8%8E3>#w!1w(=HUZy1KEIBaiWv}u=eJKfgEof9y8y}Xr7*lf zur&2dqAk8m9I#HM%vYuCfp7I5c!P!3F{YbV2aDVL`={LWp`L;hC8s`A*!E@sfJf${ zqM6%90!&>vHUof8Sf>a9o6PaAkbWu+Ld6i1-iTSfiVeGxTrf5h2^qm)hbR(4*W9r7 zd=UWv(gpBlc{^ENt)u$EU1oU&;}^nc1i}Df3F@4slfn!Vo{s^q_H2tP-U|A9$ni0HO*UiYezbzs)zxSIg@)(+(&f>N-(_E7g3cim;lQrLJ?M2Y`TRc`X$Hib*&c z^MznV0Jz^?wf_V!Za2VTYZK9+&fy662GrQR3>62i3IXCA)qX%!J^_M}0Js6*TF8VA z+22=c{;3h5wn;AZ4}xO0o1+*L5NH!^uO2hgQyU11BAIkVk!n#qEB;+``>=HUBQ>1P zaJw%6)L|`#fX!OUpFs!osEKv<6{8>sTAd_F(dKKB{BDzbocil&Ie$RhifkOwgoP3z zV9`APz9^V7>vV7&nO5$0_omQ-p`<`A5CAfHujDjx7W_&h0%@6qnqN^9udYGop7&{P z9aovA(3Qhy)0!ke8Np;((8JT}S@uGJW@E#bA2C;6&#*JcfL#FvHk?NtB%tjzuSB)7 z5Eu)gyW1tI%)-84i-r9PT@e7Kb<965?-0LtDO|KEg$F57{3sT~MQ znV0&#E^VWNYJ<)k!BUWc-h^9#@Sx0aK&=3fkMJ!r*Te-YjzDJrRD=)cqQcYy05scL z>QL!QEx*zUR)aZIS+oXRpCX5 zibANB0uk_%g}^0TU#Y4mJ?)iGiSpOk`o)r5brif|nU(rq$hRO0@{`f6QvyI@!;5y{ zU5<&;=}&azc~NDbGBl;DxUBu7K(g5CoN2Kt#==NP4@I~Bj6ca;c4 zvx!^~yHIBb3EU#N9ncW8sJCxxW3qh2P+7SHjdv{R{V87jN z2lQ`g8fw}bDWX8E6?EGHVDbTa&U-;g4$hy4$bnoL0O$zzpwNabZL@P4u*yZCiUdQS zI(QubSeii;a@bw~@XSi`gQo|8oCcf;W5(#upay`x8oWr`R83Q;C=RxFa=U4Hxx8WCmD$fR z>0U9!$fBV2lcD-F=m9|Z(~SJT(lY#Obcz!JppF1^*zp1Y05g4IsR=A@fxF2M2*L2H zqrSPF0DuzqI}Lu{GEvYgTe|v{M6*D}L*t$2SoYLM&8g8@7PMZKc33r@1K#=1%XVd0 zQOe5jg(84ld$W#*_&pHcDlW{?fFU)Wi`=DSjbMc|^zznDcwYm4#sX5 zuVrTiIS#iZL(#MMR#||IEp3(1JSZgSApkQpx@FaXvMGif^u5<4!Byi`_S>67hn$Hv z60_qUm=DD^@(Abwpy&Ksh;d#rCBVrfw6cfmvAxzoW;G(wj305INN0##Ap;KBF7>5E z0MLizBkbiU3?c_>ECnke2;au}H+gsExT>^smG*U`H_U(x$rR#hZDTO5$qLj~n0SA$QmC#hIj@NiJ%Z!B$}sc9~TN{3JztCkTpy5p@Cd$1bQZmF)J z{^gL)fbY9f_(N~~QAecE2>{Tv@lb7sq5>$_KhJ`aOc-lPJIe%03iz4wrA8}UsNOA5xwtf=j$zh-qLiyJ)MkD@^zI(Zxk5hEY z{c-O~tt5)K4Jwk?`bH?X|85s?53@M25{9u=FYjQp~z5hX&Nm+SfXHLiaIMm#;<-QC#eSaAB!(jL=H zYcYvGj;O~X`LS~+=1QLi*oSfZQBR)0o-M`8;MMTH->t_zFrdShg;p%#j0Kz?b`E=q zo$rbA#1_NtyN4SyzI50E@B7_q8*S2_pB^5jb{IXzKLjEuX@g7=GhzOBZzJJb9ekk? zTk)r&1z>r>r1d|a4r6}0*=%+hK-dsDLk|Xc62HCFE)XB}^RxLwXRkjyzbvi)db`=Y z>^7UV=!MiC6E?1i})&4tSg;tx_;QJ*2 z9eTR>NKogcH_O{`|Gx~)u$4qdQeMIXH}TUw|2&_j;~xp1GWx+*&}@71l(CsQXaXfw z56T5T(u5u|x4c#A1)$Xjgy9b`>Hbc7PxM!t&hXk64R zrSCs;%1<`=pbnt)BLFquNCTLmOH$N;DVET%!7MO$%zy@Kn*zX?@zrkbATP1=xr?x$ zD*1t-LTq3U9mfSl!7N6210%)!ecTin@%3bv95K=`)0TPHS&BgOVK&Zx)Ri1RtDw_l>VNaPCw8mCtOIM6)wg6j}7@>k< z=jw(Pb}sXa9@jrtje-V}hLim>;vTjo>h(f1k@W?ygEnY)kXa~vN*Ef_=8I_}GT~yL z%PXKBC9@C&k?HrPRQpjupVj-MHbyJDU>yNK##fjSQ87%IZo!pioIyPs)nqt0&3(u0 z=WUw%7MTAkw6b6oD1?40dimc90E&N7G>v>N3jv{*0AmaogBJ6-_W=9ZWwmcL_(Q`| zH~5Bu@HNO4hx0O-DuPEW1U931@N;Gx2bJ;-qlzh9FW$!epJ%^xrWggFD7Cyja-N<7 zAloaA05GioeKr)`tV`c4EyGTdU}B36wa-U&{4br7sjL-sWs+FurbPfi4o)&NWJNL| zWdYGQ_ij{=hcU4#$3!gWzPnND_T%G_Yuy!Jt%&<+zJ#hlVFdz&9RL&qr4sfwL4!t` zR2>3f4VFs^BO~K&mr=eZ->0B!*Bgdh?gU)UKWWY7C}XJ{@@4D4S`7liBS+$4#6`e$ zKq;2;dQM_z+VOYHB@o)yh-~O(IRQYCHxxthry3fygffz22muG{_)nfMy9xI3^;ORO zUF}V3ynOmX3v~*CV0x37+YBXHG}44_pfgg7d`pFWM78_U_9{QDRI@jQNtiDrl>%H% zPoZ|uj4qr?lgdMYWqt#R`H#+rCF;}1i`!Cnw7(R>hfR$hWdcw+?V+^J9^^x(D`3J! zINPY$c|-A+i`~h|2OYjFS|C80$xSWoUI&0G0{5Bl;tmn-=`OvcAjw)md960z_WU&RtTQD<_M6HWbUVn-5QZoLo*PuIV*1;LU z&H!l9KcK%^Sh_M5=61+41ygLm$WLTxRjfwnkX$9t?axnFHnF7Tia{NEu%$SuNN|O!=TYl6vak>v z3Za)lLRoq!l>tB$aRcg2B>#tE`C+$t8jOb|%!w8ZOT2-~FB4AxHZKHTN)d7W)Mgta zY2D?u-y!+l((G@;R8g-p0P0;|nEqUfGMQ$yDpg|&2l++aS`!@r@AwxR)PnV}zXZ?& z0FCuklYpR$9}VmOB%gn|fNY)zNm|LWA6xCpBXi{EQERx+;R&dw^{jepFg+V51#RIX zm}v**sV_soLEK|?@>4y&jiIES=+Pkp7*~b#jYkV$!AdymITucj{9V4h}7ED0UO*_1f#ILTD$(LTF)K-eUl$K(Bf4$9IskG613x0E)YlMu0De zt(1X6{tlrUG(P{G#*rwP($?|6vANa+hfR~B=BQ>3*ma;D&mv{8@0ggBb0 zH}mn`0Fd_==mLVF&{S%t49A@W-uB2l!kLqN83HKZCgb)OtxxLOpinXi6|7*j0X?1y zLWh4!Ffmqq7DA}uT{H+bS>*3FfrxwMCh@=NGp8rk@2`IvmZ<97Rok;j*g+o z1ln}mKMDX58ZywNa$G=i6cnXPYW?oE+wEIod^);$P7(ke00iv^se2ucuOqE)Lx#J^ zZ^4(0CbKXOPs)CC!t+U2`!RwcY(;LU=UmfIgxfrdY1cS!v27r7@ znAU%jvVez&TQvkkxer(E$oZI|f!-(zoNjwZsP{jGkgqY0Sk#gsQbGBsFuumefBzbj ze|FLGWRJG0HK~-#_P&A4tze@dBwsW;l~OVxN@l2xzmHt)CsTW&(utPRT`<53RFQx* z?6}M^uaCE*m25^swdn{v#}H7AW8(22lOIX>^6e{n0sw1XGs2tzz|a){ik&cd>J?eg zqp*?*nb3R8es>n`#h-OJ=t9$&YL>sNRykV%AkPAf5HJRS1bR$bX9@My833rT&8ea+ z3{aow>ef^XSkc3g+=?M!Ro;RnQ*h9^f8R}vM`7_O3Jgk5?LxN%wL3vi00^!%M(q<~ z&5|Ud`zF&%c#*%u-BNfVvoK;_Nu3Hv?7gM+S3y&$C!7y>{Jddw&@@> zn`HqKAc_q4P_{l#KIpsgyWJGQLv&eFRu=F}#K3 zPpF^|>}CC8W&kh@6#QSO7a)+%!X;BOt^ZOS|D5u|-rx2wpnm_&KfGK|hp*AdX0v(O zW!iy-!|@3FcpoV*;r~zmWRelvO`#yWLIQQcoPSD~pY!K(IUm19 zPVW>0hjuVBzJHv2?A|{oZ z=OzI8EAcV_247qZ&0nPxl16~|L(vW&L%{EYVsmlQgr1@QDt8vIZ&5FVXw+0RoTEzJ zXyg4FAs|fI&s)*@Ggu-T`7ACs6MF@04Pf`>Lw?3DeOz1dDg!;z@)$ysk7rz2})VUgWeD5hH*9^i=dhru8o{{~smc7u^%U z&=wht%4ThAkXZr1@_EcL1ve!wVhjQJ|9p&d?@{e#V!WxQA~icFC>%AF+K@G78zcY( z0~K$L0i;f(8DKR-rBHYr?xVpsf~w0*;VD?i060WVN)V8Ph|<+hp-ZJ`+yd5uNu`@i z!iYcOmSFws26 z|1WsILa4H2#!<0YKmr+{=*$=AUjvh#NBPX%dL*4?SQ|~-h8K4&?(R^m#a)ZLyIYYW z#S`4ExVsd0End91dnv`CxJ$k~-}^tuWHQ;EnVr3_^JEEInl9ApBK`)_YgGt*9OBil zbKJOrU0G2BRSDK|%uU?#-oY+)P1>~mN3Wv#tTLthv{ckt9j1FU?{%!5N^>+h96gCY z<4!noFm2NfE!FQPUhZw~=&0Xsnef4al(nB!GZ(1*kei;`+4Ie7?*2-v><|XI=dcyF zwva1};ZjP$C9ia-F_UnigRi0DmEaa!r5N|hdAO3b!Mftc@xP#cS!^-!s_4DIyBURT1{E-sfp#G%{#q1sGYnt+^#`m&&q-5XOYIh9A>J!7F&QX*dK#aG-jXAOa=18Hr3Ff)nuXK9moa-*mFJkFd z=k1&5C-2okoKrfW=aZv=)X}8Zn($?`i=-J zHj^HWOB4p>6E>(g=od*AIz33vVBrspeiBJ3{7pyGv*|Q&_c>i(*x{TjjH7dx?Xux` zmlEMjy#;4h;LN<zig zVV9hf@;)CSuiVTSc%pm&k|x86(4*kq=-;e^%oLA_JmRTL5jpCXdEtw%a^rf z1IHeF32@^4bRq7@5r7sy(CH@IeBr^oD0}?=pzOWP0K4rsmJ$7^3bjvNb_jwZK)u3w z;ih^+SeZ~QjR?Rm1vw5!O6gBd@vm-bhPDA`qVjJ; zce;s+_tde)+?xAm_$T9`=a5=juv(NX;?nvYGvAk;YS+$24YD8{zyOCTf{WCNnB>o6 zCvIcmL%&PQathbfBsV`nJ`Efg=W`M*wPW?Wr(vu5n!%i%wBsiAcoX%H<4GrKY|a8+ zZ&DIZMbs+Q{UP=xGMhMHD6pjd=LZ&s&Prdyb7nT@%RUOVCV8Vo@;QpyvZ8~(N8j=1 zNIY_fpw=@#e&rTIBy~6?!7KE;o$@0q^y}f0Fxt{g*k%v0&098_=5g( zseck9$98RS;uwy#`b;;(KL->d_5UTXm~vzh_r|8u}aqL0y~SKWep=tr_Y&n8GOw8YB;l{$b~kGU|(<{rr@Acm(trX>n=8@6qP)v#e0 zd<&DLf1e;`pd7rU7P4k(^L~ZQ-?yIP=2Q=^Q=J(jd$h#b(?UJDX`iMUl|jv#A*hJY zsZB`>8KH;q;|`eZM(Q1(AKI@O_ZLOi5wr8&$6Fj>RKIoSY{#&X&&j9{Bw8CvS^2$Gq&jAUhvk*br0zcj$g(pavf zIPp5WORc&ilC`N;KFEVDo{(x)6PqW(qR5k630p4^Z5XvX0S2TDvr?uUkE~qJ;--cCDUej zN#*32uJTxMVf7^-`w3QiTB=E}*w}EcTLJ&0CarYw*OSO+j27XJ+7m3TU)DO^?9}^H z=u&2nhE*-8a|gN`+@w7U)%7%-du{&m0;KZbfvsxi=7!pYDg{-nF5WGz3n$GNqJdj;`9{^ACFCxwjLo3=H?KXfFM#yK*TEqi;E1ZA*QxO_O!?OHm+%!<5RqMhws zMZV5a1M;pT*3Wy!O~R!*_}R2NMfZVKFa{Vbxjg4DKSh z07?Bh%<0{e{{d~=PYcyB`S-e#BZ>unadutL)r;u&rRQ|l{>oL&D0Y^|skjSHE#+!Pt3s?V)Q~-))HL&RGhwQjBnZ@JvEs@65`#6T+ z(J(SRRLAX0r;bG*wxqf zq(Zq(at8G1U2BhJs$QVgX<}nGNs7wj7em5d4|yvRS+W@|`hpiZX%=WZ!2-Fj&A)7B zLuGW1!(d!h3At9?~t z=XvmC?rQr5nZc6En@49#ylsVz^$!;HZ*oFK1m}zWlfzf^?dQMsav?W>EN{3%Sh^#E zuzqyGA)mrZil|N)`U=uo!<0vyYC)O2Oid}Av5a0I}7490R}PyX7Ndy&4FR;V~|m#GaE8!_wZO|9j2nN45# z0zUW2Z5TPP8{qS#V*k#vSV%$ke&=R0xAyDnOUGdj!)_Mb_R4%kW|D|{N7*XwMShbF zo3u|>D_5$WIWY$Hm5afVb~>L+6W1@b`|+a&`0*+tgE*9|Za#rtb~nqJUioqQ2KRql ztm)2+fR}I$O0P85*!F_(i=i~Z4LQyL%{PwQ#D$*}+{MDssuAc+=4y7315(9EeMJ>t zEG$3Mh!GXd4Pc8(D=PH`Iw@dLr6{b+uu4VrP-FH^q`ZSa%xqzGeWCFI4bH&MC#q$% z(2Y=sG6`HLj~(jqwp}%#FWVMR1pt(E0wAYwpdM zGEIlrSgFGzS}r%3TB7WvX2Pv+?ZIhzJctv#CRgIkTr`;P5D8@Gb9cww2wt^Wp5T9+ zd|G=Ect*5}sJTV=MN4%58Pqdj>eeXz>; z2OS}ZnlPw*W*__7uQ%(|yZRBUU-nwQJWiDi}b(AIjZHUVtUh-G+pZ9u)| z9UV&eEZ(bGpCoZo&t*mVTB3Qn(Hf~xbprNUG=6;>`7WWRvFx+yFVX-dK16~Y$f?+a zkkBjS=k(+IWR{2Ni+0Fy`R`uWgBQlfhL`D1T`Rs( zXLK@fia4Dcg~FUagoPEOIfAqt!7wk66nkx0S$q3^Q$ff%=#pKhe_~`HEION(TG^oi z#OWBTR!DF)Dx>NsjE#;E-%d^AfGxd~NmLF^6UA>6cmD?b3rV919P-iSc+mSd2tNHx zN`BQDWY)bE2}ZdE3(;GgCuS8`TW16!^~@uDTx-=_PcRmXiO0521e*+12s7W`Amd0J z)Xtr5;x@fQgg+{%`q;!C&FD#0x;n>s#o+Dhuvv~)=@~iaa~DgzCuel`)cRAMv}9O3 z;8OkL5wm_(PnV9EQDR0i(hUzY2@aam$d?Mv<6!OTUq~zL-r89lQ{AuEuc3nBdKlh8 zB~}yYNF-e__BqbzgzWEz^Z1f?v5D3g6h);|uisf*JTnWd2#0bI_C@z_?x;<8y<5t< z2QB|gN)^?+-9?iM!BlFW^~x)p8Dl7G7-gN|DZ|707Jkz*)tsjeQ=gPDpV3t)`7A-r z__v)Hu1R%~ zAH#d5TZThA{sNUj=E4a{d*o|h1@bvkllV0QXjCS0yqKN&iP_>^%i*S}eY?5_k#!hG zbWFI)0`utmXvJr~c!XIbsJ6=k%LXf-2hh|?CFz?IE_jbN!yJ4FjS;jnXoVg*J4n8w zrk@?qdSrDPn=QY`%hTdrnKMOv#Bg?5P6ocN?cY`XM*7SD0aHueGRE#NA{w zhK-#xSQy{1QSvSmfh=I90!g@N5oQd*ebupJ!$d1CdbkYPRt!KJK%j5S(LnEZ{}lVg z&aYtT6hz7U8)v~;m0c~m(xTwlR!0YsfuX4~p-<=7xY-q1TabdICN=K?p~pQ$x7ne! zDw!hekGN!jl#Oyee`m#No*Q{C4#3io);Zmkqh0aCymHs#&1P0bcA(7MzYiCkV;HZO&3O3qrDBu^zIAVM4Kqf210U8K*rzC~H?+ZK> z9kM5U(Z!YH4Qx|bs&Vz4+hIM{L3OOHC40EVym4&P?maf$yLy%9b3MN*wS(e+mOd%T znn;kBYiuZ^JMa+vS)KjYTr2=fzic@?aZyB5ix; z=y>Wi8a&;hi=Y)^^QUxT$5n-E%8bv%W}YZ^0-yX#3r4Y@Tw_`ucCX65(>U+(cbh68 zqmbc=58kJs@?W98%k-B~`#bf0$rSa3MVzO-Wd~X!-a1M6Qq!#aX8wac$8Ta2Q|H!x zQZvMrtR|y&UCUn4e`jtL51SX0%K%pn22Os#+Qc(j*7CX70mn3UC6Qmgi30{fl;r6l z@^A$$W7)~Q9U8_3ZA9F#;?ttaakGJr6re5zMMi!(k!yR{!HxLzZ~Fr+sB#}pk0LNi znmW;6om5e)a8doy90-L3Bb|xf=b<=-2O=o!HaBVNs|GCBGsJdGqoIot)nx5#^yK&| zx4N#lR*!f%>NtEp|DBx%qQxm7%c0gi8OapG9hhh_C&ALK21DnNNR?jU<<>J$h(Qz z4CXjSQV4@t3@PG6s4*4ZiO;#dRO6}#Qi(luDd9Fzdc$%e;%~09*f-x{{7Tb0)v&dq zx3iXUA}ZJ%XODb+o9g+F(%2ELZ^Yz^(a+iN(*28~8brTQ%$xgn<+< z!kN!0i$x0P*PS%OGNEtq2E;t9f3CEyPcRcoy~<0Dr@wZ7Dvs)Y{YbPWoMYC-EX;_F z)*_&W)L`?R{b888Z$|i+0Ua-YUr}9|{cax%6}pNN_llRv`--P%QdG$)_RdohfTT61 zy97OJ+mOXVV`Q(%r=?*s@5BP%-!E-=aqD0N`w#Yd&e@aC0tHMBDmJVsXIBjM zAxyJ|=wOEEAQlTJ1`69LcyU2rDN=XbvL9W6Bs&Rq)=Hi&i;??(9{A$-2iJ+E(zV@T z0;aUKySOUHKZX!plKNKAlV$Ja~?bzubdlI3!jm|ImK-``o$Xch|07zQF!W=l_tP9;o9?1 zyAI_Txq^Ik9fDGDT0UIglpSK%cTgYQsIZTv2n*?LgkM|V70PC?Hl(IQJhG~tbf-4` z2A#|cyx-=-hHP|E*gs%YS`^Pj4f#kHMZuZHl<5}_n32>F-RnQnLNoAKJ15>fYU#KV z<;Km8)vh0U4=OW`e-ledgIT_DEC`#il^vXa@+x8^*ZR4gvyYTRJgzkl-mdQke|VYi z{^*i+fBztuSLY#Nx${1;q#KE-SjuPAh$nld@VzEv7>DtI`v0 zYuEc9)#vC@9V^#KLbiFku2_GJqz<)10*^_;#RBD?|L;z(9%!loz4ZunMl6Xdq_cx7 zDd3{CV&>m)U;UC4a(hRBw@L5II>>mF!-b&2cqqV}_9?yx%>JTH@l^>nDp9cTPD%i>c2~r~?L_Zu+v1mw9};jsYMI5dmLd}s=ReHB+dKkV&*T(p`U0adT@mGz z6QIYssJP?C7YO*AC_z>*uv7Wy3-*f22`bBv&1t^$u~;*FmZt^2P2UHU-z60o~X zt-R8O(ZTzYZ5>-2SG#5}E2W=DCAYa-k!liH#N&*Ip&nk7nq_mS_X6ZkWI2l zZ&7aYF`v9vl!)K*P9{~ru(c=r5c0$1^MF?y=zSBgwjNW~Y6v35Z$ueG-(o67wBJ59 z)}ylIcmMx?B+x|l1o9t~Nob?X=!xy{3g6C^gPtN7`D6E}nYn)T*Tb6^`&Zo)i&yJ$?It)Hv)%<&X)HQ)Z#jKDsb6(&>-vvar4Kb3%GL-OETm%V46K#nYZQ@$hJHh6r2FLiR@(*o^b_M|VlSN0D6>6Q%gZ**bwYE@)y zzd?aBmra9yM?gi>pY~#Qr2hScd#bv6*}zX&G~|#;-|I$-d{Ec=e{e20 z2p!B)^0 zDkmj7e93%$@2g?IlsMqwA}jm`b;w&ctvcyc)kEn$>u1hg?2-jE<`mon@sO z=APu1zZz7`gaiP{X&rd8cd@ zB;B=*yd5!kT{)6AAmT^>wE<7awGC&ZO01$&c%m>YQJ4 zxbF?Kwvp&-WE7tk)S-}sZ3c2k1QcTQtwj5~u{5cW9Nrs?6d7bSS%tUA2=a2QH=`%x2n6(P4Oo z^$n+A-uboncB7=v@BHWX*)GGr;35keN5klj|AogFO5IS@Sn)d|HF^aNO;rlTPoXCN z7CQ1!ix0FOGj!te;`UOMen8=U$%cWbd0*nkrktMR->CxZ8VjZpBj)(~c-m(~t5-4W z>!*)=Rn#w=9D~pt@BcJDE%}2OK|B^;zOywByoUp#;ky2#o;7ao zykLz$yofnQc5P8%x;Fp4zFxcXo7yvV8))Qa5CaTx`K0&@VJ2+WtUxS?K<288*rj(p zTLyVYFbvFv>>R=F+R5T*m28Sy7~=&Xf93_)nBUzXfGz&zw*-F7zV&VK_4S2fo?wH7 z^D%eX+pC-<^h=9s;tRqQd5v|hpYw+vzfwm^`ol*ry^mP~@ua-{KT4_#`VB$<4VdM4 zaBBm(_w5vj2TFJ)TPm-!tg?L#n9h|Z%su;_OM`f?x1YHOSeh{OJx3Leptv3=t9JVsjUB?8KMi-B|(8#d+l#T z%OH`~;9+P&*p&Fq9b!M4Pqht!qWBh``~rR3yNod^PvnYC`cu6@aDymT5EBjqW)8vY-hRihCVEwu5@BOsn6L=^_0M^XxU$ou^8lH zZy5^ldc^UAk)rmf;K%if(zydxY5~3b^o!2{P7P9M#iLp;&QO%k0|?sNt}zn=A5rq2 zvR8=C&4As&l^rdevi$}3U@=rg7Xf(C6;1+u!B;%wklU4~F4lMuGPP?(ShKF#n)A&4 zSR<@>E~Ta%ZjrU*ki&|hIt=Y8^H$)rD%MxchY1@VAL^S{*6<>m;_)~Btz^9qCggDA z0I-~h)Da~A{>fcjl`a}^^fY0( z{k2W8;p04h{kBV+jL?OoJKvlR+C>>X4ZtRKK_9aLfDz#w-Te5xM3?{bY8i+fjP=&5 zOf-SH;FZXNUY<06`y3+Aiq?cXe2xl7F|Ls}^q4MRJNGdrgFLU3mL{u0ucSe_@!&fd zA@q2os&7esRcPu7K6uv_(G*l{hHxn#T=NKIR^~HaYM=!z@7s8%%lUCg4*o#*e%v^% zzS-`uz+y;DT5{r|*bGO4eb?~PxkFQ6W@G-ypNDdlor@Q!iQcO%fONf((uX=yR@5OQ zRy_HylJPfdRA zj5){JMK!zkn(xqiyKmjJrL+#RZ+L)m3Jfm-;712oj8}*Oe;a;B5)SthW+q4DNiu4T z2;9R*>qDqH98_rOCtwpaFKyRV2h637Sh51%s82p`Se66I-DtMP-IhRPcmDSW-Fwj5 zwC*hCW~o;@$+JCm=t9fXb`mmxj@kk%q6~Zhz%);UAqk}3wVrNF-@1dPywF(g({f>F zz8El}?Ad9WkRtP(y(H8pP(S*8YTL%Gu!rOF&(}m=8H?vPdY7rDdRC1G5NK58SZKI+ zH%X zG%lC46H<|hILL1Vh-3gIvz+6*97r7(U>92(--&elDp)v1VDUKdce=k3UCzuGVbJKo zHH(hcEL1!c^HaRVA+4i#`!P}$R2d$`xFOa0lHGZ4{(B>W{UT|9KnD3H=O0N?=EYVj ztZ9LrTVG1FjKJ-b1QZ%J-PVf_a9Y7gA+N}IY;Y2~81SiSnK@94;~T>>UN6q|NBdH* zR}=4@(ThO7pfHAHq6QtI`cJ_8A%8KFTEmo)w(U68i~fnD>}_0%RQ_{NraIv2EMEq6 zz*U>B86Ho%E)g+*H12?|vi*681g|fNg!>zr12X?4LaOD-`Q6SXGm?myZOD5eSoF5v zml@Mxmh4WIrym8VXEx$v;%g8&-cjW%O-tZ7Fh3$8oHB=W7ZiGcJCHCm%-f z;yy8wbI2pqQ6Pz(I6aGr*o4#_U!%=tpX_CXOTH6-NK}Sb8lq45fHne7^pc~_&@j*e zD&iqe9l~#A12Io8%s4%bkD7X0{(xG~}S;%FgP zbMoOIWHZpP7SsaCrb2-K0HhEDEzGixt@|tG@z7vxpD4r3`6h-L&2g2DfeA8t4cLvt z$>=irR^8Lq(Wx;WU0lctxolR|;$?3{OjPVgxojF#ylqW=0oAzC#{*B7RL+-5^N7J1 z8YQ?uq6=iyCvk}K^Bzyxf$CXbNfsO^RnJ}bFgv8U^VQtdtng(269x38|9lrwWADOK z342F+U4J6N>Bs@)=$t7m`RbLIBRu%~ke}rao4RpDA(~#wV&DnygF+kSCZ^XcP8|6T zbdvawXGCu&&kkF?uW6IF{0-TD^rbJ>6q+qKzVlO>P6*Fq(xCAkx2Nfn}+$8HHw_xk^YI4{Sf+tm20yw#lQ*Via_4_J>3?P+A(=h*{t9 z;q{`o9@=R8DxdXjI&VPyP~wVeqfmPp$|6&l0WE{bLu!ijUe9BH)!2wz2W=K*SNsEQ9G8pp4Y1gbkyKs!S)-Yn2Ia4>FQ0i=fwr@n%6)LahTxCrM~da&R^mO+;|;wt6jAwno^mdtDtF zHp)Pg=`dA60za5ZqVwGuiE0gaw2+(AO!&!vZxYuY;dDjYLtFzl6iwT>jU~n92CtQZ z$f|qybe{%}>kvhCO@KJ$js>LDOb^>wV*H};lQ^f&L)EdvfE>j~*NAClv)Kg_G(D(Z{M?v#j_(i&zD%@e_8P|_e z&LrHOS6=){Mp$fSdIKshK41nP!>ph`d?3`YW@&O;B#V|O9}aL z6qF}#DN(%jj>9aUfu>nOVU0tD$wi*U(ijGn*q8g(F~+!g=OMxWJbmTSL^AaVObY1j z(4c~=|In(%wJjhcaV_Px1~Kfe*Tc^spSxXMHgfoi-63(!*en_c2vCNVcXAp<7wvZWFCt;dwvb5&Sz*|*3}`n(o_aTqF$&swq2_{Qh5br3y2 z){Sen>;A+jBdj|AXA4x5$2b5?#znWfw6MYp4f!_q`d_IRP!!(kGD)NzKVlKp_9Z01 zkPBeQU_}Hok5-P@S=cN91uJZOpBdKTF5!JWIHxIsyNRKkqyB^pVk zz?y0bDHT!Rm0+{LyV|+;=zlVtIOt%0*m}1IrPkTjK43gUt-^qi zEQ48#tkeT2&j56C`EU^GGEUrllJWWVq^bxi3s-`#kjw}m-eI9=F&jpt5JwGg;clqY zXJSX79yI#gj0xj6tsqNdSwR!50jS|wX8m+8TeJPsrRqLSnu1fy%e@rj5pHitvJ*#b zCdReLuvowv!je8QuIm)ONSG^XhRAFLRq#>dG4`}ge4X|{dg=l?v?Rt-)<54zF-Le| z&HVc4VX1G&OjqTZwZE0EEZHrxwXNNbb!;}?8jqnkmY74^@IC-u=~p-bU>7HBc5GO6 z&Z=*NLXrW2Kdx&6kL<&@dDT2cNabuGsDiUcGkq{sJ5w=+YjLWN7 z)jqyp@-aI-(zkL?bj4@ou+qpEW9jiN$l9m|TI9t${VFCdxD8+Q2-WhWAK$~$vR{9S*1KWBb8tV>q6S9Z;75C(slv0 zd_3id9M3bJtK8+KOI^n&vtL%^VO4s_bc-2$5@|BZsIy6Z3;X-^v>L{xGU@3`XBA6A z^`){ng5v-?TDEi0cXEC}TJcQSfkh9Qp)kL)hcdTf^vkO$&9NIwpM=dEhu`az*~{C` zqb7CAr|^n-5CSL3dM4+Puq3t}Eb|VdTAk!M&|PXh8&cv}$3x$q>1q;)VB&98s^-fc zD{=gy%F0UsEk6_$lHJH7ClvXmnguaRY< z`F0sv?TD6vAooWxIuc|(LzF_XD+_Q=gHVA^gZ(0^9|tvJf(-V}iKBbosDd4BW-Nv? zOu3umbGX%dXrn;EsL&W52cvK;W6$+q&Ci#nAsUyvjTC^>{4IvIKH1*~=Ej`f$KwqT|*+ zE8JC>J{Cl^yC?T}Y24ZPX{_#@C^M)C50il83rZ3+!WOuk{p+uDVMSx>G^kYlAUlKB z?+q(~tv1eF&kdHE8(0jgwEM$^I8rrY{twM|YC2Q!q#0J6%(*^v_!L)J7E7ST3K_MO zHCzV`XuK~xd_j8wcX}V90t0b`-*&S$gS&pIDO$nszyT~Kfs3C=;~|V)6IJ|o9rl(eU5UDqK)v4bhd`>O*3aCCiS-n4N)k67OuK=Bdrh3zjK(MWQZt_*AJ zxac`W4$(2xM*&)h%D%zaAWnBq^=63m)f>y44`HFuPnyUsByBT0OG-B3tjU|4{Txi2 z`=n8>`NZe-cJ53t6YNP(Z-(MoPj3RhmmL2tC?&>bZ z^X+JisS5)qnP=CPVXC|iie;}L({ZOOp`pU>U^_wsl1=i3UN9?qE4-S-^($8yhD$qq z4>gEUDz`DL*ncQaHF^r6f1tsXJdVG6e9!cLbwjL@{DUh~5v&~^41r5xsy9Kop&HM! zm$bW`4hyFOQtS@M;k5=Sy&UGg9-jBvJY!%_+uuCKsXX{7+0}?U#ZQ+XcbxZwR@x;h zVx_@Q&_!MyXv!z(jPQ46L-Cg{RDx|oxBi0ufdp3#=cbkAMetw3Gzfl}@P~G~YbgZS zgvzC;DM0Z8Shx{Oc0{F}VsZ$^Cj`w*htQ{_2Iud5f+WJ}?w~>sCAl)vp>vsh!Fbv^ zV*Jn(Y)%-fgHH#v{4fPhuQiMjyIqA3tlz^cJ_p@8Q5Kcu&|V9{kafu7Lxy2k=#HRu}buoqWKBftN+LLjam% zy%jflgJXzXBp6}ap-^UbC{5=10y`%E_OGiy+$>Ml@2*6Fwp?5^?+!vhFL%0xyhHh_ z&>A^ZiHTWjuo;yKi~rX`pw8@WJ>3r>7;q;+P^JEIJBpqguY$)RFCN8ALu!QG8K8>@ zP{(KNAI|=9PYt0c@5ic~2$=*5Y{NETp!^-<=|`h>9SjF|LFk@|E@xz23@0xi;ZR>a zo0|sUp5d;>I}0$@H*34&^fgdO%Cr z4tnv?pN%>(rR_*^M29t4gq9!yIMp=UVm@S|StF)Zn^YbQzOZ0}m}Q1l-BnCbf?m%qGIqyt`(l$yZOdmTKA`IeQ&xW9gotE?WM{No>kCD$x8T0m1C}iu#{@b%DIRJYJ6^ zP>Pv=-kxs#x*+^Hwa%2KT$MEnbrX9YqU2a3gFD{*Wbq^Oj~*Q+QsQUj{BNYYQCD<8 zi!SpErum(wC5r3A#`)IU%IbrZoP>5S1RhM!Z@s~efQw`?*81gu1+lQ<>B(lKuk;$8LX*3MsAAL?<110TE2Vf zO4Qo-@Des<6|;ieo`q0vLwV5?D@-Z2-S)rkdm2DC->zLrrYV0LN|gXW3q39tYyz-()v#zq)l<*kNA)QXGO{qnI^Mw}oP(j-1ZU4fYT z<||-rSbz%iS;0`EG5F4PYq2h?FZtl;p||Jl8SiaN42#k0kiXRzDCBjTr~~ir@KOZ% zzn+-LHd2S^E(qawOdof}uS1rnmzS4!9(v=ZDTDX=)y;9Q1jyJr!WE4m<`By1gX>!b zfE)(EL~X6;8;Qdzh`i-hqg$Skpn$ZU`4PVra*W=Hl>tB`mWFBY@&q<~0yAvRdqP}r z<%z7^gPHRm*bhpV!oqKL%lT3dklX3=v*&~5$KwQG#_i)VcIqZ<1oRM7uE6&7+7G!F z7Ev&~2#(0X_bva=18JL>vWlx)n?c=yFBI4Ah76OA7NJ;{;ZD_@i>Bme^NL^a1DEp) zTtfbNR~uBRJ^{I27IWD>OPEm-*MoxFzxFlG0_Z0owk3#1rx8<~S2HY_LqFSE6foj- zn)@V3S$=xn;vE=XV9MG53sBpJiw@e|^QVyPOF_Q-T)*IN<^Y(DReEDEIn;>$zvKqCw82<2Lk46@O&A$7s+-_J3GFTCD-okfHLm|wM zazcNKEB(!tFmMqe{w#H1YWOl%unzh%-I7e_`Y(UAVM3eL1d_MTw^7_ar{iJi3!}dh z^X!qBnXFkjpYRPh)|he2|4HqzOvWUJAprfWwv4 z3neLw8!`VL7ZBw7`u4-E=-;#Ho+}2=LsEQ&#}{U_Q7d*T9(ywr(xRpch1KnolLQdt z^6B~j`%P7=A)O_MX1}<leQZpa!jQVgaq;AK-ob4iG#gRa8~~c}scM%m228-XZy8 zk25^0R-+Ey5>HsA8|g>MPuZIetX#1DU_=vL;Mxp?-qYwKkX^;90HbacYgy*T zNpKNIbt8EG5&H3}+Au+r6ZHZ2RkMa$cR~MuUwb3|-hXHjo$jG7Sh6-g;KQinW}M9FB8M0D#mx37k)TcnAp*JM?4;3y!+#0y z_(O8@T-9be$@SV4sY&!VxDLny#qfhwmlSu`mQJ0=VeL?p9-vn-?+A+jZ-v>MCV*(|b0&2uPG5fI8}#BG@Opc4?<~g9d+~wD{(+&g zi9a9@<}dYcZ()jZ6YK_YM*^FkuUeD1v-rBskRNyX&LD^T8uS=84lntPRyR-y8+;^K=arD_B}PBb-8KNyYXs%AR_7G~e^6^IYnUS9t1 z+NL!HA<7*l44=ZOd1u`+AS|#QX1H=4oU}M2wc#nGIwkefSkZL7jjw~o-^^5nD@KeY zti^{8n;)2J_X?zFyT}UNZl{NM-3&gmQ%nC)1+40SOHVQ zz!M-LYq#+}f3^PydAh#!M1`EgoyPdOeNw+-7ST2fSCjgHLt5MCfzVe^^MQs&?Q|?n zNIJ*=)qBzf;^;4jUV}>|AZz&-R-AxBEytWiSuQj?h&=x%P9Ck;43i!2T!3x@LpekN zC)VH{Uo%GTkeP|Uhbg8o&tGcWE?o-mmUu-AVM3>$ zW8XcTjBW0Q%Yd_?M5BC^$h%ZZg7dW)EB|K6=u*4y6kT`;Nt8$u;G?uXHoC94@A zPoa9?9tx$zbTTJldX)o184j=kDzoRyb$kdnm0+qeRs<8n<|w6VF~PbaX*06uv3Sle zrr%UP_2DA*3qK|#C*+H{&#>C4@P{nkj&BQKsHvcjrZ<7Zb2)UmyT3bNiLX~A9*Eg- z&Oix59skYTjz9oJJ{NRn&$3*yTS<%dJgoNTvK|sqlfRUY&LK{uuTI;FFRZn8``=9e z`gX89O$lA6El-60fkr5@_nk*3AX66&7cq|UnDo4xP-vVqj{FSF7SAt7*`V4wNS2D&Yodb|_M zol18^K{KC>B@%+z42BkJC{n0_>@S4KA(~Qe4L{yaBDbI@Ii_icX05?8!;2G6Ynb#C zPP}6QIVR^7T%yeT%>>-L3!y2Iu8gl2o|jiy_y*2UG&pBy-?bZABw15bq|-keM%eLc zHK)Wm^)!?_uD-y)A8)_Z65itP_sRA>BM_+=y1D4l`151f3IuM(p5K^=n8v0azlHRe zDvlFQzEC<0Gj?N{ApswgnHwNTM&JP)oJZ5pR+!0EnBj{y#rn4Bt3BQpyEeZ@05Bxe zqx+QDZD6C+P{&JH_U&X){=Cqn?sCzpKH=p7lw0Q$O(r;7nrg%~USm7gSjF%MFJ~&4 z!BZ3?3@rbo#uOY{oB7y2!mu)*&~W{F@-_7(GXU@%U&rJ{V;6@Owohxyz3|UkDS(}l znNxdT6LBmhB?JB;Z491Z%|2=Vjaws}ZVn^nRjXGepIHkcRB7sN`-4BHHALFtJ z{{;^#K*WYtS)~;p6?->yD>y*YV?obZL|cdmULs(>zF~fh8H3biyv!eG5{o7uB03ai zkCO`EmJ`7!fUE1X=D|HzlPMU7QI`+yzXAjIT0n%EitgmP*Pw^M?*2V_rS^>ZPqxtm zKUWWte}4bfj+YZL>dnRp2_G%``6llirwwcO4@hgY{(+D%f=gabKX81yN*-Z4JyQ4j z@9eAKTd#rteOdjtCc_yxxQgBf0~l#lO`06EjDD8d?tasxmxHnQE0HdOf6{W}GHV&c zJy@gdXZZ0Z62#*>bgjRsw*czG5OMFf%k#?z5Lx)?bnGRxNQ%Fnb3wT_^Dx$*$@kYS z4Vj3-K|ncF>c_<-ma-EZ;Ozc~uL=gBAw+bbN>~UWFQATMzO*p ze}9Sn#tcj4uMOVEdm^f+j~E?<5WNMyM5g3-^S|vYaw^L%TyKy1{+=+1Jpu6(gi&eR z<-jzIo)hSHYk#GdI+Bf=ycXHz7Cz9R`tfn7|-k>Nq zN1f;S?xEjX4LNek$JLoUjZU*Ty+#7Cp=b)4i+`7X!{Cj%D7@8W#uT^q@~gsQu$w6O zRmUG8=U6cQ&Y+W0dh?sQMNNJ1TY7%r3NqP_UeA{?hv5V8#*kCq&iU|SLmjk&Fmsl~ z*x(yr3nfUP%=gYW?%@OPXTB>rgD+Y&0QTR?0=sc)Bre^knHEeKLUwp2NrO)~^yCoO z+xy*vx~$7mXBI8#g-&W?{-ADnnzRIICl?hDcEi$}3%eF3)dIIg9Ey)YM9>Yz1=IsRfzETQ-#v+k@ z%5*+jd6=2{37w{Bcf<+(bYSw@;v;GmpW>!xh{8q?1rTknr{6Boe{5LEg`|&chgreo zDnXBc-%b$HyS)78!SEdeUXg$N5JMFgxWOJz$uaE9SYY@m0$w|^zUB~4ViAVC^%eC{ zo>@PUvcPYZsNpGW`C@W!7OpD+4oOrqc&6+B#+=`LzVBM^KQNP(wa-4YpMBrgRV4K!HzfK2JhwfykZmW;W)4^? z?XuJ4eQgGrGLqu675Ad+P07)VAC2|@czb$&-n)YwxW1gBD*jTb_IF~qx%>nG(-r)x zS}N$`(?JP;mH$=*0X}lXQ}ff$b1{A;+h!N--&zPtiKW!_iJ%lpdO+&BT$gaTI9Sjq zCD=qVvir*RV8bro6leO;z+BmMf}Rk82o9dJSR>QR0&g3cgc?I0+WopgAB7U7)x)j{ zA=zL~EI|tmq~&boqz+IV4cEPdnEHzR&q$6ZC6(+opIo2iDowP9 z8A@+?E=>Y+C~hWlFBqKXM>n~J3PuzytETc*#a7b9NwB5VTP|)1;(0FJd<(h>pv*Bd zt=y_VVk{RF(_#i&7_8t_XwnM*uV?5qW{LliKThBD7JdoT%C1|95KOKpEzh{=;m!S2DhPH=_u0YISFu(4^BC zVkBI--f|Xq-tsW<>UO2}Q{2g?6hD+GMnVDVp^_9RVIVASm-bFO!l&?1^P|i2T$hAC z)ytA8FwHef#Jafnk>8iBbJRUYuw4F-GQsOt<`(VkI{6$_QCVoV@MtHk>xqhCI{zB< zH_lyWMG@qPgD<=fn};TvNDn!5*G4`pH47D}weN7KyRbSsZvW(nOjj6wtK0@m5|k1^ zF^X~0fmQ##FhzE;ro{;11_=<|;+?I)zY;nz8tM> zpf1%xC6Ww`A|mR?v$A7rN1x{q!kQv#bvHuXt4~Ua7`Zp-Qc z90d>JIjASZWS&M7wkWE)-aUVo(~H6x;76qvuZ&Ir5hfv>cuXKi4a6*DEFWQLg+L|g zlLRvARu@arL?}R-ZR3m}nAHfWFsT<5Ehn2p%u}Nw_@=-4-lKBt91i#46KCXk3BB}H z-B>cJa~zxk;k7Y}m}YOg!DtJ3%J9o^GP!#g3r|5nKmhtT<$S5a(aT*`a+&_hHBr}fbF@z>%&=d-Nv*FM9zS9%I57E+ z{k~&9jvu@#Mh7M?48igbZ)Vv8sOS<*t) z?QiszL)k6!5R>#w2Sx5=7h2a5dB;KH_^;tul!ctX`C?*Y>~(8?m@lJ*kkO1n=qdhM zjA|_u6_DnVhl=*gwFg(#b>1cSQa|psvozJO+10RAEH@F_F$i=gVAb?wjz^Xk@DCPE zFw2Io5b5sjx>H`RD{Z<#-q0x8fCCn5pHgMw&K&lNQ=iFh&pRmgRGJheI!p>SS@7Z8 z36JQzD~ur;PXSzgp4-6+DsYQp%AnBWQUQa3qb(RDZ4DK&ey}(6edfvX4LT=MceefV z9F!6T^${e(Oh{&n8Ipx)c~nA;Jh&Bq&}lb{kpTdzmxW|2RO!w~&9AWg@Z$zVE>H8O z;`kGH*z8C?m%b^LN^eg@Y8cR7BpT2c0W;whuxg?6J<^IPRFs%*v4Y{)Xz3^^1K!O6j*9=AwY8%66kEK) zIcMpf$QA+ghxku(_cM%GDB*_HA0K|wt|EhOx)r(TOt9Mlfp&GPU zDjA@I5=+u-Cc=rMXh9mTZgy?_4+kOuxm!_uY$U)(`}#u&0LTbzO&LVOhbiQ&mZd;A z9}Gpg3%~1&7RF#byaAZTBR8Qal@gR2(Awt^8d=xwbf+3 zhcxD|0;VZEABBfvaxe2N!A(|t&^U@0spo#s+K){UqKo$%Mazk4l z#5n}M3)(fbl2CW#=2C?iHA~LU$q{@}eLMNHbo2D1A~e8q+(JWgUBnJKn( zUi7=gTkQ&@C9rVv#OU3AiI917Zr`-z`d(R-{-x%R6LJNQ%DF`gHlVe%U>hpp`P~R2 z)AaKqHoG;1gnpkrf4qM?fW+uKy8L{}AZ)}TW}<B=F1C;Y={AQg0i4XM_4!*lHbm(c`rG0nsX?0_@QIypXx-ClUo_lP9iA;gE{q| z4*iEWq}L&We5&5~K61G0>bBMH#leUbAqYWJ6Y#(R=Zk@*+*0{e^5t~=XAvB*{e0EF z=$H9pJy(wqsO70xR;&59;-(5wb{3W|NAXr?6W+Emi`7U3O&%B!Zcnz~r}wpDJm5JKL5JP*D3@f)T#lqR9kb^onc zDsD!4y`gYS&vA5H{GJSvsZ*Z+?!FcJPs$GIYv95y8Ed_r+k@XBIsDaM7{n5|6;XWO zge$S3tVW9Fp+IDrhatos%&hhynAK?#V#|=Yo2zFFuCf6gpe&rp4g6$CjH!HW{|#7~ z@-4ChE0XFWe@z?{BB&@)m%tE~5%`fGV(}i>tmADi_LD8b$OW@Ax%hA&nfgQD&*Siv zw0;pr)1HR-Kv#m!-Cr6NPyP0r^R1QKxZ{M+bqdZoGJSQhfNAM_Q+qCrQro%PVlqv0 zj`jc!*dV57Nd$xG5BU;ml?^ML17(^TU$B`B#pa*yj4O{_H}%4(jDEZ~L2|J)7&U*m zNoFcMh|3*gPD1V#T`D%l+h0pzK##%|yH{2W_w~f4M$7gdPH#$@X&ZuZCfE~+p=+^_FOjJ4HJV>0aZ`sawsom`NFdDZm{5fm(fP%8+Vyvaa11h0T!1GhQ9|Pwfhj>Jo=ZQ>IFT>0>O*{ z4}j7nguoSO&4tShxT&$HKHm+%X-q&eansra5<2C?8FdT7C=8>dtG0(@!q@=B0KJH zG>)}Qy-zNn1&yW_=7{cn#aRmNlbar5l*-i!y+jWqH-;oP!0IUGA(*y5fAZ$yTyfy5 z**F#s7oMx9`ZW%m$HE2iKjkV`t5>O%V+6p{RewU#!Vf2@r;c9@F%}6eYXy-X*vkpP zyX-4d`01}o$oIo}cOU~gII(W3`q{tD3-K3rh|`m4xSu0oU`-+SjaRrTrx>G(C<80M z8%g+AGd76-_8HAfNcV@XUN>0&s!n#Xp+lu&_xAS5{Hj?7;+i{+{qt;&&yD99uNt@oWc?hQAaSUtM#ozi0T%g~$$Y)T&4lxgY{10Y!+iZY$r0RlkjC z>|JuR^!(T8`>(G^c&l*ps_9}s4+ZpK0G-*6ZW5_c>N&p18r9bDTtL4Rzqeu&MZFx! z$zkEla!5W}q)`DdnA)}sT~UNxKi=_a1U^n-3$k%a62At#FS1pCfER>g$_f5ApHH44 zAHmClE#Rd+n{4!b{!nLqb0f6x+O^|;c}=i3MhDfw5Xlq(nMA2gU`7D|97+MTZAIC! z1rg-9EX>Lc{{WS=%M>9X&|pE0YQbLSX#-UO!UAsXDAnC(HJNvB>MAIHW>I8XeNl#k z?Y5W~&ypEc%V_=GF3AN_@z2%rQ}z*INWxPjg6;8~9!%wShUXA9$!|UJc~dhOs5d8Q z+HqZoL%Ui(r~S~62slRcUn_4n@^d*Kz`Jo)P{``!5GCU9EF{kPrcB4c*_C`>!7_q- zSEaY}?Vml^w6$?jxeEx2qQ$+$+8Q7mgO7uOOhwU&QxWK(tl&YlIMY^oDv4&}`7rym zttT`4zJ1BK+&m#@e;bagD~z+*7o(x4Q^Y=ACz3-jgk`VwM>T!KB7arlI+dY0sLa$z zLj^QwkIyD%ZS89P1@iRaXE<*s<=Hf}$ueRt%aD3RS?y9(%&2k}?3;qDI#`{I$;Imk zGcYL^g8Wna?j;1yZM?bCs=g-2%8Y$T+!W4PcX_&mY3Of{5pnoM62>VN96f@%e_9!l z!1x4eNiV&ab^1H`I+1IWxCr%qCAss1)Y8mUws+&M zPdCJ~;MX~Zy4ltX%ia`IjfR!V_8?QL{pT?;ZpSt0EnQfX4|bZ9Q=jfD|FIt3xR}Bk zrPPIAJ4{1O=03A3e>kaM`{kw_aQCGm28MX5Ds4J2Ut3&vE%f{&6#fgcfFja|HQX&_ z%2$}$$6sBxJ)q-R6=G78hg^YI+1##{1FJf#@e2g{5drxgcFE3MCHKU$B6m3zXbFcU zNDndK!>1T8ZBnFTn}SjFg&xs=Kj+^cdUJ&}SFqb+r~^!T3$!op0&KJ$6b^&GwrQ_* zE>a5^RhRiAoaU&&0Qpo$wsmc8o)(HH<1Ui^sw^s=(%SR6SeQqGF>Yig9V6D#RP>CX z*dELwQCNA{+{qCuKb3vA@sqxH=c$1^xR#Nt+(vCW*OyASL;_PVoC?Gs4%MCs<7Yxmy1hqdhH}-G?YTXTG=+-n%-M^@W5PfS*#!7E zK);l}zDL!&&ODu6zzsjUZOCXt0`=ZCq;5%?`@^Sdg$R|;f#~WUgR-?Gt!Im!@Gnee zw>I7|=jmCt2}X;Q)8w|CJA z!Vg$BR*>o&^nPNm5Z`wvi;XMY1lv$mtDk2(P8mT|$G40aAf7 z3Jl}QZ{370APV0LLvj@^L#)&R#argoEk$@WlZXZiB2-j33s$-L$`a|2Tw!g1U1!$o8&qMfu(z~5*1c~y0)yooQBj7XhQik2MvFzcd*S*YuW zkSxpg43{qGMy@2}^t|LikZwu7fzjpMW@ijU{^$gFlZx0l-=$rPKXrU%M=jf+yD8JB zhClMR(^rwo`Nq3o7YV3UOP>P!cu3OV(DRE) zX_043kLZX453Y-H-C5_`U7zf5W$%x3w~Q^MOi!C;dh3W8Y03tXJNZ5aS3V>1PH#r> ze(f{VxfQa`l$qFXvUOMdEJn-%geC1|?|SO6akW|fj7NIbcjeZxpu=2-^ZAxS7BrhF zo{I72+RG@8_pks}3v!W$)-DbaFh50zWMoTs`QeqM*CtFl2c(SN>#P zFig`BZ9d`F%?` z=|n+`>OqYNj3Dt3v^j9>qK~ybu6C__r*-kw9IhF12L?9kGe z`$}I2kA6oTs^b=JLpEkYjmQUqpAU_)OZ##2jKv(BMA3Wt3@(G3_xM1)3 z?iCt&Y>1^*+2&@>0%nv;Fw2$a>?iF70(^$;)stB3z4Zm7m7bwclG^$&@!`~Bi4c8= zrI}>pEN=wNB)lj&>r4H$ZM0-P0W#BH$K`?&L^_GcTEpchbch-htJW|I!k)LRUAyuJ ziVs@9-aZ;-k=7o2iV<{a@fyGZQfRKF%N2>~a7f?+_3OSUDwwL` z9Yn6d0haJ{;-$tDrhV`WZWIrFV)0U72Y)rZH+(@jr#EZ6j`7=vaaX~Ar`byUheW`Y z)qd*MV`P6*<#W43o?C>x%`s9L^QGMLh2VjIJ1NdpO8RKCsD2LJ{fM>uWGS}t5xTNi z;#}4DdnV&h(KQa0>3h1kCc^P7175#5i$N#8>`_t4Tw`83z2D)o_?8Ch7TZBG{B}H< z%@^haoK^&`gKV1Hqw}OAM2$ZBUJ_pM2!-B$JY6l!#!DYlc(a#!xS{R1R5z}2^w#&I ze;CG~pv`3JRm~86zVDQu2rJ37ylPR&0ZcDZZOOT0J^v!h0`&I+Uh@c1?7KNc_`ZrH zr*)%paxo{ejaXq)xKSn@2N z(3Z^0EcjGFgk{J;(>&tKI->HG)=`kumvf5TGf6qG+{`hlj(i4k3HiK;R}vnW>2I7l za<>s%lOds$t(?FrZj60B?93dm2CH+4i%i7Y%Hqc185;V49MaZjHB7yr&q~Eh*}e$+ z-ho#B$KR*0k>emgc*n@EH<_RuM`YA$b%{R-=4m$1RramaME&0U<8QxRp|67IKK~nk znWhlPH0f(hHk^{xpWLbY(S^apnBzp2xQwwVG&t~BtqslFQdB)CuC}hM-cbAJNR}Z^ zLAApIC#q;VQ?5u!Znxx%X}PI^S2yy3h9K4a?7UQ!maV)``reQQiGa4{jU$il9TG#Q zz_j#{(8S%n3H=q+B5(1*Yd7mWA};K_ih$D<4Vke}%*o}HFgU9aTiuDsyH1tS#{SV_ z*0s$)Cl{#v_iJP{rYdo7_cg2J%VZXR64|Y zT+~)Wl8I&Wdq0={0wxDUVd$eXqYsC9=!_Me9i^^JC7I95>cs;1QuLt6YB}zbDebl8E}7wE25XC^?qE=G>(*9K=`K}X@obiUZ%GSKV9#PVcw?CSyT+OdJTjcZ1+y@_*$FX1DN`A45G!iR8z9z^cD8_2{%!apaN$EQ^;&LLLJkSBjh2~KhpW1yIcY{CNUJ|PpX?3}k@Wl*8uVh3!jnbe5+Gk$QY?1M!O&>+hdAzY(+YAw>YrzNP6zuQ>cBxu@ zGyZY?-{sJf10A;nz=jrf$Yt6>E|r%Q=IF1Ct;jC!9jyiCOIhwjjpb9P z9e$bN^ANd_q=>G@%FgSQ*vqV|jFl=g{SwZPQ1ORwM`-c;lo-P8G(U6DmUIoGuZ~bU zCCx&6l*i*zCT5ew-oGv=2*Y)^+jUQqGOI-H_udd! zpw1r*&n1d|qo~FOtaFmY;CEgUlc>c@RnFTFqUNs?I(a|`^Yz)FaISumzi5XDgohQg z9jzVnK!b*2O(8&Q&UJjo^kW)kAjnOdhS~dND?Hz+~g7#M=w|i`0}9eG*oi@&Rjp1DPjkolI7rV5EcdH1l@S@`i5?UQOICHrfk;ckPPaj4_FbY-MW zLg5f)T~0*+`hKuFoS32Pm#^TN0+_kG@RgE z3Zv*$BK1Ph6`!b31%BRg<0Q6pq|Z3Ak__8iE4zZNsEMXe{kM~?r|0KgS|opbOV=)j zdF8UFb(KTpzNpo?8pLMUs|dT`irrzx-roi9A0~txbq=R23OlGh}i$C&wcS(=~F{ho> z$#kMpq{6N7D&=9K;60_U{YR3Tn}D9^jPIzh`uq=fS+`l;aVxQ83RGh`w6QKMqBtcQ z1{v2okm+M{4c9s)ePsl*`oRNV!yu}9Okb(-2`UHPd-i^?pOB?{9lI zH%w;_6xpWu(-0BVlysjbt1`7c;#5o*gQD<;uk{<`tCAKnV=Qn`wN0^Hyh^7tO@C9o z|GE%AOhB|?AoU5Mer5T8cN=`*DF`TPe?pPPylO*px?1s;)k3-)U!OylHkCcpNWJ-N zL?(0%y!AhnQ~G~MsekZ;3~TjhAneR--Zm^X&!}i6^zd`dU`NiH#G;JL-HRRTHwW;3 z*LxGVv$I2&HIzArD}2cyE}2BRLH{GGYZpw$409q1YmO{@oFE&`AF!X{*2X4;0*2y) zkFKdTOv|hG`h_-g;+F_EvJAfpGOudJS6&&)c#t6YKL{0ZmfusCU1QtE4-|tVo}_WZ ziDb$FJwEHq)J@&DWwJhN+lJmcU~ zzsHAHAZCX8nCa6oQ&dR^U&-NEt#WhDd1_A602 zYS6$t1VOcQ3Zap_8ZMWpd8Ub;1kT#B`Y}d%|f_v!TSl12#4MAv8 zi>K9__DyRLAl8=B6kCM0z1J{OCDd43$-;NdgE%!AD1H#8D^i4nk zTTV6+Z9{V9v#t61-EE9w(<$V6>4Q8kHXSRrkPhda4W7o!okf^`4H-kuy%8R)vIvbA zy^*Uk-O`^{{8onf){=i|HUk^ZFAj_4>RMGx=}mWDZENR6hfTEL>Gn3**R?Y1Up-_( zIS3&pd`E=&w9*Iv`-VN6_Wx10w5s^p&LIorVvu=w3!J0$!y~mUZxqZS%eS|3kRW z-8V>}Nuved0l*j!<+d3iXCUxfPQUgwUj}&LUqH$@dp=!T7lWA9;Kygd0SQ1*nBt$L z_%D4cUa25eVVG4-j~I|u*5kI*Zb z_YAjcZaIXve^DBRu*?flne8E+K8HHUid;NhcziXaxV(IEdKxlV#)P(-PYr^ADpqY8|FOB`u) zN^zRXN6px)uQNkFzk@s;e0TD6yz_qX;n!5x+IgeHf4c=ZEEq(4rOXTD%925h>SEgueQdIG z)QDe&&l2QQLm@p#YDnmpaU(#I?eE@y{FLOGv=6Wl7=)TZ%kY4zcG9()v^C~43~-kp z+6zWM}lAy4WWpXe$CsQ4RNq6FTeaI z_k%20;f*ae2Yf2j7w}C7I@E1PY^5ik;3SxDx>pSbWFYnnk+fgKsp5_ARO|KA`o<=6 zFK5ue?!o>0&ar0$@ha6&iTX!=FU7aTmaCrSstzK4MCKL#-(`+6%hTwy{N=>k5_ znXW^r;~{c3@J34j9xc{^aDIIz)?rq9>BG(I;}5?!Dff{4Hn=yD5MOsY8&7iY`cD`j`aU?h#tm3mUJKBGUP~Z1tu2DGm8`5L1W> zy$?iZLDv6KXmryyqeJL758Xucz6!w?L}{SJLapM98iRLrA^=Q%=R=X@c+z(Wxq@`} zxMjf5HasHW7yG7-j>6pK+t+g-rx%I`xvMgJ=pRWgOtve3wRa7K({w7jEy4@g1iJL8 zAA({ux2wS(eRz*qPG+qzAWov=1d;?1`ue{&T5ynL32NmQB=(hgSsdEdmLA3-R6u;{ zjD4Qtgi>ag#-lx=%g}4Z?DXr`KwKGdcWucZ(a-RUbm}6Y6Dy9rH^w zzy*|~q10xoN3hnhQXi{3n{O@cmYpusCYB7jIlY#%@CJcms9>XU;;@Memo+3^;G6V( zc3Oh)pt{qOB&i6Q3^@G-PV8`WyJBKYFD2b)s#4~R+o3~08L^@4xWZ0A{uxPi={#ui zJZb;x@l3iI;Gq!1RxZ^b#I2!Cr92&q+7#c8VO^taE;wVo$ZN-+)kP7R6=sGW2>t5e`CJ!8xunr%D$@)fjv76 zy85qD?7N7SdtGMGBVDc|1t)B$ohCkF)<^9#v9@p`FL8o&7?b@=Z#szRy9TbSMEeM# zH&qeGL-Sfp8tXQGe)U0ts672+pN&m_Gb!oqrLTEk@j)PLn@U(f5uyuADD|4YIKE8O zq6-W#K9lksVO8hx(}1GB1?-~WTeK&74bG+GT)H_MeeB_4w!c7R%|2|?gZfaB(s@tS!fw7oL7b+Q5>HV z-7XKl&En=oNpWq&M5vrH&F$|)OMYJlyeHL(P4&Uwp^W(7r%W%u2fYpNWkZWSA&U%2 zY_rJp)Z)*sI>BpclzXy>`D7QWY&}is__6)>mV+P+wXGm|yDYATKzH39ETidiRY*rr zr&Q%0udo_Na6D(;V+6vQFz}N+{kZn7M*E2P&zV$mTO%8h@k+$705&&}?K8(kU}?X9 zFB59nKa#o;C7U;q!Yj*s&fk|{W|a1lk6M?YKbm<53U0yHopgd1hct**914w`^QJy8 zESRNua267@U;)YbPjLQo#eQfP;>a7 zkD3EMNTB6F`}y?|dL{@iE6_aGnHwCMuP{`KoH5ET`Lpg1;}1uedR%8n*tl`#`%t_5+zVJdbfpICAMsqaE& z3rWM9JkWB>a~2TTL`A_jXfa!ai-)y;hIFqg-JVm z=}_I$TN-arM@8G(j}+`+)l-)ln5y4qbaHy*!*Tq-j=Ib6=wX0oZW80mwY(K-Jjf@Pr;$|3KI%}LFCN1PW8;i=bp!YYB}}^nxXE5 zmq2P~K_P52$}dVehbc98Z|tBfo&e+%NO*#Q)7F1u8jIoVy@e5L1K`axHF3=ge<}j`k&Ni5QU*AQ5Ts5{a*9`xmfw1 z#jh~#JzEAV?;=*q0c!5JpPbn13+r9aP~0RH6-+$l)?E^s-fu?=aR6*?6N1CtgaGD+ z%|_&foWTd>dg5iS-8^}PF73^smc!-ej|dc5wZZX}bXLRmO+I7O#tkLKb8ltAHn19) z9nhs0TZcNK{~o_zahBOEO6yZNQLTkDwkn+)&85oZb3N-fS-b?LefsSxqmXl{&h#EJ zqEyv)2%8tx9Q%tos}@8`1V7g?92Arz+mqpiYkuL7EHYRR=EJ(bRqO3Mp$Kh70N{w@ zB)aV}(c9)5*yT|X3QQQl-9)wWR>mBqZ&LGy#IMyw*AmDdt*j2X39^QJ&1EwTYSOn_ z)%*+aVlY_1?c6!fffcx(rn+BVi7sziM;ZIRH4=FqM*|mmc z59yn*K_Ss{z|1k;-w+dZd89$xjOeI~@jZ-$>w0a6-E}aPi0|FMZ%7-bnyU>ND2L&c zxQy1pndh*8?1#}2QfoTD81qxEiH;~h+2^%YS_<6@sun*w*l6E3-VUmM0`p8VTmTAW z{%9CE>38`wwefW*Y3u{F10xFIAF<>~h6u4HkNADBbeih`xPjKM!m{S5oiysi|YmxTreLHC)>)*6jQ9W`sAn4jnD!s#5yc&?>a3U0ZtFT{XV zMIOUvDDobkz5;}6#w+;B()By=z(HY%1f972(&{PZCiCUkD%ex(jU}L0276p;>MI+b zM1;*WL_yj?%#9`txY7Y9!V8GN-d{_j*QlVSiW`Sj#&1ouWzGQ5abfSW&oJ4q;E@0wCDiDCCZ3TrI$l=n51S+rCj4m#veqh&)$8{S1F5r#89oWZlt2UXU#QOqQ>a}Tw;3R^(oG`F-i2`T~nWaQ`FJoC?O8u#Sl~|1S&ob7#ad6J( zhP=v0eEKJ=HiD&N#3|%JR?*S(5+M1Q1Q`C+duSf6K_cRqDMNPhx^L|$a9sHn z2e};wj#M7scsi+n#uFwy6uo$6MT`9jHZ<7WEIQ;HP7+&V3CpC+$wDb5_MUt}>5IQZ_Kd#mdsW1TJqsvfJ>}idSt$|_+#aMyo^~;7TL`GJo75=QE-y8bd)ja- zc_Rbno?6x|X8hh~h@X^TkLNTQsjK+uvp|+74ltvLV*6LRW_~x%l7WNv1b|}8KWWbV z4~Qu4iE4D>WH2b=szdsCikf@WDqHKUf)Anv>%lhynTL>;?jn{{xABx54?OFnuC+lU zHDH==Q6zU>lJ*euE%^e-N!d|Z>Mb;r^Yw`RecQmw^=teGevNxY?d(m)+eln7`mSF& zdKFGp00FxP?4ZOlEtJ}s@S`>6gf?bKpCn)1R9|(8{>H|J*j+2pG}<8m61hNPGdFC; z#1gIhAP$A+ksJT5gjm<=TLEbT+wa#(r(o3dttjpx-(dIcSprrGVEx80Q_!71Yz7m_ zc;>OS%`YtmjQe)Uc{;`NU2C}oc0z{GwCh#^B)}N{5!c@{6Z%iRKpal?`?Z6R?F#p# zT)LYratu`ta}Zky1-(QyM-ofxJY5;ABSI2lD3Wm?K=LMZ+{_uJEGBcE9(24HG>X7rq1gGzJEf3@o)-G62GI@U={_o zDKQCjs^bfhkYv033neeNFA-a(y-A^P6QCgAui(X%tfH`|XN>uPR*>~z*uPn3AZv6a zIq+#QffG+n$Zq@Qi;1x*m3QaWGcZjI_t~9C#h4;;eP|RnIr6p{VmA-^%S4ZLt|H_> zn!vb2NlOGoAcGWnxCbc2!$ZfNF(ierL(U+sI{-s@+td9E!_xees`uM01h z-0SCK0Q+~##lC;o=;s)~{BsjL2IB0F7l$W_zAyQuy#uJbdsF+A91}A@VZ4R$_WhX& zF)uQpIvjc~9=?WzEKWeDRa<^hH{2V$QOqLM1ZRfBsKXJL3p+SB&D*yQI8g5=P<5o` z^9>Qd^^q@J0{Ib8V+jwXpy1zXSkr%PkKLLC2pfljr}|_Yj80VrlzT0>8<{J?q}b_u#?(Nf)691VHxweO zPXm`K(LIC>jc%e+T^TmRkziR=F*Y1i@A>-{UE;#kZn(Ewo|W@Y1=6gJ6nTwF+>??b znt19^m+MowPYBuMqNUE)=**8a)@VYKJeJ^iEaQn{Y{CG3rw{+2W6BCs{Adyld{``C zkBvebGdW){gOx9kSeqz``-I%D5n4T|%x<%_{Mv%=;8+UC3n*+ANa|s_!-CDQo_q#S z{2M~4^?@Q8K+E>)V0hDxtnrOX%xky!Nj&TGI&3Z4v^a>y%QS zhX&MTiz>e#lXRXpZ=h1+h2uFt8+888IMH=lp`IddKv7l3m+7npMCfx=VH^fG&q59_WuHuet@Rw~ z$u>2$$O}Wek9QdJa0Uw^iL*k{54HVkzSoi%KS~9w=jvF4sF`s!i_Z_Iw@>fyA2J(? znK4VT04v6<7dg$kRaFZgxek<8Bp%^U$o7C3=DnSJMl)Oyb521(q`$G(+*gfb72FfP z*#qO>kI1*&WDP9xRwOeWvSZ$?qTrv#3`vLkLYzP=UV2iF50?P--+iu&2ZzeEJ(ssia7D;YRgh8~o;cGkvl-rQIzb2j@-R^_c}(L4J4SHdfW= za`zFV&6sP(z({w7`YAxy0AMgeS;#HVq_om0B`wnxOAGy)4F!|)u^5--W%|pRwSw>A z;L@k7Q=3a=o*JuMWdneni?OJm8c^^;icFu~*;m#c;f4{TlPS_1PWA9BON!pf8L3Ff zcIFIbT5oAwP_O_#C#BE!@TOd%0`WqeU>&@uE{er|dYXZD$5H#F_hB|)+1b+*CCTTm zqopLZ25_w5BgPN1SJe|caNV1PWX;i3=iI^8-h-j9AyRiKrb=L~XP7@qF7)j* zoCQl?&?_i>Lk21zwf4mz%>JG^!M`Jgw1g&@difwYME(Gy-}u_ryEB5Gt_|Fk*P4Ov zPq+?HFI%&xeZ5TnTFdD~=$~6KRMfij@J_P4;Mogl5M5OnD41Vmp`dxg{LXQ0hoc9~sv&3ymN&$L-}ns1WD7qTf~nAx=^V z6&i`;0Tn;R9%9nJJep!g6QFOAR328tfM^uV1iXEc-Z^jpPLmu*MGg`vHnx`M&ucr2at8-f)5=vxx88Vs| zZfTh9dugLMJf@LXZs}geRH>%L9M0Z%iFT5T=&=#UcL~I}0K#O3V5!2wv?s1j~p!oQ%s@OFzi}K3aXq8&7XIus(iKNeT8BQ1UgM4~CCOlvx2Zdyj12 zij)M8AnYh-C#o@GE+RPaO!1*|*@P972c@QR{M4(Dwa-ofYcr{Gni*VJ`(ql3O8`g| zyuV|#h67IUAIH|(TEJ0@k?mf~rGZVj0g?B2kO8+IBJXYe_}1Ii-?Q_FWLi9}Wd^o{ znIss5hERUhqkAM%_~c0V2xTza1e=P49E;zFMI04h*y)`#IvAsS#WH(QMYxK7<)3uB zogIMYC+IvODf$gMZAF!GeRg=jZ#ic8L5akqu7K@bP z`UUJPEZ!?S1$b_NJ7-3sv(&fT$6Uxh@nt~O+Baa?TuR*OoI z$fP6f~aKS1(au&OIsCpcBPomnj7l_V8{%69n6Lf=RDVt?e_WOXNv0fjwW-bljyI@GmR06% zm{DOLjT1}n*EIXYu5Qnn*-6o9;VtzfgFxcV6usH?nu?l7kZ@Shaq(PT}S2eQGWwB-cT zji5xL`vlqAB5JUgJF|SvKazQQ$Mc)ar=)i7{g=T7eC$?&OA&DNw^Z@NLovY zV$3W45Fq%X{{-fT(g7()1~w>|_u}lg?rlddhxB*EC`GbFX^r_H#lzCab9+r4|H>U5 zfNLGAlM+cf#%8|nH~jwr|3Co0pz7Keo+>aK7(Zgk7+u_!A)pk+C_(@`;Wn7{A?l6K zQV*!A{m1av6kWVdpp9;ZVu*rFSHRuPsy6`0R2Zo<0?fRZtO}^bK;W>nXa|6{^_39{ zaZ~#s=MZ23F03W#3h)u{ZWj(Z08mO}7bB3)I1Q|gXAcFTmcg%$CF;+~^x&;xhvO=B zK^YTu{Y66;S$O2k&}Vl5a8EdW?_Xy)WNL~HODzm^$?&t`H)8UVa`Jnyrr`Z!#6G$p zyZN9HypVhi#;SlQOK|qfMVEizqTDfF-abZM;8a_q=77%g6I>_zsP*UN{dNXjCp9?93vXM-ME83!s`}lZ& zyIvt2KW_?AOYZKtc`s57vh)Umt)&Sa0N69VQBA2huF&6xW@oGQFQ@+( zQv|7jvly;0{^`vSKn2Y>w5hTd{xKc_L6C)+W7YRhO@nqORm;YAzWv{p&X>G4O=?yg z0G!(+w|0E9^jEl<#R{9g)>V6pw3^X~#74D8A*O))zFP&CDs zTJT|k11$g?ohd4pF2=%p*b7sTx%O4>Oa);0qC9ZUe<~%;Xqj%L+8alD10gaZC>=`` zmmG#8MDBb%m&yHI>e_c0KnN-YRtv9ESDZj@J18GGD5Ngf6Q>mde!5N zWH~fe=Vlps8Q(xQ#ynHKxbFvmdTI|Yl#VLa3M}A<=m@~NOW~@5*#Ut3I%d|};kh&e zQ3Nj37%wbs5?cehzM%zY+fZ9yHpuGsR;UHc1X)Mi+Y;k(i90Aqeh#%aFl9tm=O3n~ z4hwEcIg(+Mamw3HPGv1KA`hcW#O#ke&xnYc$H0Nt$?;CbGW?OfVD|gx{qtl5@p(L5 z$o@%Y{u>9ydA?*}s>6P_|8dyuwu5R4^*U4$&dAeCk^Q`V&d0wocks*XW}yNgWuXQm@C&87XAnnH63(6=4Y8@e>-J=;RJ^`u?hvxa4C zTbU9vP8A{Ga2Oo35TE+Tz^+3g|BDaQg>7%{XWq1Em&z>5{RK`#3x=jnC7$X7WIiPhi`)$ zBX2fR(73iL^NZ|)H-|sH1-z6yIRDAeA#7it(7;J%M{aA!ycsd_0TW ze-RQu`S*0-*L4mFQz7NJtq( zYI{Pv4OqKM;6@7P&XPGu$#CYK7VXuvHAi{;m%e8SeF!6Cjp?v?8#>l3RzrklAIDP7 znI@%nqj=(S1RUZQ3`xp)6E0TZhw}D2(^7FkitMoI&sS@AW(sAlrh#u69Bt|7YF?Zk zNs=YLE;=92OXIU({lRn{sxEsPa4=e&K{lvKUh}BdPA>Z5wlrFDzx{-L$>MKrd|IIi z$&GK6KbOWdqC^tuB9h5-m4dWQTRH-F(-lZdkEad*3yJaTbo_XGn>}DN@_ANXhN;&L z4t02^setSe3VH&75Ka>MsW6Cl9w$x?{EFm%IsUY0Q;0Zs$q^$=5Op9sNX#${Y;#B8 z{=SfmC?_QYUcNqXS@B==y+1!I4`6y!FIN%`p`a;K8~`GSt+LLVJ0i1W3XJy|U~m)?Y~G+`wT2Q~_Y_feESWh4yl0x;g-` zYF>&O!1N?TJRwC}QCVz(r|rlO9@%k^k9W5>D@y{-Va3l{e|z)z(>4-T6TgVOA*=Z9 zYEL?~Dom-fMKx$U%LW-7aZ}&2R(pD%Cm#3m`H`;S{L?}J3=JN7R!lJ2L-2=%@2Vp( zTF0+YYJ{@xryIwA?)#f^FTRSM;ccV-Lj?esG6FG6%^+bd!f89HnzceQ4HsU;_nq>( zCHX(>m%jJcxh!L9_9W^Z4~twz1&Y`lK%j!587xl%zwdTN_^T@ zVQKA7 zy}N&W%#&idDOwq+IJQBT-1p8#ClmC0VaNHeffiVUEU*k9=^zpjMcs9RT#Q7PLg;bk z$Wx0YvQ+w=eSfF@|B`7>bSxRv6$;qNJ>D-SqFw-) zlK2zAoD03bf*CbOf4LAt0TnzEZ|KQj6M6ws$x z0l?1AYH|ckb}U+qfmT~s&GKixgxnOJc>yc)eJv@#;`6XbUB2boza0VC@By$np0pVN zw28y@gQG(`Z@1lUA0O}UZUE{4%{uvi(Z!!}++hp5Tc=pp0TKeM&p+tTuE3q|C>WA5 z1dI_-U|~xDDC^hS=BXU`q`tVJH!V7i3SBV4gwx@$#a(sFQbanxf5e|FWZi`{@OpT$ZL4HxRr(FEP#QaU(4{ALP?I}?r5t4ZWea7K1l^J&g zq;}#kY&TC!N1zG-)9%$qk1wUtE0wJ>LSZ?d-xqZ^WB@>OSk(PCnBl;S;zvOapXj2c+RTnjJMC`U4bNs68AGwW@-{d&F=`>4M# z13s;pKf13E&FVG(qOCSDhBb2VLGM6qGK`@3%VAc3Y zK>(no=rcM>jvHW9jM*$%fFJ^)*#57F7rFeGYKa+@Ca5gbLxm#-dM^QAmzL$TnJG!_ zz+oXs$}E78$N%uGj(^0^QRF;YIjojsp&tMWua44Jz~nJQn`q?kkrTmTF_kp+0$8XW zNul>K`DKs7Y7_vJ+Ot+)B`v*JYf=p@N=yX@Pm(b|7GA&&4A^qPelqEO%>K^qa@z!j z8q1TeIz|9M(puVzPZ-GKVcR|F955Js8-oxDZ8ApDK+K-V)RlO_pup=!(0Gb7}eOX1V zVzGs$L~x=8{g`}}We8Y~|GP+j1?#b`>fPYiNUb-h6I>Ei=-P3@yb|g6qZfAIk4Zjs zOoUPGY9V&_tY}YB0MJ4nNG1zVP9d_^qu2Cz->19!J*OIT)5>3L^mB;2s75rYHa)xwwhrY=d;O*lPaB zhD{#F`JmtohaJV;f&IQ#b`e$JqvX|El>lFi3VEDJjNvUa`OWcAv3UX#WO4^gL z{EWUM;Y@rYlI7|jllFc%!GG$a^grTlnwZqr`IIFsCR#{Y2rb3W!g_qGJ0FN!^8bJR zi|47eyE*K4`w0!r`yVgoET)k?&R!RfGlzZX={3TtP+&*;$Nm+L5 zlJ-@TrMjHGq5@Bq0E*kimKK;(0!Wc%(`+)U=4>-HXbJ(TF!?Igo-UV$DtSiTDx2byWXR9s`}+d@y|izCRyx;lwj!3gtO}wbEUNjflZJFu5tVcmz5+4qPzJPcuX_$|Ey_0@lVqLG-F#Jct3y_OJAz~M_r4FB7N{7>8e^Jv$8 zRDvAV{aoVRxHbTIQqk6SP|O_KR`<2NbsiJi&TcwsA$`8S){P&Jnp!GTQc^j?2v<8J z(;B0Sgiz7I0K9qUOu=8~<#)qKke!}?G`SOf|6YYd+FMYLDHG!EgsO0K{!jCOS>CgH zy2*P3V_&>57vChMcW`Dy;Xvztn@u5Iz_IEAQ0wU^JBG;SF7SGSO`gA*n}Ycc!2ep* z{-eoJHD^vk;zhd}5VfpJ*dL%%?4s95GSqa@?eHQCxRvJG$?>-r6f>ISbXwE}#H1KG zkxgql5gO}b2i8SBdu`;cQbBn)`#kY!m8i5832np*uC*eyoSija;06HbS5EiH!0uq*OijUQOl(;H zpZn_e4~+E)zv~rSaGUuWzGE53z-2rB>*L^8A)h(yce`Dhp8w^&JpL#RRM0ou05(h! zDuq{?mkDt#w>Fh?ckowdoUuM%PRGwlL3tRT|4=|*4Zl40^=JcZ7e0lraIAu?g=(MZ z@m1HdIWXk{rk9i5Rld%r~(U%zTuv#s~4eiS_*jp6oW zZFe$t#9m(xV^?_ne@w{thhcGPGGJLg$9H=o2$ZhaMj&v-`By`UV^i$)_2sbLj|bpj z6+fW1ZLH*5sg_P&1vI8FX@0bvEV!SXGwnOp!Z>8RWw_uEYg>(wuS>~ns zolUW{j=x}u02P2Q8=<}QwT`==PCOsu@Bl?747ydjNb-RRIy*hC$o< zU_MY#`tV-GB2-ZLH4eSKz8ntwwAg%H&Eruazb_l*x2PgSyUT)x-T4$%=PcmmaKQg} zu6<=Qf_`7xU<1>j`=ZHorD;jtwDPWy3BR0h;~BiI0GERW<) zZWzwS*0gKCJ2u;dU<9%?oZ!%hr|mYqloNmcNV361riaELbycQOOMwDRcKt12_dgXV zr$M|tDVh$iukRn9BkhZ!6Ro9NVFLmR&e#4tp5i$y>n0>icAWu!1`nXk5Pp`A#s6ni zI&~s6o%hW?U@8s(tjzde==7kZ08|cvv>|ZAXi=l&b5kUV@aH&s*&=nG!+V1^LsKZv z^HcHXED&KOaPr{=EgVC@CcP~_5A>}!YNbQ?Qm09qIx4d{UFjJLramhXu`u(JU9X7yViE4O*N7+aaw`Sjb8u4yIVqUN#$OGugKqLj zZ?%n!2TAz>xUP`!b2=o)I)Y{ZfGI33dI!uQ5Kr>0;)<=^IoMMY}sL%$Pm1QoAwS`-6nhB)grQ( z#tKw5>{*6D0fpQsgn=ndRwQqQb3&qmp?k9CBp0UZUA=eh_$SF)`&w9Z)W z-^&n_d7g=YR&9?|Q)XW@Ub^np(T+)|GXy3l0CW_XnsLbnwJy@;4)8{Gdg?j->)~rN z$EmD{j3+ZOFn=sWCBICbPP%*KswxC4RF2b;yc@KwN&M5<&@<7-$ncqN^v=J{(*S%P z5o=lXW`D}y@#&vSgNTJd2Z71=ul8%iz<9*rqq+bDom>s3H67h7fk~8wkmUpXkQ`_W zD$Kzsg2SS`6#}TW8or0pi}(gB(y(^rFsv%Yo}mxxGQ2%{)9(C&*TF4G#cZhV8{5IU zx|4`%rkQr3(m@IQ##AjlT*PhY1>{4Q%0bngLI}{&tg>gbEkC*Kp-NZ`9BNXUAx-MN zN2}pSn` z8I>oPUegC%Pf17S8GY@c-7P$J&&|Tu@ zm-{!(!XvW8Mms^H3k|UR<@{Ze-(=?C`@g+#vtfY~tR9Z3uk)51R-$N;7XhdTkQx=-j33^!Gz zQu5`zoA!iPZ1#B9Fous2>L1whc=XSDqrS5?VYyxwyMU>oQty27^8aCXo@wn}#HSvO z^(xQQ+M6D*I95b0il1VmZI(Qlg)CPT0HS9H?@nYz2BrSDJ$V;f?&WeQhcR=R(a@x$ z*%dek!_9Fm6a|(zxj)6$yy4*g7i$R8SSt$p^OTOGr%+=n!AI|>Z|o-7oHdPS$A1OUVHxb`Ub_|Avf<&6fk4Ul)CJ zcvQOEYFshSoXWjSwCD@{Q6^Qv7rkfz;Vw4H|6=yQ^d6&YPi$L(nS{!=}E7)y!jPA6yI<>4VXrceBlNgNItyVn9?3 zKBQQJ3V}HQ%p)U(6_x};d9RQ50Eo0W;HMbS-5jW8G@sIzWim&u0&TiC%e zh0U=E;Mj;DF0sV~y$xlywDYfFY@oVwCEP%HkHVzX5u&iCp5L5u z>Sjwoaeg4)nJeB#eBS~A{+m%dadHVu@5klarGVH=cKSItOBLWIFk-HNp(A}?E4 z#?=neh8ZAJoUL=(^64===nje@Q-C2ek3;oS1Uip_;O)&%=$zb2t*cv=S zSw{qGAV7WOeb+$&!M?L4#X6{ubomzyXkUq5pGZa`!YXRuTz1C4vB0Xe4QoX<5a>a- zY8un{Kc=cGJ7_kbRW_rZQ~@I?E~o~5%r+4}46HqD>Yla8{jccQXl+pwG`lhn>TgzG zk(I-Z_0>xQuNRfkU$F4HQg?W$j~m_+f`Ht;$;oHMV=US1cnQHrXYV} z&y)}PShT&Iw63UCLjW|tl~UA!)b7R=K_Jth2!kBu_=m(zV_eZuox=cwQ%hrL*4E-m z#x>Q)vp~{Wts*x$(N>=S@&K=eTxhahYBKE?+Ff&sK0uKH5TP2T3j_vB7m@YRGYsl0 z0!kTQ+LnZFy!joEV|atJ2%uATs|JI;ur50TLh7)Fh<+Gdq=2Y}{n(KQ@w%kPSy%ec zVwU^1HD!GNTUBGP6>Rcd17PxH#K~PX+{e^^VdFV`l^0=$b~V!)acLWb|7C!SPV3yJ z>*Xaw+nCXwI^z|YIU2DG5ChA8-x!T)*`R{+dAHxFG0?*JQnJU>?BAezB-MrgQ@`Aa zR<=rNX{D!O@kv~R|aCEUns!kyv2OLAFXfxSpzd6=Y(`T6a3sMk_!~V<(LX~^NWo-dID~3!7O*Bl ztxXsO{GBhJVY3_otns*vmNIOIIjDU5{9`#V^^8UhmspOc*2~rqhS4(gtO2f=4De?= zX*0@}P0Nn^kFl;tib!(2rXi2U4FwY=>ctUE4iE9&$?Hl@fi0LB9aEmyr}y|Ci4rz; zNEp*!N=IPbSW4PnvvB%86u}1Cqk=G!{H~A(j{rqy#=2$K^+&j>MMeSD-MDJpp7reX zb+dn97k*D!wBfqSgx|^e6C(A_KLc~n)OAh#hKlUftZXz{fMJuCiiO^H{qm~c1XZl8 z6@j`?#(I1%LdT0ofuF{}lJ_tUGy?gz4h#UX8*QT|h$a@m{#zgEN>$fTLQ4tr!k?(6 z?Z){nkPCLZnw=4WSR~^94cI7(vK6UA`-s}}6#SzIV3`WD)xq{70MXrR_1KV_%)9n< zh#3=b9o=y&4}{B3nEA0_-*jK!jC!Rd*q*789E;wK3F9PPEp61AN}$i=trd_Wb5VBK zrl)u$qIp$%c7IL+Q1M~dtD{CAGs88`(ec<^gJd6qh_+xn=v7AGwV5cJ;m?sWfcnR^ z&AUeegG;YD!&?wiuA!oW?dQ4%1cZB0d*gEW22f+^o70hGb~uknwmb=&;y(Btl#+iI z!s;L*3ekR=CbprjA3IJ5ey<6P(wc!zOeX@pApINSGw~acTL=A{u^&ZMim&bTfnc|y zyVvn!bB7UaLTyu_AE^g|f)Tf4Z;+c!)lZL(&!Kfc&9!M!d@y84je2~!q+B&mIIHOn8*S&EIuCK@${pOZ}LJ6?Km2;}w7c8=alKglq@ z%H-;+wmChwPbqgubjrQ>*9ICYNUXq(td^na;RtlXd?V~S2szany9H7B&D;kH&0G6J zB8*CZbc~lE)SRdyKxBBQ&Nvu2K)?b4jPFJ1N(~tSOgR{)t2|ly@PET5?n;%IWbg_2 zy`){XY_Dt*G?FLOMYSt~-2m0jV9jtjFV!c6`zbaMCUVS`lYQeFQYre;>a zK^GlHG@Ksnt;0}YpmLC;+bE~;ZXjFpYzf$+hV?AB+7|x?ral}^q9C+VBc;VFTdEmx zGDjbafO!ogIriiY_u}N++uKN2eg0V(yYy8gsmmhAnQ~%FXbLe|C}t|rOUY<@vs3TC z4k}**yJL{GMBA2;lC%$O82;jw;Mw|0n6Z{Wts-ZX1YW0dL#ZmJrWEND&JZ$D;37&a zQ&TSpgB^EYgLr_B_KM|jz(`1>_u4l`hzalh&y-xk;gJMNv@D@Rj@NhE;hEkYDONgR2xGp>CEMf!QHplg1B<`H+Rdfua zm8BJk*_FUs3J|83Ew6Mhs^q%aGewRVjfO?=L~AHS{^)gYxoDk#DDDDg2qnz-@LQfT zs3|9eJ57&Z;+F2fI^h(M3_W>>Z*Q$EA#;tt`CZ0>?_I|73HWz__kQU-u6}fwzxgDb{G+(&DnfVWilx2rg?3 z-xRn+e+2j=|7EZv%{adBte`b7?AvA#AYYc@(a1Z>4P{M(Iwp=tj^-UqI z9s--GHC@ZpA>iye=f8Vm#!l;@6+NvGfVs6N!l2Zjq}{YjS`Pq@9KmQzkrb=HYkmd& zR9Njr-euFpD~!|AziUh!4IIjI_F($#+pOBE;QfG29N%@hDN2jlZ z!G_Ap)y$rrW@_*3#+8mQ|8B*K@Ot2^V?qWJHfv4ApA&k7*eX0BIIv4`25mf~C~Z(% z-?8zUaP6!_XUhvLj2=~gBN_$x&WI(i>*ax0_0$^O3OICXLyW)S=UV|!{FctK%XK$? zC^U`+BbSKfO{qSrcese(Us_yORapnyEcfU;0~M=GzYB?;EN5A0BHm~SSrc?bT`SQz zULnT8PPoc3VuT!{t6SPIa(Mggu;$9KyHUbEU?GGA(JzQzX|}p{g^U#I7?@B+U=!CMaYVXV-`%Ir zV-W)W>?jWV%AXbl0GB&!{v&!NGRF1)IF=!Rmc4AReNpYwS!0|CC*^sK@z=MF6}S}2 zSgr3K(#KKle?!x|7wIB|Wk?&HUYCpwst8X9D1s$c&Coemk%?EgclVE*^!i@E0_jjB zsdrZ_JMq@S-dit_9MAjvmq7Gvf5pIcdd zL_`O}LYm?H^18%&pcL2Cxg~tje%%4G3pXswzul(8>+Abx83JhPsL<{{uyJyo&N^cv zH-g4Z(IB=J_|afFGcW(c!)CKhhgTH>nyF-*V_qdeWgMYvl#Jx4D>|5c$jR^B!{gI- zn_gb#5Wq-wf8oSc)~od;1vdxKL7>wHlx;Ixi`ryQL=YUG|Nil5vrR9rIRr@EoRXl* z-_s3dfP1cCH$Q713dc3Ra=5M=0NzB_eEf$Y?GG<6Z|_SY+^vUHu`=O7dQN7{-aXEW z3{K&KlzszP>1|E6QDzSYTTSb~-7V{Xj(tApXp*pRM1IBr zE3EG3Rte!F!dCUvijf*BH-qX!Z*Cu-h9Ccr|MZ{!%YXf^ySuyFyW926YSk8#LC`@m zd)9EtTS{-t+*Y1hKxx*2>QBR|rHd5d=I&uL{P`0>B|=kfXP?r(2zZ*Jfy!s&r6 z*k8?>Rb3b?2&TjE{UJ}k1_|Qn^!#%DQJfugMJ`-rI|In0G5x!>hk82XJuUD>2S`z! z2Tz}iL3$L(Eyr!XR5Cf;v^(b~z ztg=~7N5Uf3GiV&JfPOal@pnVoAD;L7{b5OjSofBBP1fqjf7fHR)Lv;Wd8{%6rd>zC z^Cv^q_?n)7H;m7J7xmYHoEXObs+8Yf-TZO#(r=igN8zt8J`R>zk0kn*(}Q1CWq z(FHIh3%!U#xXB4)ZY5aM{=407Hw?ozZ4X0n1Y}dFkVoJZ=ik66oPi>9k^olZvM)V( zF`=acg4ONq!^6{dJM4DDZnqnTZ5oE%v;%SoV3RA{luF}wMCH&l4`j6BOaW6~U_YDf zb~^s~`G@`fa2Vm>)0wIeY)d|ND(xdV8ytOJD=wnW1j$z+${&Zh?>{~Nxcs|qUjOIf z3eeV`3;4wTsR!yAPikHh9$j5NMgax3aYg{nFr|F_x6|qmo6S>tdwqG?AIcDLy3mz= zWh-}uszT|j0f`hqVV#r#*h~Rbni)0k^7bEw-H+X{8@AicQ+j)QIZThgY=IW#Rnsmg zg;`>gHkw6oWl4ahO^c^cVrEB)Sgr5#^H1UL=_$Rxz8ntw{quf**uT8KE`T zwKXi3p|6=4x|?fKW6^M#9)G(1hutu2Hct=t>EUs+-5$nk@NjsU5#b=mP@F@hHI9xp zY19^X981_mLU3Y0ar}3?AH)3oPfy!n*zFH<7VvR8U9L72c9hl8tcSaJT?gYg3ZtHz-S+I6xqJ^qky|Lu13^l*QBlg`J_ zkGHp%L%s&bEnu*t=v6_m!8qX)MT0(B1XP49d{%T9%2p(eW`d%LuWoN2%G!5a{@Zo> zI(>e;y}rC04*UIKNd(U^X^-Ycr7K#>mTDE0;CqK+RSJi>L_&B|)&AS<=JE0V_9mTA zpYLz4FE9Hk5k9=Wy?yxXNX(fQ!Y8qTf8?5udZ`MP1L%if*jJ=xBBS6QGGR4BpT+Y( zP5Xab|HC0)0WS+L1de$cq8=e!f`^rxubb8>F!)kL#ijPr$+`Bsxf>sUN&X)n?{9Bb zDdFb!{^4n}9bTqd_+WMfXfwk5bTyKqgD`uD|V?AEFo_H;`k$_+x6Z3!{cT< zyzCAF4m@8;HTD?Fi;iYvgrxCSyI#6Oe)5k0Dy7xUZH5K*xg+%Y{_**BVb8ay$PtkO zJ-alPiP)d^3c|DCecVr}-tzo+w`+{g|M0lk4#QzLT>*0lAbQfEW_nV)x|W}@{=N%# z|F88;hCX%^^zrogcz1WR8o$!*dVB@j2@Z3bf+_v`T+;u&zt(yCPsr~uM*oR~wZhHq z{o~_iJB)?UVgKU6q5t=M86)X+lc6th{8LJ+)y?hQ!_#K7-46R1548!>y&~Ly+M4(G zK66I?bN0X4JU-st-C%kC$H%AbaF`0gd<9}g$-yMb&f0^tfMQnb zyK#RH`S@=hALiws{_fZ5`2PO-dMKMB9|{qt2?H9EO{hQ^S}8Oos)ObnE(4IXLeMB# zUk|zQIn2lZrer={&Zp0h_qUgq{b8vE>kt5&f6f$?08&pF@vs#9p>JL}Xh|{h^H4)y z)${XmK7M|@y^QPsJi&qXm=xR+fN~w-r?V_;8tU;7L^A7P2~fE@U@U$g=Hs8Sue;lI ziN$d$geN?ByaHuMh+@MAvkdTyALF-641gUn&8*%%$_}(4q&|{9ALqu$FpSAhvHa7I z`uZAc;r(IU0X0XSO3^rQ;Dj_3HI-y3`_#Gy(DbH;9f4tDiW?L95z9Yqfzz?r0!9cx zLmi_${j|(4!%^8PZsRv|j0O3=Eoo0fb^8~}PG84q{pTy-H6H;QSVa{8hGs=hpPrWFr`o^^9J1f<5BuT>z!``16r0eI8`eY>ix5n!!Npk05!La3c$gZW z!vg)R%|h;c`uuo*eLa{VphsdL4gj{7)SnCh8oAHO|6KcSHjfYYcOv`Y90JPqpQ$mY zi;Cc75>OB8*yc6$!KT$kwY#gRGe7gQD}{u78aspS%CxUvmgpIs)^TqZ)vs zFQ2Fz60rI`cnWqj!@29S&u0gKne;mhGxYaxe>c*eslPVAPM`1Z^A;$00eyt5V*O!e zrLcUET^7F~+)6`g*JTij~ef#J4HD*Ei7C2aABH@{<7s3nx(6jfbA|Tq5YUnus zbNCMei#@w6Q?WSP~CzPFC^7^4M>LSXqUh#l_H2yThX(4rXcnhWa~JLU|^S!>$035}tUi(~?CAIeEt*1H)g zc1X3SyRzs>8qZLvPmi;`ZBapPnA>?rs2Q`y#_(#~BOAiQwC3kqFc0 zI-nlN>E>{>FTOfaq^(#t?~a>1@H#`EI{A~EYTC{c4n4RbfXQKE_yGXHJy_rV@Ws<; zbFEn&|9eUL-fSKp?$+e1nKR=f2XT3&*Atg*%kime%m1@Kd>|& z=Cbm!yoVmHkZEN51`F0EQHB=;!;C1&v*?7po}-$@DS} z6_FZFNOXt9JyX91pK1yKH`Dr$jqj=RGd@3O7NvG&71>_Kr;m`#OVOuAh6&KkV zkxH1%L9cBZLn2<3gt-a%-5Se}4%sVCSwMLNUf$k6PO270r8@<~RZnR)C0DV$eQVSm zH%F0R%6^8j@i}!q7$gAmkt%M1=eZD&A%LkT0kHfbU5%zn(FEQ##fG9c7rBpx_VoC0 zzrKNO>Ze}iWln^ruHaM)Unq1Xs{mk$Q^4y`US~YUpxl#!Eu4$r&1M*eVYmA+?8eT| z^!#+JkIK6Bd_2A{S-^g|0_g;F7!6p|BLU_BB_3RQVNU?Cjz>;@c2V*hUuF{`T!ny7 zTQ5L2XwVjXgFDHIKDz>AG7E*?!OZ#)tgko#l(oxr3lxd4CIgTCtU~-rS>A#^S}7)z?zFe$MLG>274F?>cj3S zH0g=GMekr4`I*+Aey2&dgf4SabUzh>H5OvjDX6RpU}lSOY)x%>SP9m28%YsiC#XiPDHSYlDgLFDdl|EziT6M=0MPv}swp-Pi%%iYZdD=_Eg-sQtLTYB zRYcb^`JEe|3-p&+kD`&!fQc<0$-=@W)g)Xs@uZiArxjJN-foqB#nCxoFTQXo1pp#lH^SJFvD zK~$?K`r7>FA)S?cJCjp}8WMUmJ4vi;UL^8g(4Ncy5ShVYriKMq zKr;f=WG*-vQER`@ftpGlLf!ZswogxwMfS(!B>+m1mCKC8sced8)ui^A9J^E@Q%YES z061G#l5&&Cf!9Hge6XovMaycAOB6ip8%;sC%|{Ofp>4(<7D0kf)!z5abU zwHN5HcG%NR=p3aw^J;zj@VLoZZ^IJ(;gT=x0I(=@P=|m+p%);Uf(lhOmmYRUtEuG2 z&I?h{*^5H%e`Qpf2($+kl-AT5 z(K9md1%M?2P@(2_mK!1L#*9?;{kM9J4Qm`of!bNeA~L0IH||(r)JS=y{P>R%gBeA z_$pQhT>+r_0+*sMEJU=Kt_T2TtcOhj_Pm_c@Z-XH-wi*e@INg-x}dI9_1-PPReseo z5>qY6U~cvM0l=K5qCyI)zW3Dmk;u26`KhNYVK)O&YU<@HV1E8;US6oY(-$xI>AdI< z09C|Z-Hwt*Ir7!@r5CFA9QOvuS zq|ZAg_fQ8BnlYEYhPzP$i9diKAlR74cJ;-0U`wSx*_!% zhQi3f^o?m7eemXH+%5czG`$t=rZ`9rCf0n7e^t!9=H0NC<_DlIN==j9(ttv|Rm zxtruV;E4WC1xNfaqCqn|%I~q#CWtWD6lmX7iuE=w|J}{18Z4mOnl;`9g6w@ENEhs| z+!5%hf9Q}~px+DPH?_>WFB5NDN9?VkToa~%Rku_Vqz^I?W|u(_d}|Fv0YJPkES;Z0 z(B4++@!H-aRWHahvCq`FasN++z}uJw>7iZ|%C7|ku(r*FtW;IJm7Ck;@qY}vTI|Tz z@IlfJJm!dmO+(Hc@!Sq@UDru2=ERO%w_m-d{JW0{egbU^Y zVKFH;-U842T0xjm1Dxh3)_Dhm#>;lwB)B^k&TAs50<8Q*%5htEv^74wkcz>5N_Za9QXrJNl(1V zkslhUw;|XDsdgBNcoaA4*XdYIDG0qsfKrLbejw8=i{n@Qtb>0D`Qi`d$=jH=E5++lR&6lXV@M> zS%zf`PrVrtr_!1*riR0J)DZxd=dVcbE33lmJOAbnwKmITRuG=>;KEiH2HP}_09e=H z7K~QCJh)h`r+NPw`YDgUm-0eKl6hr#MF2>N=OqLbI`XG8jR4Ry53haYqcc<<|B3WF zNu462*Jw$K*a3jPJQ3@T$XogNFQJb>@UIz9K-gk1H)l(poH zfrl{*oY|qzH7~+sIV1G;qc1IMXD1`{xvh%7EO*1Qz06?wLwDf_5WSp_rF^WV9oYx~ zOMET^0M#6&IWKxM>D=$8f!9&(X1zA57Y3>uwQfJd7ggmBV+dG8ln;lO$xw=^QzOlU zbr^D5}~>1kxW z{ip}NLtX|PFHLjTPzA)IXp|vvnGrc#+Xpqoh4|&bY62{OY*IQl2YzcwE~fqf0H3$1 z7UELC;g*QLdD+;8acljIHZ2I1(DAOC_=~(0XeiET?>_pO5>zFUvw$fP+#e=GsdFd= z2#+d>&=*_Oezn2*IP$S_trucN8qi0n`~XmBCoXW192RDG1b|N>)w|&4wjjO!nAMKf z&m`T~@Uq{l5XkUmZKQ&Y05GqAHSvcA?%W*!jPL{_GDy{=+EbIU zo}(%O=!EL(mclghu$iC_Mf#-qoHQm*RnMauXK$VVl#s174u}2z`MJOXw1OW@2?4eT zg6l_^XdgqNbN}>E(SE9}4U-30sT#v}zldgMS#iVpl`lhH?LK}1NwnRUtrFgpr7dn;J=EysLc92!_$2IG^xFh5McBi$PTU` z(%(eVlN8w}k2)+iOSqgSoGqg1#-jAGS#a?-R(^n7WLOiD)^sM);Kbo5O}kZe6d1F{(g zpHsP3Zz~KTTY7zaeh?%*V4>_nL68+wajmBWFw9@p+#{)Y z$mD$4R5y`At11(Ky{s!PH?u)f6!?QO5)T(#a%ye@F;)dao4#1w=TSmPcIY8Pm{+@M z^uR43|-o0 z+#ha4IWzg$9H@$n-T9oEXapG<%>pt#2#*6Yyi z9!rkvfH*#YAZ|3`zI7$H;$q$h7O+^tOU=#fV7pJqNF6AIgaa6uRVN1A5IreM04;AI zzU)T%o*T$;fNl)2y!TH4Y#Y>mD_&U;xA2vg-Qn-tR2vsepp-AUU(P(m;t8g~q z$V`!zghMD`uiovLrlF>9C6mLcAxqPs)FrE#Iq)zrf)hMaUWT}R%irgD$^K?2{(eI7 z6a^%KT-okoL7d+VO@4t)8mlRif#A&+AU@+p_+8dA(ahn(^0DumNYH;D@}$way1I!{ z-mk{AxDs4>WIcySZ|DWxm~5t?%evP?;QRe@9dx{NIAaR^T6g$9@hZ>Dlt6-7YI>_5 zJ@I_%DMXgEy9*>wDqmQBR)$?_N!l_JsD=*?`{3aZ?t{GC-!8wsxl**GsLI?rV~s@L zi$jlDURQU!uNhS92nRUp+3^gi7S$4mh(lgpp~n&jf?w{Jw}5f?^2UA-AKb0Jcs`F$ z0CSfiU#S4V{^G|TBFlk=Z&Oi}nUn1SWa1{6khj}q)4m|^+w<)q^ej(7@2BgB$2*Qs z8RC$G5yL^D%l2F~({fUn18fOLG)kaHo>In1c_u~5HBnd5sD(znaBtrkixO z?N`7=`XIuABYU?IyQ%2}jsSEqP0KwR5Ipa~y?9Y^#oOh)On9W+X|7Ih-}kRXWFd?% z#BHYd@>2tTJm+8bZZoI=jOhGF9UsjrYjs3Rh2V{P6EE4L{{K2d{}XiWPcMO@m!|1Q zTStH*PB45L5V1RV6kA{)ru>@V&!qI^v(hz+d)G%VT!}Ny%eTbUEpf=(yKe8h$3q7C zHOVK-O^zSi97{azt&&!?AtAW+6G#e8 z=WEaytThcAn8@ua{B+V){|UeAa@<8tJ@`oO$1G_3J^M5xO+WT zS%L~!^-?lZA^+Vor-yZ>eP#QgCW?~C`ELwC0Ac^;}Ee(~?XCbjIp_9c-n7~jj2VUp^|7J*N}`ML&y zunb@3z6o;<=R0+FG{F5}05qiC?GiA|^)DPduEAb?E8LZjuY*m8Wn0r;Y%JP-eggYH z*~SJ9p93FA2fQxyMYQWZ91jQ3k=XS6m+d2mPS6kuI4c@ZLNJH3C;U{ww>N}yeZC@; z8@3nbHJ5k8Y}U%*;z^)UV$4^eB;DWx7`Whx7T6a`8HP_N>9dD3@quZx8C7!7w8vC! ztFJ#pKjXmCGwk6-1q%^P^_}_Z7uxciqu<^?&V&al56`S0#$?-5yoT-p05q&2y~xRq zR4MS2b(F7R88iBX`2PQXkR?bd$OhX#1NV0!bwHb=QxcX=2KM#ZQo%uM0k@H`-rim| zWqSnH8zjGc`>hG@e^b8z86LGxp#WkFnM-zcKxPjOCIY*e^n-iGV8P zb&}3UE+k9!)}uGQvAztwHd@nyuadVjiKl%meJhaV=4-&1Ou^ZJM(J7diaZV&3|SkIwFh0D^+rfW5L8Pq^`g_{*mB~cu9hS(m zqHMQ5ZLA?efV>2J6SUg%s0qwB-4aK&R)EkQ*L0BX_7@ef=R?}}26hShLzxFddUp>v zi(*9cm5xqrW8S>I_c!GNV&dl9hjP&hVKLvHSn2_uz217TL|kg215-JLbto_Ijv`>h z8=$Ox-rL7IF`Nz>j>vws6a-^7W#;IK9&%ZOaV{CSi2;66VR4Ihh+ua&FrY#B5qpU< zA~Ng3c5r&QO6F1iK?GP>Ni@P-v5j^qAv8OqN(qtWo4wE)y zJ@?h&ZF*(dl=~7UuX`l+*3=4sycJ0`*+M%w+DPzatM3H1zveT(`}&|G*Uj@|!0XJI zcpepXh?LBdU;=4B(F|j2zxxFmHw<%oHq%^4#Ztc99lK*6NW8rFapg&gER#!la=l`$ zSugbLSCZcBayUYug7=X_6|HD)wNjR$=C%!Qzu@z`j)dvX1J)B^;tSOtnX3^I=^@%eg)&6p6Z$eVZ3t=M=+djMg9ctkZXYB1mQ{1cs!}b1{rP4nY{SBvyzK} ztIya8;&-%f$y68^j7$sNt-(D1l_Poxdu9KYmrD!ehQAiq`w-^k)JuU!?}353>l8tE zQH;T`6XZP_q7t?+qY;!nOmGEQ&R@t7pIAQvVxSOr+<8Y~;0$sMOT1-2g_ zm^}14Kj@_3j@%4C^CoupDgcjeOA9~gXSvgF{g^q6^=+JF_~unAx}tteJJ7%y#yk&L zpbD`yNAnRNHL=~sb=3JIPxRNF7cuw?pkgH^I3H*14>mh$8S~~hjYTo7V|G_@-5?Vl zUQ0|7Y*U==T;8xM4X$)F`6-Zs-F!LyfrdW zjzvW|D(>2=ipO8muTQVM&XDEDlzYVA`LMGjoziLmjtF1YR}M-PZ#!e;fpKMZ%u%AN zI*}>Dxo6E`*PE3uc9i}~01lMffoFE}W*<cW7*$LSctpRepvn4&mAm``lBJp(>3_ z04H_vP-w~;V1g7D&D%_r9cvk$U6OYsV-KS32FAZh$iEhoxUK&AeNBQGpj;oEAq0;X zEr2~iLP;m8R@2u9d>^lAJAP6!n?KrGjYEPfmhq$#D9Rf~Qk{W3SC0M?TAPlJq^G?! z4;KSXoMLTKFtWS&E(AZk0Qf9{m{wlMdKH}2om1hFcFmlZy-v`|LvWG?Yb52v22~TX zInmMCsu_G_>X-|!Z59Hlnl*BTvl@}!P_5l)vJ1f911enu`QacG@ukUPJJxFh~~Ff>VhTe<)RI%`(5Mi6`!l zDanC#ZY~oOV>Xh={E%`T3u-g9(9sUD-NuKCs<4YH71SRwGUxa21o^>1__(I@-nh}{ z2h6CExSdRq)Bs=0Uxe3hqqzw25fY}Y+ec(?#v4HsFK|5@ZC@YgOuTtUs7}GMS;L2A zzKmHG6?DXxx_rzT^VCTJ_+RO*&q2AadnGk|Sgc3@%K2Na-wTN5$J9BYqjid0#V>-M zb^3sIJR0`U`{8=ivY1Ib2Mc1|5Xm_Q&y|!U2~Gz44cNP3U0Qgz=a&E=4h=h`YL7fy zlg{n%Sg3$a)||PI(!A`{137ag#gd94)^k|d2l;047J^5kHr<%16lr%dga#NX1u>Cf zc|ayUuUh7($+GwAg~nMvyv}UJtz;yBn-eub+hCxHyMHR?#`Xs^?g={Czi%X%lOfWr zHd;QZhGTd>1WzTf^K90YtO_N}chSJvF?4?s2=Z3#ZUgnDO_fHlNIr4dp4N3-Kfc^N zXpl%`bE)0kOqRAO?nWqK;Z@;t;oncH1P}ASL|tJ-uAelqdJ=p*8-#m?>g%Yer$bK! z>+Mq!w@@d(jY-%^>WyOTgDqpL-$I}to4=KUuqRvGC<$&t*ekYhAXmhcO3wDi^#g8R&hKc<0=}fI28x`&UVNa zJLk^n!i=$?RN1oA?X^JzaUx+9#Gf0VDm3g4b;wfcZaXsZoR(qr@_{5zCVHS;71)24 z%hrRg%4UGV%Un|&5-k%UGyz+0r~DMb--oW~3d*>^!^+$1>OU`r2k$Mhi@DkLf_Bty z`n4$02X@MqiKROTxrq<;CEb#xsDoeMfGA7Qi0?8KWmNtcx;B*Y@cG;goXwAGFlOi> z<%+`7xoOqAGof&?yQblNxOLkL#p4)LFt*4S!bVQ$pLIgX=S9ST5l9(iR2?qKTW}1E z`9a_p3h2(#hy-*+lR{e&JxSQxo4-k%&AO&+h$m#7_}B z^l&49fd}gm2EcBjd3n3tE|x3_PsMYzS3wEDb^NZ}9{_9xl zd6N*ZC8FOgPbSUO&7eH9|N3dNg>Z~NrJ=R>wFX4CRD${PL@`Hfo`;a;PyCa|{o0wX zbPE`Bdf9^gS|v=nlT-fzJ_-BB?g|+#h={VO}evuM(h0XQO!7AR(%$!c>Eb{!IGI+=d z5H^45)$B)7sD`(i$ar6Yw2@3L)LZ^bL5|Lwv!z9(gqt*5@Fr*KznDCdNaH=_muc0c z)#FQb3E=0r&yg;pVh_WZMAQnE7&|@r-9)yuvG3weAUW>E;1+&K(8LS7YQ}G>B=|&C zPklF8tcxXo2|jpjb)}b8-MJHQ;#8t{TK*(z__0Uh_-;~yJ#iY4`d8KXg^$|P#Z6lq zU!&CzrouHo_6J3+eQQSrWTNY2uyUXe-!4xK7u|9ub+e4QR&9LA>A=q2luV&p1Acor zbb$xZ!{VX5I|KqDPdw|tXA-}w&jC68r%YyMpsGYrf|#{}G#68!oK>|Ub~E}$?OYGS z$GS7tb7XZ)I+eul>(ua2G0dgwGBc8HjuAQ99`LTScXsg77|H$wZT?G^St+WMSP(WW zjIKmoZU&eo5MK2I4!*9x@vHK0P~j~(1B4h0px0xv_%0a+7i$B-*0~T^8uTE$^ApOC z_48v`$|-rbIy#iW_u8)X%Z3SVOv0$`Ug+;t?a$nId3w|2-*tPBy53;%gbD87Kte)H#jFo`Fu~Lq*d5!0aliFv z59}vlt43_eeYYqSb1sjc^*@pLyG001H7Gg-in)sP_&=RY_tjQEQowH+*Uq+W8(Tonm7YF&A|TV zfCQj+(=6FVS)f@viT$GotvCg(Je0=?=1df}#FaYYl4Q@3$PW9g+X?D~b58oF`0_OZ zlJxsO4;FUN^dr1}$E_dn9fYUT zP#$gq@tdQRlG^<_b`$E0!wLZ5v*X;>@yFwyx10%=k~FZDaAEKyMfsS+7Mn&f@%32aI)ux(C8H<$}boz6rxJfbQM>R{o?DQq8^ z1ha_nW=NU2EU;*ndt3|aZ_HB+$>$7VcsTEUB_Qg5gq4_y+kXxi51gC)Zd$>qd9l4b z=ts9efd<&Tn6wG&7Sts`04S$gfClBwreXCmb5;D2AR-h8F?B)2Qw}Q00S}Ni=c76_ zC<@IFG%AE2Ffx648yWE>*1K9gTq^(S^vDw<61PDy3v4>kuxq%zR)cILP{pUmAp7e)x?}_h@6^nl7XZ zpdyX+YmYfs2lTC}H_$g=!zs#~sB{6U9l?664DCG_A^b>~_KPjWA^~X{g3H{bb8aJ) ztX3(Byoglab_{LLLpU5qF^1}iL6G-zR{o#|i#^%a&VR>G#OEeHq1-phMjW(Vr&h|E zG~HCp>@SM~bj@UQvw234jL^p`cM;mg^DREFM;R?Judv6h%)& zZqiP&?OBQ;mmu(?;;A|x_+Oy=`A?OuV-$Ra@vn!|WZ!H3Rn&K-qH_R$x`Y4o)+{V| z#{!1xS4B$OSiJlMx>-iIC{~MJIH3dbGH*b^;=C<3pe(7H);s0TJ4igRrMCwdu;$X% z#fj+DrlxjWoBi|wH|c7~cMgOJE1|ZE76?wj7+q5DS`17sr+gfiR|p{be-%AzD`aBw zwStaNgBH-;8d=DkZtv?xk}tdCqL5#HP6{~s=ML4;UxM=ahB)F+8g{|JZ=%Yxkhp^P zG@Q<1xW;Rs!491bpx%n>9||fTEFkcNI1Zb`CB!^cZfzzA=+#gGP* zOa1IP`}5%mbH*d)&KfZpTMCP`DR^Bu!5|+9(ocfgX(-A?u9)Ef7K-v%Q3-5nWgiYo z3+E@iW0B>T(0PUL?72SmIBLVct0<=Is^8b>eSIT$l z^16((nEhvhNZ7vQSm+(!rNHPmNIMb$tQc-D#Vko$swHm)DiTkzolU6_q>#tJFgE=) ziCeFu-(q~Q=LT>r_BbC+)0aM}FastbXq!l$HX9yP_4PDSR8#&)T3eX)T{FeSmFOwwe!`(Rr=2gvKrf^2P}DWkMeSI0)#o_Zp)&G_!58S?5C z(uhNiG0p!eg@=4!fgN*L21P$4{cAktc5I42`iV{&{A2Sa2)GqLYbRubTTLzZDhRdyWf@Ozl~{eRk@Yl`7B7^5{S4{*JNe+%1MeVTZJg*ltHq=avmOdY8Eog!3(r zSi>XP0H$C7%jtQfk?|=EnYvsVu(5`WMk;XTT^ZhcUij&#sB==N=l0Tjt_lDcCgjh~ zx*Fhek5p7e&D))6B-kkJels|Wn$VTteVe|t0+_(tApmw6cL4uv0SlOnBQo>bZkgXr zv7%b!xsM}#Wfqt|Cwv}cSc6hpFDebt&#b(*W2Gn)eqSDjzbz2#LaiXKm-;wq+|1^X3&k}kEcxzq|7XMn`Py@a@V)KOrlRemboT!297eZk6KsmVEcQ-FD z*Ba;nCjfQcWo>u~2Y3KGY-T9+Dm>bhJ(k?XSIO03ZJr3t0l%ch6=l&+YadkQx>x{x z8c}I>KY2cx>f<6xZn_N3L`Y;K=Fc=%Tf|%yI&e^bhYNpyb$XDMf#6Guqw+SIgbYP? z`CmWY#7i)#ToBFSWyk%)6o=B#y#SfKtn>Izj2>%x&C(U3_b!(g6d=uuZ&IwH`xb+t z)jDbMf!=n`$YY!uL<|yk=axQ+p<@0xuzdEBy|XSZi!;(meqnq;Y2q;6p}3&{K2*T) zq=hF<(oL4V!yCb_j$7=F5JK<@^K7PQ_w66aMA)v?FEut5H73OlDONqvJV^m_M=qwa z6D6q!Lp*yHcz`qh(Isr_#>et?VDEoc$E1!+48haO%C}CO{!{toVuKTP>0WOM2eaj! z##nl|HWi9U*cun8v(fjp$#D`vu2+9?vmnW0BC_>Hv(%Ah{Ug9YlM{cV6N;%-1A%Za z!=|S&r8C>1h0+?0!f#s)0jr{=Qu(G$sLnC}QY#?~>NTkp04u-Otsgd&A}jn6tMoDU zr|(gyFpu~ch*Gm;-}|m!{XQGgx(hI4g~rYy@}Y181W3y692`vP=#WF~C41NP$?^Wu zaw+dy;l|LFCwAzSfmu|l)w%}@XjPv1(wqXfVQ6-doD`UMF)X-XrfHd&)TwfG^UEcMrh0iq~6;guG zfc-n_d7_VasTk4Fsq_0NM8#R_zE=@N>0B#_Ygo=A<6qn}Q&o?A6lzvYH8Y}#@6;Xf z_6$miYa~oLQyGc|!DW#U6}k9_i5%N5#HhR$OEAuvTZpzeOnU!^K=FxaM+}qK*h&r$ zgM<@=z3d?P?K}why{om4o6OeAW4$X~a3Qr2cQo@Bb?Nd0hRB~@QFEK(f;TG@A5y(7 zCz)MDYvrF7$hr4H{DMCV3o#DMPV;jS&hs$D$9UaY9!ugCSccrBaf>7F!~8Y_IB3nn z@j_9yrW#_A>@~sRcBSq0Zi|==UyF5wc>*nk8{dGEIRufVAlLcT4^@tlSvFA^D$9{p zO7N|r5*7?riKle8aj3y}8IEAlCCJy94_1Wz@ywBN?Ex-&n#IkRy#zPjf9Y8Pg34!e zL*%_K<@VdR5|grXKIfk*@e|+W7*6p)Vhj2wUcQIX>)$a7o!VU!$%H>XlrGeZ*l?u6 z7Bg8fd15fT&I>G(19K{002Ju=lCgC2^72NZe408&ka`{0VziOo^5NZSYwauj@E=SK zXao6e#6(&NT_n$l95!2NX5G(cv_uGsW)-}NG2zDdw2&&fcp7OG?S4XHPTP@3Z{e=j5x+{&Kzl)_AUe9!<(7^&U+T}S zR#MZ?y$>rCxplK$tLj{LWhkufwA-}_JSlO2l8(>(N{2<^*8bo%?_?QEm<9?8&4kO6 zOG~;N&Kpzpk(1sW}dy%JUvYdp&P zI0DF&<9%}(-4SwlegRS;=iq&k^!fW9zQK<=>ht`l3$yDV-wPvKCtRcW zYUs0Utmy1CN&18CC#p1AP=#g@=c5G<1V@3KIw8hCxgr`Sdq$k7l>B#ssIff(-#wK8 zzeE6cTO=|LxQU@tm_IZ=qkku{wlrNJcS*D6E)7w&^PEEo&F3UcRXcdWd@Z|Iwrh>w z#S9jqOI2;@wORdvGhZ>KUiUE%uQnJu3yLobWg@)bmOhZf!rm`fF_Mx9+ia(_nIi2c z58$oSAWERrnCMq{Kl-I~ukyvXN5jKn@>s94$NDsLAFI&%()rDOLPr$u-esXrSYbCl zK5O`o`&@~2XLh+8f@_T>RP5;@0nbef>b_L zHBn(+lkv(X&-~2n&vDk`v%JsY)}2e(`S~d3aS#vj@TLGlRjZszqV%@`CD+#e9pt|} zWX(ME2h%9E*C{fFswjn4#W?a9mN)IuwxbGaUlCpGlCsA_(Ifv^uhN##z|aeICc{t) zIW({S)!j|&T!_b*xN&H$y|9)v)s4P+AZU$Op93wI zP!%-gvx3}j`>A@{?U_XjJ`6F+?o2+au~QBX0FHVcDp0iexRUZE5nc5**Is9DD&*iR ztT#6S$-hKw7WgpjVEb7A{eE_%X}!mvz3ZTQLL?jKB=k{4y>l^ggi`Ut*Pd8CVXf8= z&@|LCq16cdPvt8B%!;#tr<~VHQMuhSXwD|OIm?iTK983;cX~jsQ}J(M-j?KPae7jM zTPGVT;PmGMb^w<;R2VRH>%^zfA5E}&>%WRhQ%_7J)J%LXQD4n9>SLEkMW1z{rfFAjds$WwU0emC~j&G%76`M$U&{#*JP;vK@H-|l*(iT^~m4H-z3A}Ir%?YkP1 z4%&AgkyJDKq?1%l6>B95PDPiAjMv(a>CDmD+alhJo6Xk1@4hn5^a$Qx8cfDyuYI-A zDrwpEM1fr)F24d$VTitn%8{u{^sS@%uXfIpX(+J{M=HZ#)(f-&OzpB!~72uEh*h#PPwEjZ5K^`9?;#qLpCZMba| zVzvrVQbmilWHaSsoQwj0451x$pQtP4Myq%QeKTkW!=7QsyvaSm2>If<Q-X?!)_TbAhn+W{7`B1uid&S}JCmMBcGg*sse|w~|DA;f%U0 z8i|_hp=bADOYA;f$)=X2{bc@)Ab-@BacrjR<}r&SrwH#qb34uhKMzlE2G(EgFbUm$kR;*nYj7*d1j$2v#M#AWP7+SEi_kdMC`QISdqr z<2g1cIHj8tD)>rL!D~-740w&^J_qW=k`PVOf|#RexmpxvOQ}*vnR6QmU|^#ES)z#X z9s5kL$*!%mErFNdBdSEDsu31Emtu?q+okQ@eLHM@~+QsD-`nFEO!C`-ivflub`p4iiyYpmJ zB}*B(>?5UiRqN)DrL7IK1whk%(UK&s{tn7(QNftK-=Wnr-l>xQLVP@*UZrT{bOVzb zsim|c+Df5mEqQCghIgF5+e?->aei(_mNhbNs#|k;;?1shbH;#;EzE{dG^P!D?cj$X^Rno{KCl2@uL*;g!3Bq` zQlD?UQ~nYEmiDn$sULkn#)75n9b%@EnnYG*Ow9K&VO$lW)4MOT5IGCO93fVby~aF# zR!3_Ez5Y&L(Ml+qb2!o4TEVma`rUfa{;BbI)#bStH5aX&-U(hYAeF%`={aRX@eD_Q zo=qcMu=n$yAzewHI{a-Tzed+KxNN4Qs{CMa3pK*B;!xq74#8rS_e3x06;4E8P8s|I zaV`$xLmgD#awL-qz)WRwl-Bc)M-=sy6)5fxQtn^|*{`Dq%h}ex-!2Vqq(^>LO)xcL zeyVGxH?*ry1Yj>)IF0)7epi(TyfH1Z@?q9IxTu!`gd|s_azrwHX$G}%`Le^SUu`)K znTaSYeR+K^?_J?J=;W0dtDa)MX2!7{W2^x`<=dP8F}dE^4M@@m8LRl#TFWNmC`K zUduYk-g)s)t*kq|*6Oeky-{ouvlnh=e}PB14zWnXdqMrTFoiXVPFgDJUcA|{E9R7b zCRP;9`_3@De8>vys+tD=id9@CPwmN%zsgP-6)medwE_F!Dgi5}`N2Zh+-hU`m2>#g zzmew*F<(x@_w$?6_~Tw316if#b4(9-Nfdr?O0m94Yq@To!T$UBK`>TdVSOAfWRd^J z4NHOrIfm90>T7TU1&XhHbl=_N4!01*ZazWl9lsj6Kil}Q+rTiRYRq7Oazmzkt2OOn zJ?SSb>-D?f#a0E-cUa%)uhG$QcU>qo!ZIqr#0$PQEZDJ7%M)|gB+pFKpp{>M->JTA zR-=Qjt4whP$J{EUX=f<_WuCW{<1bs7_|d}i*A1sk9%I`DiYxQeWLmDcafR$2t?Ei% zC9Pidf5N|htcgk2-3b{PAznwjXs*euyWu}XlH*EvB5yXvAQIpi(!rBDDxNajr|<@% zVA=*B`@^$K$gz~(pV`?}unWs6_edk%^JC}hD69O|`S{5P2AM!knjkNmRW3+4q36Vc zP6%=AO|$2ntcLk}J9y;2K8*9xzeE;J0wu2KYTk?pV0bQ{Mm>QN1TVd*!a9(>B!DPP zdJvBLNtEN$8MuMvrBE&3M`xfX(_EwM_&BL){#9rGF~8gW`Nh0nz%$jPeXwKOT9p-y zX{nsQI33#7roI}d@HueBMeKn2hbqb00qV!#4?mY&oDz||r{6|j=BJ-$+Lm7~PIs&| z=6Q%Ny76>a%3Bj~a&XDG3y_oPx|(7M8p|_5dbJD7t7~LDp**4&zrRvZeGvw{zM+>M z%UuSi-|^n8bnlpM)+{VX#Nb@bjD2XmAjuYJBRX)IhnRWohHKELAd92fl>ln;A(NOS z@Wru2XB1QCDWTG6^2Pjem85z^nGJL$i~c>YB0M%e-jMXMxEfkHJ+j&VG6 zNS*Sm1L*$0MjXbRy}rFBizV(79EE*-DMMko!-_H29b{}ik*gf5b^K|C;OltN`zFcX z=Cgb2lX`B5ox_VD4fm2N7(C%jfWl7LG*>!+SWLgjde=Y#f_R!EFMdfstE^o;kEHtI z5x)hCYVM-CHrZH_(`~S|SqHgYe?8Rr8`_|$##wV^41w#KEO#dhRaJFu)>C@M?mULF zEB=SbQae5m+MmAe_%!tbeK(w1kN3S$VX_R7;ckKz z6jy$H@;rVn=FLQzxDfMZD_^%WHDUX5`uaLL)eNQ>T@V~2B<||T>LzV=BJ5rN6mFHe zzGC<>%Cz>sZXsXJJ#f9RySuj+cwmS<_$>*uVtvsUsjvLfPK;6|{fpT7K2df%glgf7 z_9$?r^c_k!dI4+TOL-#o!wMQ2y-Eco z-FtH2)~jue9L%4TVA%vnm_Rm@F{7YFu$(M}hL69bCF6!QYyQMD(n%9XZ+63aBQPkI zsl@EggeNS%Tx?&vL#a6TKxofAiW;7wtJc5sqf9ws!6M!1xlQ9dz``-DQ-=N{_ov$% ztf32e|G$_21A0-E7hzX8@ZJ%xU|)Eb|JZJg*+f?9AHw*UzRVTjGi3aelu#18w3(@G$@} zWMh=??#qLue)-xb$(06BE*m+B??1rLDWvzm?mi>@q#oc*m0Q@6oL29_Xnw03!;_${ zQe{Q9$Sl}!&*r3c7A02zF3aXah2s(qck*}?Y2TeK)oYlA=mRr4I^ zLtwvg*y{VF7>UY#z`{X!Q8uyErxx?M(GxMsE*(5AMSHJrKH?F5P)JndJ>(k^rXO0o z+iae7o5Edes-5xDR)n`%p*-Mb~Eiy34|Yb=#KcOk2Ssj@fdq zqHOV(-|Vlytt^WAEpZCen&$+-mQU;NMBY%WmTBbXEyo#FR4#(29PP@aMBCa#aog;z zf-EZKkkKEIrRd^9eg|=3U-`K=KN-Y46$*L)l0SU=i006~TCLMc^MS~HnPD0s&#CXX zgB9&#L4Dozb=&$qaMdX)gA+EMs^1AUG_X8h?lKl8P^%298P7x0qf-!DzclbIYSZ(L zKM9IAirmygIvYxqi2bbqAy5*(^ZE8GIBByf|N9m+WXF4`ep94$%KID9%}bxQe8Ca# zKN5Mu!5>>HeH!n@oxJw>ScE!VIKyftkSQqr>v2YUBvjc1ow|*vikaCgZ{ax+LkmsL zW0u84s$ZAn5n|KzF$g+*+N|Fc6Ww`!x_)@^7ig9W#)h-BP7lkJiu|7Euvf*?4=eu; z%057y@%{|Pg+ARL)zmC+ZY|sX!*wkT1wzR+P-NE`*34@F8jBv_;~ny_`?aO(`VC#~ zucD{${yRjCU0b%Gn`>aZ+UO$`OXdC!`j3h;8*;u%k}j!G`eU6^!=+05j4iw>s6EnY zG_BrXOAM;VzZi9CTX*4yMgl^qMGr4Gji7?ycm&zE23Q@R9@c6+d9F5$^A9o}cfCao z{LroQabBidQ+{LLW;cX_;=)}7G(zLHatV5jdY>3TgZTr~pDc1m9dzIKLkQ-Zfs988 zfF4k<$;K?Lt?>#~$RBV2KhKI{f?y?sEXDUA-D{!u4U1@#$-5}V?S>@w3s-8>(Sd&d z$%A+I`@t>*itqw^*)Bb zzY#$dJJ;J_@4ViT`x*X!cW*%i$iIpBLmIU&HQDR}MLmb~_E^#xN>b^MnXSaYxKG?p z|6zaH_@5LrZvRJZ`nrmULEZ*IjCf>M{}@3PHpnfaOvh17FfvwPx}P|ne>)Z^ z*b4iepTsw|Hfxf{IN{@|+(N?CY&{GsXUYA7nmCyh@D|SEbj8y-}*IF=o zJBu_I0V^I`u#w7N%c~d1U!fel z^Mm8+v+xlV9Y=$>p|xWL+|Gpn&}8o z4D(gL8&RaOYPc8|??G1+n%QAm<7eNF!K{9H1A?Vxn^5ro?m@BXQ6MHvl2+)Q2z}_^ zcnRr9eCL*Kb(y4SZbL*`y*T@B37~XHpz^3VMacT=TViB&K8ST;?C;RbFIVJmp3$o_ z%^B_xtgdtK*E<0UNCR(cZ_hVh|252^=qU29@@Gco-&(U`4&W)juvkFFXs2iGMuwTq zpv|{8EG(!!2*q>()U_vbRWF`a_ir#(XGT*x>mse4*Qm}d*n%7s)MS)Ep5-8*&GAud zCq4iQdvOYI=Ciyp>}j2NetPbu9gG^9m^kfP#F0qoHJ}YJniF^^LUuxyP*fWYRZQ!S zGDiWPl0;3MiVqARE#gU!Ia;0s)0)|TZ~O@;n~HFAEXEC(UK!=%?`?xV0e1D?q9eL< zp$4K{5Rh8Iv|R+v5f_7xffy0-p9TRASxLo9A#jT$l&Wjj3BYH0Py6QSE6p#B&-~2q~C}GEs}Q+cO-K8b_}5nb8ZEMS*K| zT=o94Q3lMaAlZlaOJ+b{z-&P5U;=oXnIz8S8*#XBIFCd82w86jGe}{CCq_%>@YA~L zVd!xRZMXEGl5~FkeWFkro)h!Pu?hZ9jTI$+fF8V$^YjKyYhMq5XGmgp%+JMzEr2a1 z18B2h;q0z1d@7jyYC|*EjSsPOH7#8*A`HV zRXV*X&p-rZfkPz8N(AlV%48v2@Dh@0cf(WNvBWQ3jVG|JP^CJKrmLpeDtPGbP0jKF zjs_vCselV!S!E6n2~ZKjZMTl^FqU|6CT8uA8~c0VD5&V5XYv%{25gg)v>f;xFuQLQ z6`WB-pedN+3$-`NuK25pYEuXmFjU!no_i!*2l6{*>`^41*YzSy$f$dnR$K$`+$D^i z1!Sr>im5tE3~z63*G6py#gbVx{xx7f39&}>-%5rDlXR#JYy>;oVsVFlBnIR~ns~Jh zrru5Efg&Dw;+r#YUVc1!JU_A@?*2j(*psq@0#u|CM?WX0|NZ=|t)BQHXc=ko$+68^ zk^H%nFk$QXd3i9Y0~s(`XXb>eN@WK1`Q5UP2_gFSGXLp0vVQ)jai*lte)%S^Kf1wO z^OFDH)u4x5(bHA9goM=~yqiBSJ!ilGbcBuC2(V^ zPY$Dvroog(pXqxwWq<<&0=-wXxh%umbDwgDOyzp5KcxU^zyFfLyv4ub^!b%3(uX+b z#R?3saQqiX?C1tVg^Eq1xX|w-2BtZ^-VW*s?|b8+$2bm1_)}2H$rcrHO4)DyS!B~{ zDbIY2RUt?JgdTuF7(2Td@eHw6>B^W+Hpm$2+La)DN=kjyv8DM~zAlTYi;$3f=HW z@`pQ%pWK%~pTJS&jZYJ&pel+qxL}q+dX|4J!O|nW?Iq+h#pCKe#e{$ZWH)C(g^alK zV?)4?gOKHO3x3`#sTm?OFPBO}O}ME1Zz?-tqadg?K7AQu#}D`HQZb zax#FQqSH(OdWrqpQ0OKFszWBttewKS_Xb1`fc=l_e4`8*z_4059BS1QX1?;CSvb?R z@x;ZS>}JuKo790lP4j_USQCc(9j1V>#*TY3moK<(GyC&a$0hTUxXvbs6Hw3AP6s{f zNa<-I*b-;}W_ES19+0z1u7G($n*uoC2Mow90#N|=dVBHU^?6y0-^Zo_{Sq&Qf^kqbHbw5r4ahqB3s1@{5@_S%%h^!tN!SUIz_dczl7(l0|H7Rr$h zsK^Pplj+>CqG(etA7C1npeD@3Imq?J1!94YLQXX=Sx5u+q}z%T-Prt1y>~xac<96D zb`Rz*IRC4@IhpL7@71ywmLg!Kn+dvv3W-Ph(5F)(SS+MPoT93bgSpQ{#_xL1>drnw zt^IDtSVn*H(_?h~-r~?4+2~~f&{PFmcldGa{?4)p6rJCkpWAs*Mn}kaArh>F)rMxY zqUm#L^p zc+sRIqmog?8YU_uf>0_iPLPtf$R1YPC2p{22pND)7|n^;Xay8T2`rLdZ#zN7BWrl4 zy0s8%3b3;hvEYFrVs_Ankw}kKcb)rMTM^s$e*s_(pYn0yQS5)!vp5=sW!8XIA>dGP z<*(VP5Lc3|E@%y)#98H@gfV_*VHINovNPmADRc*_fvEB3QO(3 zEgL`9^B1KwwQ_b5zZW>TI07HX)8!KIvS7`*(Ym-%M_!6TO!RcC_MaI5(rO<0`cXo^ zQ})AnVi-DE<4lb^<1;@7 z07@-k2@+Q!VAPH);1DgG8UO$mTUo2xz*gXj13;elE|7nd^B)YBQ_&pe=U)nemxB`m z;GBPI^B8!PqB7hwj(pYKjhprT)c6@H`E%W|A#^Qgwd3?OPyFrI4>dtnMpRl%8Fa~Bz!s9hbR*Ce zez=+Dy|?AO|HDXnjdeGI2*4r)iZPKFD+Jg9Kn;NC9G2Gb1mf&po>gF#r`f8$@5-kO zqS0ztkTuB;-RCi>GkpmPGX;Q`b{R*W%%Xx5HY4O!uS3Waj|Kdz}SuEU=|g-WI0ALa3{DNlMod}>5fE$7@9tvKRmDiniG z04P@nAXx}{=ijISB1H=?04UT5QkD^`_3fDbm2wwz^81YdP*HK0 zEa;Gh)28RAeW@wcBZD}Fpv0pg0$7E<+315a^f%1=KQ}%>b;=~(GX((ZTBGvs<(Iex zYmU6MWv0!P(XkS?R+LMIF`2OQz~?p(yl$r2AGY%f{B9Ku7+}Ui-Zeq^>&!*&8h!#$ zD?zFNP~GU!Tm`H20JF~hB=o(N$*-I3`n^ahSeWMp4xF>VkIF|P4glsPpN#<|W7^aZ zlbf^^vdLBE{O5sJXw9MPN`R7=Gx1{b6*y@pp3bzIbQNU5>bg$Wqz8Qzjb{9tn}rJc z+f3^Z`m+N|Q}|QgPo}1o(G;`ixv%B%A9lm`>FMFlEdCq<;SvA}J3~F4G!+7~d}bK} zPKqGumg|Tj09uab_0gQ8-)M4VA)ina%XXg;p7f4V4a&TLVoWBF3Bf1%u<3`KVpB}3 zU%0Ryi_S-$_ivd>qs=*}uY|b}SVF)Y0ujo=p=DZ6Ibkk&KwbbSXfNaWFDE`7rf8(+ z2D4jR3ZI-81>xs?0f+m;%CPj)JP+n(0H|`Hm5}-zChTjyM#IoabU!~CdN@~XJ7$jb zK2Hk1yuE)aUI0_eH%Yhrk(vQO4#NqpvvK4jbN=BEQ~gc=nDhePUrU1QtGWf@M1W2w zER7gh8RCe2JUne@n}@ma33URY`6k}fVG>mDTE&N7r&&1bxsZ=NH>|>K$;2Q4uwaro z0pMm-d)f|D^v|@Pm1AMRkW_9*E#-bR4ND*PdPbxIvEdL5O&Ul&WmnIRTeb){>iAEj z??6Bksj8iqS$)ZNS;YbKkq9y%gAo_6PFPUe-gAzF-|B7V<2$BSVsV z90FtjU^Sy}7)s38$9Vd8+p+k&EyT{)$i&!oFR#xj@}#8k{K+~0_Kc4Y0r{ODRBAu! z_>0<~zS&&KUyuO+Z9Amp78paop==7$DSD};gJ5I>NR~IK)%m(v-#$Fd$mg(IqQCnj zNG6a%n1R|20Htu+%#JKoa}?57C$ns;+|rPRDl>`c_Mb@4KV;{l9vDHZ?q!R`DcLu6 z2soWCp)*vVcxp@o${BqX0I*u$-%r@bkhSh+&cDPrk}&dNI{RMfB$4xSnUvJebtY%~>FdIP}pf3w(Eb~w#qSE(FNZ3TeD6d5?% z^Qd4B0hNOA$H!^b4m55b${`;o0LT*BlMlzux^TG~>g+eUg7K&t>PRW!-5A+cl9j5f zqe4I?h|-ibm5XWB07xFkv)ZSelizK9`~!1r&2RF8KD~yclpUCxVu$B>Uj9SY!b&}| zZFi3fg|`yL(cyM8J^xVCUJSVp)s2SJu48z4l;nj%JJ3;(mc3=z0ifoN6!pJZ0xT1c zrPkwi^Z0Ogy9O8x`qJ&m(7P1i>N&E6gGT}B*SGi2(J@)FMV%e!>TmM7!YUpD99&3-d=&DbTG)Zf z64A9ve|k;f`T2{^Pl0`eEhN|)h*JQN%?o<{1F4+!by`$Y#y#-%RM z1Hd%#wOMQ)o|e8h1My_krAA35weUBmk-CgGx9w){;R&P5)AZ-hD%0E7mzjsfYbn+ZB-9eQ!vW#YeJI} zskCmZ(&BU2?RMKz=iP_^=(jTqnjhwjJCjsRnqt5*Bf(lT51x1^RoX?TZ@v<&$7SGo z4SUW%8lg5!TG8Z()i?F!EGGqDUS8jGMjFI`?9AMYgTS!ibX-7vdR8OV-TSp|zRna_{6*CGvi z%^Z2!I@tm6Y7w!Q{Q|lDt>bSAYlP$3ung>c*BM>i%-WeR`$H}SCSHIwCQr*ZrTSPm z02J6qHhOq0wf;QqIci!%@l_TK00!?i40%25rw|}L1snTGk-gwF!?V=kW*CNP z;$wzBEHPEol47nqVQ5oHr#X*GGNM$?(S~9TS~HYLkj?|A#@D8Dd0DT4WCegpc^P3M z+ztQ|SBF-9BFiK2T*1K%GZJ{_=Q0W)LthUw@-38(J>ZItb!y^_SK87sq_o$Z#U0;6wgo3laE$cs;m+4W`$_lvd1)wiWv?X$zfVJ z%L)LHgUw+8$R2N#!|gQiwQ!zcgxETtf!vAieN;`QS~*(%cb5<#3V~DYFW(LThDKgl z`)@Xd&i&IYbY1K5)B!*ocv=nm3U{Lhe-W62jX96-8oeR2&j$ z1erzJA)=x*U=$-NA|NIVjS>Wzr?#Pos{cDxT}?B&)_u7T|LQzsxz<^yy1MF|efHkp z{+bi(*V$-~7&~IfkRjTuRxY<6GUN;R=NChU!T*Oid3(7G8RA*GYWWfepTC$ zU3XTKh4^K--~AMPvh2V1iId;^ojCrqMU0)wh>e?Z)3Bl9>Bi+B7VKZQ&!=Y7qqCi! zv&PL{XZ>5rV{y9#Yp`wmiyv7_q_MU!TitJN_7SY*3F0zyQ@?S^lKc>4o5kTbd;3H) zT3t*g@NZ8taFKciF83+7$M94_!;a|qSbrY`sep?|wa z?aNb|kt*l0IZj-yY0@&m&fAN#0=i#Fq9tYbZdHf)E^gvS=ss69BD^ifn82){IXpwc zySeeM>sW4w#|q5^mEW<<=m_1X7fgAV_-9$#Z%uuyvN`IFdRlFDwyLKzWp}~RXw+TA ztSV6T);#9ZS*sGJ)?SnE@j=YW=50$*`PAj;()j$Vt_-vEH3jcWW*JD!UL+EdHtzcp zWAq)fh~bh$iyuxr!qZC9ix?%@1^>=RpV-1v!N#qh$u(~+RVfK+8;LKBaXMXtzPQjO zgdk>?X_kfT9!d~V+PVSZRRr(Q;(M}*EDc_lOUzG8h7!699nDs>dQ|3w$Uf@}Nm+cH z+HlDhGe@pglH}KM#!so6W#sU-Eexeb%os`1o)IB@4f%-n+ir7nN7-$7gtDaA=|>@7 z;GOBG#cFO}V4}@WrLvF0R3+WaNuM;o>gve*G7}Br`a3dYcj$72Czbl{8{i!>lycRLbULv*M%X=r8yJH7m4v*bM%v0b?Tb4yE4}7=1#Q3%%~}>*@{!f z7_Tg5YEh5fR$DjXj3{(JcVZ|jFv8REbP|jDK0=qphR>g@lr9NdcWGB^ZNV&|=2rqn zDEV#h8AfWG>~aK>GaHi4LV4qNzBO4{%#vNvBYCvr3bl-muyygdYo&!XQ#U-EXh+Cy z1WT}Y9i?OZl4)^nM1IOgZ`{Z7nd8i9vBw^F*HT}CIJ^4D76pLJb*pJo!vN!tltTW-H7wP+tpk{PbP_Z0wG~ulz~w>FSV4OR?7Z+3zbNL~(g4k-diAO%R2e z!B7%wgsQRS907y6J6yELO4#YQExJrp^n2LmxNYxBZsnwYT+S<|)*-W@BMa~Je8w(% zH{H1+`sE$-N20SirY{m1UGZHh@Qip7e16L-=At|wiNzm3{gRZ0IsUfl?`U)+qvd8{Gu z1T)&CF2&5K(4m=|_Qv9_G4|Va=9P;=9gLn+>wyoiVzhO=+6htUk<~|c6?5wt_Q_@x zgkdpw-R03ndgnG4Hc7U_)KT+{#i8qsjN^{MWFj|Y@}la7tv30xNEJQFV--9 zHGM`bIsQ5e`%YS@d45D`lItO&Zf$(-Ja@+m)=x${Kaj6hy1SV9(CapAG;Y%6{Q&j? z>9WL4dn3ucjG^sG?KO4H4=S1BJlJK_ZLx+B-XFDer`9vnL)D6c1F>vv-JR4GGplTg z5x(R;)S|a0HHkkIAs7$sYJFqY8XX)6jWX6|*?uFw^H7@(!xUDO`+ogr}XP7tD)VZlw2*L1p>K$>%h!$fbqNvh?ee z6j%kNd1y4B`f8Jq{e*%hU!ravHlAO8YBucBb<|tQh~~OR6f-*+j;8_Hy@h8RR7#|Y zGN39|1S%T<77ZD&sD`S^?!SsZGb*PC+laS4Q;K*y49G7ldk3$E|EfM-G-YJe?EI<% zrdy-l)Ow~mHTI*C+9`lHfFdoteZhzQOJecu3Kw* zK8f#I2?dF5f`=AXpXO(EzbPB{K%~O!l+LperBUF0xSk!I>D3>;9A8c?!A|x7m~o)@ ze+yC489Q=S#CUKa3C(`9oya8{WQ$8?Ee%l#!;g~#2SobST$;_*9Q?ye|3D*Sj0t*9 ze+TL(EfC;Zq z8To3-0oGMKz{LW;AQ4NSpUl$N(hcxS>_Ispvs7>|jTP188}NE~<%8G13OiS)e`AlCG~D?`BKtyhswu-q@E3U!xA2o_OuH|~G<*KK@(H`H zHM7*HeOpFhD3;rgcop=3Qy=0rcTnObNB6$Oi?eKS;Lo_5Q``z zH%yBsP_0sClE$@xNEu=bnJW21>)CHqKcG{S$fjoj1G&pIGlj!`fA;OFzWmC})oHP` zP&yNkK14hP-ju_O28x9Zfjc7F0b-iU|MDcaBt?M~m=k7k*n6uP<81}&TPMUooo!dh z)gP|o$s@OkDWmwFdOHNwHbQ@2LNnXGyM^KXrY^&UqIv9y1(!%&@ z_8F_c7HFuRACa2!@eOnCG@9_(7Bjbdk}3+J=) ztj$%X#E5%akUo;{TnbQ4tJ0av27)Z<*Ii|`r)xqcZ9t5zi;`v(F%j2Zie|s-x>_XS z@cyp6eI&Y+z}=m(aPm~m3fos9xyJHx+Zrtv6^R@2^4ebP58j-YUdhSZ3^qhSg9~-O zc)ZC_J!~^PRcvD_X!@h#`w#7SVSHB9U5jUq71#(0(+|tI(F1gB-@IY|&yu$WCFS-mBra5cXf$@WU8M_rd2Y&qaapJY4%eq3wbqAyJ*kd))Fxe@~G*Y{6cm`CAoQaj5T}d<1Anexr zE#dE*8;;HZ)jp{#@W)XCn}WwC34$#rBh$^Vx3DPrZaMR!MzN5cs!|5%vq?2lts}TX zx|^aic>qP*uowgG%ou!+rK>a2Hw+r;dN8XQ^j|cio=IO2Q3WC380;AacP?Z8C${38 zFqICiNMXw>)8NY1#4-t#j|1;SUz;VP=5PAZe+L-L?8!wZZ&e2*s1D|-FW?k@r`m!#$l4OFC{O0I0wOt#%EHIFR zj?1<4@rmG6BwazencKokAc-Z`0W-K>Rg;>1R`>b~Ap17I9*RA}s?i zf_Z>uOT^5sBHmI;49T}9weK^sIjm!E2atZkab5Yfr!!>)kOC!&bs8YYYak%2$mXw` zd|oq}B8FWndUT`^3^CPuwJHz8GtboSg_&oB4yy@akW0&DE!)77xW#s9C_9oF_n#7J z+zkqvDp`In!2imKi-28vmg2iT zXG&-K#W3_`Wy)@hS8f8L@yimQ&jx2uUkqck>bt!92x6(EsTFVvfjvf>VH>7%Q^K#; zqL+C|6qmo`7~<`;l(db{zG(#T2{ZX+}D7;Y_KhS*%^@CvNn;x*TBXh;-yyC3j(Ek z0|Hx4u`N$|WC=d0rw3)524XMH1f`OTOBw|{bmoNq@D({{m4LrP4qz5*I1~O`h$6x- z9>~sOx%P9qu!z&H*;iOQC@;(E$$Vp(!4HP>tO@Y6mQk)6Ix2@!M<{0ilS@55S7w|a z6~(QwyXuVi2LRIw?UQ&6O?8My9pqpF%^i{Z1=k|;`Iy}_OmGB zg(WHwuj6AW;uVl3H~NVL`zpV$lBuNxCh7X*17J8-V)IGP%;$&mrd;EXy&drIKuv** z$<9m<6YweD*7QW6Sj=D;<2~S?B_$!$&{~)j1yay`{FhM^^iV;A{WIyam!2)8 zFvh<<5wk8gy@<9X?dtELKdd8fSf&~vvIaE?%z0TMQTUH3XUd!2Y)eSVPay`+YI=Ba z+G|P>rnui1XlTm`&({K*(yQx#M>}_YGWXhLUFi}zMraFcG7=nbWLHv%=I~v<0;2sQ ziqi|Ve>cYtgVK+#=AD$izC?Di!$pcKaW-JL;Xp;gWSNHMo0PpEr{zS76HIS6ePc)U zVj)9rlVigMHRSoKgBtQ>V8}E7A&Y5!YI`wwQlpRKHkP_)9;x7JEm5^<6SQ0#8TGPq%G#G21bSc zg<7cl;G#CqfLr6LO4?F|wQU>MON8hzplT~<95*|xKXPh6Ak+hvQv9gG;ejb-#sy)p_ z`TT2Lk+FBhe(#=fcur5f`A*BFg$F%P?v=iYt!enVLCmHZ&<7|4Rl8^30rV*e-j#!_ z)W0Y?)63z4Vj!#Y^4`eAGA#`tpPW6o4?Pzzjd4~$33{b zu5*p7^mxOnrNNe&D49PY`h7{$$9?{DuCc~QUI&PRUBc6!T`(o`yGT$w7?Hqi9640gDxLAKNJ-PR4;asoq>Fk(2~UNbcNz=tV{< z$WmHA^eW05CZ{-lF^99~J0%hRR8omjvZBg2wR)YiZ$vY1|*r zqf(fW&L+D@X{?oU)&&o!59{htk{U9P{(GH@mrR&Sdi2BMb7#9xL%O+9oDSRxh*!tY zWMOhkv8<>)nHp~rms02A&m%x2zJw}v+%e>O$t|%J(!w=lZR}*q$2*ko<$hjPcg$0? z<58`(QO@U76=@l&wU*OmLY^kdNswZw55vMuT5H)TD*=VLJ|4b0)Xg9$K@vb&?0{mV z8iR2!^VW>BD{DeP!T@J9%d%4_2!J6L?5EOIw(9CnOPb%kXqve6Uez+v71k??vne1N zt4l@RRn613A0qZ#BcUl$$ad}k|Ep=58|>1lRL-GcI9bdjg4M=I%61)Q-7{F<<9GD} zDpZ=KGZ1hyJ+TSJ3NS+!nD!h9$nm0*7!z@Gi8 zvR5d54-S3C+s=LbEwIK8&T^Gj-uz$US7o9~9)3nK0YN$RkA|E)+{@FV9D4t`q}}yc z+_8jqwer*+4U;a>cNOj_W*$}1ZFwuJIf^?L7c}_#W;tOC%S4e$pkB74?I+T3@svRNAjPwO;Qbwo!cS#=B` z4Evj0F-zzgA9e+=vmc#IR^E(|ClRYqJcOr-A`zp}S?NhfBINKf1LPnQiRhTzO?zA? zQh#4v==0J5L25OA+pvDL}9GN8C0FxJj^MO2aW2o|ktxJ0?)33u6?0Axz%4r#o zp+L(LQ0-Ojd>zi_;5zLCa7DM3^cCds%Ma|VcH8S) z`G!*|X7BHAF)d5wRFwpXd=_Mh+JiT9qA2n9GmU9UsWhg|M1H;9lX`pu1vI>WQ)vl9 z_E(%y{22tRX|)#Km-LcgJSUznZt41Wq$^=J+(e)9oOh?;+w( znq4h|g7#MJw;%z+$yDii?@zW;B19k6Ps z9|F8Sf^0-DQ1b~zM*U70g4jf!BFWpqu(KQM`X|bJH5yp7p11&M2)%Zhk7|qYmSL?D zZ&ud*=&2BX(;;2>VrIZ{c|xJ7{IR%J!$HVK=_*OXM*|k2foiv=EvW7Yl91ez{g_k6 z!QFXeLBoym*%flCA6-TXA%;3|93ppr4j}Rp+MIu~ofeY$wZ{d$*%obaK`C-u_~b*W zb16ZXa(O=pLYnXpz^3O-cOZAJgOPD+f;IMvrY_hrX<^#*O~zcxdDq=oq!@>|iq}1k zap+oG%!Ja<sThc!W5~-M?|I;@w3Pj7ypVZt- zXFcQv1&rr6s{0(0)JuH>N?y0U$iS@;9}6IU2(&nG_#AkFohSwowQFVzb6b7ciOjeN zx_Fj_<@Ml921Qk6F|NQtq3NmOlwJ{41(&~b))Pe%rV?c~4&rX$o=4_zSb60N6~c7L zJXouhcA)B>7XH)e?cfs1m1MlV>lmH#m&}D^PV~arwMcf;Qu5O3Bi0Vw`B)=_0Z|H~ zS}@J+UJN){wH(=(umxA%E=N}3N!ja3RIQHBh1xZ3Vzm%g`gvTICmd)dDm0&w6+f*x z%lU45ZMyEwiVR<aEGm4kzC4d%FFqMR**|fP5hUad#}&1kh)m>-We?#l9saJ4C2@ z9IgRF4HlfzgoRu4dV!cc=IvFulL<+*Md4^y} zcxIpwNeoT8=1d@iuly%M%&vc;t0!y<%&HIT>QRzMWFGw{YlueE9{ti}Zp2umn>EGw zk0kNx3LwK1vPc&cwU3kIYsC0GD(V~;0+qBmD3J;x>1mH828)Zzg2%ewHzCz?lvBK{ zgfvUhj_;L{AciQ=vfe3)mJkp_|B(Bz55vMVCEaevQH8&6dW(027cg?bkc7%$+)G;m zDWZE-mq^YS;h_)(14HcF80R6l_gBeVPy72Ev^c(gW*149=(tVS?^zS>TC9d*>(~Yo znrftRr4rA8gAjm#T}s(we7{^WmBw;iqE2}KRbhJ_j!0Qp9N(r0REo5VF(~bF>zQIp zgi}dfa(f9mOAPmo1sXr!>zQ#V3yZkn(xdv)lSj`7mWrSV%C8qu`IW9Z&O1LeHKooE zM=vv(b$|TMB!vL05Y%95Y+3iRFcc94FiSVQl%g;z6!r^kkq&()ipKZyw7_IS{ySxNwc!ElX;;fL$80I~rTSaTu>zaz!H+FI z(MkG6uR8pkqtoATUf#iLAK;^#))r_;UV4dIcn-*@|GTAHR9?z?QYvijdRyJ~VT*@H zYrTf#wXk&j#|+rD(3AsdM8D?O{o%`P!C42wWQ82SZgu{Jvf=*~M5P}et}%#DKOE(q zDI)jLE#eVv?q4VvRkS_(28(jlAVDwBbgk-da_w192k2e;2k7O+!xmX2f;=n7%OVjy zmB(;0Jx?8zmmrZ!dR#l?IaN4kjzUS%0cgZ%RAbbJ8Y7(4GD&z&6nEhinIQ^Q%ZDn@ zl?|zLWf|3>3U)lo+PKMSS&L#$VlUKQtK`ykLTe&+bkTs^D};q z%h4%1Ly#!M%AnZjFuiBotgh#(b1Tq}y#FsR!%OY2lwj{q)6G9OsOYex^Ooq7lNllxE z4Ba%ohjRrc@5^C8^=z~19SC8WK;{&+Z`jbjx z2mz$}Ibid%f4Kzk3Zc@%UZ7SPgN%AK1hI)W#VEzwWe(>cABOT?nRz1v-lH_cQHjh) zEh0O7coppQ>xL&F{6@L(0uAOFIsgjg!#2DddNQT}D1AZr-Ono^RG_v?^c`3^4o25f61HHkV(1zm3z*nw%F@;1eI-z=qGTMZHp+Rq-wm48|OWt;^Y+FRXS*Gwa6Y%qb%C<$S&rsR6DDR`9)Vr{!PAG4q zqPiiKZHvmbMP=I}`tusfwnb&zqOxtV*WbcWwk^_s97x%=NGRJD;RltKZHv_3_))ek yD%%#(Z*wWz7SW$(Q?@O#lx>UukJ=UqX6s&SWBZ1*;pk62uCiXY{Q6SYkpBZ6lubqe literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Hammered.png b/resources/textures/displacement/Hammered.png new file mode 100644 index 0000000000000000000000000000000000000000..45fac6009bfcba404a244008f95ab8e0a5220a10 GIT binary patch literal 278647 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�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

  • kIXhziUjFaIXy3B^vK$4{jc{br_pr51e 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

    o4R@Oqooex6abP$CGzWB>9n-_W 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

    l0Jp_QCPY1{OC3bp*VPWk$C#> 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 XKjKkR`6xVV00000NkvXXu0mjfy$%K- literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Roof Tiles.png b/resources/textures/displacement/Roof Tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..ed28294cb64a5f40e6c369e22bbfcbb45af4c2ec GIT binary patch literal 75446 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

    IFt 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 literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Sand Ripples.png b/resources/textures/displacement/Sand Ripples.png new file mode 100644 index 0000000000000000000000000000000000000000..7b4cd7731236ebd6957807d6e077318d83d7b849 GIT binary patch literal 182176 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%0Ar@;1bI_srU1CWdBfs?f>FS1w}|20K~~R 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 hpN|B2oXiQ}{=;HtY002ovPDHLkV1g(sGPwW% literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Scales.png b/resources/textures/displacement/Scales.png new file mode 100644 index 0000000000000000000000000000000000000000..6976dfad9d1369bbf77113d720e80e6575b2ad7e GIT binary patch literal 40753 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℞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⁢gD`Z~PyjMTI z0~Pp;_`5GmjT!XMVKjDT|H^MWq}o|Dw3tT0xd{Mxy=95-x&T#q6KCEzMuxTYs_vh! z0`e!)eZsffQNLk%J>H1E85E_HeI&g@0azRh;R&g2Qb2E?mG8byU0F0QF3gCfknf>R zHnt3>n_|t zL@cjM0Qvn`=`xA|n^q$GR1ey_Ya{1}k(OB(PNh|AJV!^j|0z%A7$0T|2Fr{r#lE>t zEM-UK;9x&tk`cV1XtLQsfDcw`_#=;^%;>CMCrz+@f&>F@qnZ>c^2iASKW@F}jXHLO zBm72D_qdW3evOQo2a7zNq?>9;~sFZ16_e>^akItq=uv$9# zZ1(>)U}D_Y(u|}QuwqgX`kuz!BOMykg;3B(aSP>I5?$=DZ2HGeuY*xOe`<2O(<`K? z08c0sQ|+g=GW0aR`ANO3x^8Hrz$}obZhImn;vI^6PVamt>1xb~h}-ryZ)n&k>o|n!9x_mS4Kt(kZtOH z4emoRHnCt;>v$-bqjhLY#bNa+%|cJiC>DkwJ?wPO7Io+ejRc}Z6fv`bM(}SmDKh&j zUYKw9DfW|vz&b7Nh&x`v@=FGUGq4MePEHt?#u3jD>|1l51k&V8E`rpB)G5fw0PC6U zh!a<|F)^24%hbxt@GI^*P5`+^2#L-Kg&FgtVf3(r+j#jyCkn`~ z0>in^z=D%ph&kyiD?mt}WgawE?G8ZJGIR$3(mgjqIz%XXDCpi4I7TTL{4T6%4CO7A z#Ke<+Y=`JrXitPDEId0+piijr{4sP$M1koT3I`e~xV9FVm zpyRh$PxKHdz_SHhOOG0=#o|DOwa=M?5lsL=Iq*@Cj3aNW=>U8O8Q(($Y%T`f5cjNO z#ENYzN~XQcS(*XU+!m!@H0uJ%0T*cUfc#>Zuv5#ws!`Ky#C| zn{FF1f#cBXlxGhuTNgQlxayxIW7~o!DT_AZHw~Qk0eKn^=>^&lm4B<$P&e59eXpIb z5=3x)x!;d^H61;Wnb<`vW6u8Tb^H1#ia|E1l4yZN7#NAi6aD%2ig@~th~U>3;w9qa zE_@#=gDlr98K0}h4M{bdfJXugAc6Ao-EJY9ZVC_fLON=C3DnUuefc!;g|qW|VV zIr~dyS8PAeEVqc4?R`A-8Y9hR5zjJUiPXFO52CzPGoUYnSTO4a>%ZVQ9Z`givQ*Da zf@gCtDv?ATFdQ9pQJAq~SRy4QBgx>i#d$m$%T4`3bfDJ58dqS9;&@|%*(S4hgh$Iw z)tc%AMbXV*d|!f>vlPAuZ)rINX))H9w@qr9F%p3o=lElw;vwr@uGf#xEa28%wrsNV za43T5?V#QkO4PK6Q&zlYLh)5~mLd-!^qIG??S1Br_6$Ouo~;~*!& ziv?{$O7fUh2rhDG1>8r5tV$*@ckv*T<75&UvD^Z|s_W2 z`wLsHDNd zDNJe>h@=uk5g#Nv`MjC0Ff@$1X7(Sx*$lR!RC$g=Ol*QqH>kFND*uGz9zfer1~>Kb zH($)4$h@y3mfM>|O33*JCpnvR?a}}~Ag-_PQAF<1&}_^KVi2ME4*vM#q5=1(TFLfz zp)#Q?3DKzRl$1UsM6)@e;utdROALefvS}wHxii@1u{T5uh@p{5IL0~J(_i01>>|Nn z6B>TK4Jq&Pm#gbO5zEc&wDvQ3B3eY1=)wfUQv)>5E1e^bt`3^G!P#YiOIAbVA@-|E z0EgHoB_;DWB0Gszs3Px7*PQq0Sdv8yWB)lSZGFkqVlHaekWs8o96xo8U#h6-WvS1| zRBb-IcYnQ#gj<+?U8Z(=`IEf@VcRDF?P=okboCS3N5jA-V@u>gDcf5Ule2HoC zWmytU(MBG5L(pI|t?nK#f2}SuLo6N(3{!hthXv|P=7}QM0l?$YwmA4O3_XSX$8-HP zJ-*>Af~1(+ zS(%A*hf%@7IHJN0;6y(Gz@X|iDt(k6WbnxbiY(z-Dz~-4RLwbH8f_dTE=_X4gnmb+ z0@fgtpnOnZGS-J75}`T(%%996fMF&3LT8x@?58%~=sKm@x%oJjw;=}EU*GkL$IKcp zHe}a&$sQ7-b8#Py{%y z+Cw_Zfk?I;X<5Qg14pG}sckAuX)+sq>77ru`$%dqGhtDC`k}n!Rn4VGNALCZZ3~NY zV~8hB@Aa9liYUiL24C)^g@W{X>2kYkQ8jXNgTZ*BflkQNsja3uL{^bN^9XoY{&(A0 z*=@zF$%?OPglo-432kR4$2+7q9Gz{WYk)%!Y1+T^0-#1GW4rI)XSu~Vv>#V#9WBd~ zOX?0&){xNAJ;v1UBJq~!Gk??+*zjAR>b)WZ2p1bgK0z0Mas+Y266M?O&5jB!x@YK88W2 zW+DDLrB=hnUIg*5$JMO_@*|%NqE_>T69B||$P>^QIs8PcGjN)i5vj8tv{)r4_@fPB znE*mCY565~I~DbfJaSefUdV{P%GAF~z?DzxYx~$An)Su)cgdN6`U&ssneSZ-4P3r2 zl~HC2rlSv5hJzr&`J;B%`R~EJk>vIU=^J z{LG>0(DwGS5%WM_o%Z&4tIHcc9b<$y?x+z%sM#_}2e{uhYqTH;Ryp^}9TAuKzK|*B zo?Y$Zi|-`4b5q3E-kW8pkPQ=5j5oD?_>0N+8>rDiN@&*riAI2=LIE!%Tv|TyKbEsFOQ!Aqb*T4m3)PSS*TG<7 zwiK#O;v#n^&alp?%fY3gT275*e=MDy*(bRpZZRARWJEw{Iu zXShTG@n1i6vD2oNxlqQyz$j5Z=Phl?V^VDcQk{C|&-0}%6ny->U(W0Psa!a6)M>Gp zKT=c18SGQ{Bg`1jhNQ(VF2!!1C;vQMOeMP9CbY$WtY#R}{On`sr`;B-EFc*MhHB-s z^KhIvOjtvwlfcwM9wN(>031lUa1)wu3LoaA(s8r!Z5uE4w(LW3O_O0~P2QiB`g?`$GZA zZ*4=``O=hEHXt1z=emvf z{l2^`k8C5R_-950t}q&UVlGObS>zlg2DT;r(DOZ!Qw--Y@bC<*<@wLE6Zu&SBH}{E zHL+sizDji*VsM4Wq#*oAaHMd^Wv4aNlc=)wu)Varj+{2if)YRpCt$*rla z{&D&=E0w7uVhBE7^JGWUnkI83Qy0c(7MqUm(b5j`6+a5Oog&7!zxuL#9Mw5lC>bQF ztyV-?S2NH_8p!FhAF%s5oM!kaWNb|+ZLqA(@K>cGn7~D#vDl|>6Wfp(uJ z0uOd~7X@GxmLLO1>os2Y0}s2mSOxQ8lyo0b%2nk;!5 zzQL9bhmv9fL_VeFA2Rz55m`^T&A+*7OninFHn-=7`t78Qf7DK*VDmX`s~>~i`t#bg zbR=f8t(&ahgnM0Aaq4{WpZWjN4 XmweTbbo;tr00000NkvXXu0mjfIZ;F* literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Star Tiles.png b/resources/textures/displacement/Star Tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..e1406e6f26c03d7a9983fc05ef787c3c1af3da19 GIT binary patch literal 10935 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�vzv9aTfTyfu57+Lr2KhFFFxuxKD*KUEu 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℘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 P00000NkvXXu0mjf<|YN3 literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Tartan.png b/resources/textures/displacement/Tartan.png new file mode 100644 index 0000000000000000000000000000000000000000..a5c321daa3a087f731018087c654f0f93f4cb2ba GIT binary patch literal 147158 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

    ?Av!hW@b=;o7%#u)r)=f`u#9fx7-P#nOK-OJZ zb_Z`1%&9yUtXMe^sCnoxAyiZDjDq{VgDg1}jm@)P2t3Nq?|c7)Zats3IzH~Qh3%$q z1KwZiabYJtX9ctqSE^;T2EA}}`=}ksid7g_Qo2w~?abZL+GJfB!~H^4gwd>pdMAw6 zNH@6fNIR(O$|K7M>gfFS*2TjNwYHVJUWcrVnVDM@h#ZU(bY8J{Qawpw)S~nB0dFto zaskgTBH=IamvOpfg|u=XU^Yo}q$$3Da538qL+qJDWLg`}cbOT^S!!l5pqBcup_KSw z|C!jw646mt4v{?@MEV>EgUzN_hTILtj^mpZ=&xm8FDn_R`=;@z;Pq+m`pK=Q4|E;A zQpRfq7gD~?nsdKbnWbw!=KEn5o(H;saP~lE>}qu^5o?xxZL)-N&y6tNV>;3j)jd$0 zJ?>jXFM0$nr(WIud;OV)%gI+8L_TiVvtb~raX{GOx<&RmQ$KY)(@SajmsYWa#+j@U zaX~}f6W3)?9oMW#o>OY!{uwiw5*r3;LS4pByzrc6ykRf3i8JA8%2hE`!lnsS6FV}V$1gKg1A5NdR1+ckMP&(ElR|fm9n-if5bYC&f=^@ zKku3y7dykpRoO&zT5jFm+P;G2psHIo`%T)kj2c53lJ+W2PCiGUjmZ4uIXCxJh4lh{ zRGi8kFDke^_BVPqJJ#w3bGAP`yWp~OzNP_tRUV6wl3} z^&U{s)ZC;-B~_KK>KIC(G^_1iY{IumxQ@|5g5dqq{X$-|ac0y%`pSZ)Ciw!A@#b4R zD1X_~%Yvr)R!xpc@3w9nz|T5Jr7zMmaD76wDM;PqFR2Zy*9XZt z>Be8as#_*=9Ima6l5u;#Z}$ef`lnYoUl#{wZH#F+)%Ikky!1XV)UnMN-NutD|IE%j z!tY^x3GLd!So3G=d-6WbJq$+$zxCaxO@{mL7qzdx-Ct+010B3Nv~&NB1&T zmgP^%uQt_Ytz|O)#BaraLXU~ea?@8ZtLWc~@Ut9Av#rwg76;`hT;4O>@|FSKjL-Cn zZ)18}n@`Kc;?Id5u^NjtFhRd0ocu>@OY|qsHC?Aaj<@NdY@_M#eU8|T?690PU1(b zz9^-?@O)7aDStvSj}N1)ZZZ2l=^#h@Q?%D?2wk{i;?{9^IQJ{ez{foB#aodON_Jrq zU1+WE`x``}9!6PEhx)1R4I3ey*#7N{CxiHw?(Zxa50ezRnA4d$Pm)p!qRoSei;_c(7E zBjEo>Riv%p@nf|ewP&>nG~V%n5d$fLotpm-V_zN*<^H~ZDiudXsZ_QUNwSpe+o2*` zO37}DMD{i7n9{V!RumO+YEsFT>|#Wr2#sYFF=RI}w!w@se)lt~lg{~kU%!8P$?`li z&)o0(zV7R~?)Tf|`dxLe16so99pvSQ7pApL>dhMmpLyys>G9gND2=rXBb%7?$T5vETN1*t^;e+m)!fH`afd-}5!=Z_7S&jky^9l8+thwX1F`mgD$^ ztvVI*N9rFh4|`d=T*~EguVJXno>w0RD)xJHKE8YUY0dLx3TqP;cBgzAzf1PM{Ix`L z&E-OMKQ-;$^?Yv+T<0CryVLEM+i#htfhue%yY9jdgC;H(u1h-Js_ z>2{1!;Mi68b(J7b+$HZk99;03W;R*2wP$>Am$3huZV#W=hYv@|hq)e$I`ld0;P&MY z6|srBGF5k+y7ry9C*r`Z^`a;Hd4u7KfnLYxBK4;m2V%5Ip^O<-k2%Rl3s?L;T_MRt z-0WGE$nj?TlfxXEnYCuG*RDv|oWf?R)*tXi=EZsA%eDwN?0? zOzQcV?c6O+7uUbs>vZ>!N4MfKt#=1fè_Q(ih&u?gYtdBFaHQ#90s~92ccK@x6 zxbVBQ{S5<8E=8WhsU-)QXtT@jb=vxg%RjG1M*E4bVXTJ~zu0T`dzjw%4YV^CmtE;UF((3M-y~GhvM8&s^_ovj*&>CN?c7Iebs*e=7=c!aSb1mUd zrJlMI-dV_h7rt>R^tsT9sYR_xucYGFf$!=ea)o!DoJrh#6qwH)n6?IV<8S0>%}>(k z=2Z7LIzNX_3(v!0a*OUhjd#`=h*Ut`Rl%HnvMri9^u<}{enebCyzRqtQwUgkwiUfF zoD}NmvVeVxJ(d$t6znznB}dkzo-*J4#J020N>(`;XL_`*^g?gooynk(!uR<@mQVXb zHeK1iFhc#tD}CUqjE|9HDSwB;cu?&E5j|Hl#z)av7OuTU^{nLWq^9z%6J;|mavs?;Fb{g|!_4DY;_(nRN zfO++P#dK5IXpSrWn}&)8-CZ9SbfZdWp`mP31O+bn+>~|sM1k3!&|JJaz3e&8ejL8) zrQeZdenpX_GP3-0bMapZjr1EjtyhmJSX><&-=tP~pPT7wl5<1(Mg^ES$S`Fmb0fS?f6$A7w;<{$Y+L5u zRoJd!maK(j(Ch_56xGXlvdm;EIzxIhc40a) zTB-RZ{wy=@aN7&KA}7r9Z!qH{n)-{mh+`zl`ZlXHRp5*{b~t}-Y=6j9!BwWf{I4Gz ziOLA*UO=1Te7j|BQukjvR=-^-7M{hX_<)FnjRS^g@{X=e(ZbkoDTFrSVbx+|`Pik@S&<>X)%A+-_G%6` z-k{NU^9dtYEN4cZ?!+t|SM$mhR*=5o`*11)UalVLc^vX(K13Qi4PI7b!E<}$%Mo+w zg53-7_X8E~C2-#SP!KT>e;e-rk7g*yt$}V#Px6I5Gb!c}hx8;nGFc3|m0@>{n*MCN zkl$|T0~1_D?s7Qahe&{jU&XgvNaxIRH)$!E~L2F-XuHoT0kGkIvlWE%Um! z{jsz4mwB9U)muxzk?Q->oUPY%w7(jcO*6R6n39Os?ZpgQNEaMfASul|5ADpzx~6`g zG^Z2rW_xl-@TxQE&gjzFR*IbgfT)?FWK*AvEVEMjNyTCTGWlKERlkWDOsRH%mOUCh z>?|kj2xsv@q{r{4`yIuQ2Eyv5{@E!j7ip!ZY08Jwx=g_@QS`pkBTCq{h4 z!*c8kBce^Mc~V<=0M6&MWlKFj&Mq>c(FWP#JQJQ0Y#lpyh8e>wCdPjt)T2WPf@y-rVB1OZA3N0{m!`Kr0x2!i~d8~F`+|X zBe-a~Qf&8^68{L(D}Sv=2PY8=YRn3mFUUIWRGj3Br__u`j*}IIVr*K+Ns>J#QWOzz zcFF4syL(7g)P?H?4j()+WGTAaplq9mkw?T>?W%Ht1IMnGtcGP zcUg@LV(}(Q_qh`l!p>tQbJ(xpR!Hsf^3ftWrPKs(j;t#Dx=*?*PS&T3uQ$2@HrC)R%bo5x#lPIp$SD;V0|z0KWga&JIvT1^L+ZGYmj z?Jga=v*yh*>;n~^%Oh@{Ki*{UEy!k@G`^lyg&4oxQ>qS zZrmN%V%ukPw1~Qzvzp%q55NoMrld6JnMe``bM!kxylRY;%Fd z-Ypum3V)!7W~SC*JPot|N}m6U&_gq~)u(7Pl_oe{bQ(`X1D^iD$K?7PfFv^+sRxN_ zGv5sEOwNUT$4m-S8BZcQft=aIDTmWoz(2;D7+s%RsO?V*-REgL-Qw^x%_?|QCn0=g zAgwpCBsK_BD4yc(+vE^*Z6-)hB-nOFrPO>g`6wi^L7C4@Khcv zh8znZ#BZyt=s;G10&8)q1=lQ))4 zyl)Tuvr1^*sBC?N(Cm@!+GI1ri37}83^U_&@Pf{tC}z$kU3A0v;8V=0kgu>QwhVF= zU^LblY&fnrH= zUtB+i3}>RwK-v{gKtXl-%*g+-Hd!@f+g#Enq)0v@El57XtX^39Nssx_uWe(kcPK{m zQ+es(kz2J3a*>PxfC!QUY*mMEnNE+lyuFZtE3-w5r_}yf^>P9kaMRzifC=C{INO-+ zH@r08==2-GL=KeOe5Bkune#II&Mekh-e7ZLWuqgS+_$+8132U(Y`+a<9SSZSV^453 zLvl_&6*;=&d!k86%%jcCPB(e}#xvd1P@m8kb<6#wRihKHoD^Dalj>y28>c&AjP|(K z(4H&xT-{Gqo4tPP+x>YN;m}Q`x?3NJA{HR2j*n^EC)vheYPc&(qdaV>H8TNuHSC(u z8O07-prZIZcCCpui_52OdTz?bw>d9D9>KvqY>6D!@21adgr{itQy!H%$##B?bgO{~eF)#{`cGsS_Ok1wfFemHGW?=^s~~HMk>X;brEPorvH-ky$aNTPD*FpNV+e*&hpT|sHiT=6qg+Av8Z9$ z=413ee=zlOyo^>cvGOUVkYnc*k<@@vRtH>h03rx*cqJ{YCrACO--1UhgX(O~i_kcK zIS#T=cQNdmU@`UzQ>eSWISB!ZWxi9U&1v5$7?D|{1*gal&5TDYr%w-iLDWIJ^nHh2KH; zJBBW7r8Gd$xXA5PU3@cA|C|c(aL*uMH(5r3DTCblQmXfXf4U@j2YvOkzeX%sfDz8P zvMJqPH`Z>HSQbYWEw47WB$__9siYO0L3?iQ7t%j%0Z>F^9dcv-4hGzqHuChuZATcPp*jOi2Yh}7>c-b*U>u6wAkl{&g~KnKrcf z_UA28QEwD`nqSA+LxdZbR&2S~sNq~of3zxluO7QsA0hfaF@DV@K-W%|sH>VE91n2D zrDu8g_z?T^eXII4wijTTS|yDCDk8IAG=d)y{sk%iBe~pPpd>Q$rp~vq@))`Sp3yJi z#sVEJu;Z~Af_;I95bVOPMd z`xh*A7T)3=49RI{CezL!32ek1Rnzz*&Q0w|IgsWYTs!{_n#68j>^r@}jxTl=!#K3Q zetqcrn9Cd$N?Pn%XV}==v0K=yi?buM!+I9p-+CdlZ(kVG&lIL+D-;$yqhF_*C?bN^Ld)UDM>!r|fDK(I zFf&#ABbXiin6cRlRc~vrJ+)B@>3G}Ql|7<$J)X-bSwr6Lbnw{Vg^2N5jihZV@^;%9 zV}y|y<~5;`zt(r=mAO~r<)Ypsg`7^@UgC@*jWOli?x>Y6EY}(gc4xYXN3&siYgDxy z({Brn)a?{rXMHbLm@yD3D)M&B#eSf~`OE~yWv{Tv1-$9jBtJs~rd!V*?8o|<#O{FV z*Rx@fqS%k`(sS(UAF@YNH`_?{u{lJS$Qf}9lIxNs`&JHI>%9NQH-3`+VRk3O5$>ho zV;@4pVPsBfv2#`l(x|GMv+yeVjBPxa?<~C^pYh?ZcqlQsJS^K-6Icc1VuNj^-o&j* z-fOVQ&)~rcrU_;AmN`@IKUhsyjn*h~=NCTk(y#8_C3Vt((WpSYuxfB{5cl&qP1hs;d;2wGCkS#ak$zP!X@SVlQIkb zfX6x9yWb!_ zMu@##Bp}>N>dQqq)On5Zi)Y*wYyDdWc2%xSu*LR@Y8Kvo%GT}l+A5g``=`>I?S&6-{5`j-4(Ytgl2E3SiSm#3b9EMqtO-Bpu~HH zKW{h3-Dk5AvbPFSU2k6G5Ie28-D}r-%iH_9SAF2by51}?Tp2A`dWmy$C3~d0%8DgV zrJS*E`}}CYTumJ%a~|y%a)xTFfG_A~*BgqG3U96sETH-WQBm?`y0~i& z53IWfWBAoxc3Iqo!ICpxnF^S|6iLe2^FFVI0xShww+FlVcK4YS`A<5FMO=_p3C`U% zfSDF+a!>ScsF>~UX-6lQF=B^Wlgk9%LwF_1y;nZ98P@BH z+GGkRJfUeS+Jkc6OVoepp7@fp?br2lU7C6|GEy7R(QnEwoROutK(CX&AzyYRYMny# zjt?j|K_%Y(3;wmAgXi~PPi9q6W7)5W1&E0_sk;@^M4yGMzVVfsc2gK8Y@8epWNv*B zi*r^eW0w;!e8lKkA#KmBaXel|tK5-`haDjw9JeyoiIECr z_F{?wgD(x=jp5}IMkVh^e52_|eP*M4X)Wl(cfy}BB;xl*pt3%DwcRbT@D``^zPc!mNlh538Gu(YSR>4 zwY1;pGY4Dvco3ZIuks63TdLg2`J+-TFL=_Yii*)J_zoRPYQb{d8rAghu{9fHQMhij`bitPe*x{ zOr^Ioyy{ol?f&zU_j+FGyWNhNDs7HiBq!aAX@?7Gj`fE}`b|Y#Le8rye__N_EjT5< zpk-_oxb{&5qrpCx^rlWat)1~Fwzt3a(WiCN<}G%+Qb~*rvt*vx6|@x#mlj@Qx{OT6 zR%_(D{n1EyUf^Dgz-}>flrHgjQ=>hrH#(Y+*NY^YRuP5}q?A}r-vhtmY>u)U!f`O$ z%l&=i-XrXZr3FeOz;TFR%0Jz+y6O`1pCcVH%6B!`8Y%e&?h<4;9hBiM*ML>0jCY`bFf|cu<#oZ28rx zSH0~<`egl)prbGF8_A9gi?OPf?WsNpw4x{FrDshMiixIXW9jD+)CCxCgO&?gT=?7P zL{g4YJRGTsZjj8#(u&%EO;h(?ag;ld+^qV)qJ8mw!`~1Id{kzvaWT>W*HyzsiGw0! zeRJg>WL1ldaxUIRyb|Ybwk6s{Dh25$z4giDa=Z3D>w@||4~2_4bY~fLarGh^Iy`<2 zfd--FLw*hI3gO?M`wd?aWBqubd+k?0w?~Uc5?|uC#nlHAP2cpJzCpSpIB!$7IFFh# ztyb_7|ENxSIsF%5erOf5tD3m>FS<2#+OPnBVT8=rSyq+t^63ofCe1;~StdW~abVtL zY0X%Pv+aZ*pQ$hwIMajfb=~*%CCL)!bK|Hc-eQyc5mr8`la?f_N&%ogJh+OScEKlx z>WhGzo>oJHb%O0u*msWTka`9koEh8tcM}OaPTSJCybb(*oD`7bjDFPUAwp;9cl9ViKg818?aZYhxc8uK6PQs zlF!8f#%t3s?2VKpca4mUtc$PI4=&&2;$$2%o~|i=sd9Kz<=em6ID0+ofz2B>#1d?8~rxD@;@lsZAx5$Ek^~C116w3V0Yp9avK*qt?Yu zfKFARa=sR%ziOHUG2Tn8E<;*_{^u3X3*=gnLW{mWy`_NlGozgJO(+x!6_Ounxe9x{ z>kkX-Nb2oRl2Z(k^Y5|v=!m8z%4WKvkL#_CDB{NBshezeMc1E2AI6D4)Q$|iL!eKv z4J7hJtVNTa6GbD2q{0t|Cf06B57?Sx74FFVT$9nBZ{ZuoD>f#Q8wYfC+G z)^9Y@0|!6lJKae}pL1fo{Vh~_Xa`w1^jtRRMFSBIYxfM_fxZ@UUIIxzLwZW(1^yUt z0+V%FPpBy_A$Pc6TA+5vQbiTYLf@d0f636rp&GMk;A-u7LK|E~UcJ zU68|buqF{9O9E}5VYvnGWxP`%yu?@O(b@=-Ed{KAIWZ7be+&x&b5tAx=DiMu#z-9` zteF3W9`+Hzusjb&=&+U?-y zK!ld}1RzQvVQr>nq*6_&6@(b()(q+J4GjZ*UcUzm#9~1yI!#d8bW0VXztxBgo2;L&s;^)cRIXAR0NByu-)FS>!?__o*a zOYi}fLb6jt`bRAuuEGyoC3v!bI+TY?m{P08#cCa!aBqX1Ywwnkb6)NgQrpL!NiVVD zIJ7ZTXv|Bgi+|aYH!qJFBTczOC}WxCh%|?U5lb9#c!AejtZE>MxTEF13x)$9h^Ddl z0THSsD|J9YMk(VvCTX94f2#7B?ekKBa}YJ#Aj3S6J7PSCD2%ylkb26h$ad&6CH!Ow zHS8&(X-9uS3U!lP$kwH4N@5o5drpES0?P= z#h*+wFx$xMb+xNvyolE$wo5C@7Hf0g0~OE&!bE>!ketAWM9$>ePzOEx7SF4AhZVI8 z&b@G?w1Ui&+qT%?s(X$dGyKuhXNUF+-)`;>YhFr! zUA$Y)E<>*wAN$umCss~Q>5=d2s;ij@j=eouu3XxPKljzh=#GNF?eo(Q%3t~cCJsWovs4%S+N|qU^mgOs+k7*acG=wVb)=4OA;Lu4ndu7|WgK{8(X;3$) z8~a5~p#Hglnzi?5N|;BkuA3jU&nl@xr4HqM*R^L%N;)`g>KqyOhH5T0km{;&5_}aZ z91d%nqdU7el*sqP+_She8N|A>{4E0u_0D}^yyx7Hp41r6TPtz(n{$?=jjGZs|5SQd zmV2RVtTlh`0tdsr;I@e4q$`!3ifJt%nx{Mk$Yh-tYsFXYL!HA{o|On=HXT zYpcZm8(eW=0*(A%OAUc`E!qF2Lkf$ey0ehsmqyrZYQ8%arN%`Hxu|jThey&!9hR9J zeLD6%{W%3e3a3jEkmqcPqMB3#qVuLcH-rt;r!%B)#5Qk>otUe(XCETA!WFmjN(&_g zjwlAtEo1)0gw70!q$U~zY7{Se?!7H~CE)>kWX`HhWQk%c`X4xZwC{_z(&{4Jlt9JUI(pT8;=2Kg^wGP}L|9cl!g_V6F8bB>@2*qhdS>)zX``A8Y1x(>| zp8Vb#P8qpEi z5X$tg-c?L&ecEYlMH#u7u49Q818QzZCh?x&WXHrwJ z+?J^B!NGLHalTeTOM{pA%%n1<5Zmc~>>Tex$my0iD`?e{@yBNd8-&XK1G557urx#I zq8mhFeVa=tv>8VPhm*}wZ~{>AcNdMZmSZ6C9Blput$`z2PBm~z-iqqwVjf-KeoPXb zYOqB=P*YXA;8OK^wu>;br3V3T-@&&|s8I2KX&q>>jZ3%P^S$sv30m278v*#{r289s zn-Y0VgVL%Bqcs4y;1TRJ?O(5Dm%?y#w`rw24JXSb<=t}w0B$Yo^>@Q0?L<&DSr8pc z6&0EXJ|8^E%a$~1EN&tkKYunG&lqf9QS}*ux>@2N-N*&$21TV2O)1?b%-#&8N}t|2 zl;w^_nCUM0^epX#y+i4B8_Bhg2n9m7>*yOES)EC0Sn}x>U(%Q*lUEDRO9jlim1H%y z(f50lKiVR!&}9^5>l2Gd$K3i=gQM}^sg+|7f@ZC_6gjbz&mHr>m%0c z>H3Jl6XvhGG8TUZeFewYte`|*v7(>odw@W<{W7lPu*3aCyQ3>wY1vG$+t$L<$3;F4 z-rBn4*{|#WiOGSyYoDgI^y!U#d&6sJaAAb{3dh1o+o~F74`Lr!f&479@_D zPzRogd5xB8tpUJfI=!ftbpA8`LT3n^Je4I(QK-D}r zP=JRlX+Y%NpdbBvBm9#K%`rnca2Y*NQ9ig{-QPzgMUe$leet02NN!)$c%Zw{*s@0$ zt8-35rJvuL?3{k`=CDbJqan}?PuLeu+i@UXuJ?eIM^fDhgC7xxltBSr8~>?*d&`1)@p-tc?a~8Zn>KG5gNhA>H6`QV^eEDMuQIbRB z(R&SGC3tSVm7@C0a<>gu?~a2v+`GZ(ThImcskMq)&rTn2+HR8kY8ekd5I9@kauxD; z)W5yrTuwH<{P?-u3LZU<&7cBtxSci-uVkl1$Pyk8n+*lU#V2wa#YypKar9fU=y5Ga zbgh^5&_ttS)wWR)DeE{KEM=tuL0-0pvqv8hZs$M0udT(F_%!mIDt8#V0eE$FHl@IZ zenwJVk>Wz)VefOcFruw1th16gs-fhsyKKDP8##JsOSXAj?y`-t-Kg#yItGU6-t&Vb&Fb(zAIk(H^E_60hD&OAjjCawxI+O7^*xkKo13#uW zCUU$Uh-By;q*m*c?LT8T5$M#C9Xal|+Vsz=YXga|-2CNtk@)t$t5xH)iMmNyyZHn? zNk_j38UVJQXq(bAb__jt=9FIeY6c;#JH3qnh*nX#>&>L*0T>os*^s#vz`Fi$(B=rW zKvjs|< zeAQf1d)q9WFSg%%=*nk)VJ?Ky|0JPPp~JN#k9kfh}$g1nA<`z?ur& zuGUA^{{hCY(@e_y3|>m;U@cG+1Qn?u`s=0GZS~|eU_xi74O#5S2Zyy!Oau@+CQF59 z&vQqbhc==qn%#oMv^XM4{4O&<=OiH8Ze;hsK{Nkq$P4J#fN_D1ZyJ9E&9Ujwg2Ze< z%)|z3^j`cW$i<}$bjRa`w}oTi2VsBs_=1lL;u8P(wh>wr?%Z@~QS2_r1_-zg!zrklIzjQ8+LeM>O51BVOh9+B*3Rwo_Sd>68CQd;Ce(-|(G6;s`Jfir1=Mrr1FLar@_itMIL4(kdGO~PGDx8=x zlUS#$m!*tYlHq(E7V!+kG!T%vtT&Q%6+h7L;F|vezu`F^ol7L&v1w(W73n{HV zO&$4C_QaykXhbMp`0P(oTFt?~bDs==X7RY!FD(K3D3cO2ENh@uVUdB~`9|O%`;Nln zYte9q6htu0$Xa4c@YE(dx8m{L^xwku7Ass!(M{>nu~;|^jQiySIWq_J_~42)PPITJ zi6f+ABo00+CvBWOd^&8vfO9+~(2ccW7a3Iz`X5)bfsgjctKoGPR9ljmbtYD0a=sJN zk{Qy{ATP7&AoB8i;D%>gx+0x5(`~LWd9>6ABFL7gM$Co^hc;qs3}%<&0C>v{u3z-T zvWYf-EWkffIbRLCx3hufw>xE(4Fo#(1^1ecJkjK{X!BK4W{{Qkk0R8gZN1DRooK~k zFQ;`&lJ0-A(?3GtAFIp1IY*!%=MM;!I{yDocg{Dt;$iwd*SH+ug zEUgy_8nb-!e8Q~M(e@8!-3~ouaL;p%kjP3RZ~3R~x4Q<7o9Hpan|Lc)3-Ih!t5QU! z8`@st8+CvdK=MY`eeOnWieOELua5eA`hIb#PaAhyt-VVw$jASwt3@Zz zzn|h0%UntxD<^3{hgyo3HAgv-yoouhn&?S#EIKiqaTLNLiQ3{anb1Plx~C1A)eGn< z6v`5pUIO@MmPYXgTqZf9*kk37o@jz~a>XviJ)fHQ$IK-`b`-a(7{Awm{k>98HDc## zHWk>Da!Puw{JrM&*hTMpcTa+|Gud01trBY-x^hCvPHT&Pd}7#JgpmEx6S{pPN%z40 z#$F&B0Y_}u(WUANHf!%X*sQOaP)ASdA;76G(Vh91+$`OzXe#PitKOp`MQjYWfNc4EXfN{eL}wFZ-8dcoirm=tRQVdT6maLH#5C? zbvi19{2+m6w3v(PLbG!Bd`+h*WOXDy%OnwUpblSIHs?SpxnTFG*n`C*URg#i_&<5X zFOI8Y-9{N-{}wx%K63AXXL8RXk7$eVh$VJkgPuHChAK%=Ko!^BKk(k+iNp%Q!J2G^xVI3T)nAW!*d#+a%i2MS|hl!tO6Ow`LC9(ZU!e?DT75l z%+897RC?IV(gpBCer_4))7$=to3TBEV!mBOp=|S7o!UAaoGs`z>39e`4)(bY53xve1AGs= z9eV^$ZrdiH5h%LwBmt#$j4{Y7u}oWE9~{@)*X{&2cV?1$LCxx+CyI(0d{TDQi&UwiGg`@sDLYC0d?E;OEcq0TmyH6baD6Yu75; z?Qn=mJiKC;jne+LOE6_Qn7HUA@e2z3|ffV)UIAu3|wE6LZ+m_N!G0=!~|Fb zBYjWj=t}q3ykf1HcXO|Uur`$HRVMa?lX%Z0AI9@!fcSJqDDgamJZODSM+B%w9WH;Z z`yO8lV7b_WpgqF)S{a)IYjc4?<~!w9100x>5E>R_OBi{H>pcxtj7fHH-j6$(?BcN{`R@-1S3(?qLB#jGmG?_ z_2r2Lwt-U^WXfk|sO$xw!HfVU3oskMicI6yQXzR?9T0#{PoN`?iU{}~bq_##vo(YodePHO1=4IYQ>3O4N3Gg zplfce9bF}$kDto=xIbOmCP<7<I z<#$1-r!*yw7|$}%@g9AxmW+@+Q=$Xk>?W9UKO=^a;g*&=Ur0(4fWVUIhX( zYV=S8{RTw@pwMjsOjG6TX4B%(_H&cT&s2y>XCW(YKvr_+oyoO#qB~he#QzE+j92>} z7OC09yGJr3i`l&i9D2T9Z64ny@Ba^(uDqLPh))35SB2=R%d8(lsn^^=J5zqi|)>C*y$pa5u zEnROo2x)*=P8^n&A&yJ0S?z75jJtODsU2)0yg&+Tkv6@E2ifCbj0SlTlFV{1oK;T z=OCgq=pIdNbPM_)7d@vD#yZ?~ug2@^`RURe>WI}NItpPjjYkiW5Xt{)2@hDLhX$*1 z)SB?SaRpD%*_@{%mcW zg&l*g>X=sKsbScGtB<2zNo)%rL@>{ecdErH)IRU}!hGssaXBZXk*uigB}`bo=UTJH zFI^TQEZMWVCo#Ol&=m)C_0{O4jeFs6o4$9y*Y)o*(J zj4>*PAL9r9>6@QOCneUcpXkbVjjGj7z6Z1-aaZp{yY}ZM_^}Tgg)-#HL8G^l)z+1k z{`6f+8yeJ8`y0|<9tQVikoqExxM+RzxYHHvpZ_wbJcom3)SZ&!{ID$@#L(Qx_dpX$ z8rY1~C7at%{>?nP0Uv;dFghWSei`z1LRoLxr{XV*pOSF>`iJ*^jb@Q__dwfMn? z#j?0fVv}~zDrU zL@TH*_0rQLfqyq}&BmhmxrETqS?oB=(Z1B^JN}RO5`fHhLZqEcm%faxIH3x;Ii?Gy zp;39b_ETA3!DN6?)UMo((}PwUD;f)u=?Gy-E1R?0yLxT%{DySt2wn-zmTP6|zZx7? zA_fN*kCfx6XyF!O2wnr1V>>4O(5F&mv>pM&2p}R#+gl1+b|tNe8101?LVx^${Jjd( zYO-NL{7loyn@j|l6zh__Rokv>-6Wgt#K-#kc#&i)&M@TwdR=ep83jhHN$2Sdh~+KZ zw;5_08I*fE(VbC(rH{)Fzk!YC;{RCUJXnbtvm>wC{@o42B6#kc6_EQr3iRD)z#11; zx=c0E1OEYwm9Z+O$DRIet#c48J`FPf7}Dw)pc>1&S4%Iwh0J$n$7Uig{P9L4a?Bfk z!jmiU_AEXcZXHU`XXTyB0~uL2cod~pERipOTX3J@(u86*qT|s$3(jp_k9|6~{)%cy z)AS;ehy7DtPCS+VW$l))V9EXAQIIlNl~pDn5+`M+C0@RqmbgziB1OO6NiQBA zmHp(t0alk_{bzD*oV^~**8Q*+cT3hz01brPE{eElezaI+ij48r*fXjyW?YkObAJ4yMj-X- zmGQJvZPm1FpU)MuzZi3``R&lT=4X5}o-nuz-4L2xRuY)ge{>f9{L_}QM(0GE^PW{r zX-{$@j>0;Jfr`oJZ4901nGR2kJ2=%M$MFOCQgN5FajiW%Wo8`p0{4m-73qfWF5%ku z9330JbuTP7XQ@gVHGnkc10xB5i@~mrNX%i)EOFmF5N3CWALK&!nt+13%qoyI{RP_S zG`)J;?D&CGMBWShr!FjCLQvHAb1_dL;-MNT#KID_%nyBa{ht0cm%D)3 z&yK3fTIp`JcH;sESRVsEh9yB`FHv=+p$OFIOdI=Lye)N4%fwbz7IL=pDB*a#QRIhywl7rEaDe7mtwh$Sdcsr*NJe7 zKcA5v;_bby0%`en3Jh`IQB_`dro7_pS??v>qoT{y=ds@e9Yyd=z+lBsL)(ohWPEDz zqtzSo3PMPw93T{IClj2&9(#(V69K0I%|b5i@RD-U@mh%Ssd(9uvNWVbwPvExZ{}K0 zrZH0Id%S!WJ#7}KMPR+6M@&AB8>R+Z9~OX^`a`b=46L;PUNZe6Wb+3On4{C%3KXBc z51jnn_oitex^BxBs80fR5C$>Poh+a2A4}2`t3=Lq;!}UxVAG`q(Q}J7Se8`(HIHfj zifHOnG__0{VIqqhtLaYa#r^Hk`<20uIo05VUjOZsc@b<|`ioyrIYGl1DV2RJlOtzg zkL=sy!a9bC$#J*ni%;|$^I}FG^?+N)jIUpWu{(X-n2Y^OZ+bk&%NpO2_@Cm(KZFwgIEzk)61X^}pHd5Sk{r504JM2DU=|4| zTQIPJ`+@K({~u22W45c?9WcSFh1hOh+Dy^fGnNw8oUIw!dU>j zL=dv^&;KGDe=Kq5)=oJUuBiY)j1#HwgGR0S)on@wyAGvG9Faf8&gpnsOa5zmhfCj!M0Cq@Okk`)1 zPtQ_hy9K4y!BxHh&v;k7)9elX#&1pCwaI~m`Gc@HgXhklN7Euy1P82oU7h$u5cjDS zwK^;N6Me@PzS|qH!Gd1LVCoXPjm=w_0jh8JAOLeItWakdaS)N<-*Ybt@H@@X?RnyU zt#QFIRC{Bq7vkDSZ1QvQNIe~2F**y=EPc)Yb4Fz7JD*WL9*Fe8K-4l}tf>t&gg{^z z5w75$aqbobn?|XdVu<~jk}0gd8Kg?|kQrl;ba^RYQe6a0q`gE_&{D&5Rvl(xOk{{4 zl_l)xCXgbCrcEr%YXszn4~}Sp3(>UhH$FQU`iV@WpBNqi8`%iMmXx`g&bX*StMpV- z{t*|qKHr#14tbG{m4TNJ(~3Pu5~cfef;e5%ObIZY3wgAJXM`Xp42_aqik2&DcadT^ zkq>Y1uS^a$mn@hLbFB>(ro{O(r6aQyOvA zalun}F78`W$JPySF%y`EpbdZ2U`;&$H*k!l(5)ewivOsw2>+%1bGE|!961f}tqb}= znufFN4|gk<5NULd(U)vc#$+rt2m)q!Ofn>e_@qle@h1N5#B#H8N>hoSLo>s;D9`#t67mPA( z^?~#!6s6NYl<6mzi{3l#lIAcR4LYUGx8;Q1J}vd|QIR}~lHkaDyo1*6)KVT_A8Q!v zGt;=u&QFB3@D~JvFfRU<0d6?=OuE{8C{uj(4KE&gvAXSW^w=ln6q$adV z#2eLh7Mjh^_VC@{8}YM|K!u!{?mtBBiFP0SjBwI!E}p>85rLmOf~ssU?OHqgwYP0k z?B;t1esIU`NjB#V@Ar-l)vSs(+Vr7GV#IkDbLwQkYo4Z+68JA&VX2|M=kA~vDdpcy zt(r!OW|oNubb;?SA6upBY3thX_{!WKCeVT}&P}N`)nAnEtV@vY*c>Iuu4CD1utUd4 zGy<+(MB=F#T;hl=iA~bwnNjL?6vg^aIXGz>Auq|})W~2Pcbs-Y?*-GSb(BM3ZcRo- zOeP0ssCs%$0}(BT&+pC`o@=93!eEcP&2uKLRpCqid$6vRF^DP((k<%r_k28~bOANL z;7o+6W+b$_9R;S^EE}QuHOZ!8ng3zHa7ARXMYo1H2;si_PbgP z|93%0wj?7aGDy+DUtNoK=UP5KVe}-#mwzcK6O0&S5cAke{QViVf)`Q1jRNSPH`)Ve z`e8=_DEO7-2fz-q#(Op+h2cvL{mB zH}tVXNKmVtk1;ZQPmp7p*Ex>!DJbOMJ&U$hDN{r&iJ~*HC?T^vrB>KL@3Vqa2*+ij za|7x>N(YO6SriV+KPl^h&lrRPWOJ6Zhm;}#vP3pTDdBtcfQ$!vc72-*JW8M%Go-5y z6>acWUwToN5gPhwmLSo(Xc*ilgZR3te)zf+F!{H2LN&bXZIG?CbQjJK1l5m~;GEH^ zOvEgxN)L-nF(Sw)|40RM(e#DWsSLX&UsW(UpC!|p1u7&c!;_P98#g7^_1T2S?xU?vC$z(uKe7u#CITs(;| z;EO{=Mg@39-afsqmA~|U|KWJe{|E36{3H=0Go*JNdabAxy1{7>(d(TW{t@Nfn!xP3 zenQE?ibRiL-s4tp%E!lz9`dMn*6LF2QdM`JTmf5o6ZxP`74oPaNDTk(ef>F7&ifw* z*dOX_2XvC)rv^9Q&ehWQkpXHyV2xqKp&iSwSe)^dElcp;Jw+vF9u}SWMmrgCDK659 zU-nq~pM%-)1ghH1I|@HCHNGzZrp956Tlyz2cu^oQNcdMek>w}!y&&xme4s|5o`<@s zCHNKP5K0no>6VNswE=co=jr1z@~ZLj%CJAnR&-{+*LjdibF5{R|2%W2>kB$3)MorE zLnry3pvJz0=9v;Rj;N4;imJ0`F(l%gsr0GPfpsvC+FPIyU-M@Mj0~C~cSNxw_&qkPe4N3D(EDxM0dVk8E%V( zcw1^6{!c2Pcs%yJdNFIj_GdH5!sVb6W>QV~LRl`Qb`D%eZB*_koQVVV27K4|q^)IS zI`gt9mxEewOO)$37%a73vFJi5j4)eCM4GndM=YXw(N{1MQ8k3@WQbT-4_mN^6vGU} zn3PU5E!t3MvTBLUh)f8MA`^mGCJ^|Zzu-4v&LrU1tfNh~im`*hZ){90tljeKVAFTf z&_samF1OxI?qvf8>_2RZ_JcujWYhX0Q$O)wDa$x<(J{V5wN z;LQIt00F}$oqRuUpOS}9I}EY=o$a6CL@1cD8I4MhH2w#-z6U#Rk%-2I%mw9_- zgI~v(ivj2dAa|?(Ut{=iGYiCu8LKm3l$GoMTBx-6X5)(nlvh38rSR9AtAe6kBuzu%t&mjZ9PCOcMHzj6g5T1Bp}+fw z)AGuHwuh)9TArgKG7=B6zTO*xNp!r#njXi=!ATI~58W+0GJ+UwscGvw-W!*DkI>~F zQtn;5_Tide1!CK^W(V6YY@p+~Fd)Vu9N?H2UFJ;) z=LfJ<6#EH`nV)i$${f7^A8ThG59R*9|4*k*krq*rEIB2lP_mWfq@rlmk=>*cl4Q#| z<|OKrwX&488d|I&gp4+X&?rP0`!=$VWiWoP_dS$R=kxjg@%!gIPMc-ibKkG`^}4R- z1x`lzO)wXF^NXxsX=)rEm&iui5(c>g@K*94*y6o232{(5|{#45VvfN9d5x*F4gZ^`{a_;*t+H}=wV?M-+lz7BA8A2bBB=I z5#-U&CRG1uomtQFZEebswqc}%x`gIDhN+e51I8{qOi?uX zCVRfTmcK@O?lti}wZSR|b_^{64n--`IEzNh{_vi9!Y{D34W@T* zrwZmaUKjal!whjA!)und^uQbeR@aiB#H6cvHORJ7j@=)=54b^Rm8>pJp6@S6zYo!Q zn2o>E9|rO9+6%jbwd3U>!N?-|P4yFMetfho^H%cGuGNp~u4lG#CrT~k*?M!89VE|S zx8WUO%GL9G-N3!Z&cqY;ETTU6a2BH1JGpKW)+#VN!0nXsL^m0362?@jjr<5&abuQ6 zi2Xh4%hK?$1=|nv7V-#I*@@&w8m8&RS#u(1sZ^AQhF~hwgcD zd0^Y^bUKB90J3a*I1XD+Y!N=2PCB-ks&&!h_n%h0{kql5)n=F+>LW?(6hC%MOcv#~ z?C{!ZzPzC7)^N1?xkv%~75m4Qtk+_LKnL|>u#5k@acz(1nP4#8<|hc*88}84XnK5alEA246<@69tufc=qM8h~6<^}V8b*{}^O+XB6zAO6xI=h4N*j2^j zmmn1bG8wKzHKjA{dQ2YJLN(TVJvYUtbbbwpA%K~Dk6QH>#6iDJL`S@Dom;yNPf+8# zy+K3-_x)OtsPdAwe^DN1)WGCf25i!6`tKVuOZ*%=C2{@eZQ6R zyzgFxG@;tuOhd)>^adlEOBUBZVvAH8+w5K`vRW75n48x{!B9@j1Os+BnEM1q_ZbTP zJkv#wFTaIDWw4u(fcPvd2WmCbiq1;&gZi=4$e z^gMm31mYuzkl?}7b!7v|F}u_Tup~gAV!ifI)`xU)W~|Q@*>tSoVaOgONXmeq&oURP zU;wIC7J5_e&L+~}BRGnEP#itK>#ib?O6c$9LB(+`u&Z7iGF{CYsmaEa#R{`oWsx#8 z6Qjw6{Vn(oK)gJ5nyplQcHm7XX4)V>R`aAlw3`P6XQ*lCU#(L6-6w1@TCv}4d~)hU z$rJqWPV?Iv8j0nlthY;u5EssbJxA%kceIN*)n9kbsKcW41Bw4#D>J9O086ZE%fpG3 zLb}h@DjSpJmoDdf+xL$i&8x-nKc|fAj1NAo3Fod1P$fN3ut>8)me{{BOYHMYAkl#Q zNdGQB{tySxG3Cd<3m-3S?+UJxz2oj0r-bQ_uCbsyMn`S2!GiW_#SpYty>xXYrGJSs zW3sk6WUW!C{&Kd_-2N&lm-A!xH(2sc-3uclo9(U$o!ze4 z?Wq**CW||Dy~Si%Cv8<%$l`PQ^~O5`x-E=6Ci5wHL3CbtfQGJ|mlIbR-xHzS*W7kh zvfLCCA(jT=X~^mTKNg2nVu zCMC<|DV+iSVp3+N!l|i2!@jzROV#)%<|Q4jJ_`qkeKiPJ@b?t`zxW)nIqs$-N(Vxc zMC&MMdh?0DpLZWTMiD!L;`SZBfbHqex8s~QuAt-AFW+itw2M14M=1QhK2`&;Hh5<+ z-}xF9`r8wEu7dN0NANEKy6_Hy^@2O-HFL)VTg`ZXnCRNOb;7H7fXInkEmcle+x9tg zcBl0F6}_!X7R=iz!HhmD^sm5Y0PP}J=3v7^Uv~LZCV?QQRgt^c&4&-mL#CZ|iw>C{ z9^oL)t$~GYmcSd=UG3Sg2)W~m%1r0sk>v#$j_S8*I?G>BWxS3Fo8Mgoca4a@mt5!~ z2MFF5!~iWv2*-~-ycH=I`2$=MtQR23WMn}q2%w**JMR78D7BWGfr%}(uLx| zVUh;sCu_2+bXz(hk^4hd+?F)@B8j%fyRpyUNjIFa*>2M1I&W|zH}|SMTl)?kIOJP( z@JK~OZv*hoyp(afFrbh^V*UtLYICTH(Aoqj*NLOSlQ5_9Vmzsr#tI^l`wO-OqCFv zdRM@ub^sAz%17JFLk2-F^@w=RYK4Yo2{cy*TFf#i<9u56Xmss(n*K7fM2xt zKqiSdl;Y9I!u2wKEi{=vLu-(4Fg$OGuUTC5^{-1!i(hsRLDV~n)EbrB^O=5hK?fi) zpI%8%1Wtl?Xc*FGWXH_;AS)KhR+g1PTVt|xm9#ZAUf9cGXUm?$Y%FeAEN5f^Zdmra zxS?MH8=~uGuBu^gO)Y9X!8tz?r-$Gh6NDGLRGZEzS$)jw);r?^eq*9q zAKomVq>Sm<$^`q#cRi)-qf(5iS8?02;HajGC-#z~RXqgeNm$GcB?QQB)1wYg}OxMkM=V8{lut;b!k|7MX-_GcK`YtzT*PbuFR|Z}a zziux;roBn#@Qp!XP5i-e7URl{7{NEHm%GaW#B9nnTKX*i6@``IkPC>bjb7j^BsU-f z>}ORgCA59ytI0Xn`e$^<(Bl#RYGwK&s+I~p6ocdbt9fvooVmm|EgG@Bt_??jsesd< zmPsnik9xUWP$}b#5Joug!mce?{2C?E82T0Z;o?=H`+*cI7ETk{m-R*|nsu+UkkDUU zy%NUC!I64r+|zWLS)=Fv?P?ca#S50hisv5tXpb4(vR-hiw6Jw8J$j3luhHOVG9yVQzrioktuJr4++Bnxx*+mukpAv9YUKviL(H)#4#7AHlQsH9X27*V65N z>m9GdEVuW`8)aBu`yVrT?_B67tAwrOe8Di^G+AjkexUV&f8de;YkOlk`iEifJ85#l zD+RuuOdRw!e>7%MSuh4;{LT?&`vS}rJ9yrP@nK8&lAe!WIW}+Za2>LJZ4X)2{)jZU z*Cne4r(WxL4mH&j_x@hKxmDFWv@FT{d>V?*li{yE)~y``y&9vK)_)nj@Hk-en9UL` za9S&;hf{Q|)H9du>2k|pA7`98eo0QrkjK+4%Tqm9VLGk+kY6-P@tSO1QjQ-#4Oxzo z*SD@77M$wEFH5G?gdhCrKXdfL^l3s^Jw8NVM)K!hdO%jm;{ zZ?`3uhJ`fUrupXlB|x~#VL^^KIb8b#QI|Dsu`!j3F}MCyJQaE zcDtRsou#nsiv4l7irw>QTOn;=015*aE;7UJfBCXjwEc`G7@1UmuswFr9ye_jC=Fpp8d z{eCk(Vf!UOh@W+_hB?9DxU~(N`XMBzZ%}CqLrOtO3hARAj4VVzGC;zF_o^k zT^^J>i52lL7O*Vw&4;iRhvfLk#ut~|dw;Ln^dgYRD_qzS75!zjhn~Q9X?}k0AE8Ug z6u?pKS)IK2pibc|Z?>P$&?tMW25h73%aBkF09~TEwT`Tx7n{P-^`*`mxB!OHVS;C~8)kY6Z5fI_ z?H}He%zbXF`7f2^k9N5t%`Vw4#`S(+7M3gKUbUzx^GNA@sec)hmOAAGRMnEl=d2GQ zuMVRFETOSQ82ZY#3)t_T85XyCZTQC;fFxeHnb|rXO-|D9`3rt>J@3?B= z;2_EoWh?{3dg$lTt-Nl--G{>)bu|Y%*RL%Au;V}i48M^>y};b+65d2ZT_A(qr3IXq zFG^OntET4JP^U1sh%ao&(+9|Tu4mgTIMNi=1kq0KnXbRruksV$J&>5Yvm)LfJqCXS zH9@bM4a1j8X|f5=rjnkG|2`*?qr)OuGGz?NmoP7EKvuHGK2dZQ{tlpC`8|~|DiWMv zp@xm~Yifkg{UROD%V$OME73Oaz?0m)q3rc6`(Dj**tCsr)^!Hrn( zl%bWgCfi|k8UMg9x#EQC5F@azt>0vG;uwJEq252L$qfJUKs1Urc(y7zURr((GZpe^7M&BDRA|` zveU#7<@B*W6-eM=ksq|fN8bqSJ4?%JF906*FFd>sc0)eIdAW8tT+I=s=R!9n_rAyA zo|q^N`}s4cCf^lkZ$*_9F|Ljzw8V#_3MK~5=#8L37DvKojU!Zn?oSLiK*_YZ;UT7n zmNxHl-H0(LL3GrgVNIRgZS|~--b`M?3xRMEjT1<8w%r=?`x4@-FWG-@D zJcbu6I|F)jV6U$S;$N??_SDN=Iwk7mUbYV6 z(lOJUyvm;^CW}lTx>N>>>M|D*R!bVN-x?kmmB2um}+b@u!DejQpgv516?NhL`47##4X=bP2T~v22r-qX;;EA6pF0S$D3W;Q@B_~ zB``S2)8*Ar1mto0kC}B3c%jD%bO@7tK8aGEDSO0TlVRfxws=V|fLLKvsh;R`0O|&S z)f9WX$qL8#H7Ez=Q2oFAU0?7?S0gL5-RGcyhL#8p@hhy)sx5?>XVnmsMF>X?{$$MB z4|j|>-GPb|SZ5z{!_PJiVM&=7a!#*U>?|Kzt1#0|P<7%?m;4cMgfK748#w14(`5MjPIn1$egz0N^Y{GOz@HyJx{+o{Yg1lmLecgCIvKoGs zv20c;Wa^0Tkd4u$@ZIR4-3v+1@-`%ga9PXS<%e_^E2-#6!@e5>cDh^@eYCWyD|kef zo8TG;LHkeSHUs$=)N87p10B_CMGxEMP$`aY*9V5K&8i>hGw46Bft-w78ZGD}xic=R z2pZRLHXKxNy?+j@#u#PO>FP>o@z$UO5<$#i*MKhiOE{5bO_^lyF6LONy*S6E=(@#p zSm<+rdYt_3RTG7d=M5wkEtMYQY4KG*wor zRXIv{Ws_lsz}c97q~~WQ)!#*20{go;X@;s43 zWR0&JMd1CLjt;JG*5MHwcXszZlR=vK8leSLQ^*=)Vp;degA@N=B(u9i}OF^C}`k%9LBx+?v#jgjHqC!5iyZaumS6Bua-_S*2b z)R83P?Ut)xANA=(o5#^ZJPAyWrzwIN0ed#>d>4oZ$#-aI0Tmk{*zdKo>75OFyTC%5 z#TCmWcY+@l-jMO!)hv*}@9hAvTEVFH5C$~udTNfBz2G4`YkCclgxelAo*X3lT_b}u zf`zY^^34lDAR5mPUMD8{4Ny042DO1l4c3%kG(SDv_0e$14&a6~41j-`)Um95Hz~$+iv;-!fp<%ir6qhXTo6!}el<8-w)W(5tzT1>p8IQ0;XC_xl zVd=>c3E+Fc8WIfXJrB(A!d`GYg#8)#7a?`=a^wiC&=aoRhxrZ#nPASl^&TK8fT&Ls z`iZrimoc*tDsa<+4AR#PF5ychBcO%{i9KCfWnlI4o8IJZiQmt4H-y$^TlV|oyS#MR z#ak=~0aKtJB4KMN2k%2lhj@lsVt$;>i_5pS?$iZin@#%IWF?FyD`bq75X!~EV86PE zrdwy+83feeamtc{R>F+ixl8is~K@ zZxIEk@Qdil(S`AH9pKAtB2rSAv{1cdWx~HIiy$KcMHDpaE^L#RjX+kxP-F-P9t>E4 zwHE^F2@H1tgT;v{aW3WCU7 z?ugqUD9`~)W^COJ61MVp5d~5C6WI?#H^B6@1iXZcSP8THQO6StxB*FtxBirDNh*Yw z&H3CL{CH|kj0t-aoT~17@g;taSErV6N7zF@6I{WY_XWB*Kpr0oo1hw5`}}8gkn|2M zBDQ7S7UGre)iZ}HOr^!YP>}~5h3X(zFBei4*ZYfYp!nYyok`OGcM|~5JlH#&&`K$N{ZndTVJ2~%8wN(475UybT9aU;@z9>iK`e`R{>dH)s5fP?e&{h zBZSQ}Gzq&K0cBzOW)Te+an*j=Lgyb^Q3Op7?nt7?}a&PEf^nK<)@G z>4=MXcHy-Wd(w;N=LK(}4u}O)ul9(Sx@Avp@f~Tu#R+GDBChP?EiT z;TOJG=JI9m%_R5;F!0G@cJIf|2t50`5Cp~Z8kvbE`Cu}D8-il(hL~^UEHSTG8&w(- zETOC}OK%0A$^=-Oc)@5KxAI@;om1xIOV8^j!@z4$bAMlZA3Z_a)N3Coj4(v-`?VW^ z!e|fWWqyo!VyTqn1$}_9p5q!-m`rXLo=Sni#NiI9!}<9t?+w+->h7Wgoe<}SUSuov zlHIG)3+q7Te!eZlY9tIQ{=nWH4c0EY&xkiKn9#+%rQWh8)ufXxm=TSnK9Ci=D9be7n-*7ok&3YLr5EICSMeWOjBu z;gBl9^l13hi)hwB@P%##O9R{*(Mv}E;*;(88UXD6dpvA+fQR|V75-@5tMC#;XDujX zL(MDNw5;{g?~XS-n)N6c-Cbrv*nMEDalu^pR%MVRyi_IaXUecsLiTEv&}}6_o8kO1 zT^sKO0R}D#fe}dVYH?1^!+?m+EngSGy-;Wf+8EmJKIImEYeXjx3 zNyW81!j}_c>$EcUJyWn&DUaA7)!d5a1%%=5St93|u2gS?{v7=KTZEIU``eX$`ygf% zvq6eTK0Hh4ERF1kfBtN{M{KnM{Bz&Ox`)lzMZ)`hr|S>5ETaLOYU^9@<<`bqmq0O9 zVSq;pSH$ie_T%1wEncXRsT0vyxlxAuzqJot@W7JXTu+%W>tUdHen!13V0VJ&GP5GS z)W(l~QXXQ-OpB-8kQVFKvmUmdKN8p9Qz_psZV-O7v%fdHf*~B^>dV7mryqV8o4<*G zmmyI6@mBoTm8X7%^Q<31#YS-ND@|J*4%4DJ{y1{g{D664>2M>wAsn+2pfVV($w<|(r>-=WgGwcux{~dQ1ed<_~AGjUhs)AxDhu0KDRPD%=>ag z+Z$6QjB|@EP~2$K6#xJpKjaIO+<=88{JTR-&rc6Y{rWJCz_`^))WBJ2=g^!qP3hJA{PdDVWGN+|Ba`2(ff8*y%D+vo93ll zFM%Biah_&PY^z}a3Y-0LR;|b#FjP+F?Vac!lSkLEMO{HCi<|n(Zg}z9Aw8!}P4yBnwW)9gj zk*?@U`X65sQ|*2tyT+i$V+m$#MAZ6!fLb4hjE%=MPeI@O#L=k62$)ryVmTL}$dCGU z7C|uqPH*1#k&N_t%-oUa#H4mw;Mc{RIxg(BzCVsqOW$&!NvbiaJn&E5e_eR|5Ru$} z1yndsk8*3xjFwX`#8hsS(-S8;S^wyi0p~*pHzqA??G_8A%SttuLQ;)P=z@-&3lCJPzCeICdI-(#}J?gmXEGKI$znK*Y%qb8Oosa=VZ98r!GOmF;C*FNb$8e;3_ztsP^!?Y*atDy(^hI|7?o0}8zqFi~2-~<)>wO3t zvdglytBt{?_YE07ff1YPOYTH6TD#v7o>{u5(lek+DrwvJCTdB{i&H0j17BXS#G_|u z^Ae!gcQhCOABv<}Xz1(eRF%nAW^hnVw zQpU)6^AP8=)r3G?g8k`Q^W`_Oxe{fh!OsJ_(9$mNl7vA`4tXzyq zUK;3x;hFAO#p9#$>OO=8*Lm(a@U>fKXZB%qQo0T&m3;Kmsl4_PP|{d7QfEqf(ZOk` z9uy%V_R_oqAmZkYj3m-Qg>D>+aY7)ajX;u=Tdtu+H{8iDa8d9e41jN%ECefrN+vOo zq@UQLEDza-oFPl203?L5-naGb`z)9#$s|Tb0#4|u@Mpgy_6G-NO|D-u4~Mm5e%4O1 z+e;&~5U0dy1Z*4clvr?vRa`Fd_#whUhm)o{^>BxvG~fqC(wp#@*%zSXm4KcqI`9gb z;VW%C4E1qM?L4fWy%jL|Vm@eOSg~QZ4a$6K##!ER@fDm4eQM7Euu5c{zwOtps*zEb zG$;;mh{Cw;!unpb3+js2xRw_Vij5KMrl-aUe(q)Zhbi{2X1CFYm)PGSA2Q(zs~b)5 zW#TB^*VZ3Ak5E~6@a0~|wB1Cz0s0!Eo>t45F=K3PYdO&4Ho{DP$r6UY7p6+XdDW7| zH~@vUQ=QiF@Y=gfnB+CiIxO+I5bfrnWFvyBheo-;=;+$T3DNkWTE83TXNZD2~g zP&(Zc&fDX2Sj!ozcQzPM$)}~{!yB|Qy6tK zh|J=GbrDs*v$@y)4Ox?IptJn5FVy|ie?q+abm6Ujaz)>g_yrnnfT2}2kGDg0+`y%| zwFkuKqwdykt@nX3^l+mGfmotb{@SC0^bm1C_6Ut2Kf{zR_;zE8Oi8h%($|HS+a(xu=cB zw4Ik8-inR6tGV}5=r=3l-;viLz)9iTjk*Bukp<5W)JTEbUY9Z>8ya8?Dl8$Cm?-t5 zKl{JFP0Ea(JbzB2J0-k;$`zl5RjQ1__#y6#Sb;!;EGUP*iOHm!c~%d0H3E8*S@IJ) z9R0{=hU(fF4O4eMQI*yUoKBb~9EB|maG!pMu)SNwrs%NSM61cPFmkgzpeME$(X%gm z*diOERdj*vrja zJ#dM@4jZHXz@ryZ5WY9;MEc!XpGTPanVB_(CoIz>c;*!Ha{11Bxoj90psSwH%7k%3 z0VmdXuL1UsDa8G89!A}qi$dVgg^-nD{ueP1Vl7LLC&b-wBHmWA zIQSI@>v?a!%hmeFz+3)ZID~qiIgI<5X~2PhU&PnJCpGy4%Kl!EXrKTIyFaqVZ>zu2 z7*Xx@%H@@ddsX`qIbd@l`s?@FcI09CoT_HOq{$1Pd-<$K=~!sFlOWidwmUKxhff10?|Bgq$Fp=K)WAvjrgxzaOj2ON+uOk2QX&+&ao-=v_c)H7mEerX9dHqvxe#)zil_J-p8a^z zv-5{Xh336Z8d9QQv9^GFO|y6be z{L5kwhZfOK?haU}c6PdlAJ(j&AYN_}Jj}qs%B`LgDqvYJJDV4l!DXL1_0kH5=$qoY zo}p3qSo2S_=vK&6CSrNy{g}AS^b*vr^ZSH(`N6L0^BfaWzzhUtB>lu>JCRHA%UD*6 zh&No%gjqweR?D3EO>M0bD1JGD@jH%i!cspbrrS1j``j?oj8GW2OHNuiXoK)?W%oE| zt%)TcN4HidLsw6)_Lxz|))SgwFGN)S_@Bl&K&=B6=?>^T$dr)1Gc4Bo7-h6hI?T<8 z(`)2H*|&xWpP_QY&vM)oQU9MC?_nM-IdW<6qZt#%S#P*S_sdX#Q7Tl(C)I|InZyJrNsb;)T-kAwn@ z)64f-D75^@U+~3s$%4ZS-puLh@Op{{l+7?Y9TCjqY(p*_Wcr)NH}DD67a@lteCx_s z`%hVEJl5^wRM_DPU+Vvt=6XxeTdxsk`*O(u9Q*ShX~L~qWUdABZ1$a4&yHG}l^R86 z<;r_2`@E1@sh)!`RnlD=+);W^DBXMlLo85 zHi^W$e%M3{$Ne^6)SufW0~$8mhEQA3|L&?^kDczA+E%pE^Wjd36%zvsf9+Jt490Rr z-;h!1yaAFd(Jmj^w)C&%}s#^!P86%H^z!OmNPy*<@-YzP*7Q=Bc zTGUJe7^$=q_Q{y};ig<38_R8DJ=6yq%Kv9pFi20fah~SOERJd*?3u|<-|ns~8{P7D zDCtHK#oEZ5vw~$Q4wMv`jf)Ujyz5WK|qRKv^HLMHTP96$c`z3b@8FrDqsy2 zca9`T6?jV}CLj3iQ!44Du9zA-_nP>g)L<0@%MfPI8WCerau!C*hbAH+ zlF+1zHAP`HsAwfwLy!A938-F>6hY_*13bLrAvAN**neL!;4EA@vu=Z(05d) z-FH-H%-B&-mh(5Cyo?xIXL85M+Nkq#`>t>r)6$Af98yD28sFN9K|Nu8nNJkTg62$S z!rcIEKLY>?G{dw)Y{q1p4u)*y7Q*@IU-XC7kB%HpCs~Yr6D?B(o6nv|R}h1rKqp=e z?|NQvE6NIhPx83pF1=!iM=$M*0eK!a@W}|D5ABJQof4}HYG954du!-VViHSn)b{~* zPH_~(7sQBtP5$>DySXUWWD~ea01TIMu?MZSvE#5?l`beN0h7&O^2`~rT z$qHf8N1eYLyHuG}rEJ2 z5N#39CWvJ2ANVq6p!3p@7TD0JhN)SoC!uk%5UFXU>*P3;eq-%Y^}z8QK;e3*pPy9bXt>XB z-K-+Yk7rg{eUhfj$P&ReJL;eie|gxU)GSZ*4H*imf{9kWF&@K^zu&-DuR8#?9NGEx zR1vbrR9&x%wkB9@CgrLu8ZeZfu00(wy*oKr)Z}ii;_<0>)p7~EsmZw+&I7GI&4}(v zYRWi#8G2(nZ1U`fv}se|DTXFbXa{P`Y{*hI$?>+8|Y2&%0JlLB+c1Y1pA0hs7YE92!7rM!Sy zFJ=Np5`!l;nEw(4)uq3s=&kr|@lSW?S)pHmegP3K%x}NkzmEIjtCt%zu;x6k9 z2QuBfEjr|Qq}0TO^)>{Qd@>nMNETbvWDi;6*m#?P&Ts2|t*O47sPTM&Bxie|Rwm63 zcMZh_E5B3_g_QqGk<>koBPjShL$Q6%7*I9&?smo5_du&Sz|tSXyp@I`!N$+a5>g@f zTR?`S$`4u504x5HSo#_`pOrB2a;~OXf8-Xe0tfdW9xd%`pMXMoDElG?M*y%d7crE9ne>E_xKiJ=TU({-R2t>TRU`!g3seiC`1A9z&->=K$*C zq@O;tHDsY`<4@xzO9Hxka-Q?`ykv(N(z*Xip7p%y7I*eAiJc-|AE%>pRhMTH6NWqn z+~N$*jFhd|B(_OdMp*3Ze>@YHtvK+Xh5xXba*2GLwolSl&_jxq3ki-gr521P_lD#Y7 zP97;Osn^O9AIn=5^W=6MG;+L`%aa}Jjv8#wjn$mKp>-qhVbG2X)_6q`={8ARLgEK;OgG+x!+8- z(RW+Ka$xcq-z(L9j}7}GG%l|-1lgPChypeHj-AYj>2bn0p*rJ>LxxvhGj8WtZyJd{ zBEHz?`pb1B_mR)+*R|hO(X%JZH-2(=*~t^8cd|L$FE#!+B`uPsQ$KnEDl@}z5Nvr&>CYR&lf)W4o2em{I^wi=mD*SO z4#Ul67l6mh(ErA~)RLoLjunV|Y!G_kp;1Y+Ifd`i$g?cH_3bGC?<}wm*B=+!(uYnf zc{8UI_w3C*T}$915)%`o@Zw-iNpX$!e@nkj&Jj9HaHDc$@yd8|>*1j4foNkbmqIe) z%bnQs4d&`KH{Yz>G*^QxcWzwCcx)*nJY_pr?NlX;htOR?=*Qemb*)sFcXlCHtx%+J27x~xJ-m4K(U zI~Cm&H}~j`QM;u|_cmU!S?lB>MY#6(!2HOTHGV_JM`WOusBu~OQ}pK6cG0q}q4^;i z{ypxGRQJE%b~K{P#jf+#TRWf7H)E7@ZB`Rev78c=^>*7`@Y?5E-UYRuZgbnaVzZ&B zS+i#cV3s(RXm+d^w}g@^`-ivfObG*Z!tilM-&1BR}<^o_Hdg=#B{sH ziazJ;yyKLi16Nc>@{I?GLgM}DnUyZX9SzcLd#jI1t{(3bQeMWw|H}vO5yN?c`ZWoB z)g)Q5uT-~-C!FJQ#-TItqpcpUbu2;3fghcc(k$`ooV4M(1_MPZDUc7(imEXF0J=a| z8vKOSreXV1YPG@!KPk3t(a2nKJVe&;!Sd4eU|b2z0y(bXCvwNQZ(xPgq((QE*DNfLf@^3qLwhOR4gyCoVTUS6% z9*X(LmD=P`_*R$3`EwiKc!dIJ-u@74RLwS~atOdLpy>{taqHo~?U^7a@Mt~MYiIdb z^`+6rpyQ>mC`U*{M?6p^bj9nMop2-gt!v5hiKvn*{V(>hnHhIWH~*gc!TYvY_(RE! z3qnOCV~_1#*)Kmjq^?uAj9Ziaq*$P~SP|C5`J1qJ+zH1E!?5NR4^Gw!Wq+_p>*;Ue ztF(PN4eC>kbh4Fp8T=Er`-WC1ty3LeuC0mN;(5$nlx(4CsB?H@?2DjMb&evPwZM7XcTsOwThKz zKgn?+%eMzF)#9#z=sc3^R3b6t$t;dE-pJDL$mK`{}X90D`5{(mV3uCj}l$Gpg_9YQFbg7&{a&E`Er# zTP9wreEr}jFL$SNNe3xIA*A6xHzSc&fr)@vX%B1d_DU07JC{PnrIT$IaGT&SZF{pv z5Fu&bAJy?yv3YL;`4F!cXQnURY^;WfqR_|}7yYhTAC9d&%R@p1Il``geA1`SZrj0V zs^wf;`YrwgVF13YWaX#FDfUZS)(eK?COUCAH;3}HDND7Bu9F{5AAFf1=gxXFs4qnq z47>_pe}cL9-usv8=Ydg=} z_gnKyl6m*C0|cWfn)OBOn)X10W%o(+;ncfQgK1_^-;qZ0C7ByiOW0mxAKnT&Y!*!= zmS$Ill1E`58!Lo=x>fL{?UK1qrK3D(so2t=Y{QMcCngN9!etF#nJvYB2bJSNEBePU z_2{6R6}*|!;a-ziy0}W$vC;_$q4_WVTATlnRqDG4#cfBTm&5n&gw*kX61xqYYBf9U z*)E>NudBKh^6E4&O5cdRUOK*G^5czQTI`gluukAB`X64mi^&HQlF1PGH#5ht6V`+G zC7Yu<$Bn>UPDk1&UAok(bZ_1|baTs7O(Xgh@Yl6f1f4c&PvvLx{YY}W`Y+eu(APTj zVz1Z7U2_~h_bKiSyh5FB&<)~C>NtyicW{?Lp9^sP38WNVH)HV_!CYq(9PD{!}5vYnzEW4DKNmwnk~ZzjdP2nIS7P^cy!4lHuJ3>sfU+ z7|cLqo3gP68WP`MczMvH+j%tR1@3dkulC*BwXi}+mw^9yMD8#I<3dbj{CUY)`a#+y^@CSl5IpuQG5P#>9*=v^y6~4^_Ax! z5OKMgIH%^r3t`zo&&LyA3rvg~E^$UyVs5o7L29*zxLSht7S5`w>L-LpF+uE!QV&-1 za0%~cHyeTZU-V#tl<=BR__es1^JjPyDL5UWPrBSwuL>=>R26&RL295`UOFvUOJ=RI z>n8GU>S9*Y^N$HAGYh?aGT|%8j6=_l8hA_6qhR2Jo5wwx4tX@9+QtZ;n(!w8V+OT~ z>ZIzZA$701`W^aw^O!BQ?jWMf;ZwE@3R3z5>k#wy%ZU`##?bWW0dqc^%!reX2EI z{>Z?>@mI9n!=0B&DEwxSYfy*VWqyC(Dihh9mBR4cgZbi^aC)Ltj+r)G76zYj1`0rX zoPKipc_Iydo2ZdIGk{>1{@2ZB8T*8v%VYy4z@@Y)F+eDm4UOZPm%^*6Z&O93vTbkn zl`OgVWx=lpr#iZw<#2CdJ9dRmiLT0>AhTtplN=bnjf<*6eH*6U5n)Iku()ws4;L)_ z#Q=ARawE{%Kgk9^ZpsK_d^5lWjgk1p?N}TNy-I6000e*u`5i<550uu0OKg=VZ2VTKrI96iB08xq^kh43e@eS>nFjJ(mk=bpG&GYU4dzepx|Tx#qg+P|mjQ|vdyYTrB!<(W%=*Jg|T(iyut&J@7t z7F}BVIdArD;yY~FLNdPwGlqD6>;<{(Jrw)=&N$8wWEve7`L09j15}0_T-$ewmgTKJ z5Fp>{wqoBsE#G5u`vv$^TsIxxyx&-T628PBZ8M`Vy92MI7*$6H2^l$Rb7pzk&9%VspyH;Crhx?VhaQ)uhs z%zzKWm7jz@2s!izX#or43&!yQZXboC6}2OpO5W;s zRGN(5-RdR4EG;-Lzda1PTfsTTcI!(v;)USxgm%@ z6i_DMa6rL;a>M8G>bIPX)qZknFH-;TTDXEtTq(~xb)_2;wrwBQZ?t7Bp@{XkWDw3~ zjy|=CP%%B(-MD^ZPE?mrO^Y%8=;Nbz?-Z9G$em2wi(XBYh-f!j%!M{$-Z|~dk0!p_ z2*OJie)$N)*GkmP{DqCm6c5WgQ4%Ge3%}xUQi0xo=pJWVU{f`kz*!NF3%y39wUdRL zEOK4W#^_`2XmOo3jk4Ikz9AOCbhpKjR$NHg3%I88E$YYrc7$m@#c+G}#jaGf1Iajj zl(`Gr{q&n#60fo7qS(J6j;Bs+(XeU7ISpyCLBrmGTW5A=4-1Lg#RitCHrmC!^8TAu zk+fqp67H#7-zaM}{?0}BaIx8!PnkU-Y^6pG@_linLt!dzRKZBbV5Pmbc(x7nMhd!7 zPS}mdDJJb5NfdIYri?1bs-&IIc>A72ySiJDK+)|k)=3l3?@JP@8{bE`-kX`4xkAe0^)-|E)=sGk4ny%M;J1>MO+fy$_GIZiQVoo?vvaiTL zB#Vdkx?OB6y-9|T<}SfkD(2!e^~ztA!6oL{7N_WS-^gD%>$KDT)(`MGk`%>t73tk~ z?AFmgm%44zmI*x?;ZzxO;hM62$;BHV6XNOZl6_i_J4qdj8}tkbgEqjXX{ip3Y6NP_du+wG68h*nC)OihkTUXQZzB zag6rxal9yzSTYc);H$*vzn01{-4W~jt)wlQ5qHYBN*H}{xOH#x$1j9!H84&E~gj|h^5M%O#1d$~6TeF|t;*%e*#(IKp0xUYF= z<&Rsq&=_$jfbgFOM%T=C{59PZc)+Y;XSnHrP4Zti+tZevNg#N?SOFhSSZ{hMStIZ` zKV#_wh3GRYHqtYBg!Mj&PA=G|M&GCUw;KIjUhYnOGsPrKh3&fP-)}zQl*81C#7{@x z2>nU!swQq{Kbi-mQfE4mVj>o9S|hed_$_(BM(PY)21XgDcWxZtdS*41ybE8*o|mq~ z=gIiyeOyxOllZQXai@P6NX(*oWciOysknqj^n7NcqGAjU0VRr zN!4|yaFb!6^t7`>P#$c#g(x>xm^wIwJUY>|75)+BMj37+?^A_oGI^Zer*BgkE^p$- zV8C9sp=#k%{O!q!Qv8wAWX_7M;SUBnrM)a19(PglJA{55VO0AyIs&R0nIc>dp6}K6 z)r?sxB7948`|ZpBs69Eh)+jD$Z-wtEUmsF`mV?0Ub6%WAgWfy`t~c!@`ZlLZ6<>*2 z{dgTY?069mm=m=eN-m^q<=nIGdbaPSr%TO}I5l_gEdA$L+%bQR6W&3Bk(UpKad{ml z^Qh=1?cL#f%2O+<|Ahl@nY5<3b^^C;;fw6_`CCJHAJO>Qr(oi`vQEz5j*rA{595rm z{pan04QI*RKtaj+YrnGVp+QxA&ATHXqq$x-B{n}YHd9Y&(N<3pIzZv}I$ii+#apBI zcKV#Y{GmJ4V~)k{C}dNVbmV^+vnAMDyV&tb3s>y1r|*LQ^54rJdfKa4kjF@Ih3N{B zMJ`c|SI#cmEg8GWjnt1X;=RmYEH;Agn();AFl-@NVz!6P&RBh&zebLvnXAK%jb0I# zo^J{E(k{0Br{$RMv8Nwmk0oi{-Mf8-Mxj%)+nbd!Z>8hqcP+beN`C*ke6@gr_m#p2 zbhcEo3kw==wH1+25Bk+xq1{3-If?DE0yNK>~*QO59j@0M9op9q*asO5{&hOT9E%J(XTF7W5t>#X1 zsn6rj>7(VPI{qZWMajLRC;iKwWlE?$se1|Sq#fCJ>}zh4(U+CTi~n|Tpgmu=yH==t z`CjMYe?%+I`=Y~3Dss!0b?(k`r9}X#+v`)_Z55uWZ_%`hp{k>;Ev3m^gX3;Ti_B`S zO07Macj=H_7Ng+=+{-`O_{8IQL)4T;Y2r1@+{R+bCB>zEemrJ*!(CBEAs#(e1Z}kj zXVVd%&?E27YP<cK`DC&)FrY% zCQNqb&?XlWHV_(B`-R3!6HVyoEN_lA4nIc>p)9)ive5!g39HVJo3V|T`!Lq6bm@t@4A4R~ad zyTfx{N(DbG$D0O_R!UHti!1G=)zPdlbv7^2al4GRP?e5vM!IAJ-s4g=_tcf!x(QLl zqj|xO4>ZcN<7~Kr9<-qx2AD9*DVmQP4dU#~XZm6an(orLayqZIo^ z!VaIj@2%lEXN`==uD!iQyQUXa=_>5nFO}25ruM!Hj^Wi8#ikEwosz4n_7jRbUj^$mH%)pxAe z!@H*VkHCY89Fjg!uNK9wJFGZT#mesmP+~VY6I;+DQf@v7#U{_6vRK zi(#cb(x+EA^25O}Ue|Y3A-JJD|8$Mo@Q&Zw48Q&Q3(0)mU`@59R#cy?&wgsp(n(v? z?Zx9hXZ{&*S$v~2!_uK)iKRwo^5=pjE1cTKm;7LBuAC%~w1KmCgBzGjYq5g*X0A9Nt5U;xvtjKTH( zBKFAp(+93hTwA9DX(R3tnbE>2-Nym!%=F7`3dOYVS=l`I_633nV z8K96OZK=x=6XJ?6Jpz;pxjfL8tSM=x`2F~=FNi;lC|CQ!RdTAb@8TUPJ>|n$D_nYg z+-UOG6BB$Sbjy#kXC;I5&^_;#&6~nqMpJXw-Cf=O-$;QmMlbrStRt7%O zi#8=2DQk!a!FPR(5lPH5#V6Juqt_(+94PAOjY=5GOeIO?jIyb9w)>>YyKg-M8U#Me z)!`2+^$|y!gye@8EA&VFh$%NJNwjoEZ_&~Rh3Ml-ZS*3OD=D=-VpH>PG@X+Njj3wW zPP*g#pH_V_;AyH>kJwa||Ac-_xwb6(AC35ZRllo6Y42JDuN6DNt!wxW^ZnH4v5Z<` z`-Vr_mH{X7#nFwRDaMfxCQ6Ben3ZzF%naW8Z+E9WhmD82#(MbFojK8x%F{mxUei}p zn|i4!QqhK1#tN&=jWpRLgS|x)5`B*0Ia52?t*D+&zI(U7&QI;%uTAwVc__uH<{Fad zczmmdutHUpcez;+4;yi3thoFGTE&COqj~P9ugm2n_lo6ar>!=`dB&#}Zdd#8Aj!%a zA6%k#>7e-K6IE3_9mO*0Uk!0VrqyQjBMM;u#BtpSfZB`z5K@8&dgK^aotbQ-3kYpW98 z-`59KzkO1Uyr0)mq$qUXUNsOjaptxJ6ES|eg5{v5+)L(2sgoyT!M)n4+f;@RtVM?OwL?#m1;%X)h z^!^`ZUmg$j`i6Z@=`_+|k`tAssbr~RDMf@D*^@oV7Fv`FA(1gHBW0TssT{;uvPUG_ zNM(u8SW7YXZ9*}&!OVMqhdR;uy`T5}r_V^u_I zidZ%C-9f**<2Ee;Bntcud3^bms10|ttXx>_*KsWgxaQ3dD48H(fxD0PVU#XpX44=2hnSNpHtza9cJl1yEw{XteR2@rL7URM#;`&J$Jm_QTBK6eS39?%?9?VxAG>9?{) zlV2nOq?kip9ecuCm$Ee^-fxBzEfjM{*#u% zqsRD7k$odPxR<(@0jd|Mt%K)%z!H%>gQ!1WKv-sL1&tHR8`;7NH>EhBR`p-@7vIif z&0<+V1)6@vwP7;tqWLHXMxl$>FJym(o%yEgJGtH0cNqOuS*T4tOIdr(;7oX&1Z<}` zQ0f%7zD8|((alMe|6=i{rZIFXYPY3NWbSa5Q@oRWv=;@9m~ltHA}&QDJZ`-q>Mz_H z=dg=f`F7b=&X0D}EUE{&k7EL$v#3#cV{2Qr<5frhOq;Mr>b!37dR3#8)tTz|AH z_pxD>f?K-tL-&H{X%#NBM^UkGtzdBQ$eT%mh};*5U9$h)pSVldAJj3Gg!5~q!t>C7 zk=#pM)lmoDKONaCsF#@{?YkpY(dfy-N$!Cpg24B{}O)&GOcYGQ<#A?zcdKpNJKdam z`X7-Sw~=b|hP$CXcPk<{TIr7$PgJb2Npi&RlV9CI*%D3{ zk;vPD>o294=iq8TuAb>PV~OXz7>NBGE=7AwQ@X9u=X!|x7Mc*7*#-jKci;L~;z=V1 z(~BoVz3(ee+1ZYBmB-zIny3SGU4&F+tB<*Ux}Doelqty(lAX}1@Ol^bP8=7w?hQ?l z;@oighkci-7ngC$^u7Eh-vLXDWBk3nrDgdtGe_2%1K9boU67o1&llp{X-JBk*JKc+ z0}MN|IqfNa-V~lWox`T!k;%P5iV$`7(#X#*s7x(;=_4Xk+?G{Q+SJkyxqE z7H>?tqK4w%eyId`?rV+&9kW1g`uk1lrqr`o8df+QDMiQ3`9}%Iq=nuV+tcA5`R6^u zP63oN5N&giS^zRBe8d_g-eXUybQhHDSiF4n&x^P#N_8raHBzV8q`pa)yon=my^D|| zEf(QU__nMsZ^;*5ne~MW3*3gEXttE4-wDB3wB&vDO=)QXhul$@npSK%<0npdh4dVN zrVAqy+-@-onBte1hHsj}yU^wF0x?Dpb#2-9HdI`nxdmc$(gaC)c0VI(6UVOb{C8j| zy8j}?fA1T2u)8_Sl?G4L!p2P@k}9q`8mcjO4>hkk15#t)6yASV(GM*caj=2lqwyy>=VM8=)8sG3 zq>b?^KBuX)fVR>@b-lGD;18}em3RO4cYP~8lqn+Oo~46(AE|Jr23)4+f0{Usz?5Y4 zjx;VItfYE$%2(BMX|ONgUk%u3i=4-`y4jv8_5B#CD~(`iq`w<%YynDi#+}jxZB}Cc z$w>hcSWj{`R;tdJ9?;GM2(5^RH;Rb;#|mOP52KTF$bnAhqnsJx%7)_pzbsC&`(*CWnPLNkQ`-<=@E|@-?`~?l%INnd74B3dgp5fcJt? zILu4a^_O8)Qkq~=MWPEq9?FW`@Y?>v2g{~^Y+eyRZ(b~yT9E`_qTZFt2rogx*Us@9 z>1umMB%B6aM6m%;B8T=8bjK?>G&lQlkKV3bjf&rM$a$;L;BO;V@{jMdyzfHAzxiFo zrS-R0{b9m>+y4?oc|~u0$>H?bwB>UbIVg%aB=F!$x^!NI?j8Jk9M7xrV~MhGJ9Ui6#7;oG%kR9JZjbiSn&=vus@ z+z9M+mMiPqvR_BESV9vs=VoP>Ep5>r?vvHqrktXJ!AaEOGn++O_LCd_5h-uwnT zx0R#C%H`rE#Z<$V7byf;CRKIUUD?^eU}#W9ec!Vt9^QseJG`Sf|Jf5h2Hr~6u%6{t z%cGqHH;Ze@!wWBac$#@QV``MPjNN_q1UU>SG62;|B^zf1{j}2#~D_YN7{Ahc5EGkRN3Ld_7sc^iquZZqVyK}j})UMD8H}?4G zn2~JTEZP04>a3B3buroVjw++TsL-eM!9K>thv@9R8|M1^9n4SDZ)R0}y{~+-$Om^$ z?X8OjytELXqmPo8n3GVqqqSriiKqvfUN?mjcpNfJpFS+y+h@&!gl%|+QV)uXEYGa&}E#rRX3 zF;kOD{t=hLf;5R}232Jj#iaszdNYquKCH<0(WNbommKWfddfA9MQ2{!YAZZ9=#ooM zkP*ES^`%)=>G`4Ps}g#}v_u7r{d3c#vZ%Z! zY3x(mWN3@r-$a>UO{d$Ms!X3!hh%waQ_QBkGuI2^n;S{3&3>9A-O8#~%8CW!bID4# z)n0P!ij|iRBW+g`sG1!whz=^yakSon))~o9$S_&A(c`|bSLS~4>B`e$o>AWJr@zJ6 z@XLtWOn=!rH{4fZ!(*>A=6U+9LF!<}0{v+T0KF2)V=Fqpg&~w9@ebKc_{5uTbZPnWdzb15#)@Z5{H2r5G7&JkX zm7Buw7bTxD^Q2S4J2mx37&|1cbT$gb`}r7e3OwZL@nZYWRRZ5mB(Ij2X3)0?rmJqn zKHxp1mF;Hm!06Dez=oBFPzffIacNvZS1%8stvj9P4#;c^+{`B>L?6&pu*7d7a0&?p zq0c)cH>`HH^CN^_Qkmf>p4`W09@j4grN2W*t`c}pJL(P3BwKIe`p>1SXX-rDHI z$rpE!%(R?*DE*|q_$&b&9k7E7;SRYW!p7IYW6>`EQUrH9k=ghW)e--?MDynC{8QWddKmB$9>z!o$5!woqXIdNvh)dgBBF^s5z0fedJHwxL1@=@17#t+wZqGlDd>+ zHSj$zQ5sLC9cMdRbKn_t!H!8P%&XuUL%Et z<&!tjEz4fh^GV?SK}h55XtGDm2CF@ZA1 zqPQFV`sw8e@!XP+PG&kuD?cb#y_$@_)SfJ|pQGy!QkTuXCydN82wH1>x( zUV-(2Q8%sGWGHtm>FzR~WtS(%Z1%HSW^=}SW;*&|Dn;LFYFJE>6a2N8Q7Iz0=0ykt zL3*wt*85TGj5sAku~Hbq@gzQeqs={NEaz?M^_W0((w#e4adlZ2aYGa*<^_4{LEEi_ zhQS+Zs8>g|B;pPV6cCOF`i5HS5T5Fe$9EcSB18^S;Azk?p-=@71=NXl zE?7_k6=NBmFV%tlwx7c?kK4uAMCZ-& zH5{WnE2l)bogcFvQ1Fz0=y|74bk06hGm=Q=>%8_Ar@j7g;sfk0*Nz!p`+wdV1iG|W zZ9=1B4&Ai9?zstvG1xK&jiyZg?5t$%-WQDw@=Sh9=ytvv!V@s_f6q#v%!2*JvOXe3Eed(i*j*0RgK!iTj#_MRS1Po`V1IP-(AL{tIl$^%>~XC zM7!2jnDpYydx~;x6m8=wq~}EN2L-5}({DbhDq}#?(2@6^X)w}GWq8c>58QN$J~r`& z7$s8I)O^#4rl~sBo}nod&nog7`>=Zn5k$Oj(#piix+Pm`@B;GZ2~u@HnEN$iRsIuG z{gzy*PhkfH2HAy*A zH6loD*M~`x&BAH*xj9mjYEN^d z4wm-6Y0}jkbGm?&T}Pg|jXYT2wB|j2#{jj0u}WGZsCfJX962PwxooddcPno3C%>g~ zJ;#&;Iu{|dt?+$6YLLzuEFCePc2PoER6EjOFfuD;kn;tn|8? zItb?>Rc)T!cR>6-tN1E2XS#U^j@eVZM{cn3;*lpF&};NCRApRPLRU@MB-Fx%0UG$? z=5b70;ZbvhDE9Cs6^lGXv)eOJI~vARQ`3+>DH!Qd!K}KPhU!->qa$AhJ3h)4263<23{JC&zmeg05WBlTIu$Jl(P8eQj3R+;XJfiq_2?#IPG0aahUjv#trTpCX{z+)SZzOg^KfHbLfekCKBB<*H?xZ{G+|g_kFKb ze411EHKzUZ>AA0Nmzqr0^@(t7M!|oYA$MOhK*bvc$_jFXtiKG=rk*Li6yGu)! zmt0EfhVu=%{u0xpWQ_jXAm!mE^6G@{bevk0QO1NqhW??o8Y3<84RKEH19ysF6n9JH zob}>K=)P{x47rxYzb^F%F@3;7nM~;bo%(DeUHDZ+9R~kx|J!?KA^vjW@Y$Io@)I{n znjmt*(fo(eRz6T0v-9$XLIwD zk@d!-1x`5UxR;Rd+2Lm+X{TdN9%;C{ne~mUylM9NegWJx*%+A69e-F*e`gTa%!S?g z)F$P0Wo7wvhEK%d0OdO4@s~s!{hO ztKd`Q;%j~S@fU6FK}5DVyL0?^QMOV+AXQffM7rCavrbLrvL6d!D{j>YS&|n&e)6d1 zewOTGd=KI}W;@(VeK$y4gk<{kC2GzTdEl8N&<$th9x~9A2pN;{&kKqRLKN77d3j#k z`?1`SsjtVZqjgYx5L<9I*?PL>%s(k2`&6f1;<-J(E#a6*8;|l2P9XwEV_Ck!@1$zB z*7C(7CsXPxXQs8{Jv;;~m{MJ5s2eyK#GZm^7}tS!TgT|MES?z{U7`wT`td$ZQaRpf z5R2x;etq8P_aWaoiELsCtkUYW8pLMMy_5)OyYryS@vZecYQaw$)UK!eof=-#t?J}y zqf4uDzZDT{v|0GpraM8Y(ytX;vQrA(ojChdPeo9lCDyN~Ke~k$u(EE_7b|}66yZ6d znmVUjhR0b-#yzQ&*NjyW*f4jc+E}!r7?@!3pqQx6evmU{)6xd9p*{I$*f01WtDxEmGJ>EsukN?m3+83ydY!>>Cao&kd?ys7k$K-+9r zu1OH)Uwg5cX&ZP4)Ssd=YtdNPjEJkJpEs#{i*afIN)#5_G+mUmmd^SL(x*5AcSX_?h~tN6YNIRIT&e0l>&&|m!CH-8ZQ}WeQ9Xv+WfX#=^{6A)~@tH7>AYhi!(Bj zc(c4?CJVmn3vE_DIQ@miq<{LfUOcR^lCTk12}4D_^&n`jAh%l%XiAH={72nWcRPgOmad|Vvspm6)IV{tNwfG;NDqg70fs?$th?Sk`P*FiEv8Oo+7;cF zd^oXweh-?W-~lCJ4O5wU^-F3=B6isYQ?IjUtORsd!pCi;=%odKdNE|q?=CM5*yGQc z3+LIY40|*LCi9M;6Z(Cw{3Xs^%2rWa|GTHSFd3AZH^qU_jw3C$zQvjW+z8L23kRYbt-2t zN*E>4vz-F3rh1(8MLB*dJl|0dx`M0NOUv*5XGXFjXX};4ScBfrXj(z^`E39N3?z=B zy_ptU1f>8_&2Ro|pse*Pt+|a5dnUl>sZB%3(X@}kvHIfqr8?TG?@9~E{JJpXmxMKB z$&*``7++qoQdDPRru9;}B;pRgsmyog#02>gdz)62K4{!{9ttPZ4^~Z_0k8d+4;K76 zBx1SO-puJ}m+?3EoA^&EMB1&w)cB56!SvyalOEGK$|s=(IPX*Yuw>mo8ZeN96{qrJ z#pedf?>B|3aDF3ECn|o~E$1a|#_=6O@FCfRvBPdS&gqr7at@#v&^e_7DZuF-?qNm4 z*d$T_V2;B`_VRh2_xQm6j5u$czP5!2Bhi447$o!03$TU1F@k?PZJ9{oJhN=A_y}+OeJUW+or5m2oMCqJ{VPW2o+f^* z?`G*Mu~mz2_S(K$6aV#ZjuCK$_J_UG{C*+vs6OPb(wkp^YT4w*t0nLP~uvu;3(0S+A&|mC$u; zZ-3@nJWS6SZKn2J-;UMZrWS@JOJ4?!?BiHm`!YJU7K1 zbQ0qIMD2oIl*9`0d|2vg?85YF{Pbkxql=cJx~~aYj9?IY*X@zc?PWI3*5CSZYdCn< z&Q9e2(uDy@r|V~S<)3D;gpFZQUOMS2Md1cnZWT74ZAL?n?Jinre6d6p|8{?#PoqO= z?@8l~f*k1^Kj2SCkJZf07Gb?`ZqERQF4-t0VG^xzpL;rBrB0MdN-QPEgV)JgCf=q^ zzF#{pbK>x}Q!-p5afZ~ha>vpiwQUT(unQcg({BjQh0`q6=t?5rl)jJb>{ng&LvO1l zBNdIhRS{f5T?Zr0{5rF{SeFR~`J-8!@;3kc%~u*WDZDK(wJ^1>N%!Iwb)QmY{7ny4 zd)DyjZ=X$0GCibddE;Gqpn^T93nx!s!@AHZtYHh09ekxkR@aBu#edsrAn*u#mztr! zEZ)P^=~runZfrPr+?fH1+k2!flPZa;x#IUpn5K|THwbwYxouJ)pL3N{7jRATjyK5m zi4>pse8Kb75C~3$r5-3m%l@Tmk|2?U4dZFXEHzceZ4b*mF-Li$r#`7I zwbE%>jhBEA1y~k~!yvwSW7D|Y1)El=sL(clvCunFdY)Nxu(c>dHwxAS{j~KSp-5$PK5SZAQjIAw#(+>YF$asERVb5Z= z?W>@EkcLiDgH<}Vv#&-E+8h8d_nXYNy_V6qCOxKVcn8$q2_8xOCD<9go@$-2YF}$>CEyYAE`Jk;Sf*41=ToG_y3)AreQhQ7 zD249S%-H>`Bx$gkX_z{vk@okJfk6w%z8Nq zwL1x-*`={F@1bj7JiPcjOcGxhFK}B29BJF{B|K=~bmy8hAJ!>jK)6PtQ+HB=uxXQEFdn zvcd+d?VJDkSrvXMqWnnisZ=ssO|>65e#Xk=ymbh>qX&rf9WOU{4QMgvr;z8Hkb&OZ zqyfS%?*@MbOeQyJbFFgrZyod56Ia;Bwk2^_+R^-3G;kg;mvEbZ-Sezlupwx^_w`x?&*1_0vlo*$K+xX9S2TP$(f5-1; zNj#ah?Y!78h@@)}_^`-*O^}9E$+$-DNh<@`l*i)@nwoqqgsz9V8+PHhLVFhqs|P77 z*SAyqjAGp$?bK+QZE6?c{;De2&R#vSdk4m9D&(@r;;#qzj8WhAPx*jnRn@EJWII<9 zD6cb1ZhPTsdT;5|1uCSkx$R02Bn06{M#^*)^Zf#gFr!7%E!@O zt;XC%>~Nnl$v|=BGT4m=!U4{guz2}mBlqdW@CnSbNqG0eY3`YB3NMa}-_e?*Wf!W< zfvMDtJMcJR5&t(2kZ+Q};r1=^v-@$vrW20P*!JzNBNvfbWh?{2{*Vm1E!{z3uZ` zX`(7LV6ok~URlVDF}g6RxIUZRy3o_b>4t-e8?%{B!L4l8V0pHqp>1#3rhgi7O=0R5**Z_l_{u%x#4&w_7pG4H347BGkqIoI@`j9^ppCqX=LLmhy(nLo!@7&87!LM#=lT=b=h^7EuO)^chtPvzx)>6gZ>MJNUs$lnd`V5(0e7)F8eD4=xVOt=&^n0%K_W%)lq1X6J*Df{8ypb&6WL3RPrVlh1}q z35~8B5BH8gqBnvG+#xi|&l9>Y>4^G@6x}~6!P6%D9UjXIDtn2o-SxoNBP-L+?L0L( zqdR$^sH2}5lGuz=MW>@^!7eOR(zXIQPj zl0g%3Tj6e|g-F@(Es5VQNwTc_w8Y-en!3we9Al_FD(&P!zm<@{^z zF(Igx)ia34X#w5FdQy}1P}*t0(@~??ke0dh22f}Q|J3YaoYYT=;W&2`q5ke!@H33G zN{7+{zU~x`tjI?jT})F!V{hfSuSphrpwQ3)%wR!idmOSeMCW?jAxxllWjr?dCI3J2 z*+Aes)TfU&6^YJw;88W}4xL0cL^gnVD7Pg$XZ)|J>j7UWu1U{Tz2den-g${NHo7kp z5Ypy*6V7&R&F>Vj?P@7!u2(*_galmmg9Pku%B}i|xH)Mff}?L);lrwJ9bRH!U*3}y z3WnNw13Or@7VKe&dy7p6UK+gpG4~N%6$fD2kP8f?l-=dYT{r0&f)H?lge_!~UzluJ zAxsd9aa{Z%c*stSFUM$`CwPWiR09#(CY$lpO<}6yj+3L+xhFI{*mXM`M2qFmUk`p+ zng5=xs%H~yGos{#T3Z$t0vK7Qp7k3^G|pc>ZB}nnbLGh*59_ur&(Y2dp*kdJ4hUlt z!*9P#PO(k$o5^pWHXcA7K5o@uC;~VG`}Es#hj*!5I59oV+~)u&Kxs2`)BX>hPY~i^ zbP4~lNM;vkK7&JoLyD-kLkM^|c=1N0yWuVn*MQYC+T49aTVUuIKNe-}A_icTecz}7 zQ)3F0alqcKVGES;c^^mTvEfnL6&=7XpJt8J_{`9|WwhnSC}_>_5*H11)rP2S>)pyo$hQF?qAe%8HGI z&)}>sc{IX6n`^Gp;)|$1CED)QJ=*S~bB2)8HwY$Q*a z=18Y_j4#G5C6jv5Rp88IXdAe|+zvnkosTnz z{H!+G%F);i;W`o&6;mSl%Hq#T6t<$~w^N*?BgtuiRPGU))Gsq+buJ(vxPR)W*JLQL zO^rYha~LuOEO7s7Yk@aMd^&*>%$WM+19P4}!}-BLM7%5zO~~XkqWXoockmt>e8GL* zoS)C$&j2T3f(2nT@?E||dK3`Vo#s-Qtx3oS>$7TYu4BZmPBcD*SKmR~V z%zX6QGqOo3X{2pQ^I5zbcM0?a9b!QySQ4oVaMoeGW45DxiyB~3V)!}h)pI?8aD;1W(=ih|KO6U*HI5g$zkm@t! zrMD{1DK4wab^?#t8w%?y!6+14c40eu!y8q+ zj?MKKFqUkYx`_ASg86Qw?BG(4G^s1IUbi5fch&8NdkXXGX*}4;O>A^t&W4%?Sz7a! z0`%Ylkp)?BO489UM(fx5i+;OZtkke%&crX8i@}8qEYa00%s-9FpZRH`Dh=;{;Z~5Kqz@|6@Ct*IPFMSKd z@PH_i1$V(e|1c!Pm1!yJ#UCU~6MX3F6Zl4PCChJf)|`5P6-UwW=s>Eq{QIz<+iDp6 zC!W$v(Rf`Qkb&S&!rgc^X6LmKvBuNb>##tj;p|gVUzZ$3UE0%+~b6Ng3M{J%}3q&vw4)zb1vtLOK#RrRv(C+vG=MTWz6;$-_Yj8-)SC*%&eR_k8#dC^b@Fk^UE*a?&evH6 z*BDvjrX<*W=5aAt>664j*A8?4R(X44Ys|tj1Q`v&^TD?BzBtR3^j#($(6o^ZAhN1s zezA(DgJ+`_naMbhFtn*$?%qwlA9K&zTm5sDB2-g6&mGOB9hf(^m9gdR0yrKiK1+O^ zdibJuin!VGpYb;&^I#>@8&_gcFw>juykFJ@fU9h>3LvXjKa+9a9Kw(ILT!>cB%o}K zG@#Pfvhfh@%dzw8ykNZtfwKK+H)ez8mceF2x7jC;&}G2rx@bwN#~<8>KJXrn!xNrU zs{QlQ(z#xexqG9-+>%*HUqi3&m7?@p(C|4Xb&gH|LF)UZ0CZV4;!_hDe!*@*cmz9F z4%^yAe;=>{`_?R3o@nK|ct@i<)Xj6m-#IvFTAD=IO$#ViC$cE^Aq$s#9!KUQ5prx< z6GEO$QSM8o9w{VE(RLjgY!<-9s_q}XVl39r9?q>YL(HDRu(t#s8NE`!t)IU3DE}fi zD7>a{+Q^%x{97YiOMpiJ{x%2kWrj~)jw+mT^lM@L%ibb1J^)-g+tgb)J5(=I2H|xG zd6#z%5EE@NVnTS7A7Zr)X6H3PwR?Q72oc?{8SbPlv(-VBaHewIJwX@fqrPKo=7ZF< z`_v5t4^`F*y3+~ViUR`I6YvpD{Fo18-CFeig2|stqR7ZBd(`t*_aEJWN>D5RPUXUE zH4Cx(f=YX;s#k*Dl;BKN08F*FKBY|HA( zEAyWMEI_&gYVo=M3z0jZ$$AZ39Q}WBaRlC{R@Adsn$%mXbt9V8Uz1HY(q8RQ&H2u$(k**)L;UNA8G<}cY(OtfC^ZYr%qzz4$P0!(G|xIzLy z@wllf0+4JB6Dn+S4klDa4p-CAQUa?0dZkc;7BuojVD)GAEf_-F=Z?>{>-)H@ww)c# zO*Fa={TE&PEAD?2Z>w$-9|Q3=xBPBJ{;fv8N|?_5E@B5L5ICCDuaGQ`N=H^hnJIOc z8ZIAPAp3L~AkcWLu0Bv-3|+m^ml;M@%FlQi;oWBQc%+}?NUdhQ1d}h!y2xJok`~ZH zDYv)M!nkjSGO3F?18xcT1G1;aH7t;Eas0PG5%$&+lxU+TKiJ!em3Relwlt*fTGtnK z_sj{Lp?^JWoxGM_WH=K3WL==qJKs@dO#FX&QaeuH9A{IwYzf&x7@4{Tjhf4T08AwF zkR$~Tl7?4j%~W~KOHx087Z^R|ASe(KqMEl;_f}8#@I;GveQkdIpf1DYKPX%{9b<_& z4Zb^bWU8(*|D{-i*WQ@vTy_}c(7fd?f;tat*#eP7!BV~f{k8FQbvSA-;k|#IG07>6 zw-dd2O(#|>9)*$GwrY!1oI%L(&OxDrHe5_ ztDD=$Y`WZr>M&mHCW3hpB?p%hM(U~#XzhQ577)#L0U-(BHhOzijGMpV%^Uki_iG_G zyr*Ei2v)0;sEl8=uf*3bFs|N2r+WrQo~-2Ve~ zfJ$wzW5eiOsj&(;H$k87C7hd2(k%&3u;6j2Tkqib$$}^0gb#l+O=Z2x++F;)93>!Z|fi>{d<>T>m?D5&XPD z2a0ZNq6|2-&8yFp0e&4*pEG%v+f)UG&2Ge~eijz~m_`oJje?d(W zE{nu>%{ME{50aiW*z&zef>K}3+Rj^%2KwXnfA9~ZjA>LKwh#z2`TG?(dwuNT? zc~@8-DViCmTig+nG#xoVK>hyL5crPVc#tiwJ6bgiA$m@1)z+T~J9vvoL@uSbH9qYM zEOdl{QI7okLFM;^>Hq*xJ^2mpocw2JT7GGx>5+jtFWr+hz`+gbAKR?QHd7*kJA7I3 z`mx{`6$MQW3|2!Rc>b{K1^h3juBw=M`q10RDgd>t;(}k+OJ&r5kabBsyKyO(5PCLq zdQh*cawS5?yN&`Bg1 zW%FGSGze^}=NUPcqGSFO@j^I0H!OxcId3e3;xVuoIdO`%JJuk+JRt1S%u#Ovvk}nO zeN7GgoVu>Huqk^+*Gt64%JIJ)uAV_13ZOz-utsVa)^w!GF zs1pG2KNqIVGTM0GqSuTD%m96X9D|w>M)h`0Lz$uI%;8 z3|39_Ky?6U@IOV~#3#b0HP5~O6nSI9EVi4cVc;$Ra~>76Coln{7twd7DvZ0!{)@g7 z|GoKp9LWd7TLt!|BzMC`b|VwPuS<(PyU6AHlfTmw@-mQn56;|*y}N93`N!79eI|0I z#HBRGSKwcg%1a)AoG7pb8)=!518?-jIrwc4x3nauoKw6at~=N1on8 zRPB5L`G56zBBz(ECzAie-uYDORXx#=mxhh@!G8UAl;4!}cUr(@Ih_-*diLFgH5Kf0 zC8-O%4oEj(hA#T;J>BWM3!wfpBNoBTE~vjDpjBg)Hz4pO>xhDBcT|xpZpwcwfK2Pe zq^5C&qp{AZ=J3VtW@_@%Hb?a!T6TZ}$0;YPgQUB<&PDUU1K(zfgNs|2;3YG_NIXU z443Tnlj#Oi9D#N9Cl-pGfUd|-G@ec7e0v4VT0m+0xH1&F*y51U@gmXlt#AgPqvQXF!4v#k@TK}{_2xo5AJjyWqfq>j#UA>=LByNk2-fo@_eL>un7_Tx^g$>RJx!;u$x_c&~5px>OC)#>uoJ zf%mO1YqND}4P1)&G5pb>V76BM7>y-iL^Ypo@sTOUbjZ-Ur0t z0J?YLoFi|-OJg@}{XF}}HHds(O=n5B%x;*TA8o1hrhAR)I=Gp4M~R_^mhAl`O~hIN z9ILGy@q50Jp;LAave$Xt)+k4OQm_6sod9?86Y+7;?T56mL74$lQ!z(7qh||LFa3u* z0OGmT0m0WG06KsF+cd6)zo=ZKJF!hHFcehB;N8;{j(u{pGd}AVN0KUj!h98}*OX)_ z8Iz(Rff3Mbjjg~CLZ-58sgmK41ivZM04a_6 z;JgrFP-QgoKYMIwbjW9|yEZPh z(O@w9P&{M4Q{L(!3uaa?%pFWOq{3((h~4d4*I3>Bui3>&M3czuL72X&LH^)@|EBtC zqO-3+# z7zFu_0rfDBNpg2trV+ue=8D|3_-E7d$djD@v@quol$Mc9?wd%6CAv}mHN8FqKkJ3=4j6!NS&$bE}mKsGqr3#}l=?M@8-K#X>!{`BjI)kD@Rr{97H&!WzFC@RSu!|ZZe`c5YUPWUShm4m+26rOfc!(q+iUh zML1>E$gdTaEIgz^;_s)dw)q;dIao?t57qr!GwJl-BWjoB2V5PpkthxRL%OmzMZ7WzGR&`TUQG>r@l!GItFqJ4HDIFWJOF_W7StYg`0{Z= zo8s2zMOAl^jN~A+*Vy9^Okp0)?c+_qlX1oSwFW5Zx;_&T6iS%K#U{C4nc3q-6cLrk zNcRn*92uTTPu6&M`K}M7SgaiH??24#e?E-FGdeWFNoYXwB|FrN2mBHeX^^9QD3L26 z;pjduMI}c`QNE1np?-(!l!!)>vBX?XxC{&2Zpi1HR(7&hjP~fP@e9=*c~NL4;Z~N^ zFh-ySsxXYqWi*Cg6!P19_nt{L=nS>1CJZYrcK*OzuLt^>YKuoxj#!WDm)6slR_fF+ z9H`f8AuKj)u6DRd^=hxpuHDfmi)!fw89Y~vYKqUE-J>g}vg?k|oP&9#r&;8Ey=QIi z%Ri<;U~G+*&sjxvvGk1<=aB$#SV!|&PUNwM!}>`dMTV4ST*dla&r$1@aPg;b*(0x; zqyDMQ^oahrUVJyKtkbxOILqK6{5HX&_D4y1l@v4m>XQzT5;D*X(GlyLX1?q{MKhG% z&^q&Z{{`AiREX>S=tmdNNa<38HKJWx-{$SOA|^bScW=ymVz{2t@2FpS`Xlr7rJ27l z^nI-(g+A}@on#k-s}4PyfcJd?XG7JNREJF5$s{2~ zf@?aGq3fl_!(So{(x!3+x5H{-0^RygVa9oLZO^8LhP3E+5Xv1BUa3MI|n7-w7ywuUhc8tC(6ZZFK4#5MDnA1DzBW(0o-uDNp?I$A_TW^Jy zQIt?RE-K}M8uVUY^gp#bn}eD~#G_9p@pbI9R6Oxa?p{JB zzt5encG28lwn=xTUx#Kq(}2@5qu8ujzR}gY4Rq=922m)*s_%(1{RIeD0-#doqvR@WY9>_P?_D zAuS&Q^`S+sO8B@dclZdZx-i*kc$DnspFZ@|v73v0}e|rdM!6ZGV4?e&xx2WBHN0 zTGf^+E^VgwU@g7-yI?#yez@N~cc=sfwN`CH*-ZA)U3jM9%d>O)leQnJZo+)=ilxe9 z_g`!pB~P10hgQ`kI<>sM`zXh|voK{_&(1Fc78+_5aRbkChi|KC#rM0~d?3}9jGDSy z`hBrbojRh9nrY%0zBPOza6Y`SHgRNcEIT3N4Vh8HB8LF;pA4ss!Z^IqT zuz?4<#XfL5z=Nt`S&eY~Y?Uo;KxWiI;aKX7-&=PLCvnynvlXH}3?#W&l{xA6S}6~? zJN22bks}dsgEURBbr#RaVlQ`l#3w_5km_x<$8eT_451Ig`7sVdH~p0kh{MSTaAT^e z8t%#!ydj)EAR?5eulgtcif>}H zZ*h@^+WxwGt2)3B;b8T+KbOBwwKUo1%3FAZ4J$6vaz~|>I8iezAiJ&hd@C3~n+EYL#Ny9lq=2K&t1Og9ZJCY!+VkZIQ1YkF<=LLfKc;D~+fYzlSG zBUAVI#F26^F2w9gD$x0hyu$qh}5rPIrwt9DkZ7f>5zE5=n_GZF%O^*=f;(oN zf3n@fErdF5JzrAZLGfy>${5}xJ9AXhLcZKhl6D#9LA%|<8EoF_#O<29{;>@;wB$bc z5n5P9%+#_DXSwj?Cz?uIOP?G}Ht<*51m_7fdd@?bSBWJqgN@%Aj+NCA4ClRGc@p#Df5hMm4$25})QxdqZtZPw zLG@LRDQHx*#lugF6MC2RxVAcPliw+zN%~5Wtkj2b1qzZ0xtYz1@88Bi$JBE$O

    mjnRWs`>U5(N&(&t=V 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

    6+$t& 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 TI$(booj5cN4J7*IOWt#~djPEJ6 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 IM6N<$f^UXx#{d8T literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Triangles.png b/resources/textures/displacement/Triangles.png new file mode 100644 index 0000000000000000000000000000000000000000..f9f21d88e5ddb0c71893d4261b25cc86d4218f00 GIT binary patch literal 31465 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 UH}q>W&9Gj$z|l6F?REJ70E*hI!~g&Q literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Waffle.png b/resources/textures/displacement/Waffle.png new file mode 100644 index 0000000000000000000000000000000000000000..06b5de6377e9eb961beb7bff83bf4486803fcc06 GIT binary patch literal 17731 zcmeIadsLEX`}d9Ilr^P!SCg8X%-b3@Gb>9>rKYk%Gebee+!W)OkXaJB7pI+$na()w zGB-_$8xYA9N)u^gnyFAKP*`ATl9Ev&k`P?aiSz#6weI&>&sy)j-sk?U_m8JPS_;C+ zbzawX9G~NRd=GvL@ZYy|vBhE-47T*Z{yiud%n~7PgiO$Xgw2zRF?^@SiAecie`sH)e*WQQgMl>gE;wO!e6T_LzDQ@9;_Q1!El@OpE*P&7J~auBVYETR^q1&h>$*P+CZoK>bx70m)HF00gl zN~JRe*tq_dv;Jrt*Q0{jW$YS?4!Xv|q|y&IR`Fx}=vKJf)Z>Fhzc9i87pl}r?m!*E zljTG)Tw2aNe*sOP3$VU)W=;!{iGO`BNY>?UScmuWWim4ywHx`OUBin8$9}cxL@fq9%r+4(l((ms2jotI1NG#+sI~SevinUNse<>4E~H|HjpJqQ2w>$16+%OKX;$Q|(?t z!_z2Z<$<~I! zx`N%D4u7$akF}{uu9U`Bo>QI7w%h4!Ut7vW#r>(cl~U|+$kuRs=L0IgfSB{Vl}L@C z3_$m0IFa*DWt5dfhZZLrKgbUt5bSHs&foEA;p6={3&xI=jz@yXfXz(_DxNXDW)=tA` zWr3c?@q4lK$D$NxjJBm=xQ*WH`5ofn18PzEThsEDv3wzMx`E{F*V`5+yWJOP_9lu# zuRde>AT*XKIMx^lQw=OedzR>GanvUJjeEuZYORu5Kc9D38Jl! zHR7Edtp9BG33IR?R<(%^DQ*|sw?2G`CHQw+mj8hvoR;1$)Ja+)UEA%=F2keJ z&o7FTEso7AT%j7tZ7FC@6g+8dOgQS{8(!kD#ML0d<81sDaH=MW{3prAcTXY^`EeEN z5uJwnKPSPRu)Z_gGUCyLkbIadk%y>bBLq{QyLq_qb2i83b=tY#q)$8TlFQKILy9)< zjEoEx$@pZZeN_Kx6E@_aK1AVaVnbQoJRUA=+;-j0p6-wImNH_FFnp{2a*4cSz&oNH zZK4k@TfU~dCyZ|Y*rC0a+QhxMi6@)+Iy>65QU8q9gWm+@<92r$eCqduLuAabM;3Um zvJ^;Dv_{dobUzd?B9kwoOG~0tJ#|S9ftz-B;^mO*Lb;fiRzAVG@JSrZE@!<8(RJugYD_|{7yaJ#WByF(s%f#F$!oIzo_3V%V zFH!a$iql^#YOl*+=SX2Cu6ZyY8NBXeO34k*_iuNXjz>&-cADN_?L1LhyDW_?A5DpW ztn6t67f{}5!&?IhWwj1p8PdTC=!!-zkzzU!IJisivVAa`+rdC1PL;nrzA?$>;nse| z<}Slc^iJAX{bZ1k|&l-X{IikHU@_{i#3Y9RECtT4lCv+ zOVf=qF4$j>eUK^*CtsVnH>B{|A!nQH-e6$=Rs7AnPPjNzA6ay%@&e+Wi?)^67D1V6 z!OC%$9i{B;VPCYkP zz`9L;WNOyJVDn%w16U>uwi5R|K3Vw zTVZD!Zci&pba%)NkA_}3&BkAxd}6m@8WMvvC4#ob(V18RbN&#PKnLk22#q*jyOt_y zVT!OceknQQpM*F%U(ixdO%qb7qQ=8k*C(bd&~Y(?JdX)*UCZ}2dJvrLrR8+vQxO4y zTzE;s58urB5qJlC>Da;5QS%DR6E;$Yyu%SiEXFR<+XbbtSwes^+)2wh4QWI1bo<&B zxFtTlM$6a8yG;YRtwS}U;Lw=s#pT4D&!=iyP4i%%-H*92+0smE9ZTfvb*{+cq2Z6+ zrZ_)`5-bAYq$ZE*G#%e)ZqmZHuE{s~Cawl~!VJrOee4&98U(0>oNiC};L|nBNBA*> zi9eN=Qy4-I+!_SQmQ9_8*$iPuXJJC4^2mY#C`=`}oWsKS`hR|L{touf?emAq^0l*R zR<04&ka0(S$m@0&-~;^>!szqtRJ`+ z(QaM)Y4Gpl3(e79ReoMJ&EGZ{#QosqOFElPB9U`z#(2xm{shTilU;7~59tt(Ql(u# zqN`)+%t92hzG`@9|4_!M_mnc*b(yTh`ght3^lgEA!urLj(sJ{+Y(gB_LD>M(KMT@s zi;{DQTn|DY$QOJ8o(6z2EFE!Fi>5LKPhjgYUox(IY?%J$!h2Lo6ol@0dVP|yi(T`b zmJ-H~?rGv2GhO4G#BL@YMhqdj-HKL$67k;j5-It7AN@@^8t?_tfLAX+M7^z(n zo66}x>M6nQSf?2-w@J%zbxh~@)7Kw1b@l03Y*jntJ1Be1Bn0sA(YH{;s_O&LYLz7W zi0v9-)lt@l!-t=%b`S=-nUE`c@((0?CgiT8c7;b!z1mz52{-S{`X-NJhA!w+l55Z#8*N87d8~m)lxR zIDSf`3&ZE|EcK6!n8#%@@#Hf8Bq<>n}k)VJ=}OAjr+U-mO_93tuj~F=jdV zrouF?-^|tV7+rC+QVKvk*5cLt)f7ip z4Yr>gW;OPDUiqCyb*mjU5?jPE3yJ;~vLknq6$?cxxA{LXe~!JZDV%)dsoE7@>uM)>>zTUqn1O27?(2#+Z*z`abePE0-tn&{Wo`Eg<6;05`qr+mVkXWKUIh2*cuEdM`x} zG^NJ06qk!v9lk;mAL_mB1xP#Y>LUQhkyTU=-y%7Gaz>#@YUl;U$d9v=WPe-uu#F)F z+Wrw@&NEWn_O{kzz4@EL0F-a}BPP@gHP9Nq9|VO853sq__+Y@jI|E4nz$8i8t&oqWrAr=CSg=oPN@yJapAhT)|7bsygzTRfVt~ z-u$H7HoqDA{MEM}YzHVl^1D4u^nV!^3*i2)90m&ADKdH0{S+JSF&o(jXjZmT^yN)8 z-!gxh_o1|MUW4D?R33eym1_T;6G#kdanNvxEDapR$$YTxl3Q4`^Ybt|+&b`6)gkmV zeHgi>`0KIvd_ml>kC9Vc&sEHzD`bIgVlf(u^*c=ZebGpD(cQklPan0GuTvYjy(}+Q zw8b0Qt(U8v;WN9uw!>-*2DI(;>SSZ@yQ;>gUDK(U^dlvt ziY)hqXHdLdNHi=dHR!NgDd|%9O z`A6*tqq+c;IvYvZi`Fm^yJh4lD8ni=z4W2yA-nR%ZM1iYLL?ObiY!qXy4G-FC(08~ zURV@Wc29gzqa2W;>q*>HsZYqf9ECWXQLAGLBI=15*|hs99c5?HYOzU}6ol(_Yk@g+OIuORIp) za8M&p)u7Q~1f~E*Gmi=l@TF4(0+GDwo?{%d7)1kw9+YZ7wL$5^oIw(t&t@)q=38Hd zRH|nO_3>KuQy7g>SRRNJZ{2d=@QLeqp|1mz#k-~3$p3Bi(RrPPC@!T?q(kB9;I9&< zF|T3!GQZjQ&IOH?Fc73rp6g<;U#0ml(ZJ~su3bkG(pS+OMOX(QpyZEKs}0ueJfJz< zS(@NLVRjUF$KdNjTl?qTlm{ zGEyEPPIv^=7X^Wyd9TCj0Z5J{bH19kZyJIm-@A?( z9>dU=vvQ}*vr5TT%v;z^N3hat+SIPs?ORap7Jg>lT>jwO3VN|zk3S_#u*PPz>rC^zfkDMedbZ8y$)JgSUwnPDH9Ab@b>kmN+~ z+|&t=%F`%#LIUEL&bQvdI`EVazKp&4&DsEu2p*zJV;a~fJVF8D9b^u(@3wO%w2a#= zIc@mk#EUAPlpuGa9cmaa%xi{-W+R=*usl7z6A-AURp-%>w2xx*IJkcXP_gnrQIXam zoz&a{Q9wj?aij~%2T#wCt!*)sB1^DavmSZVZoy&B_Eutm_RfC-@ul$05lS(kaV8S& z(Y^%>qWZEq5*I3z9w;u8m9f24C%Fp=7q6KWL+*z%E|7`@<#{+sq{huaJvE6*_`==q zG+X*;4NzY0${88A)InHZf9_Rp^!Y7z4H(~?^Z?#PWh&s0YcmpGPIE!IDc#;`aM?{q z`n!y##Qe{xi$Vxzx!Hp9nx6b+v;pLS3HQ8o1h2u{_x|xKtb$SNb5?JRo+15RKX!L? z56MFpX*r1yZ{&wd5{JVX4K3J)sX(_&n;teH_kbd;#;$AB zh4Kp_h>$lpmh}~VfCiVE;XIFVCHnX()TJJd5+?7D8JQ)_i0YVuC--aS|F zPHZUIRPFOZ0LY8sZxWa}*%eZ6 z3`~bl)Cy`uC)}iU##`z{Eo`T$g`jqp=vJHB`Sk7WdIG5;7N<-y8dtk=@K*IhG%f=r z+3gWf&%v);sV2*H*CI~v1{4w>b3U=EjM#0F9IS36UPti`l61Iwp9M}zTF75OLxyT^ z9Y%i3-}0v9sFQY8ky@pV)#I+LSPQB;x=- zqj7$ILacI#E4wdQ_3Ko~ss_Zxj)kBWybDF_$!!w6I9Vv&x#wN`_ph*ogWiGs3^d-y zd?@j+gXn`*v?^O>*V7*<9Zq~fG?jsLSxedx1&x4 z0&XQhY%^EE#n_1A=!hEllBWK1cqe$bnF%OrtEfJ?QxtCRW}aKIE-=36+Dgg z+hJ2&g^8klL$4>#_N!?gO@!=mwR--NxC(Z2PFRvUM3t5RsWaLdSG)Il88H{2Z3`cJ zW!3}ZJlxjM>-ntm)aUmagbKxLMk5aP0;$}CcQl)RR--H0_o;?cAJMP*2iT)nNaK)c z0>B&~atQ$Zu=i!SS3DFEu4pa+0x2s_w)t7(t?sdk!*u4)3T9(vNnGj)nFnw8hn8Bo zoV=gLER=)tQ1yW$55|1k>M}f|c)^M;O@EVbH%-R_89e8?V(q7`3;?N3bcN>=Lw6H2 zeLO9qY@5F1zzVuYfp9+e_a%4Ios&M+?FJD8Qf#|!0(csrc|OS+@U#dyNhvNbNEF5e z5z;{`V&PR9mgsqIiwNmIAwmtB5gRc!*bBDc=eHRp`OZdR~OE@kC~P~ z_palXMJ9qcVx@tSjiWw3YNIPa4mHAHdrlkcv`?~{1j$e(jqqu{UuJLov{mXP_#RT+6U~adFc@J+JFm znch{g`xBRahq}GXL;h&1i@pV7w)}n=-GMtO?|$Dv(%u*OTH)ro-vs$(#W~`Pwgz08 z@et#VL51RI*?4kS=9i5-To^&5qE{2G z=BrPH`?!b4y*yqi)h&A+6P&J>2vx96U07K;!e)`p(Rur)Ob%DAzTc}^7S+Jys-z8B zr7%qGP?aB~90;yzkngfOHm`KXe25oDp6lkx!!aL1ZxQWYEhtc2sYLEz;hO6BaxK4tIZO^^F=YX%#I$ESoJ5qe?a4(?>6nu-2vHXceUU3!#@)P?=^ z%b4j8`j`=Ba4O93KdpfO7mjDbqKy9gSpWa0E8T~3P;-5V?_6%&xZx;ri;K%_1BroI zPtD0#a2LvosNELV|4q>!uWpKYG~6@Vx#y1ES)k|@Y<;N zT$!%}MF3Fs5=+&gL=qg}VOCM|hRS{GDFW|U4sz-|*4M%Qnn4DJK33qHY(njbnTxkt zIF(sk5a{oKUG&R^MW;GT0fi4Sj!xmQNUp=)c>a}525*BY9ACC$MS1MW%p$z^F(h1h z;L}ywTq9@gz(Tj$75NS!v49)ze-`DN1#YB7$Y_=I_RnmvdI7`Qbpi5%14W+ z`d;gbQ4lRgeB9@C3Oj6EDX-ScnAv*Oj3aCw0+jhde83lHY4BF21aHQ}-A_gH7m`fG z_=-#J^QuRcDO2gUJA||tU3b!l2j=}p0j-Lv7+QA}SIR?P)JfW)4i}hj&*#!epoRZw z^Co5Q9L!p|j-zBTA%uRob?PaMZ`lGfbiJO5f#zOL(E5RPZ{Fvm#k`;$n|2td++nT4i1dAe>dcRROkAm2YZP1b||=W>G- zD}w|iNCQBP&d3I}g?3h|Aaxe{YRl)%K~mM_q7}R4SbYN)q@R&Ve zc?vU*W1Kx@^aamuCmRDgssxNA{E#8wBdV`v(n6^D zUqiBAIpqnu4$#O2CttBGDl&ZbJ-gX1cT#x{y#c0_K0Up$ybk%((7mMpR zqEM$-1LT2XMNEayQpWa4E(hiV&eIX~%Xhh}AJtB;=k%%K0KHd6q+d2{d&e z^~O`Kl6gLaLNz(S@q7vc^>U@zT~~ht_wTL)IgkS-9x*H+^Y*S$Fj|fN2~}POn+8## zh)%PnuH5w1`-i5Fx=D(RQ&zuMA6D+zxm&UTrDuD+H|6y$lXIm4Zauy)RC?Fs+!JN9 zI(Oo_{)aVlNDBxWF8y{l|6ESFNr<^jOfmlYV;x-?IFVRy=e^i*?Lp9%xTjx9R&L^* zB+p7S@66thU~U$`<>{X?o|WwKi)_!CIG=K5GTY^R_PpXpF9qp|Ct+1&=(x!M%YiKz#$@yZCPsD{x7Z(>yx{)n}ddJ znmz`SDmB-QS+*C5(jYbxRPa+|&VmtuiM_WsC_Dwl0T06LM0mHF$oUByfh4)HcXK|= zzVQEH4(S&C!|{J*3kkUPQ;O37-dP|ZG)>ksp;u3C7%cKmRA^=Ab{hCDz z?Gmln?Cu22I(?+YJE8btGC3mj>UeZI5RA?7Veg}^9H+nYc)6n_Q#SL$TDEzB_3VGP zf6R~j-fLk@H6Qz`b%S1v;BXon`g&)+CVR4?ubb>PjHxavXE*zBNk!iehEBFEnLcXt zKYKnFsyAI|zQO!i-T+@Em`gU=JWAC#P*BO=JO$CEhjxDlLS2Ua`*ikGO>D#*jk36p z(kvXOb+zviMIQ3n=$C)%%+kRCF^EjHNw4?b)N3Q8)t~zKKppmY+HJc&o>{!2_EROb z%VX6;Pd$C-cemvNiFjlW!CzteN{HuQjGaW#;;i0{{vIeNbN=zO_Z@>dHH>jj8iHGWjEN#{0>N53g2+!Z$2F*qmPtv&3*aHK2e>T-`N@mL z!@Qs=1T8uA&eZ1+pHdioj}x;q+T^dQd6ja(bA8vaYTk6}4?yphu{MHKnkyg%yjE`9 zOJ3EPwW*&yl$>psxg9&;ddT*xPTZ{&=g9sfh29}qjT%K!-fcY;_$iWCJE$jc4v(Hj z?n;O#w#|cK2%yuFfv6Bmj(qmM5L+gV&RrCR{Y{SsTI!h#gZ%MoitJYjqx-ntk0h7K zHx8?YzqdQWxP@(3NiPRQ@=}#|HdH9Mnq}XNe0L6>Q*rO93B6~v;VhSWEpIU!Os7zi{!u7 z%!K{-(fYr$Qq(ipv!WQ=iEQtk3@&7sESLvr%l!!zc)ggV9c8P(Ao--p2 z0?#Y125&!qQz%8aK1#$RJ)fX#hA@L$cQzmoepY@{%z7MiOb_Adgax=)=kE+8SmN|t zoz^jv5v5N&ASaW%@jx-aDqLI19hf+KD~3>yqYfpiI^dC^*d@iZ=(QQczR#9|LL^;X zNwcrLyD)}Q%cWtQNpi&`$2`Zs8fOi{*B!z9z+=e(qm^EdXS5~_=~ z>OCw#c~J?82=y$I^W7`FvxA48Zs)XP9GM-F>LFP5FGrg$-U}ouFc5d~X1Dk+P zG)cTfhsNLbX&DE-WV-p=D0G6~KF>TLd?YW>=T|6W^{rp*2JrrYpE52yMj~AL9Vn&Z zS|@x9v+N3xiObI=f)8|@oV|`xit0Go_!|q`U>axtcv<`EH@6(M>r#-ej|*keZEN%m z1l~PZSGJ%G_ep%^ceHeARXNey2J5~G!#A@7dQQ8|&q2zx&C0aCFU^K6q0<}qFKq>U zt!r5U;?RpZJ)E7rEl>k3kyos~8T3aD=<^^QC>bZ`y9H_<{8fU#R09G9*70B%d9PC8QHl>_vV*pC zU4N?ij!#3eC*`u8ga$W^kArec^^{gUr_5Z*HpS(-cFl0Q>ZY|G%*s7`JI!Vt&y=HF z4l-5=8+UopiXI^2Bp`#Hz}0z&0_9eN!U>7E~k~+{36|Dy}gG8!A|yk%Yy#7J3bPA3iv)yXdN5K zR?tz(xGXKYrMXC!`;NFcNf09@o18KJD)DvOESjLERsAi}r_PdCq3t}oFDIHm7s%iHn%m}@Z76%+{>WYFfa3$*rW^cbiX; z#6VwplU)Y(k;;I_-OrheI+ynJ=R zRaiPT2bd4Hb(OFBHNCW$lnO}xDtLkS#|P$d%rV)+6?2M$*+ej=I8FnUUYX^kuUmr- zbbqXPj!j63+~X!lMmPg{pMA-SHU@G-S~_)d1G*!a2Fx0NTcWRz(IEK6u@mHFpro2> z^7ZwqlfHcexPRf$?gP%JjfFa%uDS8{M!&(^z|oD!Cjoq)5+j}92j8R^}q~> zS!3E^95#4biBL!CJ8OfwNRxS>9U$oD*&zPLbs-{pb8@1$B3eu&4QJ?*PV@p{+LNp6 z1`Rr}9VhdF?L+1XY#-}E8+AqPNXPt4y&FLr4wI$3UrujmhNO((xlTChgNG z5=-1GHor%wps*}ez^w5OHL3S>f#VQ5)dRz|b^G(TS7xQ_cX~%Kgz!dYI1$trK+SbO z8&B~EAUUcxPuCp56Q~HgP zsV$Z&se)K!8RKeopa@?3T9zxt^|< zNde}h2NC=~m$nyS2F5PkHCmA`tYW`VB~Tb>qGt_oT>sutfjvmr(MB zkRnWir(Pm<9{`3JX5pD6fc7nZdPRBQbP=|(RYXQleBvGlQ5_@$K;n@+p=>`~C{n+U z(zmxFoifK#p;6CSFmr_^dgP^(r-X*@@6f_}dVpd*hBH3zmuWfL!BIb$42*S#!(-Gz zOSxbi#@PW_-&P6adj0^ZU&}=%__P$C@dLtiOR?s$*sDu!A4Pu&x5E<4)&cz&=>!C5 z_Zh8cvl{dP-2j}Y8lAmhx6=v!7`V2r135AF`u;DN+AK>yk&(ORax)b=SX(sOswmP=ku3Yk%*#K`&m>o_O~6Mb9Qdd|gy0!n#uF z<74bZK`xNf>s!Z;e2EDMb5E}Tg6s^Ux%BLa={7?@SMUo__xz)a&Be~AIc5uo=E3MOZzDJR3jsu&x00CP1s zGrANkjmbY79=bg423B(&yXA@Dvzqk%1VSW0GJTzi4=URV%m_d-?t{rgW>(c-W9b;Y zdxC>O2bo=1TtjXEH3vBe{1Vt}_-20jO^?3o_`#XR78_7Ir)%Q(ysSfY! zIc+C9zfnomD24|InQk(%lcJ-dgY=qdd`gp-FOR%BI*6B!=UNfQ!YA%=e?@w2=g;O- zEX*5*7{@@f=N?q`(8u93f_owI5`8){Xi}o8)#EQBQ{yTjUp@RM62dl_y9Gg*3C{6= z31}(ElC_yWeVW|p>j2Sj#&Iiuy5k`v; z-TTKzIxDi|SB*Vd(`xMbl)?|(!I)2uG-TnzZ|PR3twAj~s&`$0qrZ2!a=vMiV&f^W znX`Uq4Z7YuIzV!98`>?sG2_yZh11PKbz@N2J!8zApvgwXR1iVE^uer0l!%T4hIrZ^ z#73PXAG_np`8DZPYOLmF)VQOP5TGxWM%1U4;6Lm%dBO4Z3ZCoGfJsoSbhpvb#ooDJ-VV&>ptBZ!xp3{L&bk>F^jJCl57j@OVdc9^Q|fWw8n_2ZzWy)EWQwq9NT5}DVbV*- zV=HSw2jW^L;7A&BmT z!pp(!C0ggRH{gm(Ud0G`oT3_O^Tb`7wl1u zR$dF!+oQc$Kpl_Gjr)s`cN*c_|E`Z0F?ky}vgy4$?>)5sGtq}BDCifqr9`l%n&>i5 zlcvGS4i01s##HAVKh01_Wa#I?_7lTA+;B0lQzP=8-J$l2L!Eqhm+$K>Bvsukt^!N~ zc?L1d6v?q7eaR6UYNPjS&;5Obc!oLBdec}RF?hq7-hiip3Byy640t-$slEi8fE0({ zIZRiAxk;;^8}VR_kbB$-P9gXn#Pzje)4zRgj>Dxry1|zPy4lzANDgzeN%Ac;0Mhw! zR~G=!#s@I^7?>un$LF4wUgeKU=X7by%^l@{`petzjZ-Ixq;MNOtVdgqjsCvxl#k}R z2O;CNK8}2Vr0~pE6B&yOa;RNMtbI59tIEy>TwmJM#>NNwp08L_OMN@t`i8*uC4fo? zK?a(A?xJPqx6sGUc7OfbUQ8ArnQ)@Tfr=#g!eCL^`9CoOu35aMNso|&y?T=G-fYrj zm$S2Hby=yw>yyA?D&ZM>~3S+Xc9Oz^?3%eIauZ^}G9V zrvE*ojzFZImudSSnxh17{;xBd|N7g%4aqzI-wXEt?n;mo(&3H|1g&b<`>c-(TbKru zc)A*506 zvvwQ4$8Q97P6A=dLL}A*oPhD;1PLhK#Eq0AL%-I6Z}PE-~|C9pJSq*y!yK4Z-}HZljWVCZVR)07IPx96hxD zK@2e4f>!Y5{We3udtDPn(Q{)ra31@$7N*b{DK*9Afw@2g{{eB|@W)sQ7|3hS4+dWp z0DAnnAX=PX{l;inq((o~Sk)k0TdY^jB7k4czq=`Qz4MjR`iV#IW}f*5ax~gz$Sa9P zvUqFsd!A6nRQ0GM7?ra&qfHBAwq}7-Kq? zGzb@gRxtrM`*s2$-VXFVtAUr}$ItqaN5IUH`(R!g8!)CmWT~HR>DDR%_;(W*yeAnx zaj^O`v8HIN#&+FKpxXl{iD}?QCGr1inQp|Omaw7I)D7v5r>Y|t(om>Z>@A{% za^OSW7U16vL~@l^4JyM5GiciG2E_KIt_JwF42&yV-|YnMo`lMIKJwg_vSPjK1djy9 zjE)(nhG~y^)$c2wW8Qksf|U<^eo@5`nhuf-pUG}b(vSC_E_($&XzgecyOLp7x&GI= z=qTaQkO_??Z6>Z%V4g^_)X4acCfSXS@MIu4o$)(5hx|F+I+3b0VHQg-9xFGEgL84; zs4Yc>z^suEQ)0n1A}E3)=l}Q;4xE8W7r-tZr%QIhXKqTGld9;&Kkvo%pI$7!W{?L1 zm)d9dJna#BB{Uys>NAf&tY~L~N}(LgM`l=kB`yVe3=7hwMcJCif-%!`V6YK12)OXa zW}~gx2^EIW4yk{MzL7$1D4!b1j9J=I}`^WYhv=lbj7 z1${m(0^l&=UIET;yfa*?8yNr)lRbSIBi^wC)JBL;q+&JO(9?J6gB>x^7f_6ax`d~* z8og*v(C625(z}}~Zr&!Ua1a<|+21OuA}Ub0!M98_@9qR{YhUNqLRmdic8*ymC}V?l zHrLUM2|=9r`Ey_Cz<}-9Lm+Wmcd0=Mh9x*N1u09x&5Q;^>Vm7bNHkwf8`5PQbYT{H z*PYGX#Rnqp5KlkJ4(^B`?qO$OufF28c~6K^)q$na(-AE3e4z(;2-u|uBK_zw^eTYz zq{m?X(fn8=z;<3~r4SS)dLeO7+3_uckiqY*{@E~=CcVcwkU0=GQ9o{>KRuhUy-~%WDlo@5@|NlcYQ4$Ej^a108$eHf0%2;}c4`1%#JF9x` z1x*6f_nf(NdZw$gGIw~mFJB*MfBeh;@?ZZ~|JA?#SO42@kH_QjxUQ>RtpPBSMjDOA z&fa_P&)z%xvp>h*k!CcS8O> z0sFVwZ__V)Jnc#PM<-uuV;!r`TN z&KC~@(AqVAHMIGkcC~gj0I>1a(K`F{`TTf(e0+S&SKT?jgpI$Vp|$bHTWj(OjsKhf z8NX=r-6T1GW4<4Kd|evM`v3I*`uG3Czx}uW{y+R_fBegT^}qi&|N6iDFaB58{(5Hn z2Wb0y+4#)JzW|_4y|gvB z|K`8`Z~vSBh2Y>+x7WGhn?xKgO@;$Ma);oWOh? zZ*TjR?C(cEVB@EG_UFg*`LTbl@!-$5cRu;+ae7==yU<`}=b)pr3f zvXkrhGZg^N%E8Ob*w)hoLnC<^$yfkZ9g+etKfhf7dNVV?49};Wk>VH34BY<%`JYq% zI^IJ4pZQemUNJU8Zk}`2PVjz<}yc%eR|vL-|wUD?cR1lXbq37w_TCCtN*C z`^C*0mArYQ@#POHza_MNC18I;+QW<-73X8t<>%F&ZB~?30aiWis{oAagnsVA3kJ~o zKAWQew8nn*YZZl2j^=%`&OeI+(Aax#vj7-jXsz|^+1{k7*idi?JCkr2KY_Dl3Dfa!@} ze{_99`tuzpJia=u?(2ooE0q*uq>8W}7On?^p z`-{W*XT16yZOXi=cy?+C6<|E9`v9%43+@kQ{{E;zpxv}$`oqRoG6$Abhp>tv&-OEUb@>sao6q|NB9L-UIuoEK7j?S?%A#xjyb>&VOnC z%U(571y~(Xx&@F2n{Y9u^Hp4x=i@ZqtN8})NB=lvNAKf3rWKvp#@`_`)NwKS_Ots7Jsix^;6= z=@%nnp&`P%c3b=(X6(<8_xGPaIBwMeFf@wL{jkH_PIA3uMF79d|@UPFNfG(872 zOD8{6fpn(0TQJ~+6y_r`PfOV$5XJaQDe*ltg8FJ^``>$fWvE6;=-*-jxd>hTo_3Cz zZyo=2vPgElgXU$7mt!&+dm+M~zTCe6Kr=IiT)X#yP!g=H1z=TxfG0GJmw9Gsl79+xfQ4P|t`okBR}1{7hcrHW#I}e6 z+R$380FDBzbIQ5dnQuU(0xT#X6abSE_NV~K zLKtsEV{hz`iE54Ft8V(M-0wG~RqcvQB?uDXb9E#{Dw+T+K>qReN!TA&Kmoz(9eTCH zg%5J$O`_FE&gcN%fByJ>eYDM1nHO$2R0{>zc&PU28=GW;ltM4L;ZG^|lmnX1zr&m; z3IMzSEU3f_04Mmr-_K6=+0I|r^}x@+e*Apb0ucAn(6!q=dS5JxWYR;r2bdb-S16zi z0kduOohy+yRQi0;(SIF(DtN*he!meH!)2EM;4C^`0?H#ho)+^FVEw@R2j44J;(GS${r%{`~$|d!LZnns^lq;TR{oK2Ro4-T|bez>>X- zw=_J<^{*&41Wnj~7*YXHywugLY);u~tp*M^s9Sjd{^RGzlX3A|(q&=+iY-D|%JAf< z%x;|-fvMMFMGfHW;}mIh){kNj2!o98ZuR_!xjkLoEp-3_eU7)D)gvemsw}_Yi#O8= zAR`*(CXvUgTZ)t5buTxXb18uFQrLTM>>a>Z51>XukJWj+l!Tt@05@=R8|z{iX~W18 z{$&1~4j}K?8J8pMEN`tFnh_HwB_g^a9l-PB{m1ve@MsIC6ZAJ!0LA~-3tdN4e;$NR zM9%+BpSmi5`O+pr2|97vq8YdSTKyRAHNZqH_iOD7XCE8_y#Mt*bO6U@+9qTy1OOa3 z@2&t0-EsU(CLM0iOvKp>D+6qIxB>){9rHhQ1aMuoYrcT==c^Eg=a+ZfJuh&I|8KMk z#RZfm0y&}J{14|#Rg!J3KWbxA<);;sRos=i#cHyj=W{uX-jAKm#mTkaeECGIef8%4E zsP*B?3X*0St~>g4?ZIG21=w$wnV^etz!Fg1bYW{gJ$Q#YYtkBG#3-8~Q1g)@F5wm%z>H(efQ4qL#q5U@3#>?^G8 zw@@S(f`{Ipcmn9)vG;^oy@1u~nVA}i{%24)A-_H-)aZJV@O!rL)xqsvXz}A1`+`B{l5mR$O5X?+uaQ8K$8z{ zlmHN+Q1taSC=GNs5Pc1E^JkGajOEev$;ymjUw43alTKj=!>$JA=u3O&sX`?R=FFP; zKP}>Hs7IL`AA#@?a>IHUO8Vy_vC*MmFoHCfvtb0$lw8Uw139L|S&g ztNX6K3j;tkU3w31$kR%y0Lr!IK~IB<<8-kQxN-7AH=iQz6()-aAAf!Sk(odgKIYb= zEx_Si!8Z2&NLJ^6{l@4#0l&bN+Z|Y=43CU3=?R8n)fnbXRto|DbovL<@F#)F`yrDM8MihKP(;pKG){@Q|Pi`of>v@bUZOz07KRl z0LO9$`fWOkv70z?qK(6L-#_WX)W2vLix_a#ur#sOwFST+H<@9!lu;$HwoUupZ@rm3 zoIO&=u-AYe??3*+^V*OaN~r?VXU0#E{rm0l%m7nwH} zqXOXL`}d#kPv-f|yPQEA82>ta9Y*Zq&))jnJ{RX-TCHw|4&b@ogVvj<{=5Qo2P+f; zo(=8#_zaWjia0xFf#PTZWN*L4x#!3@C|XGcAm9pO!^!=y?Jq3N0!{`~acrdf*)Tdv zwM%5q_=l9&8-4{ldPcK>os9GU#F~zeT~DNnW9(|h4GIW_ zYz|4I>1%!M&ByENt(yTHm`0!nBp@23bu_m9n)}(!tq$N}&nD-;)+|~kzh*-8jbaIw zR1ER3ikL=OrZ}2_3Ro-!YXMmBuZLb~f9hp||d+ESZl# z2o4mMqx+l>diMtqlr5XeK;~1pkU;K_&&+@UWrZzul<4FUmdE}t>@>z?bf6yj!6Fj| zrwXP)0Y=GSR3YlwKQr#s_Q_2zMw#*T{`~&qzyK^1 z4eouIn>UQk4V*22lU@#Cj4tc@IJ9e-(Acd-G#MsHNT>jMvG?69PbiC01N`knQY_>L zEDM!=1mH$lN`5E3W>=&y=Rqa9Z^3O=FlzDD^!zJFK~zw(IPRM~w!ffR>t3o9y0E&E{C6tEoFoCkDou^pNX4t5Rv4A4ZZCWCE^u@pZhVNz|VXN$8={d zOv}C?2?GcfU>;WXMj|Xx?7;fC<+jK93K|kjt!8SwtChUU^G;zAP`ex5&{m5YkcM&5 zRi8^hw-h_^O)Z{|ymj~z`q%I5iRLzZw7Bi1C< z1O{qG10pHGCcCk8N{E81b_kvj8<>Z&_vbs42;vJ`Yr@dHN1xI$==Y~NEn*stIggB@t3d-j21)Yyk zmn`NAthyc`qG1p-vdzRm>#d`W0X(;Jh@G!a8vP(rKx^kd{>QGLNx+q)38@0Od4~UH zpQ94!Lja~s+MN>OEjD_gMZf5q;W3m^El#JHVjzIM3EAcogEPIbk;?rmvl-rUNp6mR zZXqHrc-wd|d8;XnYmN=QzrTMx>-~922_76=rLS0ED4S)91@{gY+Kj9fde+~Y4}VUR zq0rim@-SmCisG)r+>(XJbh@za7H;Eo9R(WqO(cMN#QsisKEy&yd$fw9=FME{DjX$D zec&Bmx4QW@a}7#(VQjzWe*)tU*{+Fs=`AY{1S>)@zc9OHO>7b$!(29Rk~JAE)s9fT z_hR&XviyzWE0aJpGaGXNiAF*dnM(}xg!N~O#~&{(8X468uU#ZhBa>4I`ia>jayN1Q z_pm-|{Q5jIute`J4|QJE$$`1QD#q z7fXLjA7F|2(h?aYIlH-=^N94FC6~btoiSr z{+DKjFYS-a(WFS!k!2ygO(;0dgX(udR&?Y{7RU^@|j-~T3)?EWwdj))^Fj4YUH z&jx^7cHi^5FR5>!o|Kb{6rR|5%7mGdxP%A2&{+6$Mqos#8tVwxMhMFW%cpI(gW1Lp zVydNP4V3?n8>7-RY}l!dhWj@p7Fo8{Rkd`Wqn7$nWMh}$eN3aQTlWDB>(2KA;9CHib+Bi#>lb~_|H;38{fog~`=axcFYZHOr>)ooL}^0T?2_AX81} zJ+A=jpD?&z%?4xYjRjm#zjQLqP{(cWRoj?3}4mtwu}wO+f=lhOPJdMWaLGZe^#uk`wnKrXM^VwuDMiFXFdocMk{gGtY`x?W3<7+5 z>haHh^uJmP@U@!2ua8NUteTee+f3@ft!Izu@i_jIW5ELgM9LaC?h9HxqP_Kbgt>fg zs9RvH4#1brLLp)}|BNo*yUQ~Kr--SMZU^0sbpbH)=G-^&Z-P1twU(?a^!y8PZm->U z^3JvMq_hRtXG=>!5YRIVQj!%;gh!Ohw6FhONdcW3)`HcQbn9@LdO#w4sj^o>^X7)R zyV2H**U#|DUsP`OqF`*Z=nYHl&Z+e_Ic}Q=U?6{an&(#z>VMRN@zT)Y7k>uQgxe4? zFN?mi5EO8l6OKQIy{uWSzfJ&Jh2wHq_@u)^nXaah`Clqax7G=99Mld#&cE`0e4hO=OE(S#bykz}FAft5vxf=5&E~h){5bvFrqa(p7sg0`=&8&pOAghU z1;sLFAg&oV0Wj4&Nbp@yqzk07*(M5AO!$T>R|A)yJ2K^=@bJ7-Awv9R_H*OAq-M(- zbYC*v;P2?8aKrdtteO8`e0umKYQx8UAgsMiX-{ zS%DdhQ5i{bb|S`Eg#`}nX`|vXy^)SwJnaAfD|WlyVjZf2C34pj-Pm~X8rlJe>>a{C zuNf4Z({*61H&V|(mJz^$0f{x&MVWTK*C2+ig2d|<0NT^G*4x&Ku>8WBy>Yk!hbBOJ zfhHOgoZ3s~VAv&CbPV9q20-*D;ru61mD#0Df;bObE<-S@?+cXFAD`KHU;HRqC~X#; z0;!cd2{?u|0iwjU_13oryn?)wzDUU)N!`j7F0lXV6a($xov_f#u2y8%TPK^Y6>s#KgahsWVHhJf3_OSVIL5+T8 z3jn=K+8`Jbg`6T60NIKo$59-=W|w@>Huq{?PrrvN@A0a&7D)@+878d(N-t5x3={-B z1t+nclBGmX&qVq2Ws`AaKD7gZ>D|rfQ0uVP2QFx@yl7Dgt-*6+*4Vg&D=QCF9={eLJfGP^^FZH z&49TD*zN(_njZQr>T}xj9`JCF=2-x!`_gp3;xK^@e;-XZC_4s^F6hx`u0edC!||xw%*8l54Sa+ZPEWC0@BcJie$lqQmE`)6R)x3D@(eHU;qeBJgr)O^fPVPQUrw9 zzuk5M$+tM!U`J0VIav)wI1$y6B$a`bFNSmZa~qKm+=e^=Gs$`YwW&FoK#YW;0L42u zeKXFHIlH~}HY&i-Exfj*pZK#5o6#beuqhrJUua zWdy$FDDg&_3>)!b(ZM~KlgGrCAs}P9`@k?@Z|rSotq>|e+^1IwdC1x#U1@`@1DGl< zZz`Pn!W%#HvS;-Xprj+Jl)hSm>FM%3^X#VuD;RPiNHf|mNf-vR@p@qwteH%{yk%5! z1@12P+sR@`$P|Gy1lx7JP@}S3;2`y!`NMA~1Ww=$cKS4~A66F=q=9oZ^|p3WmaZD@^Dr0WOdKs9q-( z)($hruwz3`30?YvXr_MpOZJco5m^yMkzyK8EQPWjy%qxdlI0T62Y~Jf;LO3K82cc; zaoCn+Eu=+@nLHwHRho4`{FNFYw_{VPbhw0##I-vc3{rUV5H~$fU+C|ANem)(2kUChU zb6Lu&mZcrlyt_A#n=YjQ&GzTBklaZsSQK$82w>4uD%?6mAnJRL{0ebP4y_Vjnw>hx z6a^SOte~vYEmp-$R6Wd;gRPg`{XGJWxmriymJFs8KDsH_&LmANod27}y4c>I4L~nk zl@Q$NT6Bn>Ny9^8$)jH`JU16xt9AF#ZM_-W+yd1WDq>MoRsbigh_x~I8gPyPtv%iz zkA~X30WWNQ_^Ka|sv*Wd7eWV)Du5eDAS7G6lTX7S1~vArYQo|=9T)X!VNi*Uz9WB0DWoYSC)fv1cPol2@DZ(XgOcyGY zD?sp+PdRnorXYv?E)RNg*!ueC1_fr)=cJwhSmv=qXM)?Dl+iFUCM_g(px;kz7J$tJ zdziKMc)WdkTxz^q56%|-w?Cx|GqmD5ULuKsX)oM_#B@3q1SCULJ@-uaqIwC!tiS&> zKEp#sk3N(?w=E^3k4$>Tuy{NnP{n(ISpjZmfPw$_7H{)m2EM>K>1b6}ihVEprfUYy+`zbC2^gzQGm>%APIs!!yUlvP=rXSO$8qmrjw~ zA${Ryb-2&J5R(Hmfx|l#g5MT9pQ75D6yi2BDh?lF-hf^ksfSodv~KVV{ijI zbU|)n&9++IKn2Eiy?y)b+Zznk=moiileG_DT7EDaeiaqqxTlM`$Z`O)cS%PariHYe zXuA+F+vgAKAMY(fOHq!x>08rNwvos9Lxq{UIUE zA%r5sn`JHT?Kuyr$)-RXj!4|Qq4TsbkoX*&0w0UNSP?T{!QK2PLC~=oB ztx#5M>CpxZYJTsc14u8-{U>Fs8C;7GM(H?nT-}k)gfyHVly2?k%4Zq-lYOiMsJ5yB z@>Dk}1o4r7+1dY(Ex>TKo)`dt_IUgD+wX6vD?sq8`V0qT86^;$xmcM3-y#4w3G_H^ zT-EA<`gbwEOxEEampy-8KziDHe&%T$M!d^P>Hq=N8N~gYkc5Hp%Gx$iYX>yaDoAcB zu#F9%#V_mbU`Q=s-U4Jy2%lc*f*60&1n`9v8@bWlZpwN>mZ!I-&0EedKb*gwa43UzyI;pU@7M(jLyk_mz76)alxxEFN*@e;M!XjfEKTl zN;bv#a0FZM@Moro%RXBF_;IzGQ+D`Zb~N@mJ~7zYCy0e}ZJTnPQwuPK_uCRqd9KX^ zY?q@GcJL*Wyj0$GoYzW)3-iY6jZA4hpHnk}9WXqNTYlZUD&U$3b0Mp z-Ql9QZq56-z7gZmNV-9&iTC~eaEQATf3u1hfu41hd>uIRHs?<1y0W zx0yVe%F9N|@?pS88R=N0snWtqlA;eM0|fwq{ZQ{!68}m+4u3%1^#$4j_`m{!z3r9V z)|qcyd1YQp+z&(uM2P38I^5HA4QObOx8HvM{kJRZbx}0ofys5p>|u4jL-t4MG9B{3 z!e!skglVy@;*d4F(#59$Gd=$!>dSuO{jA_f$9q9|F(NKsaSFXDp(l^-p)t}$ zxdwxOB~>8113?qX=>tfe!ftu8xG{(R!dnTgVDrbJS#TQa=xfV&+FcJa09_+I2vt1F z$Z|UNyWerZ+$yOw?BSwexWJtCWS?!@blC(s@sUJSOGLhy|dh8 z?*?FLt~I-|1#>Ae#pwC@iiKqU;+y^a>nH%&El@sb4k+vq9@cuJv9~a!8e88{>?vxJ z>@(MSenCAoal+d+asK}6)mEJAFd^X9nCt~x_?I5{Fw&-T>xeBh;nN(IRWo1*aZ+M& zk^2-Je&RE}vKS6SP{zO|nHN%WfMY;=z_?j=CO{!4j4n!LSB#7n@wt9`zf_BgUtBIw2D`HUl@wJdvWe?7Uv#x~EUoCY}%x zW>L2xgp>h+ee9$dAKkN^=YMl>nptl#Gks@*$TUDRVQ`jiTLA&?j3pp#weJ?ITGnrU~X)NW!t$C;dz1h^RVZj5DhXbz~qm(7jz-d0doYA{~HfOyRNry zzy1FELwQa?>=yFkVXrEHnnzic(VJ-H0c&N1f446jmM`oshfVwDeEJi^e55Vy0&ul< zH9nqy{U#j%tb+360aVj~71B*3_oS|GY!BLf2X1=fP6t~RW~ww9s`avRyYn!^&2BCu zKu9chl(tg)&{n+6kORzHKv9!afzS{~s+{D1@XQLYa>5umEkH8@&E{T4BY};_*BZ}U zZ!y8EbSGZo$5QuIQ7e$mMK<(LHp)Hfd%OOer~e|`Dgs^sh6-rJPCFSCjwI?MKMH`g zlmBx&`XK_ys{{D<+wXr|%jr}xKs9!YjH*`y67~M?iKtb%PI?c3$p1NH;Tv;z6Gu~C z`J$H54JKe-N5=WTczyi&EsOwS^+F-Aoq&fky={h34zSLErvZ4D_a$c9JUY+^t$1QO ztL>An6_PJ#zb?4m3_xK`IEbybB$^G_8grDbSRfn(QzMcoh<90vA!*>1K>aD4lP`b? z?F~5?(?k>6UDM?Z^7}>`$DgtjIH`{8-8Li1DTQ{}FW}V2cplU0=h(#cKlU1Zv7sA> z4`VYa0pMr>taTBf+scgb7GR4Ofe`|X?9|W0eo1{|JssG9c0JyH`~CN;?USBjW1HAI z<`IB5Qb38V=^U}XI-#Qt^jJp6~mKR>N8P+ z|2wAxx6cF1ue3Z-6V0mmRK)%CNC%*&fyUD^BVlIlO#(%=R?%khK7_1Sc3dC;Qd*$~YX1E@2-@g6!+oQ>}z2}dHegEn%E=T%gr&~S$s`j8I zi&^8r198@Vb6dF?57EL(!p~&a3b5Q9Jm%X0z^h$Xy9_^m`}Vk6>^EnQ(v(kju8z{8 zn@gX_|AL|@Tb9NACc^s+7Z%N^dh9*G03SBnYm3)G@xHLH`KeIgAiePYNa2(MtkJ!* z@+*H8vk><-t57%JGD92#wqk=y)#t1MuRL!&@f%#SNpwRe#-`UPS}^1xfU2@ArFe^) z&_z^KIEOKaD*}mH>x+#RpgVt8-_^dLz>AG-)_`Vg)Az~4Z>d&N0s}97X>Ywh@iZH- zGLAZxiQl>?HQfTVQ2`!1g+Q_HtPjzi>71bIRE zJV&O0JDJQ5;11oSXfQWUZbm~N6~N!1X{VTzG{-k_O+WJfU^;a61RYrv6|5f@(PebL zLhJ%%b1U2(bP49}eKr822iRSJp)FqMUf1te0QDB|jM$E+5I6m%;;HxCibfA1+**LK z2OI_93UJWuoUgVcP);5I(*iw&tLRl+v;fg`Kah45dYCU_y$&(f^j2e$%KY#@C(U&I z80(8%?YgZ0_U-N39l*N2TL+rcCj4zBMKjdF1~zR1md%PF8}QE@2OqFwa{@El!lJ}6 zY^3-loF~MXTL}UNr%WVRHTmjAZr$B0$jATS08u*tMq>Cq)*I8)dhQzq2S|+M_F#oe+QIlj-LVrINZZS+$MT8f9? zPJS(+yQj1qZl%5c_;-A*51bcCwqU)?pgrWp4VNRy#PGr+ZtglVu06W z&$qY7mEtA_1EW#NcPl4nLwNITpFNusIF^GGFZNXdqB3}06&ccVz*x_VS>EA49hW z2z`~wIG!V&`nLMF&$z&?WB7^gtt z$=P9NWNq&HU=IMeuIur5ykU0$kp1vAuim*IL~Z+*RTtY}ZQaG82T-O!(E-SbUFUzl zj250!$~Wrn=yW~%^>lkb?Eq#0cz=6bSIbaXV{dFbsTHE)T<^8q)QuJ^!1xL~Y4aRL zedCNh1}rXs&CWxIY~4Gu1aZq`$k_GeDDG}|U}?XLUCXkCC`U%O$XdK@~bqz9OH|NJZaDmp}I(j(cy<2v_TfYz?Z<3f`p zMoe&c765p_J3jRVrdzB&dvCq%!x=a~sQ{R0K{jc}Pp+T;?N2h{n{oc1BiW`4{k-+I z3V=WW?Yb^LuB)Nw0A@Rv?d*07U=!V$?30C?tC)KhfZn*AJPQd&XffyOZQ@yXV_fqh z0^;WE^SI+Z3MivONK}E+w6XFZ+lsj(F9#(}JSKu@4sQ&~37sXS*zS>;+eaUzL5c-m zfx_iDBtY6>Gr)pcDJFwj0eUkqX%s?Nrm)GxEn)Dp??^`qk_Ju;z~B|&Ip3?LMK7)c zbqs{j*%}Wy9%ri41OP5s!2LqlUT5#{@T`fKhv-(MsMo_;w{;2A+*#bCw zR#6(0#r6>J(T}a%|Ds76^n#_A-c*`6f9--YzUXxgk#$l`ObX!iV7ha&O$7kz=fWKn zv`Ajt%v+`COuExC0E`VXdxmai?Jqo^UQY!(rZhNCLV~C&k3bYQ+*t^~Al5um2=w^h zoqus)T}8lrPnS?chqK@**b@k7)-^bTH@?3j#~9vjrzKeMkvrnRqI4oApV*XmPgP#s zARpVwun_i}a6Clxz04dYu^d}XcGDw3@VQC=+p+7}4^AM5&=c1mrlt!VPpOb=d#>TU*|Fu>a|4*NAd%96iD4Pot@XyhK6;RR7huG1A3e%x2MDe^pd0ns{&D2!LjP(K%e?#zvT z4gio9T7gS|4s^}t4Sl7F?cjPS?;ii7u>dwd#>%%mWU@z1fr{8;HZh zw`N>P1=t=yxXJEad9FPfx^Rl{^^4b4J|Y)Js%hIL zAZ%xM53p7NKyPa(tsRs^1mI}UBUs3A`H)Eyy$ugo=5W0vr=EeM&Tlon`m4BB$zOIQ zLWxkbpmCWDm8XXLka{SJeMcA5=W9K~&=!D8sZ29o9J|0RM=NH4dGE|Gv7s7NN_))% zfF<{YT06|58CHjhyE6Dm{ zOEu;Us%xJWFCe%aK*2sqFVGlpCvQmn^1y~R42awj4(G16WNBdrpvhM-M-lo&Mp>AF z;Am#nBk(xRFkLvYNC}nmOeqT^GHC5L!?~K`4Fwbm12jP*L@E{Db{=N&hfPC8gU#ok zRUBPh1cJp=vveS~clm9c|53a*{nI(1(HSzXjrd}nMJv-0apnaj4?&(mWcl4#$7mM` zergAPnny$Z+6oJ4CIk1voDbnEhl8(Rnk}^N}9Sc;o=cvJ;hGc}zwd zhQoI)u&}r_FJj*QeOJsee|rlsW`Ndp1_)lWNU-vqkt?=U+U9=GVIb?odFZ}NKGj_+ z@0sjyXd>*B5aHyhI<<)H)+H{a?tG$MbxYa1$NRRCj@tY{W-}jOC)nuPMRmsr)|Z;K zeLiee9l8lghetUe=fm%?YJOr22=%c9^7+1vZAl@SF)8a{^jr4yw>z%#;XjZqx&jCp z*55^?Z5kN)o)s5b=!ZRHKSQ1ny*DVRTI! zibmJ_jf+S^omb8BxZmV}fX{eqyeYh}QrmL-(F86{Z5OH?z!!E5{)L?JZhHE&jY2M( zw^8x181*=z)Y=EuF>J{lLbc=IexlU9`@>v4uz!Zl8TI)1InEK8|Cj| z3bUiaIxqi*(5E+HipS>N8x6Y^;P7}=$Hia$$Pn>ePRDjOtX2ng^Y(aCS_OkwykzC= zg)mM=?vo#{=u5_`4`Y~@UV?kMZ48^67=C7lC(zb;?wHaZ)E79~uVf@AZperTiR9Wz zp@3z<+&<5c+^<|XVx?93trQXHu0pKOTStezb5Dq z@@|H#cU{(I<|&H&SC@JmZch>p8VTv4YfUP({n$w6lTyV~GrV6g6DW6UtC%6fs)*KAA*1<1Ev*)wf6sJ+v#d2@IJs*K`%gj-H+kGSN#n}|PDc>UBanje2>5P)%c zv@>+QZ?q;+!eia)(3H^aS$S(CaxxA9x2!i~V(F`0U&tkm$s_9H$+}?gXFTzqj(Kf- zxb$T`ZVf#IsNL6{nX6>;*A88l`zPLXpE3$d1D4H;836FRMKbe2Oxq(A0L*r;6fUhB z%y!5M2RSg#Y;E$J!q{smOiKYHaSy0WDboxVSUUc)U(72d(F8!=?MPigFhaZM z03b3g)v}b1%`vP zbZ8ifVHKveGaZkQukut~pPbeto^_VmQ(9$)wXdR`8$h$O1=979=A@OL-J2Mq4udFm zD{)8T`X7@GUb#%(v>d4br4>4z$p;huyjIJD3u~3oi>VFIyfn*!$3>TXFrqasE>`II z!g^CS@a)?Vb}JHxkXx^F;|e>i`5X#HPD`|uj)!cNpQu^)UJj}v=3OT4w z3=t0xhEA{6=9DbF6oTpqkeeC(nhNYD%QCTWi1N`p@u6w!Fbeoqv_Vzp&TPot^h_HXK)_Nmq5=Yg$HSCH@?0eP(K*B z_Cou?6O0y~YedY&jL%qN04td+1=g@ezGHvRyS?q^YWTdcK}4zuEwup!b%KFF#zvv8?+ZOcA5lC^?&+DHh36-1yaV3JnTQjvL(8LHkve9L2MWX9TQ zR=m65@cA+yAq8nsU^XqrCnA_VQPD4b3mtoXoC0rz(iW4R@fwT0*-BAzn-M~7J@p=i zy|DJnf}PO&!+1R?D3r4wumhY}RJ@!m8t+onzPyWoN4PUUChVdrmL8tDQ}Zg)VPrOC z5xf2mn=U-=ldj9a8FJHy4T2Q#e(IaHN1h1ma2?@_N z&A327zRrQKT?Rm_79}?|+IaBUWW>a)u<@i&*ZojG$N+r(6NSwfj%Bc$6A%6Mit%>p zo+XzGH4g_vwetVL8!}y~$%(|-$d{W92K zWS^f|qFa1g4${8l@EBTb&e;M?XMIgmpL~wv`!Uf+4zVzT*I!l!TWsbeQg52X9kAq` zQ&~Y!tHRfh1l7Cc7qbKh0uHg-0^puJbS!#`JP>=uL*SRGrvn6P?u2Seey9$YmEzzy@N=Elfm+nsq6*=Te!8fLKj_MDU10=urU89l(yw@7u zOy}FPy$6V`ay#Y0XGB55DIV<5wR>{h%w>>_$Bd@ta+;@p9t7==a=?V3RJLd ze5IN4DtT~5!_Gxl(LjY=Rzj%Ccq3Fz*z2b1#k-4OD9hOPAH4OOyCFkq#%iMYO1qBG z_!J*mmrZ-%uOBQC3);;@1GQ{VsCMI2gY$J`z?V0{h2@qt4&K=wTckT0$_qN70*&lQ z`G@yhdL~-KS4;N`;v-n8c32hw7A*T)r60fGOGPWw-{aEyoB)C%H1EIs>6zW+at0LF zVO^Uakb4`(BIOFRAe`(tX36q#%X`#kIh1D|z z65Mq#qWStUE48}{0d028reRG$pkUp`NYg;7jFGJhp8Gm4#d+ zlG}`XH~8k)6)x}oeNNrzp4gtqH@b>UhiwN$cxF4p9c4jY7ApX|g2D*e z#N25InBR?G)J!eemE9?g82H2ffwHR7!Z<&9)uRMR#Mdds3UA?_;Sj>X(y@dd&4*Qi z;-`J>-hY456pRU`v6n(+93RGhpdecsPu!I(pv)Mlp5fI`ZnI&APtbrPMj?L&85-xpNx&4g=6y5g4IgPrc1<2rE1lvzQU3UHIQej~n0dcCBlZ0SK#i2BA zLaI?=horn1O2k006iQo>VwC&jru4;l=SmSvbGtb;_n&o<$}iUXkjvjb0Rl&e z)AKos>T~?+z4tCXKwY~HCRBDWB-6dw@BR6e(|L6ZPPZf&&hgVM-_5c{@SpRfLn!3x zE0Q)6QHj1cK~Hq+&)&(rWn4#GA)773#qQ~mG6*!gIIX?aCy_W)Mh`EMq-jx}I5iPw zxtmo%Co-}_EGOmB?0EzzT))cp0!I6qH?U_ws_F8LWjc7sn>AZXLQ_KUNa_G2(U}L2 z9WL5LJKpjmPVHDwP2l)MU2qd-XF;gUER&Vsd-u3CLHo1?nEe+#s&H@rZ$FDbGzRWq z0Hd+@=kxh7lboI(&ySB$0umC)(+U7oY4dZ=nCRlaV8a373d2W)1#h z_hMQARsc0U12XszY;yNjclAbI^I!aUKHWIl_j#NC70qy*Z68268h9&DIPDWrO#ml} z=eX}7o-#-F%jsY-IqHUN_B!}K>|`DRZif<~-Qo0;;cNb1@cZN|_)7PH(q~EUcgz6c zz7IZo@q6J=yGGOBY7Ic%5JJwib+HMc#GZYEkj|&HsHiw?H6taTDIdI=0D^rF#O$^8XCLIDUBz>NnsP|L>F2FlyGJmD?; z8D9kld6DmgqV{Nf<$g7?)85}dp1mjFnW87tk_~}X;_sDD3X5(G0A91;?rhPJ6(Xjw zwaCxpIR1xyqQ?UyM~ zkJ9pN*L8LK`TqX#?7VsFcsHiMa^KOhp#1cK8`mw|xZzkb)puG$NP!P)2YZ#$A*p@q zvwghwWIRg3FYN$gL&Q&Uf+ou*cg>vPq zx%a3jg{Acn`}5=d=Z|k*0G_h|#G$RIJOJADysqnN*W;Qo(oLt2Spdd==HMOFDO8b4 z%!Qpfjw`SlYU~*3VBFJ6YyqCn{?r*@ErG75{rLI*@$CE}$+0A6a?{v8A;Euka`57m zh0nj%a9T)x#A{3Ug9-h(GpSR9_Aej&U$FhPms*t4%@uz`LU33@i~Oq6Tz{f#3QDlj z+t3onxVk!D8u60>-Fa=kth%6A?=W8cFFv2|KY#pqYh5eAyf|MS|JhcL{OIdr6#$L& zBl#Q^;Q8^~hmip&W7OQA6~IH`)h)qNptltTU`EpmNqDS*F8lH0=le%z&VA)4nap#E zK1EqMIt{NLXxf*e%WpW^FKJkTTwWSu@U}8N^4=0h!h=5Mco{`~KC8!7nxy5tF>ZEj z{}8kI+6+5<_ht26R9>r$JWu}dZE{fHs-GzP!hsp%&=RRAuR?T7PYJ0y-SEE5%qvY< z7kqQFx!C*1`_CWWuQp@W`z!!Kr&E9HainGiXt@B)5n%Te16L4$G^ukTskHN`wrj^i zn^=|U5Fmt5HH%)=%>75OQ=+fSzJLEAGXPl!hbQ~vaEF>FBb&W2IrgGbTso4O)knN& zumlP(=vYvA2(C(!1%bJvdux4q)T&2|!JuCf9oQ`!#@{Tn^UC7+)=;4Tf^1LOthf#) z#XjeZ3|}X$IJVRL9L^D@GRUGOta_GPlq)Ux-qcki3QtHQb5=)T;ihnz@x{p}ZXABj z&jZ^QOe@#(;yWLcP?SI*%1gwZ? zxrr$6lDWP>sD82_d^%gq_Se4Jh40^g{QU5~K|IS}vmd9x*_sg!uP-`&{3rs%$@DqG zPqpWRwLapr4^I9rkJ;%$QxvOfn=yb#VsWr8R!$FDfinoCzle#?srQn1A%#~*N~mC$&p=Cz6^xQ7V$ZKyV&xaYI4Zd`QRQV1j% zdT{^uBycsUDpAcMnh;UDTVyqLdI6ZP_p%><{rLI*(KQ(a+Y_2M{)Ysux4tiE+5Z>;Jlke=!c8K1Q4f&( zv<=9rbg)RW=P~8%s6}7?p)^E0EyuUPZ&kZHR@UmILh3;(-OsCtStJ)2<#BR0^kWwm zNPl82rit*1D^mwp_Ee+_iQelsy|h;aGonTI=f}_QfBk0gL$k4SI|S4Dw(#`kOP`on z0Q%D}V@ZZgC?|qqQ?UTF=(xiWHL~{=!83A^#U+PeH33(%pMQP-@$u|jvg$qzqt7v% zNGZ5|T-i;0`qdOJ*uRHHAoRV-)Qx?-pV2STJ!)p?bPfjtiZu0BCOl^xiWap1A(O`| z`*P#Xzf4AgC6&XaB%6DMaWsbJdiAd)DA7C1%yJ;UwN~eNzwaQcj>QTM|3z zee9|La{QSssCh<%OLlk{|7?C;`far?ZTak3h5*J-`h5TS{m*ZNHy%%KobM|@j&I%% zj5=~oFm4_H9`d0Nrz#iRw3H0r663Ne03*uU?IApJ;~*N~$DiMSzCTG>8zLIQnQ~v$ zS^$*nr^#Gia+x9>^x!*H-VD8U8`09e_onJzg)q0IAPO<`pS5s{@fJ1O@A~7${Lm*c z*sILU7eyeT4IWO&qXme>dxj{1MF)dB3^S%RFI0p917HD1%pm2t`i@uJekWUB(rMnr z=b3WHg9G=-2eroPQ!tI8L`~szEAS+pvtKR2h%V}cIzI-X82j`6$M?V9o~`eDvmfax zWdRVq-nuVVx9Ad8jH-`g1+e$O{`&F$>`E&| zfelAW%~XbiB+*s~oDkKv`Z>?syx-G~tD}RX09;24Am@KPSurO%x~5bHzPBY{%7M?I z0smO5apLo5P(!bD?6MMxr~Nd)(36rSju|KaPO6pJ_A_m}DC`PV(bmp$tIZomdC z!Gpu7Fx*BXtB@Y`E#CUD#Mlh`r?lnTq)Dgi8EoRf4u+r>$moSt~ckgy{O(2gnn_H)TRn)+H7Xrt0i>+ z*@bN@VEqdt;0g#mzytwJ;iVKqn&pZ>1K$7o{^NbF0cUyv{M7}7i!s+>J*3SrlVNSG zt31-Awp1ug6}g`sm_5-hc`m~W$XkHKCMwenR+*A z-TC@QRx)cI0|T#eEZrDfu6f5QT5`S)GwVSMk`GI191opWWIe&t%+DWLlrLk<07nH- z3V@Nnw)3~sQ)j=GD4Un{miFiS&+mWQ`+0Sq$3J_wYj#|lS4JoROS0QF{esajk~wZX zl>jX&LURdbkSetfOT7rAER@zh{`&rN6@c~c8&9*KJh1N1u#Iomj9>eVDq~=}E54a^ zgG$`p17K45#ad|5`U6J*=k5Q6r{!@9k;Lxq75;Z)n*E?QfuL*)=5&nC|ZUm4U0FebyiS0NO zJ`Q@_7y|6$`}cqW^k6GczyQ>gF-+$XMic4NeO*Nv3&a9-ujulwrm__CYDxwh7QT3I%tW4(JOLjBs z)j<~~2#cG019(H3-g@ia=F`Lj>x@(eZ;Te|2(!bM`;~I)F5`3aK^UkMNOB7DBbfe;&jC(PF8tw6KD>0Gyp*(cWb2bnLoF@t{~csRPhCfzW;dO`GAh- z@!(YNFD$eGmM2i;g!8l(EBZC0DxfZ$u{V$}ACnP-%?Pc^0fGVc@&03t05m1sL=#eVwaFqaed=+iOGzFgT>R3I%BzT^2(H{Jx zaj$9f&?G4Ur6qNgk^Z6vP-CsdOP?|SGB`u18llKp4b0jdSr?pyiAs17*16|g>mKCi>Wr3xD;LPrF+w|ZG`aq&;98-Un#MnEv0e+7g>J;^pBrT2sx*gt;0e>^9H z|Kw|F4Ool1@O$0Okn+4Y?i)2HHn{_1Siy2uUVX+sH^FYTJxhCtFKE+gr&V!O*F0Ws zxp6RFUMB>rMenAQObAnHzo7$&j9f{V*xiG&05oKHbMrrMk5@1(*7?`Fcm9vfR|&aY zp#ng`da!YT)HYf)CxQu+a_nzYFsvCrQ=Xn)e0&K7>H!xL$5cXEi17^YE}tJi(LXLL z*g5y{ISB}~9f65 zZ_6I?i0JgsqIsam`kOun$=w935H*^ar zM@sw;B>>y8Z#$f=RG`K%5#PIy@he#oOR&BdZI91YJb2+O!72;V#I1EpUe-vhCVuw~ zfC;Bk-dd_~2pS>AuTieBy**hKue}EdQ1eCtX?@j&F@iONZS($VrHX}4HVL&NcP3@Xlg@LLTEa<^E=MPbibFNs^TAR1Gf24Gaom7_l5W%Lub#DQ7 z58yb!IR0j^)<+awcw=LxT@rC>PSxvcy}-&|6#)s;XlQMhQJgV;UgOAa#@=8r65ROG z#|mdjxN$_H-NqPK4>(@_)rzdpJ2psVgtj<$#qO6k7MNJGfPQvSdo>AaE2AX8CtP<( ztN;Ns?LOxQcZtJ7im$U9;|N6E-S1cY_!@QK-pVdnF?HXk^_xaJ8bX|m-;e_HB1;@0 zN#U?cD9D#!$?K@HgK&HmUIkUzj^qBJKh#v);1RwNP{or7O%{v z3QNOC%1J+(1r4*`NyhGb+F$>Lmss>KdgFbHZsRMD;#UkJ`I-Mu{hHTCnSno`b^xma z&_^;eq8S_KME}XyXIx)VaOL81z`b4Lt%!pbV3|iDO|;K!adGojv-nm0ld#w3ClW(a zGm924g5CkZTxopAk@M?$a{A5~_6kyfWH7b$bekxC=5=*iYTQHu zP&4}$nEt2~Z_yFwJ`OPV|Jkxx@;as>L?{=9$Ypis#%hwBmYZWkr()dsTh_Xx8@U2F z47ey=;Vm0yt&xTm9N9W#;EVBP*`p}fGm4`@^kF}pfMe<_XoaxEJk;eggvOaDLY^1` z_$||`vyA_4_y3>FFQ*ruO+ek63WKmWb_8Y2yrZ0}2CH3iZZd?Tnt~d#cg!v!Y013w zRKsxGlcoZ&I$PTsN-;X&iy*oUROLmzaD7~2PG$3k;g}F~0H#*Io^`lW=j5qveQ-0| z{9dBq2wVWJEvOpq?u;vpj4N~u{R&14z}kC!HEHsOo7(#^dYh{AZT4vpnIxx~pUG_2`XC#?oc~L_yvOp>Dgk~m_ zgJd$jB_z?tOwRyV61*NIUoI};>VsUg1MJ+T_v4b##sS)*v!+3urX7GT`TS(v zxO%i;#=mGa{@>^)>(lrB9-tQP&pPtR4rP8in9{~s_W(Q+K&X`|+uZLO07~2?n0yp- z5(7L58-6NR?(^XGq7xn6Hr?{*=BP+VoI-#$d0j-Ou{X0>)kAY`!s-I@_?_~<-3Occ zFEugHIX@~5X9?aXuPDGbA+uShi=%I}$n^ea*aQ3mqW&M@%l`rjXqrmwf-XhaU*q+| zN^li`SpftK95d~-M>bV17W;>J_G5OvUL+AkOLAK7#nS1g#ocRzhj&Jb2Ai(@raCnS zXXLx{UlCj6eVQx542^{fFuSH9nc9vx+jD8DZLkJr%@m)NYMlRNvdZ8Wu*Wi$6zID@ zpM?%|bnDHmFVn#g$715WcC-MoY;PZI=QKjnZxV{YVC}NM%LF0nB7k&_71xT}>hH1= zuAtKloqzLe;%}Tt{?gIM-#z%J`NMpKRLSJ&LRc%YSNn{aG}H0aWVwa@F=TUFnB=V% z>Mmo&wz=#lD*!O~{-(98>5CCRl zJ<9qHv87z$Se1<6ks5$?jZ>|akQQKs)d&41^N{Mdz~FRdc?wt#-ibKi2o0k5$?gnR z1(;u?Llj(e&mc%hO4uV{3OE6bFJfJ^zPzK|x{syIZFLNxGyraR?{6p^GJ+-yZLsn4 z_*xu?BwK39nqvR)k-mY?=>MO^+GbL4B}ciZ-tjY{AwE&2mFe@hG%B(`tp`1CQyM6b z=+(3P+Ie2Sei>Hb2&Zq07dM<&P!PgohzuMgms8hw$N25ExQ`=1+=_>E;4+?8H;@yM z!;6jJS0Vy$RbaaUYzObK``C;dLT|x&Gfo}ussJ`+gCRIn{+=N!Sj-48KfIn`QK$icwAiKa2#UH3T~63UDxAk_%){PjbaCSb$ zy{|HZkwDI1dV^Qtm!F6Ndhd98I@?5Kld{x5bXo!l;aju5KgQPExOgNhD$H1Ff`M9U3I|yj|{M~N>UX*i2r(H&LGI=F42y?2M zr_d@Q1(VyUV@js?wdQ*Uq!~jcGQ+n?Urw7P;KTu}h-3-l9$7)Y*ztEQI31!&_0zDz z12C_@SSWY#Amoq>OujyE;-M;Uf-tm#>Qcw?jsx{1X{cQ zPGw(T&45iWH|i81=6WGV$tys%)l9jF2;moq=XCvK0Z~{_63?1>%lllGvysTjN>Yb* zJ-$62?Zyb?3oU?}_Z*3-!T%%|0Di_qf*63r?^Sgg$W8^VHWE$ zQ@gQOfhH6Ke&Ra1Th~`Q-J>T2V>Wou4ito2Y!UT7J|p~RzM);LKxly-#B8UwR>4c$ zJFCDf0k*8zhZgAY-i=A>VOP7fN~dXO;UuF0t-JYH&5Uj_D&?@RI60R&DF-Ns6$QU+ zkGF5%9+z1*#ru*NFrEIC;TL_*tyqEAqKM=>dh{8&&Xjgwu?Yyh#`6Q#pQ4s%?P6RU zQ+t3^c@g{64oropYeOmS)@U7QeQ`t-3EPp#Ghj6kNj+tbeoHda|566eb9gj7vvw0^`Dn9gerE3dNCaWRJ-58pD0Bxx*oyzN?eybY* zQ-2Ta5@1AIf83Wto>~zzRJ~L4oyFPvb1)y*uG5v{fP}NVe3Ulr;@0$y0{iF9Fib^9?Y%7h3_?)0*QBXKo-lkKTQ@11aU$tF7JtUu$80^r(W_OHc$moejt#1L zI$F0SFD!YrKsspM?N#v_$IB-d;Ke&B3IHXjeD9@S*7PV$pRqV4D^cG*l~ za|ITevRflVaNzv>U^;#nuE)3E{`mH=^78y*|F2Hu@qtxB*qOW&@9h^xN8&iBB_PG% zQ5a}OS_a+r(ferZA8l*3(XLeh_83j-GN6iGcPrmPK!b#O2n+`__R$7xT58A%pU3|i z+n6Rl)#_z|GYe8&n4xKW&I9YaSlNJYjN0^e>gQ_*Z^=#``X zP20EMK8xutCwAcZ1~7N<7h~MMguV1lDB(m!%YwiHFK@cA#C3Hj0Zd~cg-IXi&e0>yVYt&q}CKBA(! zVHm|gO>5<8Y@R%CO2Y)KVPF5vja*i7kW~A!4>oA41+uj7g-`LOa_~nMSW7;^Fh7qe z(U}qufoWGESnI8~oqKwq|K;&l5CF=YsvHj7h8M*=0t`nVxIk*TdTd|*Vi{@1qX8JH zO;MSf%(sQq=L=ig-8{3b*DF^DbKdM|3@k$JAlKOz--9rsM!dDRj<(ZoPD!_V1-0w# z+i!pT@fMapUow~p(q_j(TP^@v$&bXbBQ5l<07Q%dqI^E3a{+}7X#w-M-qGLp?sInl z>kZh>FQ0NEG%wCZW*ou^S@pboP*c4BH#zLOHvShGX0ri6ZPhp50O|yAn0N0fU@R-x zqAM^k*Bi9WYCOjGvtNei~s>(IAX(=S?{jEJr)4XBsA*}`}p?b z3K-zEtJI{G99r};k=ZN2qOT!fvZ&(i5~Hj+HLPWB=fKjc|fAamu zW6~cnr||VkUd7&K?0wULHhDG1z^08+G$D7U)_mJqiwAvlB6q=f`=9L4jZN7TW6~rv z)7hb90(x%g8C~V(m!Sm+=Pn=wRaANQ=&89y2xdBD&7jDz+P1u36(|rocaN+}qB3-6 zc>^+3-~}oA8wg3qbbg_Qb?pwz|NgjS z67vG!;GalIaT7aREUf9-d&KErUn9P_ig zZjFZ=s$Lddb{kmE+b_u@2r%%QgjC~gmT`cBBm6a{k*Y2T8V6Jt6~v(&u$vjoG&c#5 zrZ$A$;}bjOk|+^2go(<%m(r-r%9iHotHJf{dVKr+kAM8uX2I`V`6&EUou|iB``Fb2 z%#LE06k0dgOen|wW;L8=j8p)coSR9)n#aE@YnHaZjd2u!pWhx=a7}Cj_SPpxX@Hdt z98I#<(tH!1SHOT*Qbt5(*kLq@Tl2-}?%*)-api(hCVp!OQuN|^-Sq0o{pb0mQEYR& z5s}9Y1O~YbH2jvP+1@jvPv)i>>0FkmSm2~O=+$*Z&1L&5q#UTJeJOllT zyNG!Ne|XCv_P6a9sV-M?jgK-qVlrxd(@J|8!( zoXlDyp*FpUvf88EY5aH37yfmLw{IN1-+(yZn_2<=Jrju~!8!4fOeA6|H^DCmHM5>F zFtD1{7(j4-R#!4(KKTCEpDld^czpZq_dovFEwCp#@M|4_Gyqp?bDbRzWIv3e$7|@T zVV7X^y8_?~21!5*c4wggban}$by#3%Mc==@?G6B&cE_;eLqbnjgv}7YU{-)z=M~vq zzGyUYSeDE`w%%%OiG#xYR*n6LJHXL8PD}FOZX>DUWtgo7O9vMqu6GjE^Yodvkjz({ zwmgusMA6&s1Q({jgHPAnTWB#w7MOv)8$3pxWdP> zq#6Wg6%=eDe6V|tG|#@?=oQ(~ht5g2?y#x|II_E& zySqgy2!6>-K#KH!0S<%7@A$(ZShz+MuwH0|{wPi^JO#Zo!4X>kVvB$|G!WJXb!Z92 z4~#?`1adxzqh?WFV+C8 zLx8ja!0i98Yf@Tt3ov?s?F=Osix&V_3$RW+P5A~bfdHZB*mDdw6`?y0`0T%bdpw*6 z6lv(=+_bKlc+?iakavB$=qJvh5y72FT7Y?wJMdgzspD|<81FEDj|jpziXDDN;^|2m2Gf35Yg@bD9T5&RBiYzb_2*!WEZf75j=pQFsr}!g z5wsmA7ip2IiBP#rVC0OO4S7MJ9t}3sK;M3A-6=HvpaSv62tfJ`KmXUI%;RR(9Knsy45>Bm@45>3l`*?o)_I5RG+>>r%{#_e|0B#cyT7dZ` zBCFFXg@+LPotQr51U=Bu`*_U!%frwb8yiWAvV@*si^)W?B>p%?0Lem?Xt1$7b7yXb zeu3Sk&Zx_P-m>zMuLQ=8gwmUJp8@#w)qv)uDdAvIVfPcceiTb<<`*`&jQzTcqZ1o~ zw&dNIQi(Rb$Om!M;N#=l+hPJc^M-K&v<}y<5)AxH-M34!T#cDh5?UA~xuXK;iq3!p+l)q# zl0?6n#Pu474t|aSC}Dt<)VDhTx1s0y!cJumL5e6$6W#dJZO=Qk;FHNBjV5FTo;6Bo zYWK;&hiN1+iPx1OVQ_&ivvJfFT}OR!VU=XrRjLub!Kwg2W1F9sw&3FEl0{XWwfY$O z#tD;wCbML_E&=k!C_lU^d`gv4gXfqGk&ZvvIHrItQe0Wp<7@W=UT{TXzN!7^eF#tK z2o131EYWA$c^QKj1td=W@Q>h-u0c=~e!gfDClw!-i@4|@`ozjks`?R9;) zzC=YEI6sqI@G_gKX#d@D?**YjY3f~-CF>`|thiwATc?Dh^|1_xy2KI_1?+7b$@J|d z=YQBBDF}CBP}9I~1ecN~9JvJBZelAHU%DRGc2?Du?rjA?ZU6=bIL^Ox05TM&0x&y8 zDFDjfk4Xmm(hoHt_^}#@M3b z{ThceXL;a>j*ceV^EqBLl6|iN80Jxuef;o!!cWn*>lKgWp;q@^RtCB_#Eo-*y`=`H zzF&!uA}5JSCdtc)hnBbq-ple?7aT8KU$-&gRAb^f1PWoG{$&+9he zigeU<`qh}(b@BB5msPRyDs`MWwr$E->0knFsEONn9~z_F*73uEA?~v}nt=Ub^r;=r z-W|!yOhB+lKrmMYSRL+!BYn~#!ZFMkgGmrz!Ff5hfUmP|%6UEPSoXezqU42^fMouM z^!Ogo!h&Z*-#V8W{?Uv4YxZl0r3%#CbFV#;;{y6xCDM9Rzn)eWgqO9=LA$nFmn)5+OhIe;9ohy z7P=)`*KKrvj(@Yy6RE;m1cMBCDG1Om#y&5ULOw*Pc;+DBo{5)fPM?@UUzgM52cSBM>gBoFi4)ir zxVegl2*6;$n}`BxYBo!B1h`as>yZ@LE4{kiDTsQTBr-!Smy7nlY@`8j(GUFI$0xSW8s{HK$`@ z9lOB!o0p8gjt)_Gsbyl({0BR@U=Ox9!EmZ(-_l*wtLGHWDvTGzB~vmD6k=U(&I?0` z4q7X5Gwy;lep>T9yd5+q7!20qDBLHDNj39f?QPoOjtK$_Xibg)G1?w+1X$59i(;@6 z4-L~=QcWjC^e@hd3~@qq<`ttgV_Y!bhFwVh4^0-Mvi!GXjj_R!*J2o0&{t6j0uF+7 z{pCg3H%+#VKb9M;$}m#~2&=fHjx9hNIDmkN%bZwNmyAy%(+m)KfOAGRx#uFsGP9ka zzMa3C4k)*EYT>qpjDcY5@|0Mgoz9YA9;*!`L9B9{=A(f1#w+#RKZmxUtnJ8Kh#asK z7aBb~0zcfx1+a8yLOWi6jQMhp)|WrN0n=owubo4olzWRn8%*g zc7G)-6v4A`C&8ajy?Cb2^Z)GqDPX4bI(^>M!+LAF!HNV_eS=RrKizkA|7sMQO|-q$ zNWnNf(iZd~j?lQJ&Q^=Sjlki?3yhzr5b1X32p+Eb91od^XI;7F;_yE%CN&@!gDJAM z7x?lNE$Pc*WQ{uAm=c>3rW4ej-XFJRjN5Wdv`PZyNAYqU%?SiW8rRe8MmC3Bz7kbe z1IaB+Z$+3O_9oyt$0 zC+Ax9XrdI!xB==2nOb} z*Z^P-vCt9#cCXsYi*UMvYQJM)9yKD~!q_JBXyIp+Irr?2)R>1~iS6^}AZ(lB$|Bm* zu04nhKl7)=a9zgTsu4IGuxJ#|+?L3jY6`P_sk8$0C`-h-b>1IWvnHo{_)eez`Tdw~)1~LJc+WvzWp#FcKl)+_h z2wsA?b)-m7A5V+cuedi@^1V8YDd#$Ece>pNVg}d6l5>c_$lIA34K3=t#w=zM8|9RX z*qlZpg^0PgdG3BgxHEVHcj05@N;=Jk#s8O|8W|-az&QBgI%ofb|&- zX4yaiRu7QLgcQRrs3FL*M8ig5Xh!S1qPP8wK)fczcwEDbvqw@v8uEi%9RI^iaqirN zYj@BFK{s7!W=3&fwqNof@gF8`r;oxlQCl?jwl<`Ph9e@kMfL({-Hijr9I;@4j^?cl zV%T#==p!DslLw=cPn~*C1%xM2e)-L^L#HjbA3_TdVmJ5sZ<4<>XU?0?jHdv_N7GzGeX*=bGGIf<7Ic-;15XHwh#TDTNA^WcLmPqk_`ilHjUC^Z91uj1SykuEj_gWK>f(J#W2LK zoX30>HCof)I^|6{L=MHm)8PD)z;pyjdz5pi0Ffo?yF+mbe0|*8R~0t;1Larh&fEWW}O%Mj$&2-(-vUX{c%(V)-QI5 zefgnnSLDf77{b2fD6?-+KPyndyQOeq$<%hC>Da11lQK}`pvjYRiGdp0$i+0@cK1nB z75V!9TwgeYskt;W0fk{*jYlcIN+gmWyFYKI7qaLc`$RPtb zA|YVW?H)Wj0F)}RbnnhJ?qmdLXXT*~VJtG?^-ZT6S++MYuf z(wZu(2ZYbdWRKsB^FSN{22K|hO@q&b6I-EX_w4S#@4OHnqMpt1Q!-j|Vg@ZnMTv|j z0$@AFPOcDJW@tS$_As3b^^q4+T@-tW!BmKv4V++8FtYnBh7{p7d1@xnU}0hHLjYX6 zzCwq%jJS-&zF3`6O+1q86v3^0jSWEI5xU)X_M^XS%7Tc-hu!R{pC7BwhJ!j_;(ml? z6`8U&3NBF0S+47DnPSe7E5&_{?mU8MbhK$$Jm5>HE^R+5#3z1kS`hdv6O+`A)K)<{ zi8uqe`T8sXt*_=2`8km{b;Z;#sdS&+6Fk3C3IP1!;JKd6Hq*Yii>^E1)M;?PtMmgB z@|g4~0SYl$;EUhsQT?TnQSIuAPbjv-yKmv`aSa5G-5N_!s{1h7=+F*{Yk{<*4uxy3hHzqca39)~fLW zm{>^9Qv7F)^S%>xI3IS)p+;Qxx;Lizgm$D3K@FHf)GDNlNe9D%6yn5C+bC;TKwXzlRJAvJZ26x#BU)Oh)zet5N&+q zCZq*xE(xF6U{&z}7G4*Mpyl&hFl|z9U4eO;2-1g}>3~OP6QJ6vMR+Bz?p}EE)n_o$ zkEQXwarDOC4+|K`kfP^wd_`PmB3!6faf^sde2aNccp0PZM1Yx1$PPnC<6Qv(MGD8Q z4q}W~jad#tK{Xl6&z9w$BFW>OwqLMRUXOce9AO7BNHL&Y0!#t6_0!I@3LqfA4 z($6K}GzCZnV1ZuX(cXuZZQp*u=i|Mn+%3a*`$At2g!R21y^?EUVN+AJ-4)^Fj9l5d zpc0>>HP{VFT&XEL>V|G3fHNf@>pSG5jn6#h6(%OF6ig z3rq$BI0Zepr(|qHrTan+E5o_@HIHo2kTBpycV2Ik{&AI2#D=0Q@*vVWr)fH^ISw?aihdZ(#tHIcr?#-=A7%`<`i&X zt3C>8#*@x37-6Sq3RGfp)2N|s4Qgf=Rql&$hsn3$_&=1nMspnjQr_(_8JS4E)S|NB zz{#PzL3+yKZ%)q(yWdYsz$TrA*H9%L+Hk^Yoa{mR08i18-5WR-BY(i~$p)G*ifw}{F zSQaf7nw9B8Bo-k-Q|J2=mnrF`LqWY$1Dno2&-si=z2vG;p}=-ML>~}TZ)g^S?N3WH_;K6}=k~7R@Rxa*e4U z!b!i{Nto@@3Sd>Tn^sAEwzqnN7CpSrt7)D8ASlpb&9NGYe|Xvr){ofXAK z`n3koIR_T0ET4(4&uBxR^n&S5L~jOjs6!bAyWCObh~<7{lOe$fhs}C^`Akmjv=XeP zvO^cp*KgN$$&}M{=u2^GUvj#jn1ZobIv`mPgv`}^|ND>RJOG!cWi1Fnb8isdPJpmc z0fem*7K#ifUL(m%-=x zYwlIcWF0H^PkSaZ)uC}Oh9O?M+CrdCRr752ZY$Mhqom! zPBlKtOhWUYcZX6varCV>n{ZjBLwQp^o79K?leJ6^)ZH;SQUOd)8b!U0q*+)MU^s#Y z&#!19xV*(E)G;?Z4QT@qsRkuqD=k}94<1*rJ~}fDxcAq;3iK<`A72S!^hl|6$F7KK z3+d2Ub-;b$W|CEiWyINj_~&z)ThEq`o6kAfMluAXcF^U=c59!)2-W%s@XU7EZhxGd ztQ4GGcYy=HJJ=x#To+A!%>a=~91UL~Yrx@zWl{RO&0Mxs;a!?FbyrG%B(R8q_j#hx zm(PmNPTNe}!q8!XEC9G$yQLAX37R>Tg#6`U~I$^?)VbAB=jM2~|i zs}SyeijAI6dlBYWMc55{0TE*PK_~#x#ks^o?HJotCgRw)5h?0GT z%5#5Hu6CljKTnAV(;y%50tErvgMN*%=+KbLI0 z?1*50e6k^~to^Dr0G-r9FU>ApVRR)6Sj5@?AF;Y$3|Cgjg{)$}Su#`j;-0IBY}*Dq zGDoCLo!iKR3~Vg;quFn5;edU0v6YYFyePSoVBIVt@pfwSTRE&@WqKpiPGrtlhCfvg z9A>S-G^%hN2)oHZ^$cJX68)owZUIkku!C(MN{*G_wDcr_yv{Gh+__Ok9^qw8agwdG zG2oN{w?Ih0?L~ztf=tB;^x~maWNo&@0K{j+_(=Lap9+CnyaiV7^Fbx!YqP0bvp#*o zRMvWK<-|-5pOgU9=_H?D%xm`Tq5A{x)5kA55E#gv-{}`;U3OLM{JAM)&urEGo?Kp_C}?* z7lC_G#wF9Sx*V2jy{`th1~ax_Kl~?Y?Vff7b&-qSrAm*5Rcxw?8&+#Jd>2^+)-d2m zpS?XWM|XDAW6cDHMdf&4bBfh`=AI_jE~+;-ASC;6>pRp^8}gz|$CULi9N$y!ann=R z-y4;UJ;TN?HI!=il1aQl@l+s0RgRPs7iw2agKi`J4YCV!mU))^=Awc=u&MfS9 zr!Ni$3fj8$?0|FEu?DpKRkaZPg3NHtyXM>Fir-C?JfgzEItE&m1p{iCcf4d8D@J_~ zsKAihnM%44q z)zlT(Kkeww5#@X#nt6n&_cod)G)6&0v{8d zf-j!vumS}Fs<*qe^0K5j*2@$(6vB;-6oDR^Uz*|7NrU1}ecM*Sg((;m;&2da$k`cn zpt->P7zV!DuAF1*}~BGLMc6=fd8DHXuAJo4mZG4X!$A-2o`V|AwGHqtcsJ{iF} zqg*oz)n||0bpKsl1a5-;B;lbZTbel72GV%CGYX&O*a9?~8Haxs3(o;x$Zs-FA1+UG zdw$-gY5TK|eXSo+Yge3s$;LCuoB7%h5ZtVE?wKvXWPx#6=(2Fm-FL@kiS@QgbVfVanJE{0 zSuVjNu)kCR?#S@3edEHEB8!{5L_#Je2*PepRpVOTgEDqlk={9v+KXM2hJ&P7MkPb7 zj}y+_Y1TnG{G~03Zy@eAnfu%d7{9rt{+UTA#1#2PgGS5_AY%Z<9Bj!)X%HE2RRi%S zj3ZFXj?s~=?^IIMIy5y(vLlT#MtS!Y<{P*9Na0~eSAWdhBVyBHcYyq9nfQ8`_*q1P zBh?%z@^LAN3MV`m#tvZ}R(lMDy0R}>MhF0Q+9^>vWv**@0SZZeMPSV16FIdG=i-|m zQJXKU6Ik4tXAmrdkBqyeRtqVrA72Q8u?QXq+{)JuNTXj|4FRk>jNP4RGkW936v9nz zJw!4r64*A#`qWmaS8ou7z+~bpwu8Vf76NjPCAkSfvl!k^52uZxl-xphh!N%s)GoKE z0JvELW{p12C4ptw=hpf2BwA>bR$V<|4{REesSM+^$@g~jML4VWYFVM<<{7XXifm_Oka8FF_4|o@?Q*+(T^Phaxp5K6pSJ+cJeq-HbsMvHA=hE>`Dz zew=Z~v*NvCzRQp#wDu`?2w!226y~0%g9v}g+b*eg>KAwWQPn1h z=VkulZRY&}Ig1p&PC65o!9BiZzwG^0vd=zQ0`j#Wfc*O}9ryC4MLOrpEBHQDFtGV)p1E}jfliY4t?UuV z!rfmhp~*MfR>o;^c{5g&o%%qWAJ*%1@ao3pi-}$a!_Q76fAyT-wZ+NR`DIx;=7+)y zTSwt2j6@=~LU;FikErNj=C2eOGeq@{zrNDG>PqIrxd3q5OpOc{kgq;d_?6d&9hy8aFTucH?*kHm zsvf4Vre{Ay)W7h6cbCx}>{g|>u>a29P11v7h`h&=+}?De?knEoT1`-r4%ATYu`wbl z#-Aj-<c5wbT>i^w71Hr(LckFb8%(N@<8v(p$Hx+IXH~3EN`5Gu;T05|5P`_P zej@4dRz){iXV-OI`RYuad=%yT3?NTolL#r#FTJ0t9(Tnhj^U_oe;n^zllQ7?7~BkL zr1I2mI($UI$kw)>d9#G5oF-e2Y@A_korgkgJ7tuhnFdG?c50bDwsU4z1qefr?p=6z ze*}eq(-PV?7%<6Nk7g!;Np)sIkjB15qtPFNPTn++3y_9?c5UhggdHl%yI{wn0Scok zUL*J8Q z?OPgY!UhqDk`W~FfhwSv%~d1Xq}4;V>q&cY9-%ZQ#0dEai=u5YY4D-k%;3!S6qCaJ z4`LYOd}AENP|dgt+gJH|29%hc^JhCJUCMB zwB$1&JiDE>|bVfMCuUOfT9Re_f}#eio5WoeRlF!T?lgh*P%uVwC{`HWNvD>tdg< zd`b5Rpy!{(H4Fr1PsfOVT3%y1M5u6ZIp`U6k|uAaeS1kz>xW2vGlYwbfW^M1hP;bA zQIdc_Jp!703+V86xGo0kfqI7NfLU*T?zVKsR<^DWxpst31mJ;w^3N#%h0jiksq~lu z7w~!R_P8`T6VPYw!ToCdh3_U=iVn63mgkWZrK!ddFBi^ea0X?_xR9#|J^~aNqeK{H zSCfT?4u{zb-U0-H(QOK1aJmTLrat+lAt18C9vh!tE+|VNs*b^YhXthshft2&XNu5>sw^B5bb# zLca_xS!7MC#aBj#T;}<$H-a9bFs0Viw7|$`a}ZFTecXFE6*{U!RNr>A`s^`Uy3(_5 zhQ8?q!Q#dFpSkr#U}a%jeZ%l{2Q_1owp&Oc^;o~{R7%ZS*$M6z%|Dh*E&lP@yGqVn z)1wQWA7?PCn~Lu*!z~oi`+w{2C3l4)Jt1QL@=}w^VGRH>>sNK#_5`pMhs$9!S;2%% zI`A_p5JRF4y#lDVl6c{E0f=gvXBU05ESGsL;#9pcMxC*gx4m|Y`eY2)Z#2aH@@5F2 z2c-n(EsyK)_)cnmPnRh2?T6#$JKd&X_O{QSSyX;tpS|~IfBL~SO`KM@4k2uieJ8Gs zFZJxt=kxh|j<5A(XIHoDUB%R&S3gv*ocC~fP znfB5{?T$)|^**rMZUMGdyf$R_plLV=TrT}sIfD2-9o!(QR84mWQ>z9`1)z(1QgU-l z6^HGxg3{jTEAD(0sD?WT1%1`jD5i^|RHaACPG^XHpg*n`` z1p9r4OS0K&);WV&hQ8?D`}6rUIscmA)7OW%8i2kp)v@<7Lr3rZi6=(v(}w=p`}6sH ze$0dG7x%tt8_dvp?>j8YfVmps?y5#PdOn{Y&yVNFC?=hq!zjdB$7qtr@NI1H%(K>d zLqngBSD!!bh$cI>0OAjH-lKum!|DnSF3veZ@v{`ZyzepgHu1#u-}Z})O!bhwZv=oB zBz)d-jt`+T-6M*m=f}s#Gj#y-2kwn#z!ShU-7)~Z^$iE^z0|WmKV~uM{mFTPtwDmW8$oomz5PUp zuUiG+{o`X+fJaQ?W+pRcj5UhHqTM(YwbrCiMx8LzHOKq5pa*>*CUX%bXZWzXkvMIb z+YZ%JEfVi&fjb&2yq>mT^VzaL;C4?LN_UNzqLuN**)4$qj*o`t8-d}i{|41P3mS!a zo1Iaoc+>`Vw-y1=?Jt|nz3_bnkX)Fx^h9wJRIFL;Nx2e|&ufXty5Nj2XYbFC_n$u> zEDC^}{{R8F&0uS7AJIp@uBHWG-nJhf&yVNxxw;9l`$p@Hm+jKw$WW7o^XLGceKr$a z3&7YRknLjAl*#q=8$tmX59a%*0MY@NVv4?om&x9>&A0YzH+KjlnNrFC!&>fW)H_;N z__1y7GK5W=h#{-MPP}d-Sstlzdb;6+)){CFhS`w`>jVa8vlbFpv9J#jSSUgUsv$VF z!f{WV0(7i&Qw$ntMRaiNnpUU_GCq4Km60bQkE1g7ZIRME{#YsiZk>UQ$IzbQ%(ARU zvty|KH!oetc$5J4=g0f|&!1264_lC(!)-kn;?7L)Spa(PynTq20vT7u)vti!i+SM$6Q2F~od4Zp{N%s(YxNAxR4=|W?5DNO0{q{9zQ2Dw`@jI4 zC??tQkfu_S5;o=KSS>nn1;#9a-tjNGNL7)Fo$c%_0Xy(m$;&gK916XbL6Z(XE;qE( zL;XfmDjI|JMHI|~8;7-|KcZUQxV=PVI!GUg6js5#zM{p52Pcyt8L7(K=GWnAZc@#_ z$2wO^>4})&NIl)|fn)h&Z2O&Rvk5p7s;SRK;Ga}*Q7i!>%nziwSf5YG`i9KT-ap=d z{`m3z3eata?T-e4zJGsSSG%r{>$)CI3&0rON9X?hn1egnZD*Pcm%Q6XUpquIJU-HJ!KN=8*dV-7Ye-pLqZA^L?}cn+Fgabo3A+(qMuc<{|14 zgU`Ecp=0C1W4XZX!wDGJ9F@oLTifi*=)@2$fz}YRHjkjQyb2}9EiptU8BR1(EUDYf z2T|~TqPFD9fLEU+Y?9VqA$u&(fvLpDw@Z$BB9w_CW;D&)_rKf^so_AJi36jbEcNsW z{2O((qxJD|Iabr04v?+7$3w79!Tm=*V@ar*ql)kci~T=JDOM-}`@wlW-+%u2{Ph~h)H81B* zIC})HoLqyH%qtG$xdZs|{jcj18i)h9Yw4HxA#41Wl-O&K`L0c=+L!m--;G@0{j;uz zX9dk)O67HvQKN1X=fd*2e-7*t2CKZ-wqmP=;t3KDqjLk=8U0HsO)HengR9|I-_`=VFj}2N~j=VkU{jAs|ukm;~W25k9E^;ll)E zPH~L5Hl9zXh8DHZ!3Pmq0MrU)D`>hkcAG^TW!kekx%b3xzxP}Vt{0LFtiR_Xy3_{r zY?ok0_UHSL?|=Qzc5xlpYGO5M0BoZ`tG}Lljxma>2e4NFEdaLrhieglTN3k?0V+O; ztO6i&#DbzC^xF^`fceor|NQIw&yQzU44?{DL{i~H(!Z-UZQio+WQc50#bK+05|HO^ zg^w}u*k?!bt+=>Yr2ytDU&HY*aq}7(705??U}BKY8LeyVMXUe_(Z59?W@%9tLr-gh zC2`{tu`vMq+#Q=@lAjWl5xkwY*^Yh=OMJ*et|x2@v0l-0$@`E*sP9{BZ3-7VoK`QlbDMBA zM*x@$0NUx2_wnanKYqMF_ZpBzoNmg~sI0gO*m|!3AoBXtpq__u4obh>YI95!k`{4Y zk=ad^lO-F(UeNqZrHhhUhRBGBvSrRn|LnpbO3~Sg-arXliw|g5*pZN(K9}s z6WJ3%(LX5fh_V`D=75$3AZXZ-y=StQ34`%_Rex;s6?3auS!7kTj0 z*7y16pMU*)e~vXEX?8c$`(=@&>Yv#c-l!oN2yrZ3FV#cbzcKE-&5^r|UUYFhoH;)+ zH=dzx#)`+5MEtt@pInVGuz9~G4~?(o)=u@M+h=&YV4q0?P{kXxtboAbUqR z{*a#{ge^erH*X}j78P#2pV1&JA{DA@KVEPi`(s*^!~U-nd9gp=fBg05^=x5L7^U1| z2C^I2<>V#gJ$J+NQcA_`mMsvsfX_9&Ek!=~C(&acDIL>(4LdOEj7<~fhtEQP{TW7p zD#L;rYb#DCEWBP+?-N|-s9^7I&C8)9p86w*Z_D*Y@+4!*vGzq}-b@U8dMWk;46XIX zW<9PAcO^V<*Y!NFh0jx8N)I_D5cAXq;68(cDLZ34XE`QCJM3fyv|wSvlud8{mYg8=o7JPin_dY0 z-qVMzt6(!VY{3mqjey!csLJs8ct+UvG&`-!{oZxIqP=8l(hKJA z$rL#4rIY#72;>4o#sl|Zh|9Smzikj?j`MHH8>$Fpbp@3eso*pi_5u(#coqtQu|MB` zet*MuyG z2e(jD>%(4@b^EXG3r;e5ZoPKj@V|wt2kIiSw?5|TL6Xbr;iI1WLytB_vKOq3P!4>q#1igZ8`m`MRmwwL^Pd@;lu8zK*!$G z9>=01FJC$vZf}d6ha|`G|IqVKMWBVoW@|s|CO`LA?=g{v<5|6cB_yZ}Z_+Ru{sJ(4R}&cF?7&n?dgaxs;9> zx(P*6Du66wwl2+QpXd9Jw~IfaBq&*iT8IRQ;SSK)fg`!m_ozTH<&sywIig|y%Bs45YPxwGq$Vcm;pAvWsx8vq}C8G z6I!rM!`Vj(IAd&G%%@s3T?69z22I>3OMt3ZLne$qv!N5;8;=Nqhxe!zQ+ibaLNJNa zNG<1<>pOSEat@jO$M^3)-WL%lxK#%_8T=7)isQVCta7kC&Ggk2y@?{| z^%Z6oA$Q{jEaZsBkUS*&^_y&hwvM zHjoAYw^a+SifL%>fo`biDarov6a2VBWQ_vG0s2qORV2Xr$Y@VHMDN^l_ikkaAS)mJ zn_K`=<|z{6>@4`t_n$vi40L3#5PHy7Y6ZODDC43Ok;RoyX^4}88$$sQ`l8D|!lS;l z9DPodB9wGjo`7dFX<1(5fHle)?U$v;nZ{3I{1%x9LLQ!=T+7QFfg*xlrf)`imbLRt zE-_ioZMkAY9tV#E0%78b<$)qeDhglguKfx5DjLx~Ci-`*^WV2io?zotHKd^^X|P-Y z5|n1i+SL~^$~X#FRDXUn>mQdD$$5;pKqV!uX%gaOWqF7Q&4aeTr&}G((nwPEu}D|4 z@-I>?J@LXd}Zau8SfBP#OU-a?=tb92tMeK4}8s9tx8$WqTtW z<5ZpZzT5X21NK8ep|jb-VoUhCbnb_obxgB!1)S^@n>S$y38U^G#SnMX8IQkCuIwLR z|7dr+HogxzmcRs)FI$Z5+Y6#b4Rb`6qM-4 zzEKjbMeixeEnX~UU-6)4nguo`$g=^Gt^D(;?T2LwVH{sCy}qj5yv9=RgW26@;HalW z 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 0 else h + +def smoothstep(e0, e1, x): + t = np.clip((x - e0) / (e1 - e0), 0.0, 1.0) + return t * t * (3 - 2 * t) + +def value_noise(u, v, freq, seed): + """Tileable value noise: a random lattice of `freq` cells with wrap-around, smooth interpolation.""" + rng = np.random.default_rng(seed) + lat = rng.random((freq, freq)) + x = u * freq; y = v * freq + x0 = np.floor(x).astype(int); y0 = np.floor(y).astype(int) + fx = x - x0; fy = y - y0 + fx = fx * fx * (3 - 2 * fx); fy = fy * fy * (3 - 2 * fy) + x1 = (x0 + 1) % freq; y1 = (y0 + 1) % freq; x0 %= freq; y0 %= freq + a = lat[y0, x0]; b = lat[y0, x1]; c = lat[y1, x0]; d = lat[y1, x1] + return (a * (1 - fx) + b * fx) * (1 - fy) + (c * (1 - fx) + d * fx) * fy + +def fbm(u, v, freq, seed, octaves=4, gain=0.5): + out = np.zeros_like(u); amp = 1.0; total = 0.0 + for o in range(octaves): + out += amp * value_noise(u, v, freq * 2 ** o, seed + o); total += amp; amp *= gain + return out / total + +def wrapped_points(n, seed, jitter=0.35): + """n x n jittered lattice points on the unit tile (periodic).""" + rng = np.random.default_rng(seed) + gx, gy = np.meshgrid((np.arange(n) + 0.5) / n, (np.arange(n) + 0.5) / n) + pts = np.stack([gx.ravel(), gy.ravel()], 1) + (rng.random((n * n, 2)) - 0.5) * (jitter / n) + return pts % 1.0 + +def voronoi(u, v, pts): + """Distance to the nearest and second-nearest point on the torus, and the nearest point's index.""" + d1 = np.full(u.shape, 9.0); d2 = np.full(u.shape, 9.0); idx = np.zeros(u.shape, int) + for i, (px, py) in enumerate(pts): + dx = u - px; dx -= np.round(dx); dy = v - py; dy -= np.round(dy) + d = np.sqrt(dx * dx + dy * dy) + closer = d < d1 + d2 = np.where(closer, d1, np.minimum(d2, d)); d1 = np.where(closer, d, d1); idx = np.where(closer, i, idx) + return d1, d2, idx + +def tri_wave(x): + return np.abs(2 * (x - np.floor(x + 0.5))) # 0..1 triangle wave, period 1 + +# --------------------------------------------------------------------------------------------- +# the textures +# --------------------------------------------------------------------------------------------- +def diamond_plate(u, v): + # Raised diamonds in two offset rows, bevelled; the classic tread plate. + n = 4 + h = np.zeros_like(u) + for ox, oy in ((0.0, 0.0), (0.5, 0.5)): + x = (u * n + ox) % 1 - 0.5; y = (v * n + oy) % 1 - 0.5 + d = np.abs(x) / 0.36 + np.abs(y) / 0.18 # a long diamond, 2:1 + h = np.maximum(h, smoothstep(1.0, 0.72, d)) + h += 0.06 * fbm(u, v, 32, 11) + return norm(h) + +def hex_cells(u, v, nx): + """Distance to the nearest hexagon centre, normalised so the hexagon's edge is at 1, plus a cell id. + nx hexagons across; the rows are stretched by at most ~2 % so a whole number fit the tile.""" + W = 1.0 / nx # hexagon width (flat-to-flat, pointy-top layout) + H = 2 * W / np.sqrt(3) # ideal corner-to-corner height + ny = 2 * max(1, int(round(1.0 / (1.5 * H)))) # rows per tile, even: the two staggered lattices + H = 1.0 / (0.75 * ny) # stretched so the rows tile exactly + best = np.full(u.shape, 9.0); cid = np.zeros(u.shape, int) + for k, (ox, oy) in enumerate(((0.0, 0.0), (0.5, 0.5))): + cx = (np.round(u / W - ox) + ox) * W; cy = (np.round(v / (1.5 * H) - oy) + oy) * 1.5 * H + dx = u - cx + dy = (v - cy) * (2 * W / np.sqrt(3)) / H # undo the row stretch: regular-hex units + ax = np.abs(dx) / (W / 2); ay = np.abs(dy) / (W / 2) + d = np.maximum(ax, (ax + ay * np.sqrt(3)) / 2) # hexagon SDF, edge at 1 + ix = np.round(u / W - ox).astype(int) % nx; iy = np.round(v / (1.5 * H) - oy).astype(int) % (ny // 2) + this = (ix * 7 + iy * 13 + k * 3) % 1009 # periodic, so a cell's colour matches across the seam + cid = np.where(d < best, this, cid); best = np.minimum(best, d) + return best, cid + +def honeycomb(u, v): + d, _ = hex_cells(u, v, 7) + return norm(smoothstep(1.0, 0.86, d)) + +def knurl(u, v): + n = 24 + a = tri_wave((u + v) * n); b = tri_wave((u - v) * n) + return norm(1 - np.maximum(a, b)) # pyramids + +def scales(u, v): + n = 8 + h = np.zeros_like(u) + # rows of circles, each row offset by half a scale and drawn over the row below + for row in range(-1, 2 * n + 1): + cy = row / (2 * n); ox = 0.5 if row % 2 else 0.0 + x = (u * n + ox) % 1 - 0.5; y = v - cy + y -= np.round(y) + r = np.sqrt((x / 1.0) ** 2 + (y * n) ** 2) + inside = r < 0.5 + dome = np.sqrt(np.clip(0.25 - r * r, 0, None)) * 2 # spherical cap + ramp = 0.4 + 0.6 * np.clip((0.5 * n * -y) / 0.5 + 0.5, 0, 1) # thicker at the exposed edge + cand = np.where(inside, 0.35 + 0.65 * dome * ramp, 0) + h = np.where(inside & (y * n <= 0.02), cand, h) + return norm(h) + +def herringbone(u, v): + # 2:1 bricks in the domino herringbone, turned 45 degrees. In cell coordinates (x, y) the brick a + # cell belongs to follows from (i - j) mod 4: 0/1 pair horizontally, 2/3 pair vertically. An even + # cell count per tile edge keeps that rule periodic. + n = 4 + x = (u + v) * n; y = (u - v) * n + i = np.floor(x); j = np.floor(y); fx = x - i; fy = y - j + k = ((i - j) % 4 + 4) % 4 + bx = np.where(k == 0, fx / 2, np.where(k == 1, 0.5 + fx / 2, fx)) + by = np.where(k == 2, 0.5 + fy / 2, np.where(k == 3, fy / 2, fy)) + # local coordinates on a 2x1 brick: the long axis is x for k in {0,1}, y for k in {2,3} + long_ = np.where(k < 2, bx, by); short = np.where(k < 2, by, bx) + gap_l = 0.03; gap_s = 0.06 + m = smoothstep(0, gap_l, long_) * smoothstep(0, gap_l, 1 - long_) * smoothstep(0, gap_s, short) * smoothstep(0, gap_s, 1 - short) + return norm(0.85 * m + 0.15 * m * fbm(u, v, 16, 5)) + +def cobblestone(u, v): + pts = wrapped_points(6, 21, 0.55) + d1, d2, idx = voronoi(u, v, pts) + edge = d2 - d1 + rng = np.random.default_rng(22); tops = rng.random(len(pts)) * 0.25 + 0.75 + h = smoothstep(0.0, 0.05, edge) * (0.55 + 0.45 * np.sqrt(np.clip(1 - (d1 / 0.11) ** 2, 0, None))) + h *= tops[idx] + h += 0.08 * fbm(u, v, 48, 23) + return norm(h) + +def leather(u, v): + pts = wrapped_points(22, 31, 0.9) + d1, d2, idx = voronoi(u, v, pts) + grooves = 1 - smoothstep(0.0, 0.012, d2 - d1) + h = 1 - 0.55 * grooves - 0.25 * fbm(u, v, 12, 33) - 0.1 * fbm(u, v, 96, 34) + return norm(h) + +def carbon_fibre(u, v): + n = 8 + x = u * n; y = v * n + cx = np.floor(x); cy = np.floor(y) + over = ((cx + cy) % 4) < 2 # 2x2 twill + fx = x % 1; fy = y % 1 + warp = 1 - 0.5 * (2 * np.abs(fx - 0.5)) ** 2 + 0.06 * np.sin(fy * 2 * np.pi * 14) + weft = 1 - 0.5 * (2 * np.abs(fy - 0.5)) ** 2 + 0.06 * np.sin(fx * 2 * np.pi * 14) + h = np.where(over, warp, weft * 0.92) + return norm(h) + +def chevron(u, v): + n = 6 + h = tri_wave(v * n * 2 + tri_wave(u * n) * 1.0) + return norm(1 - smoothstep(0.35, 0.65, h)) + +def ripples(u, v): + # Rings spreading from a few points on the torus, fading with distance - rain on water. + pts = wrapped_points(3, 81, 1.0) + h = np.zeros_like(u) + rng = np.random.default_rng(82) + for (px, py), f, ph in zip(pts, rng.random(len(pts)) * 6 + 10, rng.random(len(pts)) * 6.28): + dx = u - px; dx -= np.round(dx); dy = v - py; dy -= np.round(dy) + r = np.sqrt(dx * dx + dy * dy) + h += np.exp(-r / 0.22) * np.cos(2 * np.pi * r * f + ph) + return norm(h) + +def hammered(u, v): + pts = wrapped_points(9, 41, 0.8) + d1, d2, idx = voronoi(u, v, pts) + rng = np.random.default_rng(42); rad = rng.random(len(pts)) * 0.04 + 0.07 + dent = np.clip(1 - (d1 / rad[idx]) ** 2, 0, None) + return norm(1 - 0.8 * dent + 0.05 * fbm(u, v, 64, 43)) + +def perforated(u, v): + n = 8 + x = (u * n) % 1 - 0.5; y = (v * n) % 1 - 0.5 + r = np.sqrt(x * x + y * y) + return norm(smoothstep(0.30, 0.34, r)) + +def rope(u, v): + n = 6 # ropes per tile, running along v + x = (u * n) % 1 - 0.5 + body = np.sqrt(np.clip(0.25 - x * x, 0, None)) * 2 + twist = 0.5 + 0.5 * np.sin(2 * np.pi * (v * 12 + x * 1.6)) + return norm(body * (0.65 + 0.35 * twist)) + +def stone_wall(u, v): + rows = 5 + y = v * rows; row = np.floor(y); fy = y % 1 + rng = np.random.default_rng(51) + h = np.zeros_like(u) + for r in range(rows): + # stones of varying width along the row, periodic in u + widths = rng.random(6) * 0.6 + 0.7; widths *= 1.0 / widths.sum() + edges = np.concatenate([[0], np.cumsum(widths)]) + rng.random() * 0.3 + xu = (u + 0.0) % 1 + in_row = row == r + for i in range(len(widths)): + a = edges[i] % 1; w = widths[i] + dx = (xu - a) % 1 + inside = dx < w + gap = 0.035 + m = smoothstep(0, gap, dx) * smoothstep(0, gap, w - dx) * smoothstep(0, 0.12, fy) * smoothstep(0, 0.12, 1 - fy) + top = 0.7 + 0.3 * rng.random() + h = np.where(in_row & inside, m * top, h) + h = h * (0.85 + 0.15 * fbm(u, v, 24, 52)) + 0.04 * fbm(u, v, 96, 53) + return norm(h) + +# ---- colour textures: (rgb in 0..1, height = luminance by construction) +def lum(rgb): + return 0.299 * rgb[..., 0] + 0.587 * rgb[..., 1] + 0.114 * rgb[..., 2] + +def colour_bricks(u, v): + n = 6 + y = v * n; row = np.floor(y); x = u * n * 2 + 0.5 * (row % 2) + bx = x % 1; by = y % 1 + gap = 0.07 + m = smoothstep(0, gap, bx) * smoothstep(0, gap, 1 - bx) * smoothstep(0, gap * 2, by) * smoothstep(0, gap * 2, 1 - by) + rng = np.random.default_rng(61) + cell = (np.floor(x).astype(int) * 7 + row.astype(int) * 13) % 97 + tone = rng.random(97)[cell] + # brick reds of varying warmth over a dark grey mortar + brick = np.stack([0.70 + 0.2 * tone, 0.30 + 0.12 * tone, 0.22 + 0.06 * tone], -1) + brick *= (0.85 + 0.15 * fbm(u, v, 48, 62))[..., None] + mortar = np.array([0.30, 0.29, 0.27]) + rgb = brick * m[..., None] + mortar * (1 - m[..., None]) + return rgb + +def mosaic(u, v): + n = 8 + x = (u * n) % 1; y = (v * n) % 1 + gap = 0.08 + m = smoothstep(0, gap, x) * smoothstep(0, gap, 1 - x) * smoothstep(0, gap, y) * smoothstep(0, gap, 1 - y) + rng = np.random.default_rng(71) + cell = (np.floor(u * n).astype(int) * 31 + np.floor(v * n).astype(int) * 17) % 64 + pal = np.array([[0.90, 0.85, 0.70], [0.20, 0.45, 0.75], [0.85, 0.35, 0.25], [0.35, 0.65, 0.40]]) + tile = pal[rng.integers(0, 4, 64)[cell]] + grout = np.array([0.18, 0.18, 0.18]) + return tile * m[..., None] + grout * (1 - m[..., None]) + +def hex_tiles(u, v): + d, cid = hex_cells(u, v, 7) + m = smoothstep(1.0, 0.9, d) + pal = np.array([[0.95, 0.93, 0.88], [0.25, 0.55, 0.60], [0.80, 0.55, 0.20]]) + rng = np.random.default_rng(91) + tile = pal[rng.integers(0, 3, 1009)[cid]] + grout = np.array([0.15, 0.15, 0.16]) + return tile * m[..., None] + grout * (1 - m[..., None]) + + +def wood_planks(u, v): + n = 4 # planks across, running along v; staggered ends + x = u * n; col = np.floor(x); fx = x - col + rng = np.random.default_rng(101) + y = v * 2 + rng.random(n)[col.astype(int) % n] # each plank column has its own end offset + fy = y % 1 + gap = 0.04 + m = smoothstep(0, gap, fx) * smoothstep(0, gap, 1 - fx) * smoothstep(0, gap * 1.5, fy) * smoothstep(0, gap * 1.5, 1 - fy) + # grain: stretched noise along the plank, per plank phase + grain = fbm((u * 1.0 + rng.random(n)[col.astype(int) % n]) % 1, v, 6, 102, octaves=5) + rings = 0.5 + 0.5 * np.sin(2 * np.pi * (fx * 3 + grain * 2.5)) + return norm(m * (0.75 + 0.25 * rings)) + +def basket_weave(u, v): + n = 4 + x = u * n; y = v * n + cx = np.floor(x); cy = np.floor(y); fx = x % 1; fy = y % 1 + horiz = (cx + cy) % 2 == 0 + strips = 3 # strips per cell + along = np.where(horiz, fx, fy); across = np.where(horiz, fy, fx) + strip = (across * strips) % 1 + body = np.sqrt(np.clip(1 - (2 * strip - 1) ** 2, 0, None)) # rounded strip + ends = smoothstep(0, 0.06, along) * smoothstep(0, 0.06, 1 - along) + return norm(0.35 + 0.65 * body * ends) + +def chainmail(u, v): + n = 6 + h = np.zeros_like(u) + for ox, oy in ((0.0, 0.0), (0.5, 0.5)): + x = (u * n + ox) % 1 - 0.5; y = (v * n + oy) % 1 - 0.5 + r = np.sqrt(x * x + y * y) + ring = np.exp(-((r - 0.36) / 0.09) ** 2) + h = np.maximum(h, ring) + return norm(h) + +def pyramids(u, v): + n = 8 + x = np.abs((u * n) % 1 - 0.5); y = np.abs((v * n) % 1 - 0.5) + return norm(0.5 - np.maximum(x, y)) + +def waffle(u, v): + n = 6 + x = (u * n) % 1; y = (v * n) % 1 + gap = 0.12 + m = smoothstep(0, gap, x) * smoothstep(0, gap, 1 - x) * smoothstep(0, gap, y) * smoothstep(0, gap, 1 - y) + return norm(m) + +def bubbles(u, v): + rng = np.random.default_rng(111) + h = np.zeros_like(u) + for (px, py), r in zip(wrapped_points(7, 112, 0.9), rng.random(49) * 0.05 + 0.03): + dx = u - px; dx -= np.round(dx); dy = v - py; dy -= np.round(dy) + d2 = dx * dx + dy * dy + h = np.maximum(h, np.sqrt(np.clip(r * r - d2, 0, None)) / 0.08) + return norm(h) + +def cracked_earth(u, v): + pts = wrapped_points(7, 121, 0.7) + d1, d2, idx = voronoi(u, v, pts) + crack = 1 - smoothstep(0.0, 0.03, d2 - d1) + plates = 0.8 + 0.2 * fbm(u, v, 24, 122) + curl = 1 - 0.35 * np.clip(1 - d1 / 0.12, 0, 1) # plates curl up at the edges + return norm(plates * (2 - curl) * (1 - 0.9 * crack)) + +def sand_ripples(u, v): + n = 8 + wob = 0.06 * np.sin(2 * np.pi * u * 2) + 0.03 * fbm(u, v, 4, 131) + h = 0.5 + 0.5 * np.sin(2 * np.pi * (v * n + wob)) + h = h ** 1.6 # sharp crests, soft troughs + return norm(h + 0.05 * fbm(u, v, 64, 132)) + +def bark(u, v): + grooves = fbm((u * 3) % 1, v, 4, 141, octaves=4) # the ridges below stretch it along v + ridges = np.abs(np.sin(2 * np.pi * (u * 9 + grooves * 1.5))) + return norm(ridges ** 0.7 * (0.7 + 0.3 * fbm(u, v, 12, 142)) + 0.1 * fbm(u, v, 48, 143)) + +def slate(u, v): + h = fbm(u, v, 3, 151, octaves=6, gain=0.55) + steps = np.floor(h * 6) / 6 + 0.4 * (h * 6 - np.floor(h * 6)) / 6 # cleaved layers + return norm(steps + 0.05 * fbm(u, v, 48, 152)) + +def triangles(u, v): + n = 6 + x = u * n; y = v * n * np.sqrt(3) / 1.5 # rows of equilateral triangles + row = np.floor(y); fy = y - row + xs = x + 0.5 * (row % 2) + fx = xs % 1 + up = fx < 1 - fy # which triangle of the rhombus + # distance to the nearest edge of the triangle, in either orientation + d_up = np.minimum(np.minimum(fy, fx - 0 * fy), (1 - fy - fx)) + d_dn = np.minimum(np.minimum(1 - fy, 1 - fx), (fx + fy - 1)) + d = np.where(up, d_up, d_dn) + return norm(smoothstep(0.0, 0.08, d)) + +def roof_tiles(u, v): + n = 6 + h = np.zeros_like(u) + for row in range(-1, 2 * n + 1): + cy = row / (2 * n); ox = 0.5 if row % 2 else 0.0 + x = (u * n + ox) % 1 - 0.5; y = v - cy; y -= np.round(y) + yy = y * n * 2 # 0 at the row's exposed edge, rising toward the covered end + inside = (yy >= -0.05) & (yy < 1.0) + arch = np.cos(x * np.pi) * 0.6 + 0.4 + cand = np.where(inside, 0.3 + 0.7 * arch * (1 - 0.35 * yy), 0) + h = np.where(inside & (cand > 0), cand, h) + return norm(h) + +def star_tiles(u, v): + # 8-point stars and crosses (the classic Islamic star-and-cross tiling) + n = 4 + x = (u * n) % 1 - 0.5; y = (v * n) % 1 - 0.5 + a = np.abs(x); b = np.abs(y) + star = np.maximum(np.maximum(a, b), (a + b) / np.sqrt(2) * 1.15) + m = smoothstep(0.42, 0.36, star) + # the crosses between the stars sit on the half-offset lattice + x2 = (u * n + 0.5) % 1 - 0.5; y2 = (v * n + 0.5) % 1 - 0.5 + a2 = np.abs(x2); b2 = np.abs(y2) + cross = np.minimum(np.maximum(a2 / 0.12, b2 / 0.30), np.maximum(a2 / 0.30, b2 / 0.12)) + m2 = smoothstep(1.0, 0.85, cross) * 0.8 + return norm(np.maximum(m, m2)) + +def terrazzo(u, v): + base = np.array([0.82, 0.80, 0.76]) + rgb = np.broadcast_to(base, u.shape + (3,)).copy() * (0.95 + 0.05 * fbm(u, v, 32, 161))[..., None] + rng = np.random.default_rng(162) + pal = np.array([[0.85, 0.30, 0.25], [0.20, 0.35, 0.55], [0.25, 0.25, 0.25], [0.95, 0.90, 0.80], [0.80, 0.60, 0.20]]) + for (px, py), r, c, ang in zip(wrapped_points(12, 163, 1.0), rng.random(144) * 0.02 + 0.012, rng.integers(0, 5, 144), rng.random(144) * 3.14): + dx = u - px; dx -= np.round(dx); dy = v - py; dy -= np.round(dy) + ca, sa = np.cos(ang), np.sin(ang) + ex = (dx * ca - dy * sa) / (r * 1.4); ey = (dx * sa + dy * ca) / r + inside = (np.abs(ex) + np.abs(ey) * 0.7 + np.maximum(np.abs(ex), np.abs(ey)) * 0.5) < 1.0 + rgb[inside] = pal[c] * 0.92 # chips sit a touch below the matrix: darker = lower + return rgb + +def camouflage(u, v): + pal = np.array([[0.36, 0.42, 0.24], [0.55, 0.50, 0.32], [0.22, 0.26, 0.17], [0.60, 0.58, 0.45]]) + a = fbm(u, v, 3, 171, octaves=4); b = fbm(u, v, 3, 172, octaves=4); c = fbm(u, v, 5, 173, octaves=3) + idx = (a > 0.55).astype(int) + 2 * (b > 0.5).astype(int) + idx = np.where(c > 0.72, 3, idx) + rgb = pal[idx].astype(float) + return rgb * (0.94 + 0.06 * fbm(u, v, 48, 174))[..., None] + +def tartan(u, v): + n = 2 + def stripes(t): + t = (t * n) % 1 + band = np.zeros_like(t) + for a, w, val in ((0.0, 0.32, 1), (0.32, 0.06, 2), (0.38, 0.24, 0), (0.62, 0.06, 2), (0.68, 0.32, 1)): + band = np.where((t >= a) & (t < a + w), val, band) + return band + pal = np.array([[0.12, 0.25, 0.20], [0.55, 0.12, 0.14], [0.90, 0.80, 0.30]]) + su = stripes(u).astype(int); sv = stripes(v).astype(int) + weave = ((np.floor(u * 400) + np.floor(v * 400)) % 2) == 0 # the two thread directions alternate + rgb = np.where(weave[..., None], pal[su], pal[sv]).astype(float) + return rgb * (0.9 + 0.1 * (0.5 + 0.5 * np.sin(2 * np.pi * (u + v) * 200)))[..., None] + +GREY = { + 'Diamond Plate': diamond_plate, 'Honeycomb': honeycomb, 'Fine Knurl': knurl, 'Scales': scales, + 'Herringbone': herringbone, 'Cobblestone': cobblestone, 'Leather': leather, 'Carbon Fibre': carbon_fibre, + 'Chevron': chevron, 'Ripples': ripples, 'Hammered': hammered, 'Perforated': perforated, 'Rope': rope, + 'Stone Wall': stone_wall, 'Wood Planks': wood_planks, 'Basket Weave': basket_weave, 'Chainmail': chainmail, + 'Pyramids': pyramids, 'Waffle': waffle, 'Bubbles': bubbles, 'Cracked Earth': cracked_earth, + 'Sand Ripples': sand_ripples, 'Bark': bark, 'Slate': slate, 'Triangles': triangles, 'Roof Tiles': roof_tiles, + 'Star Tiles': star_tiles, +} +COLOUR = {'Colour Bricks': colour_bricks, 'Mosaic Tiles': mosaic, 'Hex Tiles': hex_tiles, 'Terrazzo': terrazzo, + 'Camouflage': camouflage, 'Tartan': tartan} + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('--size', type=int, default=1024) + ap.add_argument('--out', default='resources/textures/displacement') + ap.add_argument('--sheet', default='') + args = ap.parse_args() + os.makedirs(args.out, exist_ok=True) + u, v = grid(args.size) + thumbs = [] + for name, fn in GREY.items(): + img = (np.clip(down(fn(u, v)), 0, 1) * 255 + 0.5).astype(np.uint8) + Image.fromarray(img, 'L').save(os.path.join(args.out, name + '.png'), optimize=True) + thumbs.append((name, np.stack([img] * 3, -1))) + print('wrote', name) + for name, fn in COLOUR.items(): + rgb = fn(u, v) + rgb = np.stack([down(rgb[..., c]) for c in range(3)], -1) + img = (np.clip(rgb, 0, 1) * 255 + 0.5).astype(np.uint8) + Image.fromarray(img, 'RGB').save(os.path.join(args.out, name + '.png'), optimize=True) + thumbs.append((name, img)) + print('wrote', name, '(colour)') + if args.sheet: + t = 256; cols = 7; rows = (len(thumbs) + cols - 1) // cols + sheet = Image.new('RGB', (cols * t, rows * t), (40, 40, 40)) + for i, (name, img) in enumerate(thumbs): + im = Image.fromarray(img).resize((t, t), Image.LANCZOS) + sheet.paste(im, ((i % cols) * t, (i // cols) * t)) + sheet.save(args.sheet); print('sheet', args.sheet) + +if __name__ == '__main__': + main() diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index cc59351d72..2f56b2d56e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2943,6 +2943,11 @@ void ModelVolume::assign_new_unique_ids_recursive() seam_facets.set_new_unique_id(); mmu_segmentation_facets.set_new_unique_id(); fuzzy_skin_facets.set_new_unique_id(); + // As set_new_unique_id() already does: the undo/redo stack stores FacetsAnnotation contents keyed + // by ObjectID, so a clone left sharing these ids with its source can be handed the source's mask + // on an undo - after which a paint mask and the mesh it was recorded against no longer match. + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + texture_displacement_facet(i).set_new_unique_id(); } void ModelVolume::rotate(double angle, Axis axis) diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index f08331c8f0..13d18c5314 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -98,7 +98,7 @@ struct HeapEntry DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool harvest_flat, double harvest_tol, const std::vector &locked_faces, - const DecimateProgressFn &on_progress) + const DecimateProgressFn &on_progress, const std::vector &face_color) { DecimateResult result; const size_t n = geometry.pos.size(); @@ -211,8 +211,10 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h faces[size_t(er.f0) * 3 + 2]); const Vec3d n1 = face_normal_unit(pos, faces[size_t(er.f1) * 3], faces[size_t(er.f1) * 3 + 1], faces[size_t(er.f1) * 3 + 2]); - if (n0.dot(n1) >= DECIMATE_CREASE_COS) - continue; // smooth enough to be no crease + const bool color_edge = face_color.size() > std::max(size_t(er.f0), size_t(er.f1)) && + face_color[size_t(er.f0)] != face_color[size_t(er.f1)]; + if (!color_edge && n0.dot(n1) >= DECIMATE_CREASE_COS) + continue; // smooth enough to be no crease, and no colour changes across it const Vec3d e = pos[size_t(er.vb)] - pos[size_t(er.va)]; const double elen = e.norm(); diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp index 8876ab00df..c40d9de266 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -47,10 +47,14 @@ struct DecimateResult // `locked_faces`: one entry per input triangle; a vertex touching one may neither move nor be // removed, which also pins the ring between the two regions. +// `face_color`: optional, one entry per input triangle. An edge between two faces of different +// colour is treated as a crease, so the simplified triangles never span a colour boundary and the +// boundary keeps its place - a per-triangle colour read off the result then has nothing to smear. DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool harvest_flat = true, double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL, const std::vector &locked_faces = {}, - const DecimateProgressFn &on_progress = {}); + const DecimateProgressFn &on_progress = {}, + const std::vector &face_color = {}); } // namespace TextureBake } // namespace Slic3r diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 37b58a6f3b..f59ed51163 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -1,5 +1,8 @@ #include "TextureBakePipeline.hpp" +#include +#include + #include "TextureBakeDebug.hpp" #include @@ -111,7 +114,8 @@ size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol) PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, const PipelineSettings &settings, const DisplaceBounds &bounds, PipelineMode mode, const std::vector &face_excluded, - const PipelineProgressFn &on_progress, BakeStageRecorder *debug) + const PipelineProgressFn &on_progress, BakeStageRecorder *debug, + const ColorSampleFn &color_sample) { PipelineResult result; const auto report = [&](const char *stage, double f) { @@ -193,6 +197,46 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } } + // 2b. Paint finer than the input triangles. The caller includes a source triangle when any part of + // it is painted; now that the faces are small, ask once more per face and switch the unpainted + // ones off. They are pinned like the excluded region from here on (their own corners at weight + // 1, and the displacement's boundary sealing pins the stroke's rim on the painted side), but they + // are refined pieces of painted triangles, not original geometry, so `soft_excluded` keeps them + // out of the decimation lock below. Every stage between here and the decimation rewrites faces in + // place, so the per-face flag stays valid by index. + std::vector soft_excluded; + if (settings.painted) { + const size_t nf = sub.geometry.triangle_count(); + const bool have_w = !sub.geometry.exclude_weight.empty(); + std::vector unpainted(nf, 0); + tbb::parallel_for(tbb::blocked_range(0, nf), [&](const tbb::blocked_range &r) { + for (size_t t = r.begin(); t < r.end(); ++t) { + if (have_w && sub.geometry.exclude_weight[t * 3] > 0.99f) + continue; // excluded from the start, never asked + const Vec3f &a = sub.geometry.pos[t * 3], &b = sub.geometry.pos[t * 3 + 1], &c = sub.geometry.pos[t * 3 + 2]; + if (!settings.painted((a + b + c) / 3.f)) + unpainted[t] = 1; + } + }); + size_t switched = 0; + for (size_t t = 0; t < nf; ++t) + switched += unpainted[t]; + if (switched > 0) { + if (sub.geometry.exclude_weight.empty()) + sub.geometry.exclude_weight.assign(sub.geometry.pos.size(), 0.f); + for (size_t t = 0; t < nf; ++t) + if (unpainted[t]) + sub.geometry.exclude_weight[t * 3] = sub.geometry.exclude_weight[t * 3 + 1] = + sub.geometry.exclude_weight[t * 3 + 2] = 1.f; + soft_excluded = std::move(unpainted); + } + lap("paint", sub.geometry, std::to_string(switched) + " faces switched off"); + if (!report("paint", 1.0)) { + result.canceled = true; + return result; + } + } + // 3. Align the mesh to the height field's edges, then displace. if (settings.relocate) { std::vector locked; @@ -247,12 +291,40 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, std::vector locked; if (settings.preserve_untextured && !displaced.exclude_weight.empty()) { locked.assign(displaced.triangle_count(), 0); + // The corner average, as the displacement stage judges it: after the flip stage's + // per-vertex merge an included face touching the excluded region carries one corner + // at weight 1, and must stay free to collapse and to take colour. for (size_t t = 0; t < locked.size(); ++t) - locked[t] = displaced.exclude_weight[t * 3] > 0.99f ? 1 : 0; + locked[t] = (displaced.exclude_weight[t * 3] + displaced.exclude_weight[t * 3 + 1] + + displaced.exclude_weight[t * 3 + 2]) / 3.f > 0.99f ? 1 : 0; + // Faces the paint test switched off carry weight 1 too, but are refined pieces of + // painted triangles rather than original geometry: locking them would keep a partly + // painted source triangle at full refinement. Face indices survived relocate, flip + // and displace unchanged, so the flag still lines up. + for (size_t t = 0; t < locked.size() && t < soft_excluded.size(); ++t) + if (soft_excluded[t]) + locked[t] = 0; + } + // Colour per face on the fine mesh, so colour boundaries become creases the collapse + // respects. Excluded (unpainted) faces take no colour. + std::vector face_color; + if (color_sample) { + const size_t nf = displaced.triangle_count(); + face_color.assign(nf, -1); + const bool have_w = !displaced.exclude_weight.empty(); + tbb::parallel_for(tbb::blocked_range(0, nf), [&](const tbb::blocked_range &r) { + for (size_t t = r.begin(); t < r.end(); ++t) { + if (have_w && (displaced.exclude_weight[t * 3] + displaced.exclude_weight[t * 3 + 1] + + displaced.exclude_weight[t * 3 + 2]) / 3.f > 0.99f) + continue; + const Vec3f &a = displaced.pos[t * 3], &b = displaced.pos[t * 3 + 1], &c = displaced.pos[t * 3 + 2]; + face_color[t] = color_sample((a + b + c) / 3.f, displaced.nrm[t * 3]); + } + }); } DecimateResult dec = decimate(displaced, settings.max_triangles, settings.harvest_flat, settings.harvest_tol, locked, - [&](double f) { return report("decimate", f); }); + [&](double f) { return report("decimate", f); }, face_color); result.locked_over_budget = dec.locked_over_budget; displaced = std::move(dec.geometry); lap("decimate", displaced, "over budget, simplified"); diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index aac6710cf4..d56a0d09dc 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -2,8 +2,8 @@ // The bake pipeline: // -// subdivide -> [regularize -> re-subdivide] -> [relocate] -> displace -> [decimate] -// -> bottom clamp -> bottom snap -> [resolve T-junctions] +// subdivide -> [regularize -> re-subdivide] -> [paint test] -> [relocate] -> [flip edges] +// -> displace -> [decimate] -> bottom clamp -> bottom snap -> [resolve T-junctions] // // Regularization sits between two subdivisions on purpose: it dissolves the slivers refinement // inherited, which lengthens some edges past the target, and the second pass brings those back. @@ -66,6 +66,13 @@ struct PipelineSettings DisplaceSettings displace; + // Optional. Asked once per refined face (its centroid, in the soup's coordinates) after the + // refinement stages and before displacement, for faces whose source triangle was included: + // false marks the face as unpainted (no displacement), so paint finer than the input triangles + // is honoured. Faces excluded from the start are never asked. Called from several threads at + // once, so it must be safe to call concurrently. + std::function painted; + // Export mode only. size_t max_triangles = 750'000; // Keep removing zero-cost flat faces past the target. Only applies when decimation runs, i.e. when @@ -87,6 +94,9 @@ struct PipelineSettings // Stage name and a fraction within it. Returning false cancels the run. using PipelineProgressFn = std::function; +// Colour class of a point of the surface (a palette index, -1 for none), for the decimation's +// colour-boundary creases. Only consulted when the mesh is over budget. +using ColorSampleFn = std::function; struct PipelineResult { @@ -105,7 +115,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, const PipelineSettings &settings, const DisplaceBounds &bounds, PipelineMode mode, const std::vector &face_excluded = {}, const PipelineProgressFn &on_progress = {}, - BakeStageRecorder *debug = nullptr); + BakeStageRecorder *debug = nullptr, const ColorSampleFn &color_sample = {}); // Snap anything that ended below the model's original bottom back up to it. void clamp_below_bottom(TriSoup &geometry, float bottom_z); diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index cfc75c286f..a81d8b3c21 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -277,18 +278,26 @@ DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer col.bytes_per_pixel < 3) return result; - const size_t n = size_t(col.cols) * size_t(col.rows); - const size_t bpp = size_t(col.bytes_per_pixel); - result.width = int(col.cols); - result.height = int(col.rows); + const size_t cols = size_t(col.cols), rows = size_t(col.rows), n = cols * rows; + const size_t bpp = size_t(col.bytes_per_pixel); + const size_t stride = col.buf.size() / rows; + result.width = int(cols); + result.height = int(rows); result.pixels.resize(n); result.rgb.resize(n * 3); - for (size_t i = 0; i < n; ++i) { - const uint8_t r = col.buf[i * bpp], g = col.buf[i * bpp + 1], b = col.buf[i * bpp + 2]; - result.rgb[i * 3] = r; - result.rgb[i * 3 + 1] = g; - result.rgb[i * 3 + 2] = b; - result.pixels[i] = uint8_t(std::lround(0.299 * r + 0.587 * g + 0.114 * b)); + // decode_colored_png() fills its buffer bottom-up (its other callers hand the rows to + // OpenGL, which wants them that way); a height map is top-down, like decode_png()'s grey + // output, so a colour image has to read the same way up as a grey copy of itself. + for (size_t y = 0; y < rows; ++y) { + const uint8_t *src = col.buf.data() + (rows - 1 - y) * stride; + for (size_t x = 0; x < cols; ++x) { + const size_t i = y * cols + x; + const uint8_t r = src[x * bpp], g = src[x * bpp + 1], b = src[x * bpp + 2]; + result.rgb[i * 3] = r; + result.rgb[i * 3 + 1] = g; + result.rgb[i * 3 + 2] = b; + result.pixels[i] = uint8_t(std::lround(0.299 * r + 0.587 * g + 0.114 * b)); + } } } @@ -386,6 +395,24 @@ TextureDetail analyze_texture_detail(const TextureDisplacementLayer &layer) else if (out.sharp_fraction > 0.05f || out.mean_gradient > 20.f) out.pixels_per_edge = 1.5f; else if (out.mean_gradient > 8.f) out.pixels_per_edge = 2.5f; else out.pixels_per_edge = 4.f; + + // Colour spread: a coarse histogram (8 levels per channel, 64 levels for a grey image) and + // the share of the eight fullest bins. Tiles, logos and camouflage put nearly everything in a + // handful of bins even with some texture noise; a photograph spreads across hundreds. + std::vector bins(size_t(8 * 8 * 8), 0); + const size_t npx = size_t(w) * size_t(h); + if (tex.has_color()) + for (size_t i = 0; i < npx; ++i) + ++bins[size_t(tex.rgb[i * 3] >> 5) * 64 + size_t(tex.rgb[i * 3 + 1] >> 5) * 8 + size_t(tex.rgb[i * 3 + 2] >> 5)]; + else + for (size_t i = 0; i < npx; ++i) + ++bins[size_t(tex.pixels[i] >> 2) * 8]; // 64 grey levels, spread over distinct bins + std::partial_sort(bins.begin(), bins.begin() + 8, bins.end(), std::greater()); + uint64_t top = 0; + for (int i = 0; i < 8; ++i) + top += bins[size_t(i)]; + out.flat_share = float(double(top) / double(npx)); + out.flat_colors = out.flat_share >= 0.85f; } std::lock_guard lock(g_texture_detail_cache.mutex); auto &entries = g_texture_detail_cache.entries; @@ -399,60 +426,40 @@ V2Resolution recommend_v2_resolution(const indexed_triangle_set const std::vector &layers, const Transform3d &volume_to_world) { - // BumpMesh's smart resolution, the numbers included: equilateral-cover triangle density, a 16 M - // triangle refinement cap taken at 75 %, a 0.5 mm reference relief for the budget. - constexpr double TRIS_PER_AREA = 2.309, CAP_TRIANGLES = 16e6 * 0.75; - constexpr double EDGE_MIN = 0.05, EDGE_MAX = 5.0; - constexpr double BUDGET_MIN = 10e3, BUDGET_MAX = 2000e3, REF_DEPTH = 0.5, MIN_DEPTH = 0.1; + // bumpmesh.com's defaults on model load: edge = diagonal / 250 in [0.05, 5] mm, budget 750 k. A + // texture-driven variant (BumpMesh's smart resolution) was measured to give better walls on step + // textures at 2-10x the bake time and up to 2 M output triangles; the user preferred the site's + // defaults. The texel size and sharpness are still reported for the panel. + constexpr double EDGE_MIN = 0.05, EDGE_MAX = 5.0, DIAG_DIVISOR = 250.0; + constexpr int BUDGET_K = 750; V2Resolution out; - // The finest layer decides: the smallest detail edge (texel x pixels per edge) across the layers. - double detail_edge = std::numeric_limits::max(), depth = 0.0; + if (mesh.vertices.empty()) + return out; for (const TextureDisplacementLayer &layer : layers) { if (layer.empty() || layer.tiling_scale <= 0.f) continue; const DecodedHeightTexture &tex = decode_height_texture(layer); if (tex.width <= 0) continue; - const TextureDetail detail = analyze_texture_detail(layer); - const double texel = double(layer.tiling_scale) / double(tex.width); - const double edge = texel * double(detail.pixels_per_edge); - if (edge < detail_edge) { - detail_edge = edge; - out.texel_mm = float(texel); - out.pixels_per_edge = detail.pixels_per_edge; - depth = std::abs(double(layer.depth_mm)); + const float texel = layer.tiling_scale / float(tex.width); + if (out.texel_mm <= 0.f || texel < out.texel_mm) { + out.texel_mm = texel; + out.pixels_per_edge = analyze_texture_detail(layer).pixels_per_edge; } } - if (out.texel_mm <= 0.f || mesh.vertices.empty()) - return out; - - // Surface area and diagonal in world mm: the tile is in world mm and the pipeline refines there. - double area = 0.0; - Vec3d bmin = Vec3d::Constant(std::numeric_limits::max()), bmax = -bmin; - std::vector world(mesh.vertices.size()); - for (size_t i = 0; i < world.size(); ++i) { - world[i] = volume_to_world * mesh.vertices[i].cast(); - bmin = bmin.cwiseMin(world[i]); - bmax = bmax.cwiseMax(world[i]); + Vec3d bmin = Vec3d::Constant(std::numeric_limits::max()), bmax = -bmin; + for (const Vec3f &v : mesh.vertices) { + const Vec3d w = volume_to_world * v.cast(); + bmin = bmin.cwiseMin(w); + bmax = bmax.cwiseMax(w); } - for (const stl_triangle_vertex_indices &t : mesh.indices) - area += 0.5 * (world[size_t(t[1])] - world[size_t(t[0])]).cross(world[size_t(t[2])] - world[size_t(t[0])]).norm(); const double diag = (bmax - bmin).norm(); - - const double budget_edge = std::sqrt(TRIS_PER_AREA * area / CAP_TRIANGLES); - double edge = std::max(detail_edge, budget_edge); - out.budget_bound = budget_edge > detail_edge; - const double hi = std::max(EDGE_MIN, std::min(EDGE_MAX, diag / 50.0)); - edge = std::clamp(edge, EDGE_MIN, hi); - edge = std::max(EDGE_MIN, std::ceil(edge * 100.0) / 100.0); // up, so the cap holds - out.edge_mm = float(edge); - - const double depth_scale = std::sqrt(REF_DEPTH / std::max(depth, MIN_DEPTH)); - const double target_edge = double(out.pixels_per_edge) * double(out.texel_mm) * depth_scale; - const double raw = TRIS_PER_AREA * area / (target_edge * target_edge); - const double stepped = std::round(raw / 10e3) * 10e3; - out.budget_k = int(std::clamp(stepped, BUDGET_MIN, BUDGET_MAX) / 1000.0); + double edge = std::clamp(diag / DIAG_DIVISOR, EDGE_MIN, EDGE_MAX); + edge = std::max(EDGE_MIN, std::ceil(edge * 100.0) / 100.0); + out.edge_mm = float(edge); + out.budget_k = BUDGET_K; + out.budget_bound = false; return out; } @@ -917,6 +924,9 @@ PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_a std::vector uvs; std::vector to_patch; // chart vertex -> patch vertex std::vector indices; // chart-local + // Parallel to `indices`: the patch triangle each one came from. compact_patch_with_map() keeps + // the patch's triangle count *and* order, so a compact face index is already a patch face index. + std::vector faces; Vec2f min = Vec2f::Zero(); Vec2f size = Vec2f::Zero(); }; @@ -950,6 +960,7 @@ PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_a local_tri[i] = compact_to_local[size_t(cv)]; } chart_mesh.indices.push_back(local_tri); + chart.faces.push_back(int(f)); } if (chart_mesh.indices.empty()) continue; @@ -1043,6 +1054,7 @@ PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_a result.vertex_chart.insert(result.vertex_chart.end(), chart.uvs.size(), c); for (const stl_triangle_vertex_indices &tri : chart.indices) result.indices.emplace_back(tri[0] + base, tri[1] + base, tri[2] + base); + result.source_face.insert(result.source_face.end(), chart.faces.begin(), chart.faces.end()); if (!chart.uvs.empty()) { Vec2f sum = Vec2f::Zero(); @@ -1509,6 +1521,41 @@ std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const Tex return per_vertex; } +std::vector compute_lscm_corner_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) +{ + // Padding 0 and the layer's own seam angle/edges, exactly as compute_lscm_uvs() does - the two must + // unwrap identically or a hand placement would land in one place on screen and another in the bake. + const PatchUnwrap unwrap = compute_patch_unwrap(patch, layer.lscm_seam_angle_deg, 0.f, layer.lscm_seam_edges); + if (unwrap.empty() || unwrap.source_face.size() != unwrap.indices.size()) + return {}; + + PatchUnwrap edited_unwrap = unwrap; + apply_lscm_uv_overrides(edited_unwrap, layer.lscm_uv_overrides); + + // No first-copy-wins collapse here: the unwrap's triangles are already per chart, so each corner + // simply takes its own chart's copy. A triangle the unwrap dropped (a sliver a chart rejected) keeps + // the zero it was initialised with; the callers treat that as "no placement" the same way they treat + // an empty result. + std::vector corner(patch.indices.size() * 3, Vec2f::Zero()); + for (size_t t = 0; t < edited_unwrap.indices.size(); ++t) { + const int f = edited_unwrap.source_face[t]; + if (f < 0 || size_t(f) >= patch.indices.size()) + continue; + const stl_triangle_vertex_indices &tri = edited_unwrap.indices[t]; + for (int k = 0; k < 3; ++k) { + const int uvi = tri[k]; + if (uvi < 0 || size_t(uvi) >= edited_unwrap.uvs.size()) + continue; + // The island transform is taken against the *unedited* unwrap, whose chart_centroid is the + // pivot the UV editor rotates about - same as compute_lscm_uvs(). + corner[size_t(f) * 3 + size_t(k)] = apply_island_transform(edited_unwrap.uvs[size_t(uvi)], + edited_unwrap.vertex_chart[size_t(uvi)], + unwrap, layer.islands); + } + } + return corner; +} + namespace { // apply_uv_transform()'s per-layer constants, worked out once. Triplanar sampling runs the transform // three times per point, and recomputing the rotation's cos/sin and the tiling reciprocal on every one @@ -1820,7 +1867,7 @@ bool compute_layer_paint_anchor(const indexed_triangle_set &b // these once, up front, and every layer both projects and displaces along them - so a vertex // covered by several layers is pushed along one single, well-defined direction rather than along // whatever direction the surface happened to be pointing partway through the stack. -static std::vector texture_displacement_vertex_normals(const indexed_triangle_set &its) +std::vector texture_displacement_vertex_normals(const indexed_triangle_set &its) { std::vector normals(its.vertices.size(), Vec3f::Zero()); for (const stl_triangle_vertex_indices &tri : its.indices) { @@ -1943,6 +1990,98 @@ void despeckle_triangle_colors(const indexed_triangle_set &mesh, std::vector &color, float min_area_mm2) +{ + const size_t n = mesh.indices.size(); + if (min_area_mm2 <= 0.f || color.size() != n) + return; + const std::vector neighbors = its_face_neighbors(mesh); + if (neighbors.size() != n) + return; + + const auto edge_length = [&mesh](size_t f, int e) { + const stl_triangle_vertex_indices &t = mesh.indices[f]; + return (mesh.vertices[size_t(t[(e + 1) % 3])] - mesh.vertices[size_t(t[e])]).norm(); + }; + + // Connected components of equal colour: `faces` lists every coloured face, component by + // component, `start` delimits them. Uncoloured faces (-1) belong to no component and block the + // flood, so a region never grows across the paint's border. + std::vector component(n, -1); + std::vector faces; + std::vector start; + std::vector area; + std::vector stack; + faces.reserve(n); + for (size_t seed = 0; seed < n; ++seed) { + if (color[seed] < 0 || component[seed] >= 0) + continue; + const int c = color[seed]; + const int id = int(area.size()); + start.push_back(faces.size()); + area.push_back(0.f); + component[seed] = id; + stack.push_back(int(seed)); + while (!stack.empty()) { + const size_t f = size_t(stack.back()); + stack.pop_back(); + faces.push_back(int(f)); + const stl_triangle_vertex_indices &t = mesh.indices[f]; + const Vec3f &a = mesh.vertices[size_t(t[0])], &b = mesh.vertices[size_t(t[1])], &cv = mesh.vertices[size_t(t[2])]; + area[size_t(id)] += 0.5f * (b - a).cross(cv - a).norm(); + for (int e = 0; e < 3; ++e) { + const int nb = neighbors[f][e]; + if (nb < 0 || size_t(nb) >= n || component[size_t(nb)] >= 0 || color[size_t(nb)] != c) + continue; + component[size_t(nb)] = id; + stack.push_back(nb); + } + } + } + start.push_back(faces.size()); + + // Smallest first, so that when a small island borders a slightly larger one the larger one has + // not yet moved and the small one joins whatever the two of them sit in; the larger one then + // reads that colour in turn. + std::vector order; + for (int id = 0; id < int(area.size()); ++id) + if (area[size_t(id)] < min_area_mm2) + order.push_back(id); + std::sort(order.begin(), order.end(), [&area](int l, int r) { return area[size_t(l)] < area[size_t(r)]; }); + + std::vector> weights; // neighbouring colour -> shared edge length + for (const int id : order) { + const size_t begin = start[size_t(id)], end = start[size_t(id) + 1]; + const int own = color[size_t(faces[begin])]; + weights.clear(); + for (size_t k = begin; k < end; ++k) { + const size_t f = size_t(faces[k]); + for (int e = 0; e < 3; ++e) { + const int nb = neighbors[f][e]; + if (nb < 0 || size_t(nb) >= n) + continue; + const int c = color[size_t(nb)]; // read now: an earlier merge may have recoloured it + if (c < 0 || c == own) + continue; + const float len = edge_length(f, e); + auto it = std::find_if(weights.begin(), weights.end(), [c](const std::pair &w) { return w.first == c; }); + if (it == weights.end()) + weights.emplace_back(c, len); + else + it->second += len; + } + } + if (weights.empty()) + continue; // bordered only by uncoloured faces (or nothing): stays + const int target = std::max_element(weights.begin(), weights.end(), + [](const std::pair &l, const std::pair &r) { + return l.second < r.second; + })->first; + for (size_t k = begin; k < end; ++k) + color[size_t(faces[k])] = target; + } +} + namespace { // Wired to the same layer stack via make_combined_displacement_sampler(), so layers, blend modes and @@ -1960,8 +2099,13 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set if (!combined) return mesh; // nothing decodable to displace with - // Unpainted triangles are excluded, keeping them out of refinement and pinned thereafter. + // Unpainted triangles are excluded, keeping them out of refinement and pinned thereafter. The + // paint is finer than that, though: a brush stroke splits a source triangle into pieces, and only + // some of them are painted. `painted_pieces` keeps every layer's painted pieces (they lie in the + // source surface) so the refined faces can be tested against the paint itself, not against the + // source triangle they came from. std::vector excluded(mesh.indices.size(), 1); + indexed_triangle_set painted_pieces; { const TriangleMesh selector_mesh(mesh); TriangleSelector selector(selector_mesh); @@ -1977,11 +2121,49 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set for (const int src : piece_src) if (src >= 0 && size_t(src) < excluded.size()) excluded[size_t(src)] = 0; + // `patch` carries the whole mesh's vertex array (see compact_patch_with_map()); append + // only what its pieces reference. + std::vector unused; + const indexed_triangle_set compact = compact_patch_with_map(patch, unused); + const int offset = int(painted_pieces.vertices.size()); + painted_pieces.vertices.insert(painted_pieces.vertices.end(), compact.vertices.begin(), compact.vertices.end()); + for (const stl_triangle_vertex_indices &t : compact.indices) + painted_pieces.indices.emplace_back(t[0] + offset, t[1] + offset, t[2] + offset); } } - if (std::all_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; })) + if (std::all_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; }) || painted_pieces.indices.empty()) return mesh; // nothing painted + // Distance to the nearest painted piece. Built once here; the tree is read-only afterwards, so + // the parallel stages below share it freely. + const AABBTreeIndirect::Tree3f painted_tree = + AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(painted_pieces.vertices, painted_pieces.indices); + // `foot`/`normal`, when asked for, are the closest point on the painted pieces and that piece's + // normal. The pieces lie in the *undisplaced* surface, so for a displaced point those two are the + // base position and normal underneath it - the frame colour has to be projected in (see below). + const auto painted_closest = [&painted_pieces, &painted_tree](const Vec3f &p, Vec3f *foot, Vec3f *normal) { + size_t hit = 0; + Vec3f hit_point; + const float d2 = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + painted_pieces.vertices, painted_pieces.indices, painted_tree, p, hit, hit_point); + if (foot != nullptr) + *foot = hit_point; + if (normal != nullptr && hit < painted_pieces.indices.size()) { + const stl_triangle_vertex_indices &t = painted_pieces.indices[hit]; + const Vec3f &a = painted_pieces.vertices[size_t(t[0])], &b = painted_pieces.vertices[size_t(t[1])], + &c = painted_pieces.vertices[size_t(t[2])]; + Vec3f n = (b - a).cross(c - a); + const float l = n.norm(); + *normal = (l > 0.f) ? Vec3f(n / l) : Vec3f::UnitZ(); + } + return d2; + }; + const auto painted_dist2 = [&painted_closest](const Vec3f &p) { return painted_closest(p, nullptr, nullptr); }; + // Before displacement the queried centroids lie in the same surface as the pieces, so anything + // beyond a hair is genuinely outside the paint. + constexpr float paint_tol = 0.05f; + const auto painted_at = [&painted_dist2](const Vec3f &p) { return painted_dist2(p) < paint_tol * paint_tol; }; + // "Auto" resolution and budget (0 and -1) resolve here, from the texture and the model - the mesh // is already in world mm at this point, so no transform is needed. const bool auto_edge = options.v2_refine_mm <= 0.f, auto_budget = options.v2_max_triangles_k < 0; @@ -2012,6 +2194,30 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set // of vertices that samples both faces' patterns half and half otherwise comes out as a row of // notches, since it matches neither face. settings.displace.blend_normal_smoothing = 32; + // Refined faces are asked against the paint itself, so a stroke narrower than a source triangle + // moves only what it covers. + // Only when some included source triangle is painted in part: the pieces then cover less area + // than the triangles they came from. Whole-triangle paint (the usual case, and every bench) has + // nothing to gain from a query per refined face. + { + const auto area_of = [](const indexed_triangle_set &its) { + double a = 0.0; + for (const stl_triangle_vertex_indices &t : its.indices) + a += 0.5 * double((its.vertices[size_t(t[1])] - its.vertices[size_t(t[0])]) + .cross(its.vertices[size_t(t[2])] - its.vertices[size_t(t[0])]).norm()); + return a; + }; + double included_area = 0.0; + for (size_t t = 0; t < mesh.indices.size(); ++t) + if (excluded[t] == 0) { + const stl_triangle_vertex_indices &f = mesh.indices[t]; + included_area += 0.5 * double((mesh.vertices[size_t(f[1])] - mesh.vertices[size_t(f[0])]) + .cross(mesh.vertices[size_t(f[2])] - mesh.vertices[size_t(f[0])]).norm()); + } + const double pieces_area = area_of(painted_pieces); + if (pieces_area < included_area * (1.0 - 1e-4)) + settings.painted = painted_at; + } TextureBake::DisplaceBounds bounds; bounds.min = bounds.max = mesh.vertices.empty() ? Vec3f::Zero() : mesh.vertices.front(); @@ -2039,6 +2245,26 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set // 0 means no simplification, i.e. Bake mode. const TextureBake::PipelineMode mode = settings.max_triangles > 0 ? TextureBake::PipelineMode::Export : TextureBake::PipelineMode::Bake; + // Colour, when asked for. The sampler is built now so the simplification can see the colour + // boundaries: a simplified triangle must not span two colours, or its one colour is wrong over + // part of it (half a tile in the neighbour's colour, a tile edge that wanders). + const bool want_color = color != nullptr && color->out_triangle != nullptr && bool(color->quantize); + const ColorFieldSampler color_sampler = + want_color ? make_combined_color_sampler(mesh, layers, facets_data, color->quantize, color->quantize_pure) : ColorFieldSampler{}; + // + // The *palette* index, not the printed filament. The decimation treats any edge whose two faces + // differ as a crease (TextureBakeDecimate.cpp), so it must only ever see where the **perceived** + // colour changes - which is exactly what ColorResolveFn's own contract says the interleaving may + // never be fed into. Handing it the resolved filament made every Z band boundary a crease: on an + // upright wall that is one crease per band, so the collapse ran along those lines and left a stack + // of horizontal slivers, each printing in a single filament. Those were the horizontal colour + // lines in the baked result, and they also spent the triangle budget drawing a pattern the eye is + // meant to blend away. Faces the paint excludes are skipped by the pipeline itself. + const TextureBake::ColorSampleFn color_sample = + color_sampler ? TextureBake::ColorSampleFn([&color_sampler](const Vec3f &p, const Vec3f &n) { + return color_sampler(p, n); + }) + : TextureBake::ColorSampleFn{}; // The pipeline works on `oriented`, whose winding was reversed above for a mirrored placement, so // the stages it records are wound the same way. Note where they start and turn the whole range // back afterwards, exactly as the result itself is turned back below. @@ -2048,7 +2274,7 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set [&progress](const char *, double f) { return !progress || progress(std::clamp(int(f * 100.0), 0, 99)); }, - debug); + debug, color_sample); if (debug != nullptr && flip_normals) debug->rebase(debug_mark, nullptr, /* flip_winding */ true); if (result.canceled || result.geometry.empty()) @@ -2063,44 +2289,56 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set // Colour, per output triangle. The topology is new, so unlike the classic path there is no base // triangle to inherit a colour from: each output triangle samples the colour stack at its own - // centroid, and takes colour only where the base surface under it is painted - found by the nearest - // base triangle, which is never more than the relief depth away. Then the same despeckle and + // centroid, and takes colour only where the paint is - measured against the painted pieces, which + // an output centroid is never further from than the relief depth. Then the same despeckle and // filament resolution as the classic path. - if (color != nullptr && color->out_triangle != nullptr && bool(color->quantize)) { - std::vector out_color(out.indices.size(), 0); - const ColorFieldSampler sampler = make_combined_color_sampler(mesh, layers, facets_data, color->quantize); + if (want_color) { + std::vector out_color(out.indices.size(), 0); + const ColorFieldSampler &sampler = color_sampler; if (sampler) { const bool all_painted = std::none_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; }); - AABBTreeIndirect::Tree3f tree; - if (!all_painted) - tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.vertices, mesh.indices); + float max_depth = 0.f; + for (const TextureDisplacementLayer &layer : layers) + max_depth = std::max(max_depth, std::abs(layer.depth_mm)); + const float relief_tol = max_depth + paint_tol; std::vector palette(out.indices.size(), -1); tbb::parallel_for(tbb::blocked_range(0, out.indices.size()), [&](const tbb::blocked_range &r) { for (size_t i = r.begin(); i < r.end(); ++i) { const stl_triangle_vertex_indices &t = out.indices[i]; const Vec3f &a = out.vertices[size_t(t[0])], &b = out.vertices[size_t(t[1])], &c = out.vertices[size_t(t[2])]; const Vec3f centroid = (a + b + c) / 3.f; - if (!all_painted) { - size_t hit = 0; - Vec3f hit_point; - AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.vertices, mesh.indices, tree, - centroid, hit, hit_point); - if (hit >= excluded.size() || excluded[hit] != 0) - continue; - } - Vec3f n = (b - a).cross(c - a); - const float l = n.norm(); - n = (l > 0.f) ? Vec3f(n / l) : Vec3f::UnitZ(); - palette[i] = sampler(centroid, n); + // Sample on the *base* surface under this face, not on the relief. The projection + // is a function of position and normal, and the displacement has moved both: the + // triplanar blend weights three axis planes by |n|^4, so a face tilted ~45 degrees + // away from its base normal reads the image half through an unrelated plane. The + // patch border is a ring of exactly such faces - the relief ramps to zero there - + // which is the coloured fringe around the border, and the steep interior slopes + // streak for the same reason. The classic path samples the base patch for this very + // reason; this path was the inconsistent one. + Vec3f foot = centroid, base_n = Vec3f::UnitZ(); + const float d2 = painted_closest(centroid, &foot, &base_n); + if (!all_painted && d2 >= relief_tol * relief_tol) + continue; + palette[i] = sampler(foot, base_n); } }); - despeckle_triangle_colors(out, palette, color->despeckle_passes); + // The despeckle filter is for a fine, uniform mesh, where one facet flipping colour is + // noise. A simplified mesh is neither: its triangles are as large as the colour regions + // themselves and already end on the colour boundaries, so a majority vote among three + // neighbours would repaint whole features. Bake mode (no simplification) keeps it. + const bool simplified = result.face_parent_id.empty(); + despeckle_triangle_colors(out, palette, simplified ? 0 : color->despeckle_passes); + merge_small_color_regions(out, palette, color->min_color_region_mm2); for (size_t i = 0; i < out.indices.size(); ++i) { if (palette[i] < 0) continue; const stl_triangle_vertex_indices &t = out.indices[i]; - const Vec3f centroid = (out.vertices[size_t(t[0])] + out.vertices[size_t(t[1])] + out.vertices[size_t(t[2])]) / 3.f; - const int filament = color->resolve ? color->resolve(palette[i], centroid) : palette[i]; + const Vec3f &a = out.vertices[size_t(t[0])], &b = out.vertices[size_t(t[1])], &c = out.vertices[size_t(t[2])]; + const Vec3f centroid = (a + b + c) / 3.f; + Vec3f normal = (b - a).cross(c - a); + const float nl = normal.norm(); + normal = (nl > 0.f) ? Vec3f(normal / nl) : Vec3f::UnitZ(); + const int filament = color->resolve ? color->resolve(palette[i], centroid, normal) : palette[i]; if (filament >= 0) out_color[i] = uint8_t(std::min(filament + 1, 255)); } @@ -2278,6 +2516,10 @@ static indexed_triangle_set build_texture_displacement_in_place( selector_dirty = true; const bool color_this_layer = want_color && layer->color_enabled; + // A flat-colour image is matched against the filaments alone (see TextureColorRequest). + const ColorQuantizeFn &layer_quantize = + (color_this_layer && color->quantize_pure && analyze_texture_detail(*layer).flat_colors) ? color->quantize_pure + : color->quantize; std::vector patch_source; // sub-triangle -> base mesh triangle, only built when colouring const indexed_triangle_set patch = selector.get_facets_strict(EnforcerBlockerType::ENFORCER, color_this_layer ? &patch_source : nullptr); @@ -2302,34 +2544,11 @@ static indexed_triangle_set build_texture_displacement_in_place( } const bool pin_boundary = !options.displace_border; - // Only the Cylindrical/Spherical methods need these; Triplanar blends each vertex's own - // normal and LSCM solves the patch globally. - Vec3f average_normal = Vec3f::Zero(); - Vec3f patch_centroid = Vec3f::Zero(); - int patch_vertex_count = 0; - for (const stl_triangle_vertex_indices &tri : patch.indices) - for (int i = 0; i < 3; ++i) { - const int vi = tri[i]; - patch_centroid += patch.vertices[size_t(vi)]; - ++patch_vertex_count; - // A brush stroke that split a triangle appends new vertices past the base mesh's own - // (see the get_facets_strict() note above); vertex_normals is sized to the base mesh, - // so those split indices must be skipped here or this reads out of bounds. The main - // displacement loop below guards the same way. - if (vi < int(vertex_normals.size())) - average_normal += vertex_normals[size_t(vi)]; - } - average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); - patch_centroid = (patch_vertex_count > 0) ? Vec3f(patch_centroid / float(patch_vertex_count)) : Vec3f::Zero(); - - // Cylinder axis auto-picked as the world axis *least* aligned with the average normal - // (perpendicular to the outward radial normal, as a cylinder's own axis would be). - Vec3f patch_axis = Vec3f::UnitZ(); - const Vec3f an = average_normal.cwiseAbs(); - if (an.x() <= an.y() && an.x() <= an.z()) - patch_axis = Vec3f::UnitX(); - else if (an.y() <= an.x() && an.y() <= an.z()) - patch_axis = Vec3f::UnitY(); + // Only the Cylindrical/Spherical methods need the centroid and axis; Triplanar blends each + // vertex's own normal and LSCM solves the patch globally. average_normal is also the fallback + // normal the colour pass below uses for a degenerate triangle. + Vec3f average_normal, patch_centroid, patch_axis; + texture_displacement_patch_frame(patch, vertex_normals, patch_centroid, patch_axis, average_normal); // A real unwrap of the whole patch, computed once here rather than per vertex - it is a // per-chart solve over the whole patch, not a per-point formula. Cached, so repeating this @@ -2337,6 +2556,15 @@ static indexed_triangle_set build_texture_displacement_in_place( const std::vector lscm_uvs = (layer->projection_method == TextureProjectionMethod::LSCM) ? compute_lscm_uvs(patch, *layer) : std::vector{}; + // The colour pass below samples per *triangle*, so it takes the per-corner unwrap instead: the + // per-vertex collapse above would hand a triangle at a seam the island layout did not join its + // neighbour's placement, painting one triangle per face from the wrong part of the texture. + // (The displacement itself stays on lscm_uvs - a vertex has one position, so one height.) + const std::vector lscm_corner_uvs = (layer->projection_method == TextureProjectionMethod::LSCM) ? + compute_lscm_corner_uvs(patch, *layer) : + std::vector{}; + const bool corner_uv_ok = !lscm_corner_uvs.empty() && + lscm_corner_uvs.size() == patch.indices.size() * 3; // Colour, if this layer carries any. Area-weighted over each base triangle's *painted* part, // so a triangle the brush only clipped a corner off takes the colour of that corner rather @@ -2371,7 +2599,11 @@ static indexed_triangle_set build_texture_displacement_in_place( const int vi = t[k]; if (vi < int(vertex_normals.size())) n += vertex_normals[size_t(vi)]; - if (have_uv && size_t(vi) < lscm_uvs.size()) + if (!have_uv) + continue; + if (corner_uv_ok) + uv += lscm_corner_uvs[j * 3 + size_t(k)]; + else if (size_t(vi) < lscm_uvs.size()) uv += lscm_uvs[size_t(vi)]; else have_uv = false; @@ -2388,7 +2620,7 @@ static indexed_triangle_set build_texture_displacement_in_place( } for (size_t i = 0; i < mesh.indices.size(); ++i) if (sum_area[i] > 0.f) { - const int idx = color->quantize(sum[i] / sum_area[i]); + const int idx = layer_quantize(sum[i] / sum_area[i]); // A quantizer that declines this colour leaves whatever a lower layer put // there, rather than punching a hole in it. if (idx >= 0) @@ -2548,15 +2780,19 @@ static indexed_triangle_set build_texture_displacement_in_place( // to keep, and interleaving before the filter would have the filter treat two halves of one // blended colour as a disagreement. despeckle_triangle_colors(mesh, triangle_palette, color->despeckle_passes); + merge_small_color_regions(mesh, triangle_palette, color->min_color_region_mm2); std::vector out_color(mesh.indices.size(), 0); for (size_t i = 0; i < mesh.indices.size(); ++i) { if (triangle_palette[i] < 0) continue; const stl_triangle_vertex_indices &t = mesh.indices[i]; - const Vec3f centroid = (mesh.vertices[size_t(t[0])] + mesh.vertices[size_t(t[1])] + - mesh.vertices[size_t(t[2])]) / 3.f; - const int filament = color->resolve ? color->resolve(triangle_palette[i], centroid) + const Vec3f &a = mesh.vertices[size_t(t[0])], &b = mesh.vertices[size_t(t[1])], &c = mesh.vertices[size_t(t[2])]; + const Vec3f centroid = (a + b + c) / 3.f; + Vec3f normal = (b - a).cross(c - a); + const float nl = normal.norm(); + normal = (nl > 0.f) ? Vec3f(normal / nl) : Vec3f::UnitZ(); + const int filament = color->resolve ? color->resolve(triangle_palette[i], centroid, normal) : triangle_palette[i]; if (filament >= 0) out_color[i] = uint8_t(std::min(filament + 1, 255)); @@ -2615,6 +2851,32 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set return out; } +void texture_displacement_patch_frame(const indexed_triangle_set &patch, const std::vector &vertex_normals, + Vec3f ¢er, Vec3f &axis, Vec3f &average_normal) +{ + Vec3f normal_sum = Vec3f::Zero(); + Vec3f centroid_sum = Vec3f::Zero(); + int count = 0; + for (const stl_triangle_vertex_indices &tri : patch.indices) + for (int i = 0; i < 3; ++i) { + const int vi = tri[i]; + centroid_sum += patch.vertices[size_t(vi)]; + ++count; + // A brush stroke that split a triangle appends new vertices past the base mesh's own, and + // vertex_normals is sized to the base mesh, so those indices must be skipped here. + if (vi < int(vertex_normals.size())) + normal_sum += vertex_normals[size_t(vi)]; + } + average_normal = (normal_sum.norm() > 1e-8f) ? Vec3f(normal_sum.normalized()) : Vec3f::UnitZ(); + center = (count > 0) ? Vec3f(centroid_sum / float(count)) : Vec3f::Zero(); + + // The world axis least aligned with the average normal - perpendicular to the outward radial + // normal, as a cylinder's own axis would be. + const Vec3f an = average_normal.cwiseAbs(); + axis = (an.x() <= an.y() && an.x() <= an.z()) ? Vec3f::UnitX() : + (an.y() <= an.x() && an.y() <= an.z()) ? Vec3f::UnitY() : Vec3f::UnitZ(); +} + Transform3d texture_displacement_bake_frame(const Transform3d &volume_to_world) { // World orientation and scale, but the origin moved to where the volume's own origin sits: the @@ -2704,11 +2966,89 @@ void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector namespace { // One decoded texture + placement per sampleable layer, in blend (slot) order. Held by shared_ptr so // the returned closure owns it for as long as the subdivider keeps calling back. +// An unwrap turned into something a *point* sampler can use. LSCM has no formula from position to +// uv - it is a per-triangle map - so a point is placed on the painted patch (the nearest patch +// triangle, and its barycentric coordinates there) and the uv is interpolated from that triangle's own +// per-corner uvs. Exact for a point on the base surface, which is where both samplers are queried: the +// displacement samples refined positions before moving them, and the colour pass samples the foot +// point on the painted pieces. +struct LscmLookup { + indexed_triangle_set patch; // the layer's painted patch, as the unwrap was solved on + AABBTreeIndirect::Tree3f tree; + std::vector corner; // compute_lscm_corner_uvs(patch, layer) + + // False when `pos` is not on this layer's patch (farther than `tol`): there is no uv there, so the + // layer contributes nothing - the same as a non-tiled texture outside its placement. + bool uv_at(const Vec3f &pos, float tol, Vec2f &uv) const + { + size_t hit = 0; + Vec3f foot; + const float d2 = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(patch.vertices, patch.indices, tree, + pos, hit, foot); + if (d2 < 0.f || d2 > tol * tol || hit >= patch.indices.size()) + return false; + const stl_triangle_vertex_indices &t = patch.indices[hit]; + const Vec3f &a = patch.vertices[size_t(t[0])], &b = patch.vertices[size_t(t[1])], &c = patch.vertices[size_t(t[2])]; + const Vec3f e0 = b - a, e1 = c - a, ep = foot - a; + const float d00 = e0.dot(e0), d01 = e0.dot(e1), d11 = e1.dot(e1), dp0 = ep.dot(e0), dp1 = ep.dot(e1); + const float den = d00 * d11 - d01 * d01; + float w1 = 1.f / 3.f, w2 = 1.f / 3.f; // a degenerate triangle takes its centroid's uv + if (std::abs(den) > 1e-20f) { + w1 = (d11 * dp0 - d01 * dp1) / den; + w2 = (d00 * dp1 - d01 * dp0) / den; + } + const Vec2f *c3 = &corner[hit * 3]; + uv = (1.f - w1 - w2) * c3[0] + w1 * c3[1] + w2 * c3[2]; + return true; + } +}; + +// A layer's painted patch as a point-in-region test. Every layer is sampled on its own paint only - the +// analytic projections included: unlike an unwrap they are defined everywhere, so without this every layer's +// relief was stacked over every other layer's painted area, and the top layer's texture showed on all of them. +struct PatchRegion { + indexed_triangle_set patch; + AABBTreeIndirect::Tree3f tree; + + bool contains(const Vec3f &pos, float tol) const + { + size_t hit = 0; + Vec3f foot; + const float d2 = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(patch.vertices, patch.indices, tree, + pos, hit, foot); + return d2 >= 0.f && d2 <= tol * tol; + } +}; + struct PreparedLayer { DecodedHeightTexture tex; TextureDisplacementLayer layer; // a copy of the params (depth/tiling/rotation/offset/blend/...) Vec3f center; // patch centroid, for Cylindrical/Spherical Vec3f axis; // cylinder axis, for Cylindrical + // Unwrap layers only; null when the unwrap failed, in which case sampling falls through to the + // layer's analytic fallback exactly as build_texture_displacement()'s classic path does. + std::shared_ptr lscm; + // The painted patch of a layer without an unwrap lookup (the unwrap's own lookup already stops at its patch). + // Null when the paint covers the whole mesh, where every point is on it. + std::shared_ptr region; + + // The uv to hand sample_layer_height()/sample_layer_color(): nullptr for every analytic projection + // (they project `pos` themselves). False means `pos` is off this layer's paint and the layer must be + // skipped. + bool lscm_uv(const Vec3f &pos, Vec2f &uv, const Vec2f *&out) const + { + out = nullptr; + // Queries lie on the base surface, so anything beyond a hair is off this layer's patch. + constexpr float ON_PATCH_TOL = 0.05f; + if (region && !region->contains(pos, ON_PATCH_TOL)) + return false; + if (!lscm) + return true; + if (!lscm->uv_at(pos, ON_PATCH_TOL, uv)) + return false; + out = &uv; + return true; + } }; // Shared by both point samplers, so the height field and the colour field can never disagree about @@ -2731,10 +3071,12 @@ std::shared_ptr> prepare_sampleable_layers( const std::vector vertex_normals = texture_displacement_vertex_normals(base_mesh); const TriangleMesh selector_mesh(base_mesh); + const float mesh_area = area_3d(base_mesh); for (const TextureDisplacementLayer *layer : ordered) { - if (layer->projection_method == TextureProjectionMethod::LSCM) - continue; // no per-point UV -> not sampleable here (caller falls back to uniform for these) + // Unwrap layers used to be skipped here ("no per-point UV"). That made the default pipeline + // bake an unwrap layer as nothing at all - and since the job then clears the baked layers' + // paint, the painted region simply vanished. They get an LscmLookup below instead. if (need_color && !layer->color_enabled) continue; const TriangleSelector::TriangleSplittingData &data = facets_data[size_t(layer->slot)]; @@ -2750,30 +3092,34 @@ std::shared_ptr> prepare_sampleable_layers( if (patch.indices.empty()) continue; - // Patch centroid + cylinder axis, computed exactly as build_texture_displacement() does, so a + // Patch centroid + cylinder axis, shared with build_texture_displacement() so a // Cylindrical/Spherical layer's detach criterion matches the geometry the bake will produce. - Vec3f average_normal = Vec3f::Zero(); - Vec3f centroid = Vec3f::Zero(); - int count = 0; - for (const stl_triangle_vertex_indices &tri : patch.indices) - for (int i = 0; i < 3; ++i) { - const int vi = tri[i]; - centroid += patch.vertices[size_t(vi)]; - ++count; - if (vi < int(vertex_normals.size())) - average_normal += vertex_normals[size_t(vi)]; + Vec3f centroid, axis, average_normal; + texture_displacement_patch_frame(patch, vertex_normals, centroid, axis, average_normal); + + std::shared_ptr lscm; + if (layer->projection_method == TextureProjectionMethod::LSCM) { + // Solved on the very patch the classic path and the GUI solve it on (same geometry, seam + // angle and edges), so it hits the unwrap cache and lands exactly where the UV editor + // shows it, hand-placed islands and UV edits included. + auto l = std::make_shared(); + l->corner = compute_lscm_corner_uvs(patch, *layer); + if (l->corner.size() == patch.indices.size() * 3) { + l->patch = patch; + l->tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(l->patch.vertices, l->patch.indices); + lscm = std::move(l); } - average_normal = (average_normal.norm() > 1e-8f) ? Vec3f(average_normal.normalized()) : Vec3f::UnitZ(); - centroid = (count > 0) ? Vec3f(centroid / float(count)) : Vec3f::Zero(); + } - Vec3f axis = Vec3f::UnitZ(); - const Vec3f an = average_normal.cwiseAbs(); - if (an.x() <= an.y() && an.x() <= an.z()) - axis = Vec3f::UnitX(); - else if (an.y() <= an.x() && an.y() <= an.z()) - axis = Vec3f::UnitY(); + std::shared_ptr region; + if (!lscm && area_3d(patch) < 0.9999f * mesh_area) { + auto r = std::make_shared(); + r->patch = patch; + r->tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(r->patch.vertices, r->patch.indices); + region = std::move(r); + } - prepared->push_back({ tex, *layer, centroid, axis }); + prepared->push_back({ tex, *layer, centroid, axis, std::move(lscm), std::move(region) }); } return prepared; } @@ -2782,22 +3128,34 @@ std::shared_ptr> prepare_sampleable_layers( ColorFieldSampler make_combined_color_sampler(const indexed_triangle_set &base_mesh, const std::vector &layers, const TextureDisplacementFacetsData &facets_data, - ColorQuantizeFn quantize) + ColorQuantizeFn quantize, + ColorQuantizeFn quantize_pure) { if (!quantize) return nullptr; auto prepared = prepare_sampleable_layers(base_mesh, layers, facets_data, /* need_color */ true); if (prepared->empty()) return nullptr; + // Per layer: a flat-colour image is matched against the filaments alone, when that quantizer + // was supplied; anything else may use the mixes. Decided once here, not per sample. + auto pure = std::make_shared>(prepared->size(), 0); + if (quantize_pure) + for (size_t i = 0; i < prepared->size(); ++i) + (*pure)[i] = analyze_texture_detail((*prepared)[i].layer).flat_colors ? 1 : 0; - return [prepared, quantize = std::move(quantize)](const Vec3f &pos, const Vec3f &normal) -> int { + return [prepared, pure, quantize = std::move(quantize), quantize_pure = std::move(quantize_pure)](const Vec3f &pos, const Vec3f &normal) -> int { // Last one wins: `prepared` is in ascending slot order and the bake lets a higher layer // overwrite a lower one's colour, so the sampler has to resolve overlaps the same way. int result = -1; - for (const PreparedLayer &p : *prepared) { + for (size_t i = 0; i < prepared->size(); ++i) { + const PreparedLayer &p = (*prepared)[i]; + Vec2f uv; + const Vec2f *lscm_uv = nullptr; + if (!p.lscm_uv(pos, uv, lscm_uv)) + continue; Vec3f rgb; - if (sample_layer_color(p.tex, p.layer, pos, normal, rgb, p.center, p.axis, nullptr)) - if (const int idx = quantize(rgb); idx >= 0) + if (sample_layer_color(p.tex, p.layer, pos, normal, rgb, p.center, p.axis, lscm_uv)) + if (const int idx = ((*pure)[i] ? quantize_pure : quantize)(rgb); idx >= 0) result = idx; } return result; @@ -2816,7 +3174,11 @@ HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set float total = 0.f; bool any = false; for (const PreparedLayer &p : *prepared) { - const float h = sample_layer_height(p.tex, p.layer, pos, normal, p.center, p.axis, nullptr); + Vec2f uv; + const Vec2f *lscm_uv = nullptr; + if (!p.lscm_uv(pos, uv, lscm_uv)) + continue; // off this unwrap layer's patch: no uv, so no contribution + const float h = sample_layer_height(p.tex, p.layer, pos, normal, p.center, p.axis, lscm_uv); const float sign = p.layer.invert ? -1.f : 1.f; const float signed_h = (h - p.layer.midlevel) * p.layer.depth_mm * sign; // The first (lowest) sampleable layer folds additively; the rest use their own blend mode - @@ -3264,10 +3626,10 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, p = std::max(p, ll / color_sq); } // The band straddling the paint's edge, refined by plain edge length. Deliberately *not* run - // through detail_error(): outside the paint the sampler still reports full relief (it has no - // per-point paint test), so the chord test there would chase texture detail on a surface the - // bake is going to leave flat. Length alone is what this band needs - the error it is fixing - // is the size of the triangles spanning the displacement step, not the curvature of anything. + // through detail_error(): the sampler reports no relief off the paint, so across its edge the + // chord test sees a step and would chase it down to the length floor. Length alone is what this + // band needs - the error it is fixing is the size of the triangles spanning the displacement + // step, not the curvature of anything. if ((flags & REFINE_BORDER) && border_sq > 0.f) p = std::max(p, ll / border_sq); return p; diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index ea208fbcb3..238422b5fe 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -333,6 +333,10 @@ enum class ColorMixMode : int // height, but its cell is around the size of one facet, so a fine mix can read as texture rather // than as a clean blend. XYDither = 1, + // Per triangle, by its orientation: bands where the surface is upright enough for consecutive + // layers to alternate, the checkerboard where it faces up or down and a layer would be one band. + // The default - a flat-topped part with a mix on top gets no blend at all from bands alone. + Auto = 2, }; // Settings that apply to the whole layer stack rather than to one layer, held per ModelVolume next @@ -396,9 +400,11 @@ struct TextureDisplacementOptions // printer will realise the colours, not about which image they came from. // Interleave pairs of filaments to get colours between them - so four loaded filaments offer far - // more than four colours. Off means every triangle takes one of the loaded filaments exactly. + // more than four colours. Whether a given layer's colours actually use mixes is decided from its + // image (TextureDetail::flat_colors): a texture of flat colours prints in single filaments, a + // photograph or gradient in mixes. Off forces single filaments everywhere. bool color_mix_enabled = true; - ColorMixMode color_mix_mode = ColorMixMode::ZBands; + ColorMixMode color_mix_mode = ColorMixMode::Auto; // Majority-filter passes over the assigned colours. See TextureColorRequest::despeckle_passes - // this is the control for it, and 2 is enough to clear the salt-and-pepper an image with detail // finer than the mesh leaves behind, without eating features that are genuinely a facet wide. @@ -423,17 +429,18 @@ struct TextureDetail float mean_gradient = 0.f; float sharp_fraction = 0.f; float pixels_per_edge = 4.f; + // How much of the image its eight most common colours cover (8 levels per channel), and the + // verdict: a "flat-colour" image (tiles, logos, camouflage) whose colours should each print in a + // single filament, versus a photograph or gradient where interleaved filament mixes pay off. + float flat_share = 0.f; + bool flat_colors = false; }; TextureDetail analyze_texture_detail(const TextureDisplacementLayer &layer); -// The default pipeline's automatic resolution: the refinement edge and the simplification budget the -// texture and the model call for, when the options leave them at "auto". -// - edge = texel size (tile / image width, in world mm, over the finest layer) x pixels per edge, -// but no finer than keeps the refinement under a 12 M triangle cap for this surface area, clamped -// to [0.05 mm, min(5 mm, diagonal / 50)] and rounded up to 0.01 mm; -// - budget = the triangle count an edge of that texel size needs over the surface, scaled by the -// relief depth (a gentle relief needs fewer), stepped to 10 k and clamped to [10 k, 2000 k]. -// `edge_mm` is 0 when no layer has a usable texture. +// The default pipeline's automatic resolution and budget, when the options leave them at "auto": +// bumpmesh.com's defaults - edge = the model's world-space diagonal / 250, clamped to [0.05, 5] mm and +// rounded up to 0.01; budget 750 k. The texel size of the finest layer and its sharpness class are +// reported alongside for the panel. `edge_mm` is 0 for an empty mesh. struct V2Resolution { float edge_mm = 0.f; @@ -510,7 +517,7 @@ using ColorQuantizeFn = std::function; // criterion: that criterion asks where the **perceived** colour changes, and must not see the // interleaving. Refining on every band or dither-cell boundary would spend the whole triangle budget // drawing a pattern the eye is supposed to blend away. -using ColorResolveFn = std::function; +using ColorResolveFn = std::function; // One printable colour: either a loaded filament on its own, or a blend of two of them realised by // interleaving (see ColorMixMode). Plain data, so it can be captured into a background job. @@ -529,6 +536,7 @@ struct PrintableColor struct TextureColorSettings { std::vector palette; + std::vector palette_pure; // the filaments alone, for flat-colour images ColorMixMode mix_mode = ColorMixMode::ZBands; float layer_height = 0.2f; // sizes the Z bands float dither_cell_mm = 0.4f; // sizes the XY dither cells @@ -623,7 +631,12 @@ struct PatchUnwrap std::vector uvs; // one per unwrapped vertex, in mm std::vector source_vertex; // unwrapped vertex -> index into patch.vertices std::vector vertex_chart; // unwrapped vertex -> chart (island) id - std::vector indices; // patch triangles, re-indexed into `uvs` + // The patch's triangles re-indexed into `uvs` - but *grouped by chart*, not left in the patch's + // own order: the charts are flattened one at a time and then concatenated. `source_face` is the + // map back, so anything that needs UVs per triangle corner (as opposed to per vertex) can place + // them against its own triangle list. See compute_lscm_corner_uvs(). + std::vector indices; + std::vector source_face; // unwrapped triangle -> index into patch.indices // Per chart, the centroid of its uvs - the point a TextureIsland's rotation turns about. std::vector chart_centroid; // Edges belonging to exactly one triangle: the outline of each island. Indices into `uvs`. This @@ -675,17 +688,30 @@ bool join_chart_placement(const PatchUnwrap &unwrap, const std::vector> &seam_edges = {}); -// One UV per patch vertex, for displacement. Displacement is inherently per-vertex - a vertex has -// exactly one position, so it can only be pushed out by one height - which means a seam vertex has -// to settle on a single one of its charts' UVs (the first, arbitrarily). That is not a compromise -// in the result: the surface stays watertight either way, since neighbouring vertices each move -// along their own normals and nothing depends on the UVs agreeing across the seam. It is only the -// *display* in the UV editor that needs the duplicated-vertex form above. +// One UV per patch vertex, **for displacement only**. Displacement is inherently per-vertex - a +// vertex has exactly one position, so it can only be pushed out by one height - which means a seam +// vertex has to settle on a single one of its charts' UVs (the first, arbitrarily). That is not a +// compromise in the result: the surface stays watertight either way, since neighbouring vertices +// each move along their own normals and nothing depends on the UVs agreeing across the seam. +// +// Anything that samples or draws per *triangle* must use compute_lscm_corner_uvs() instead. This +// collapse is wrong for those: a triangle at a seam that the island layout did not join gets handed +// a neighbouring island's placement, which showed up as a single skewed triangle per face and as +// every island's texture following the lowest-numbered island when it was dragged. // // Returns an empty vector if the patch has no triangles. Takes the whole layer because it applies // both the layer's seam angle and its hand-placed islands. std::vector compute_lscm_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer); +// Three UVs per patch triangle (corner 0, 1, 2 of triangle i at index 3i..3i+2), in the patch's own +// triangle order. Unlike compute_lscm_uvs() this keeps a seam vertex's separate per-chart copies: a +// triangle belongs to exactly one chart and is given that chart's UVs, which is what every consumer +// that works per triangle rather than per vertex needs - the fast preview's flat mesh, the checker +// overlay and the bake's per-facet colour. +// +// Returns an empty vector if the patch has no triangles or the unwrap carries no source_face map. +std::vector compute_lscm_corner_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer); + // The TextureDisplacementLayer::lscm_uv_overrides key for one unwrapped vertex (an index into PatchUnwrap::uvs). inline int lscm_uv_override_key(int unwrapped_vertex) { return -(unwrapped_vertex + 1); } @@ -749,6 +775,10 @@ struct TextureColorRequest // RGB -> palette index. Supplied by the GUI, which owns both the perceptual matching and the list // of filaments actually loaded (see ColorQuantizeFn). ColorQuantizeFn quantize; + // The same over the loaded filaments alone, no mixes. Optional; when given, a layer whose image is + // made of flat colours (TextureDetail::flat_colors) is matched with this one, so a tile or a logo + // prints in single filaments while a photograph on another layer may still use mixes. + ColorQuantizeFn quantize_pure; // Palette index + position -> filament. Optional: without it a palette index is taken to be a // filament index directly, which is the no-mixing case. ColorResolveFn resolve; @@ -760,6 +790,11 @@ struct TextureColorRequest // its edge neighbours removes exactly that, and leaves any feature wider than a facet alone. 0 // turns it off. int despeckle_passes = 0; + // After the despeckle: connected patches of one colour smaller than this (mm^2) are recoloured + // to whatever borders them most - see merge_small_color_regions(). The despeckle only reaches + // single facets; an image detail a few facets wide still leaves thousands of pinhead islands + // that the slicer's multi-material segmentation cannot digest. 0 turns it off. + float min_color_region_mm2 = 0.5f; // Filled per *base mesh* triangle (the bake is topology-preserving, so this indexes the returned // mesh too): the quantize callback's index plus one, or 0 for "this triangle takes no colour from // the texture". The +1 is not arbitrary - it lines up with EnforcerBlockerType, where 0 is NONE @@ -767,6 +802,15 @@ struct TextureColorRequest // straight to a TriangleSelector without a second mapping table. std::vector *out_triangle = nullptr; }; + +// Recolours connected patches of one colour whose area is under `min_area_mm2` to the colour that +// borders them most (by shared edge length). Colour is per triangle, -1 = none (never merged into, +// never merged away). Removes the confetti a detailed image leaves on a fine mesh - thousands of +// one-facet zones, which the slicer's multi-material segmentation cannot digest. Patches are +// processed smallest-first, reading their neighbours' current colour, so a chain of tiny islands +// collapses into its surroundings rather than into each other. +void merge_small_color_regions(const indexed_triangle_set &mesh, std::vector &color, float min_area_mm2); + // Where the volume sits on the plate: its instance transform times its own volume transform, i.e. // mesh coordinates -> world millimetres. // @@ -796,6 +840,22 @@ Transform3d texture_displacement_volume_to_world(const ModelVolume &volume); // removed, i.e. world orientation and scale about the volume's own origin. See build_texture_displacement(). Transform3d texture_displacement_bake_frame(const Transform3d &volume_to_world); +// Area-weighted vertex normals of `its` - the directions the bake both projects and displaces along. +std::vector texture_displacement_vertex_normals(const indexed_triangle_set &its); + +// The frame the Cylindrical and Spherical projections wrap around: `patch`'s triangle-corner centroid, +// the world axis *least* aligned with the average of `vertex_normals` over those corners (a cylinder's +// own axis is perpendicular to its outward radial normal), and that average normal itself. +// +// Results come out in whatever frame `patch` is given in. The bake calls this with the patch already in +// the bake frame (see texture_displacement_bake_frame()), so a preview that wants to reproduce the +// bake's projection must too, or it wraps the texture around a different centre. Corners past the end +// of `vertex_normals` - the ones a brush stroke appended - contribute to the centroid but carry no +// normal, exactly as the bake's own loops skip them. +void texture_displacement_patch_frame(const indexed_triangle_set &patch, + const std::vector &vertex_normals, + Vec3f ¢er, Vec3f &axis, Vec3f &average_normal); + // Convenience overload for main-thread callers: extracts the mesh/layers/paint data/options from // `volume` and forwards to the overload above. indexed_triangle_set build_texture_displacement(const ModelVolume &volume); @@ -844,7 +904,8 @@ using ColorFieldSampler = std::function &layers, const TextureDisplacementFacetsData &facets_data, - ColorQuantizeFn quantize); + ColorQuantizeFn quantize, + ColorQuantizeFn quantize_pure = nullptr); HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, const std::vector &layers, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index bd396d9484..34b47c1825 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -224,6 +224,9 @@ constexpr int PALETTE_LUT_EDGE = 24; // Ceiling on the printable palette, which bounds that fill cost (and the shader's uniform array). constexpr int PALETTE_MAX_ENTRIES = 64; +// Ceiling on the filaments the palette's entries can refer to (the bump shader's filament_rgb[]); +// mmu segmentation stops at Extruder16 anyway. +constexpr int PALETTE_MAX_FILAMENTS = 16; // sRGB (0..1) <-> CIELAB, D65. Exactly what the bump shader's srgb_to_lab() computes, so the CPU // quantizer, the mixed-palette entries and the per-fragment preview all match in the same space. @@ -408,7 +411,7 @@ void GLGizmoTextureDisplacement::on_shutdown() m_subdivide_preview_tris = -1; m_subdivide_preview_glmodel.reset(); m_bump_active_chart = -1; - m_bump_active_vertex.clear(); + m_bump_active_face.clear(); m_bump_island_delta = Eigen::Matrix::Identity(); m_island_drag_active = false; m_island_move_set.clear(); @@ -481,6 +484,7 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() rebuild_paint_overlay(); m_paint_overlay_dirty = false; } + rebuild_other_paint_overlay(); // a no-op unless another layer's paint, the active layer or the preview changed // is_initialized() alone is not enough: render_bump_preview_mesh() also needs an active layer // with a decoded texture and a compiled shader, and bails silently without them. Hiding the real // volume for a bump pass that then draws nothing is what made the model vanish - most obviously @@ -518,12 +522,33 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() render_triangles(selection); } + // Every other layer's paint, in muted grey, so all layers stay visible while one of them is edited. Drawn + // before the active layer's tint so that one reads on top where the two overlap. + if (show_paint_overlay) + render_paint_overlay(m_other_paint_glmodel); + // The translucent paint tint. Needed in the bump view because the opaque highlight above is // skipped there, and in the true-displacement view because the displaced surface rises *above* // the undisplaced overlay geometry and hides it exactly where the relief is strongest - in both // cases leaving an erase stroke with no visible effect until the next full preview rebuild. if (show_paint_overlay && (use_bump || use_true_preview)) - render_paint_overlay(); + render_paint_overlay(m_paint_overlay_glmodel); + + // The UV editor's island selection, shown on the model. Polled here rather than pushed: the pane + // changes its selection in its own mouse handling, and a compare of a few ints per frame is free. + { + const TextureDisplacementLayer *al = active_layer(); + const UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas(); + if (m_show_uv_editor && al != nullptr && al->projection_method == TextureProjectionMethod::LSCM && + uv_canvas != nullptr && !m_uv_editor_unwrap.empty()) { + if (uv_canvas->selected_islands() != m_island_overlay_selection) + rebuild_island_overlay(uv_canvas->selected_islands()); + render_island_overlay(); + } else if (m_island_overlay_glmodel.is_initialized()) { + m_island_overlay_glmodel.reset(); + m_island_overlay_selection.clear(); + } + } // Diagnostic overlays, drawn on top of whatever preview is active (both pull toward the camera // with a polygon offset so they win the depth test against the coincident surface). @@ -1055,6 +1080,49 @@ std::vector GLGizmoTextureDisplacement::compute_layer_vertex_uvs(const in return {}; // Triplanar / Cylindrical / Spherical: the shader projects on its own } +int GLGizmoTextureDisplacement::layer_projection_frame(const indexed_triangle_set &local_patch, + const TextureDisplacementLayer &layer, + Vec3f ¢er, Vec3f &axis) const +{ + center = Vec3f::Zero(); + axis = Vec3f::UnitZ(); + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || (layer.projection_method != TextureProjectionMethod::Cylindrical && + layer.projection_method != TextureProjectionMethod::Spherical)) + return 0; + // The bake averages the *whole mesh's* vertex normals over the patch's corners, so this has to as + // well: a patch-only average would sometimes quantize to a different world axis and wrap the + // texture the other way round. Both meshes go through patch_in_world() first, which is the frame + // the shaders' tex_pos lives in. + Vec3f average_normal; + texture_displacement_patch_frame(patch_in_world(local_patch), + texture_displacement_vertex_normals(patch_in_world(mv->mesh().its)), + center, axis, average_normal); + return layer.projection_method == TextureProjectionMethod::Cylindrical ? 1 : 2; +} + +std::vector GLGizmoTextureDisplacement::compute_layer_corner_uvs(const indexed_triangle_set &local_patch, + const TextureDisplacementLayer &layer) const +{ + if (layer.projection_method == TextureProjectionMethod::LSCM) { + const indexed_triangle_set patch = patch_in_world(local_patch); + const float aspect = layer_texture_aspect(layer); + std::vector uv = compute_lscm_corner_uvs(patch, layer); + for (Vec2f &p : uv) + p = apply_uv_transform(p, layer, aspect); + return uv; + } + // Single-valued per point: fan the per-vertex result out over the corners. + const std::vector per_vertex = compute_layer_vertex_uvs(local_patch, layer); + if (per_vertex.size() != local_patch.vertices.size()) + return {}; + std::vector corner(local_patch.indices.size() * 3); + for (size_t f = 0; f < local_patch.indices.size(); ++f) + for (int k = 0; k < 3; ++k) + corner[f * 3 + size_t(k)] = per_vertex[size_t(local_patch.indices[f][k])]; + return corner; +} + void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() { m_bump_preview_glmodel.reset(); @@ -1087,11 +1155,20 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() // reconstructed in the fragment shader the way a triplanar projection can. This is also what // makes the fast preview follow the UV editor: the uvs move when an island is dragged, so this // mesh rebuilds (on drag end) with them. The other projections keep projecting in-shader. + // Per *corner*, not per vertex: the mesh below is flat (unshared) anyway, so each triangle can + // carry its own chart's UVs - see compute_layer_corner_uvs(). const TextureDisplacementLayer *active = active_layer(); - std::vector vertex_uv = active != nullptr ? compute_layer_vertex_uvs(patch, *active) : std::vector{}; - m_bump_preview_uses_vertex_uv = vertex_uv.size() == patch.vertices.size(); + std::vector corner_uv = active != nullptr ? compute_layer_corner_uvs(patch, *active) : std::vector{}; + m_bump_preview_uses_vertex_uv = corner_uv.size() == patch.indices.size() * 3; if (!m_bump_preview_uses_vertex_uv) - vertex_uv.clear(); + corner_uv.clear(); + m_bump_projection_mode = (active != nullptr && !m_bump_preview_uses_vertex_uv) ? + layer_projection_frame(patch, *active, m_bump_patch_center, m_bump_patch_axis) : 0; + + // Which triangles the in-flight UV drag moves. Computed here, against the very patch this mesh is + // built from, so the flags can never be indexed by a different triangle count than they were sized + // for (the drag starts from the flushed facet data, a brush stroke changes the live selector). + compute_bump_active_faces(m_bump_active_chart >= 0 ? m_island_move_set : std::vector{}, patch.indices.size()); // Colour is quantized per *fragment* in the shader now (see the .fs), so this mesh carries no // colour of its own - the palette and the colour texture are uniforms, and every pixel matches the @@ -1112,29 +1189,30 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() // quality here because the bump shader takes its surface normal from screen-space derivatives of // position (dFdx/dFdy), not from a per-vertex normal. normal.y flags the UV-editor island being // dragged so the shader can move just that island via the island_delta uniform. - const bool have_active = m_bump_active_chart >= 0 && !m_bump_active_vertex.empty(); - const size_t tri_total = patch.indices.size() + rest.indices.size(); + const size_t tri_total = patch.indices.size() + rest.indices.size(); init_data.reserve_vertices(tri_total * 3); init_data.reserve_indices(tri_total * 3); unsigned vcount = 0; - const auto emit_triangles = [&](const indexed_triangle_set &its, float weight) { - for (const stl_triangle_vertex_indices &tri : its.indices) { + const auto emit_triangles = [&](const indexed_triangle_set &its, float weight, bool painted) { + for (size_t f = 0; f < its.indices.size(); ++f) { + const stl_triangle_vertex_indices &tri = its.indices[f]; + // One value for the whole triangle: island_active is an interpolated varying, so the three + // corners have to agree or the shader moves part of a triangle and not the rest. + const float act = (painted && f < m_bump_active_face.size() && m_bump_active_face[f]) ? 1.f : 0.f; for (int i = 0; i < 3; ++i) { const int idx = tri[i]; - const float act = (have_active && idx >= 0 && size_t(idx) < m_bump_active_vertex.size() && - m_bump_active_vertex[size_t(idx)]) ? 1.f : 0.f; - const Vec2f uv = (weight > 0.5f && m_bump_preview_uses_vertex_uv && size_t(idx) < vertex_uv.size()) ? - vertex_uv[size_t(idx)] : Vec2f::Zero(); + const Vec2f uv = (painted && m_bump_preview_uses_vertex_uv) ? corner_uv[f * 3 + size_t(i)] + : Vec2f::Zero(); init_data.add_vertex(its.vertices[size_t(idx)], Vec3f(weight, act, 0.f), uv); } init_data.add_triangle(vcount, vcount + 1, vcount + 2); vcount += 3; } }; - emit_triangles(patch, 1.f); // painted -> bumped, and coloured by the shader + emit_triangles(patch, 1.f, true); // painted -> bumped, and coloured by the shader // Untouched surface: flat, so it still shows but isn't bumped - and uncoloured, which is what the // bake leaves it as (EnforcerBlockerType::NONE, i.e. the volume's own filament). - emit_triangles(rest, 0.f); + emit_triangles(rest, 0.f, false); m_bump_preview_glmodel.init_from(std::move(init_data)); // GLModel::render() unconditionally re-sets the shader's "uniform_color" from this internal @@ -1156,24 +1234,25 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() } } -void GLGizmoTextureDisplacement::compute_bump_active_vertices(const std::vector &charts) +void GLGizmoTextureDisplacement::compute_bump_active_faces(const std::vector &charts, size_t patch_face_count) { - m_bump_active_vertex.clear(); - const ModelVolume *mv = texture_volume(); - if (mv == nullptr || charts.empty()) + m_bump_active_face.clear(); + if (charts.empty() || patch_face_count == 0) return; const PatchUnwrap &u = m_uv_editor_unwrap; - m_bump_active_vertex.assign(mv->mesh().its.vertices.size(), 0); - // Flag the base vertices of every chart being moved. For a group/multi move that is more than one + if (u.source_face.size() != u.indices.size()) + return; + m_bump_active_face.assign(patch_face_count, 0); + // Flag every triangle of every chart being moved. For a group/multi move that is more than one // chart, but since such a move is a pure translation the shader applies the same delta to them all // (see on_island_edited) - exactly the "joined islands move together" behaviour. - for (size_t i = 0; i < u.uvs.size(); ++i) { - if (i >= u.vertex_chart.size() || - std::find(charts.begin(), charts.end(), u.vertex_chart[i]) == charts.end()) + for (size_t t = 0; t < u.indices.size(); ++t) { + const int f = u.source_face[t]; + const int v0 = u.indices[t][0]; // a triangle lies in one chart, so any corner names it + if (f < 0 || size_t(f) >= m_bump_active_face.size() || v0 < 0 || size_t(v0) >= u.vertex_chart.size()) continue; - const int sv = (i < u.source_vertex.size()) ? u.source_vertex[i] : -1; - if (sv >= 0 && size_t(sv) < m_bump_active_vertex.size()) - m_bump_active_vertex[size_t(sv)] = 1; + if (std::find(charts.begin(), charts.end(), u.vertex_chart[size_t(v0)]) != charts.end()) + m_bump_active_face[size_t(f)] = 1; } } @@ -1324,6 +1403,12 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than // projecting; see rebuild_bump_preview_mesh(). shader->set_uniform("use_vertex_uv", m_bump_preview_uses_vertex_uv); + // Cylindrical/Spherical wrap around the painted patch's own centre, which no fragment can derive: + // captured with the mesh (see rebuild_bump_preview_mesh()) and handed over here. 0 is the planar + // projection every other in-shader path uses. + shader->set_uniform("projection_mode", m_bump_projection_mode); + shader->set_uniform("patch_center", m_bump_patch_center); + shader->set_uniform("patch_axis", m_bump_patch_axis); // The filament palette the mesh's per-triangle indices refer to. Count 0 means "no layer is // colouring", and the shader keeps the model's own colour for every fragment. @@ -1334,11 +1419,32 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() (color_tex != nullptr) ? int(std::min(m_bump_preview_palette.size(), size_t(PALETTE_MAX_ENTRIES))) : 0; shader->set_uniform("palette_count", palette_count); shader->set_uniform("has_color_tex", color_tex != nullptr); + // A flat-colour image is matched against single filaments only, as the bake does. + shader->set_uniform("pure_only", color_tex != nullptr && analyze_texture_detail(*layer).flat_colors); for (int i = 0; i < palette_count; ++i) { - const Vec3f &rgb = m_bump_preview_palette[size_t(i)].rgb; - shader->set_uniform(("palette_rgb[" + std::to_string(i) + "]").c_str(), rgb); - shader->set_uniform(("palette_lab[" + std::to_string(i) + "]").c_str(), srgb_to_lab(rgb)); + const PaletteEntry &e = m_bump_preview_palette[size_t(i)]; + const std::string idx = "[" + std::to_string(i) + "]"; + shader->set_uniform(("palette_rgb" + idx).c_str(), e.rgb); + shader->set_uniform(("palette_lab" + idx).c_str(), srgb_to_lab(e.rgb)); + // How the entry prints: its filament, or for a mix the two it interleaves and in what ratio. + shader->set_uniform(("palette_a" + idx).c_str(), e.a); + shader->set_uniform(("palette_b" + idx).c_str(), e.b); + shader->set_uniform(("palette_num" + idx).c_str(), e.num); + shader->set_uniform(("palette_den" + idx).c_str(), e.den); } + // The filaments those indices refer to, and the interleave the shader resolves a mix with - the + // same inputs make_mix_resolver() gets, so the preview shows the pattern that prints rather than + // the mix's smooth average colour. m_palette_filaments is what m_bump_preview_palette was built from. + const int filament_count = + (palette_count > 0) ? int(std::min(m_palette_filaments.size(), size_t(PALETTE_MAX_FILAMENTS))) : 0; + shader->set_uniform("filament_count", filament_count); + for (int i = 0; i < filament_count; ++i) { + const ColorRGBA &c = m_palette_filaments[size_t(i)]; + shader->set_uniform(("filament_rgb[" + std::to_string(i) + "]").c_str(), Vec3f(c.r(), c.g(), c.b())); + } + shader->set_uniform("mix_mode", int(mv->texture_displacement_options.color_mix_mode)); + shader->set_uniform("layer_height", color_band_mm(*mv)); // as color_settings_for() + shader->set_uniform("dither_cell", std::max(m_subdivide_color_mm, 0.05f) * 2.f); // as color_settings_for() if (color_tex != nullptr) { shader->set_uniform("color_tex", 1); glsafe(::glActiveTexture(GL_TEXTURE1)); @@ -1374,6 +1480,64 @@ bool GLGizmoTextureDisplacement::bump_preview_ready() const return wxGetApp().get_shader("texture_displacement_bump") != nullptr; } +// Appends a painted patch to an overlay, lifted onto the displaced surface where that has the base mesh's +// topology (see rebuild_paint_overlay()). +static void append_paint_patch(GLModel::Geometry &out, const indexed_triangle_set &patch, const std::vector *displaced) +{ + unsigned n = unsigned(out.vertices_count()); + for (const stl_triangle_vertex_indices &tri : patch.indices) { + for (int i = 0; i < 3; ++i) { + const size_t idx = size_t(tri[i]); + out.add_vertex((displaced != nullptr && idx < displaced->size()) ? (*displaced)[idx] : patch.vertices[idx]); + } + out.add_triangle(n, n + 1, n + 2); + n += 3; + } +} + +void GLGizmoTextureDisplacement::rebuild_other_paint_overlay() +{ + const ModelVolume *mv = texture_volume(); + // What it depends on: the volume, which layer is active, every other layer's paint (by its timestamp) and the + // displaced positions it is lifted onto. Compared every frame, rebuilt only when it differs. + std::string key; + if (mv != nullptr) { + key = std::to_string(mv->id().id) + ":" + std::to_string(m_active_layer_slot) + (m_use_bump_preview ? ":b:" : ":t:") + + std::to_string(reinterpret_cast(m_preview_its.vertices.data())) + ":" + + std::to_string(m_preview_its.vertices.size()); + for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) + if (l.slot != m_active_layer_slot && l.slot >= 0 && l.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) + key += "|" + std::to_string(l.slot) + "@" + std::to_string(mv->texture_displacement_facet(l.slot).timestamp()); + } + if (key == m_other_paint_key) + return; + m_other_paint_key = std::move(key); + m_other_paint_glmodel.reset(); + if (mv == nullptr) + return; + + const std::vector *displaced = nullptr; + if (!m_use_bump_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() && + !m_preview_its.vertices.empty()) + displaced = &m_preview_its.vertices; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) { + if (l.slot == m_active_layer_slot || l.slot < 0 || l.slot >= int(TEXTURE_DISPLACEMENT_MAX_LAYERS) || + mv->texture_displacement_facet(l.slot).empty()) + continue; + TriangleSelector selector(mv->mesh()); + selector.deserialize(mv->texture_displacement_facet(l.slot).get_data(), false); + append_paint_patch(init_data, selector.get_facets_strict(EnforcerBlockerType::ENFORCER), displaced); + } + if (init_data.is_empty()) + return; + m_other_paint_glmodel.init_from(std::move(init_data)); + // Neutral grey: painted, but not the layer the brush is working on. + m_other_paint_glmodel.set_color(ColorRGBA(0.55f, 0.58f, 0.60f, 0.35f)); +} + void GLGizmoTextureDisplacement::rebuild_paint_overlay() { m_paint_overlay_glmodel.reset(); @@ -1403,27 +1567,18 @@ void GLGizmoTextureDisplacement::rebuild_paint_overlay() init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; init_data.reserve_vertices(patch.indices.size() * 3); init_data.reserve_indices(patch.indices.size() * 3); - unsigned n = 0; - for (const stl_triangle_vertex_indices &tri : patch.indices) { - for (int i = 0; i < 3; ++i) { - const size_t idx = size_t(tri[i]); - init_data.add_vertex((displaced != nullptr && idx < displaced->size()) ? (*displaced)[idx] - : patch.vertices[idx]); - } - init_data.add_triangle(n, n + 1, n + 2); - n += 3; - } + append_paint_patch(init_data, patch, displaced); m_paint_overlay_glmodel.init_from(std::move(init_data)); // GLModel::render() re-sets "uniform_color" from this field just before drawing, so the colour // has to be set here rather than as a uniform at draw time. m_paint_overlay_glmodel.set_color(ColorRGBA(0.16f, 0.79f, 0.35f, 0.38f)); } -void GLGizmoTextureDisplacement::render_paint_overlay() +void GLGizmoTextureDisplacement::render_paint_overlay(GLModel &overlay) { const ModelObject *mo = m_c->selection_info()->model_object(); const ModelVolume *mv = texture_volume(); - if (mo == nullptr || mv == nullptr || !m_paint_overlay_glmodel.is_initialized()) + if (mo == nullptr || mv == nullptr || !overlay.is_initialized()) return; GLShaderProgram *shader = wxGetApp().get_shader("flat"); if (shader == nullptr) @@ -1443,7 +1598,78 @@ void GLGizmoTextureDisplacement::render_paint_overlay() glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); glsafe(::glPolygonOffset(-1.5f, -1.5f)); glsafe(::glDepthMask(GL_FALSE)); - m_paint_overlay_glmodel.render(); + overlay.render(); + glsafe(::glDepthMask(GL_TRUE)); + glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); + shader->stop_using(); +} + +void GLGizmoTextureDisplacement::rebuild_island_overlay(const std::vector &selection) +{ + m_island_overlay_glmodel.reset(); + m_island_overlay_selection = selection; + const ModelVolume *mv = texture_volume(); + if (mv == nullptr || selection.empty() || m_uv_editor_unwrap.empty()) + return; + // The unwrap was made from the painted patch in the bake frame; the same extraction on the + // volume's own mesh gives the same triangles and vertex order in local coordinates, which is the + // frame the overlay is drawn in (with the volume's transform, like the paint tint). + const indexed_triangle_set patch = extract_painted_patch(mv->mesh().its, m_uv_editor_state.facets); + const PatchUnwrap &uw = m_uv_editor_unwrap; + std::vector chosen(size_t(std::max(uw.chart_count, 0)), 0); + for (const int c : selection) + if (c >= 0 && size_t(c) < chosen.size()) + chosen[size_t(c)] = 1; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + unsigned n = 0; + for (const stl_triangle_vertex_indices &tri : uw.indices) { + const int v0 = tri[0]; + if (v0 < 0 || size_t(v0) >= uw.vertex_chart.size()) + continue; + const int c = uw.vertex_chart[size_t(v0)]; + if (c < 0 || size_t(c) >= chosen.size() || !chosen[size_t(c)]) + continue; + bool ok = true; + for (int k = 0; k < 3 && ok; ++k) { + const int u = tri[k]; + ok = u >= 0 && size_t(u) < uw.source_vertex.size() && uw.source_vertex[size_t(u)] >= 0 && + size_t(uw.source_vertex[size_t(u)]) < patch.vertices.size(); + } + if (!ok) + continue; + for (int k = 0; k < 3; ++k) + init_data.add_vertex(patch.vertices[size_t(uw.source_vertex[size_t(tri[k])])]); + init_data.add_triangle(n, n + 1, n + 2); + n += 3; + } + if (n == 0) + return; + m_island_overlay_glmodel.init_from(std::move(init_data)); + m_island_overlay_glmodel.set_color(ColorRGBA(0.10f, 0.55f, 0.95f, 0.45f)); // the pane's selection blue +} + +void GLGizmoTextureDisplacement::render_island_overlay() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelVolume *mv = texture_volume(); + if (mo == nullptr || mv == nullptr || !m_island_overlay_glmodel.is_initialized()) + return; + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + const Selection &selection = m_parent.get_selection(); + const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + // Above the paint tint (a larger offset), translucent, no depth writes - a marker, not geometry. + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(-2.0f, -2.0f)); + glsafe(::glDepthMask(GL_FALSE)); + m_island_overlay_glmodel.render(); glsafe(::glDepthMask(GL_TRUE)); glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); shader->stop_using(); @@ -1475,6 +1701,14 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() std::vector uv = compute_layer_vertex_uvs(patch, *layer); const bool have_uvs = uv.size() == patch.vertices.size(); m_uvcheck_uses_vertex_uv = have_uvs; + m_uvcheck_projection_mode = have_uvs ? 0 : + layer_projection_frame(patch, *layer, m_uvcheck_patch_center, m_uvcheck_patch_axis); + // Per corner as well, for the same reason the bump mesh takes them: under LSCM a seam vertex has a + // different uv in each island it borders, so the shared-vertex form drew one triangle per face from + // a neighbouring island's placement. Only the *drawing* needs this; the distortion metric below is + // a per-vertex average by construction and keeps using `uv`. + const std::vector corner_uv = compute_layer_corner_uvs(patch, *layer); + const bool have_corner_uvs = have_uvs && corner_uv.size() == patch.indices.size() * 3; // Per-vertex area distortion in [0,1] (0.5 == ideal), only when both requested and possible. std::vector distortion(patch.vertices.size(), 0.5f); @@ -1513,13 +1747,23 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; - init_data.reserve_vertices(patch.vertices.size()); + // Flat (one vertex per triangle corner), so each triangle can carry its own island's uv - see + // have_corner_uvs above. Costs nothing in shading quality: the overlay shades from uv and the + // interpolated distortion value alone, never from a per-vertex normal. + init_data.reserve_vertices(patch.indices.size() * 3); init_data.reserve_indices(patch.indices.size() * 3); - for (size_t vi = 0; vi < patch.vertices.size(); ++vi) - init_data.add_vertex(patch.vertices[vi], Vec3f(distortion[vi], 0.f, 0.f), - have_uvs ? uv[vi] : Vec2f::Zero()); - for (const stl_triangle_vertex_indices &tri : patch.indices) - init_data.add_triangle(unsigned(tri[0]), unsigned(tri[1]), unsigned(tri[2])); + unsigned vcount = 0; + for (size_t f = 0; f < patch.indices.size(); ++f) { + const stl_triangle_vertex_indices &tri = patch.indices[f]; + for (int k = 0; k < 3; ++k) { + const size_t vi = size_t(tri[k]); + init_data.add_vertex(patch.vertices[vi], Vec3f(distortion[vi], 0.f, 0.f), + have_corner_uvs ? corner_uv[f * 3 + size_t(k)] : + (have_uvs ? uv[vi] : Vec2f::Zero())); + } + init_data.add_triangle(vcount, vcount + 1, vcount + 2); + vcount += 3; + } m_uvcheck_glmodel.init_from(std::move(init_data)); } @@ -1560,6 +1804,11 @@ void GLGizmoTextureDisplacement::render_uvcheck_mesh() shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f); shader->set_uniform("uv_offset", layer->offset); shader->set_uniform("use_vertex_uv", m_uvcheck_uses_vertex_uv); + shader->set_uniform("projection_mode", m_uvcheck_projection_mode); + shader->set_uniform("patch_center", m_uvcheck_patch_center); + shader->set_uniform("patch_axis", m_uvcheck_patch_axis); + // Was never uploaded, so the checker disagreed with the bake for any non-square height map. + shader->set_uniform("tex_aspect", layer_texture_aspect(*layer)); // Coincident with the base surface, so pull it toward the camera to win the depth test. glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); @@ -1870,6 +2119,8 @@ void GLGizmoTextureDisplacement::update_uv_editor() } // Padding disabled (0): the user asked to pack islands with no gap between them. m_uv_editor_unwrap = compute_patch_unwrap(patch, layer->lscm_seam_angle_deg, 0.f, layer->lscm_seam_edges); + m_island_overlay_glmodel.reset(); // the islands were renumbered: rebuilt from the pane's selection next frame + m_island_overlay_selection.clear(); // Re-apply any stored UV edits onto the fresh unwrap, so the pane shows exactly what // compute_lscm_uvs() will bake (which applies the same overrides). apply_lscm_uv_overrides(m_uv_editor_unwrap, layer->lscm_uv_overrides); @@ -2159,6 +2410,17 @@ void GLGizmoTextureDisplacement::run_uv_command(int cmd, float value) // Closed with the pane's own X: keep it closed until asked again, and upload the background afresh then. m_show_uv_editor = false; m_uv_editor_bg = UVBackground::None; + // The seam tool belongs to the pane: left on with the pane gone, every stroke on the model would + // be swallowed as a seam click and nothing would paint. + if (m_seam_edit_mode) { + m_seam_edit_mode = false; + m_seam_hover_edge = { -1, -1 }; + m_seam_hover_vertex = -1; + m_seam_hover_glmodel.reset(); + m_seam_path_anchor = -1; + m_seam_anchor_glmodel.reset(); + push_uv_pane_state(); + } return; } if (cmd == int(Command::SetBackground)) { @@ -2574,10 +2836,10 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse // Decide the moved set once, at drag start: the whole selection + join groups for a move, or // just the primary for a rotate/scale. m_island_move_set = is_move ? build_island_move_set(*layer, island) : std::vector{ island }; - // Set up the GPU drag: flag the moved islands' vertices and bake the mesh once (via the dirty - // flag). From then on the drag is a uniform update, no rebuild - see render_bump_preview_mesh(). + // Set up the GPU drag: bake the mesh once (via the dirty flag), which is also what flags the + // moved islands' triangles. From then on the drag is a uniform update, no rebuild - see + // render_bump_preview_mesh(). m_bump_active_chart = island; - compute_bump_active_vertices(m_island_move_set); m_bump_island_delta = Eigen::Matrix::Identity(); m_bump_preview_dirty = true; } @@ -2604,7 +2866,7 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse if (finished) { m_island_drag_active = false; m_bump_active_chart = -1; - m_bump_active_vertex.clear(); + m_bump_active_face.clear(); m_island_move_set.clear(); m_bump_island_delta = Eigen::Matrix::Identity(); rebuild_preview(); // the real displaced geometry moved: recompute it once, at the end @@ -2989,7 +3251,13 @@ void GLGizmoTextureDisplacement::update_model_object() if (!mv->is_model_part()) continue; ++idx; - updated |= mv->texture_displacement_facet(m_active_layer_slot).set(*m_triangle_selectors[idx]); + FacetsAnnotation &facet = mv->texture_displacement_facet(m_active_layer_slot); + // See m_selectors_stale: the mask could not be loaded into this selector, so an empty selector + // here is "failed to load", not "nothing painted", and writing it back would erase the paint. + // A selector that does hold something is the user's own work and must be flushed as usual. + if (m_selectors_stale && !facet.empty() && m_triangle_selectors[idx]->serialize().triangles_to_split.empty()) + continue; + updated |= facet.set(*m_triangle_selectors[idx]); } // The fast (bump) preview reads the live selector, so it has to be rebuilt after any stroke that @@ -3018,13 +3286,28 @@ void GLGizmoTextureDisplacement::update_from_model_object(bool first_update) ebt_colors.push_back(GLVolume::NEUTRAL_COLOR); ebt_colors.push_back(TriangleSelectorGUI::enforcers_color); ebt_colors.push_back(TriangleSelectorGUI::blockers_color); + m_selectors_stale = false; for (const ModelVolume *mv : mo->volumes) { if (!mv->is_model_part()) continue; - const TriangleMesh *mesh = &mv->mesh(); + const TriangleMesh *mesh = &mv->mesh(); + const TriangleSelector::TriangleSplittingData &data = + mv->texture_displacement_facet(m_active_layer_slot).get_data(); + // The same bound TriangleSelector::deserialize() checks before it gives up - silently, with a + // void return and no way to report it. A mask recorded before the mesh was replaced indexes + // triangles that no longer exist, and the selector then comes back empty even though the mask + // is not. That has to be caught here, because the next update_model_object() would otherwise + // write the empty selector back over the mask: the paint would vanish, and the bake would + // report "nothing is painted" about the very data the flush had just deleted. + const size_t facet_count = mesh->its.indices.size(); + for (const TriangleSelector::TriangleBitStreamMapping &m : data.triangles_to_split) + if (m.triangle_idx < 0 || size_t(m.triangle_idx) >= facet_count) { + m_selectors_stale = true; + break; + } m_triangle_selectors.emplace_back(std::make_unique(*mesh, ebt_colors)); - m_triangle_selectors.back()->deserialize(mv->texture_displacement_facet(m_active_layer_slot).get_data(), false); + m_triangle_selectors.back()->deserialize(data, false); m_triangle_selectors.back()->request_update_render_data(); } @@ -3079,6 +3362,7 @@ void GLGizmoTextureDisplacement::ensure_panel_icons() "texture_displacement_map_view.svg", "texture_displacement_tile_repeat.svg", "menu_mirror_x.svg", "texture_displacement_adjust.svg", "canvas_drag.svg", "texture_displacement_move_up.svg", "texture_displacement_move_down.svg", "texture_displacement_drag.svg", + "texture_displacement_select_all.svg", "texture_displacement_erase_all.svg", }; std::vector paths; paths.reserve(names.size()); @@ -3870,9 +4154,10 @@ TextureColorSettings GLGizmoTextureDisplacement::color_settings_for(const ModelV if (!any_layer_colors(mv)) return out; // nothing is colouring: every colour path stays switched off out.palette = cached_palette(); + out.palette_pure = make_palette(m_palette_filaments, /* mixing */ false); out.mix_mode = mv.texture_displacement_options.color_mix_mode; out.despeckle_passes = mv.texture_displacement_options.color_despeckle; - out.layer_height = print_layer_height(); + out.layer_height = color_band_mm(mv); // The dither cell is tied to the colour-detail target: a cell much smaller than a facet cannot be // drawn at all, and one much larger stops reading as a blend and starts reading as a check. out.dither_cell_mm = std::max(m_subdivide_color_mm, 0.05f) * 2.f; @@ -3906,6 +4191,19 @@ std::vector GLGizmoTextureDisplacement::filament_palette() return palette; } +float GLGizmoTextureDisplacement::color_band_mm(const ModelVolume &mv) +{ + const float lh = print_layer_height(); + const float edge = (mv.texture_displacement_options.v2_refine_mm > 0.f) ? mv.texture_displacement_options.v2_refine_mm + : v2_recommendation(mv).edge_mm; + if (edge <= 0.f || lh <= 0.f) + return lh; + // A refined triangle of edge e stacks in rows about 0.87 * e apart (an equilateral triangle's + // height), and a dither needs at least two rows per period to be a dither at all. + constexpr float ROW_PER_EDGE = 0.87f; + return lh * std::max(1.f, std::ceil(2.f * ROW_PER_EDGE * edge / lh)); +} + float GLGizmoTextureDisplacement::print_layer_height() { try { @@ -3968,7 +4266,7 @@ ColorResolveFn GLGizmoTextureDisplacement::make_mix_resolver(const std::vector

    int { + return [entries, mode, band, cell](int index, const Vec3f &pos, const Vec3f &normal) -> int { if (index < 0 || size_t(index) >= entries->size()) return -1; const PaletteEntry &e = (*entries)[size_t(index)]; @@ -3977,7 +4275,15 @@ ColorResolveFn GLGizmoTextureDisplacement::make_mix_resolver(const std::vector

    = e.den ? e.a : e.b; + const bool bands = mode == ColorMixMode::ZBands || mode == ColorMixMode::Auto; + if (bands) { // One band per print layer. floorf, not a cast, so this stays correct below z = 0. const int slot = int(std::floor(pos.z() / band)); const int phase = ((slot % e.den) + e.den) % e.den; @@ -4018,15 +4324,25 @@ ColorQuantizeFn GLGizmoTextureDisplacement::make_palette_quantizer(const std::ve float l0, a0, b0; const Vec3f lab0 = srgb_to_lab(Vec3f((r + 0.5f) / E, (g + 0.5f) / E, (b + 0.5f) / E)); l0 = lab0.x(); a0 = lab0.y(); b0 = lab0.z(); - int best = 0; - float best_d = std::numeric_limits::max(); + int best = 0, best_pure = -1; + float best_d = std::numeric_limits::max(), best_pure_d = best_d; for (size_t i = 0; i < palette_lab.size(); ++i) { const float d = DeltaE00(l0, a0, b0, palette_lab[i].l, palette_lab[i].a, palette_lab[i].b); if (d < best_d) { best_d = d; best = int(i); } + if (!palette[i].is_mix() && d < best_pure_d) { + best_pure_d = d; + best_pure = int(i); + } } + // A mix is an interleave that only reads as its colour from a distance; up close + // it is stripes. Spend it only where it buys a clearly better match than the nearest + // single filament: ten Delta E is a visible step, less is not worth the stripes. + constexpr float PREFER_PURE_DE = 10.f; + if (best_pure >= 0 && palette[size_t(best)].is_mix() && best_pure_d - best_d < PREFER_PURE_DE) + best = best_pure; (*lut)[(size_t(r) * E + size_t(g)) * E + size_t(b)] = uint8_t(best); } }); @@ -4929,16 +5245,25 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const float approx_height = m_imgui->scaled(24.f); y = std::min(y, bottom_limit - approx_height); - // Docked (the default) the panel is pinned next to the gizmo toolbar and cannot be moved, like - // every other gizmo's. Undocked it becomes an ordinary floating window: a title bar to drag it - // by, and no forced position - this panel is tall enough (layer stack, per-layer controls) that - // it can cover the very part of the model being painted, and being able to shove it aside is the - // point. The position is deliberately *not* seeded on undock, so the window stays exactly where - // it already was and the user just gains the ability to move it from there. + // Docked (the default) the panel is pinned to the right edge of the 3D canvas and cannot be + // moved. Deliberately *not* next to the gizmo toolbar, which is where `x` points and where every + // other gizmo's window goes: this panel is far taller than those (layer stack plus the whole + // per-layer control set), so at the toolbar it sits right on top of the part of the model being + // painted. Pinning it to the canvas edge also parks it against the UV editor, since that pane is + // docked on the right and the canvas therefore ends exactly at the pane's left edge - so the + // panel follows the pane in and out instead of being clipped by it. + // + // Undocked it becomes an ordinary floating window: a title bar to drag it by, and no forced + // position - the position is deliberately not seeded on undock, so the window stays exactly + // where it already was and the user just gains the ability to move it from there. ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse; if (!m_undocked) { flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar; - GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 1.0f, 0.0f); + // Right-aligned (pivot 1), so the width the panel auto-resized to last frame does not need to + // be known here. Width 0 skips GizmoImguiSetNextWIndowPos()'s own left-aligned fit-to-canvas + // clamp, which would push the window back off the edge it is being pinned to. + float right = float(m_parent.get_canvas_size().get_width()) - m_imgui->scaled(0.5f); + GizmoImguiSetNextWIndowPos(right, y, 0.f, 0.f, ImGuiCond_Always, 1.0f, 0.0f); } ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); @@ -5251,7 +5576,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_erase_mode = true; } - // ---- Tools: brush / face / connected area, and the active tool's own control ---- + // ---- Tools: brush / face / connected area on the left, the whole-model actions on the right, and the + // active tool's own control on the line below ---- // "Face" and "Connected area" reuse the exact same selection machinery every other paint gizmo has // (single-facet click, and angle-limited flood fill respectively). { @@ -5276,7 +5602,36 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_tool_type = ToolType::SMART_FILL; m_cursor_type = TriangleSelector::CursorType::POINTER; } + + // Whole model: paint every face with the active layer, or clear its paint from all of them. Actions + // rather than tools, so they sit apart at the right end of the row. + const wxString whole_na = busy ? _L("Wait for the bake to finish.") : + active == nullptr ? _L("Add a layer first.") : + wxString(); + const wxString erase_na = !whole_na.empty() ? whole_na : + !slot_painted(m_active_layer_slot) ? _L("The active layer has no paint yet.") : + wxString(); ImGui::SameLine(); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - (2.f * icon_md + gap_s))); + if (icon_toggle(806, "texture_displacement_select_all.svg", false, icon_md, _L("Select whole model"), + _L("Select whole model - paint every face of the model with the active layer"), whole_na)) + select_whole_model(); + ImGui::SameLine(0.f, gap_s); + if (icon_toggle(807, "texture_displacement_erase_all.svg", false, icon_md, _L("Erase whole model"), + _L("Erase whole model - clear the active layer's paint from every face"), erase_na)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset texture displacement selection"), + UndoRedo::SnapshotType::GizmoAction); + int idx = -1; + for (ModelVolume *v : mo->volumes) + if (v->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + m_triangle_selectors[idx]->request_update_render_data(); + } + update_model_object(); + m_parent.set_as_dirty(); + } + if (is_brush_mode) { ImGui::SetNextItemWidth(-(3.f * gap_s + 1.f + 2.f * icon_sm)); ImGui::SliderFloat("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f mm", @@ -5302,33 +5657,6 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } } - // ---- Whole model ---- - { - const float half = std::floor((ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) * 0.5f); - m_imgui->disabled_begin(busy || active == nullptr); - if (ImGui::Button(_u8L("Select whole model").c_str(), ImVec2(half, 0.f))) - select_whole_model(); - m_imgui->disabled_end(); - hover_tip(_u8L("Paint every face of the model with the active layer")); - ImGui::SameLine(); - m_imgui->disabled_begin(busy || active == nullptr || !slot_painted(m_active_layer_slot)); - if (ImGui::Button(_u8L("Erase whole model").c_str(), ImVec2(half, 0.f))) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset texture displacement selection"), - UndoRedo::SnapshotType::GizmoAction); - int idx = -1; - for (ModelVolume *v : mo->volumes) - if (v->is_model_part()) { - ++idx; - m_triangle_selectors[idx]->reset(); - m_triangle_selectors[idx]->request_update_render_data(); - } - update_model_object(); - m_parent.set_as_dirty(); - } - m_imgui->disabled_end(); - hover_tip(_u8L("Clear the active layer's paint from every face")); - } - // ---- View: Normal / Fast / Checker / Distortion as one group, Wireframe on its own ---- // The underlying state stays m_use_bump_preview + m_uv_check_mode. { @@ -5651,13 +5979,15 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled)) m_preview_params_dirty = true; hover_tip(_u8L("Interleave pairs of filaments to reach colours between them, so a few " - "filaments cover far more than a few colours. Off means every triangle " - "prints in one of the filaments exactly.")); + "filaments cover far more than a few colours. Used only on images with " + "continuous colour (photographs, gradients); a texture of flat colours " + "prints in single filaments either way. Off forces single filaments.")); if (opts.color_mix_enabled) { slider_label(_L("Mix by")); const std::string mix_z = _u8L("Layers"); const std::string mix_xy = _u8L("Surface"); - const char *mix_items[] = { mix_z.c_str(), mix_xy.c_str() }; + const std::string mix_auto = _u8L("Automatic"); + const char *mix_items[] = { mix_z.c_str(), mix_xy.c_str(), mix_auto.c_str() }; int mix_mode = int(opts.color_mix_mode); ImGui::SetNextItemWidth(-card_pad); if (scoped_combo("##color_mix_mode", &mix_mode, mix_items, IM_ARRAYSIZE(mix_items))) { @@ -5668,7 +5998,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "blends smoothly on upright surfaces but disappears on flat-facing " "ones, where a whole layer is a single band.\n" "Surface: a fine checkerboard across the surface, which works at " - "any angle but can read as texture rather than as a blend.")); + "any angle but can read as texture rather than as a blend.\n" + "Automatic: layers on upright faces; flat-facing faces take the nearer " + "single filament, since a checkerboard there shows as a pattern.")); ImGui::TextDisabled("%s", Slic3r::format(_u8L("%1% printable colours from %2% filaments"), int(cached_palette().size()), int(m_palette_filaments.size())).c_str()); } @@ -6409,9 +6741,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_parent.set_as_dirty(); } m_imgui->disabled_end(); - hover_tip(_u8L("Auto: the resolution and the budget are chosen from the texture (its pixel " - "size on the model and how sharp it is) and the model's size, the way " - "BumpMesh's smart resolution does. Untick to set them by hand.")); + hover_tip(_u8L("Auto: the resolution follows the model's size and the budget is the standard " + "750 k, the same defaults as bumpmesh.com. Untick to set them by hand.")); ImGui::SameLine(); ImGui::SetNextItemWidth(x0 + panel_w - ImGui::GetCursorPosX()); float shown = auto_res ? rec.edge_mm : opts.v2_refine_mm; @@ -6424,10 +6755,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); if (auto_res && rec.edge_mm > 0.f) - hover_tip(Slic3r::format(_u8L("%1% texture pixels per edge x %2% mm per pixel%3%. Budget %4% k."), - rec.pixels_per_edge, Slic3r::format("%.3f", rec.texel_mm), - rec.budget_bound ? _u8L(", held back by the triangle cap") : std::string(), - rec.budget_k)); + hover_tip(Slic3r::format(_u8L("The model's diagonal / 250, as bumpmesh.com sets it. Budget %1% k."), rec.budget_k)); else hover_tip(_u8L("Triangle edge length the painted area is refined to before displacement. " "Smaller carries finer texture detail and costs more triangles; the budget " @@ -6487,7 +6815,10 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::PopStyleColor(5); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) m_imgui->tooltip(mv != nullptr && !mv->is_texture_displacement_painted() ? - _u8L("Nothing is painted yet.") : + (m_seam_edit_mode ? _u8L("Nothing is painted yet. The UV editor's seam tool is on, so " + "strokes on the model mark seams instead of painting - turn " + "it off in the pane to paint.") : + _u8L("Nothing is painted yet.")) : pro_mode() ? _u8L("Turn the painted height maps into real geometry, by moving the vertices that are " "already there. Use Subdivide first if the mesh is too coarse to show the detail.") : diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 8cbfa9c84e..7ec7428c20 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -104,6 +104,14 @@ public: // The print's layer height, which sizes ColorMixMode::ZBands. Falls back to 0.2 mm if it cannot be // read - a wrong band size is a cosmetic error, not a reason to refuse to colour anything. static float print_layer_height(); + // The Z band height, in mm. One print layer is the ideal, but the interleave is realised per + // *facet*: a band thinner than the mesh can resolve does not dither, it beats against the triangle + // grid and comes out as broad horizontal stripes - and since MMU segmentation reads facet colour, + // it does so in the print too, not only on screen. The refinement edge is chosen from the model's + // diagonal and knows nothing about the layer height, so the band is rounded up to a whole number of + // layers at least two facet rows tall: still exact on the printer, and representable by the mesh + // that has to carry it. Used by both the bake settings and the preview shader, so the two agree. + float color_band_mm(const ModelVolume &mv); // The Normal preview's triangles, grouped by the filament they will print in. Colour is per facet // and there are at most sixteen filaments, so the mesh is uploaded once with its index buffer @@ -480,6 +488,17 @@ private: // painting into a slot with no texture assigned is harmless, it just has no visible/bake // effect until a texture is added to that slot. int m_active_layer_slot = 0; + // Set when m_triangle_selectors could not be loaded from the stored paint masks, because a mask + // was recorded against a different topology: TriangleSelector::deserialize() rejects that and + // returns without a word, leaving the selector empty even though the mask is not. While this is + // set, an *empty* selector says nothing about the paint, so update_model_object() must not flush + // one back - serializing it over the mask destroys the user's paint for good, and the bake then + // reports "nothing is painted" about the data the flush had just deleted. + // + // Deliberately not a blanket refusal to flush: once the user paints, the selector holds real + // content again and writing it back is exactly right - it replaces the unusable mask with one + // recorded against the current mesh. So only the empty-over-non-empty case is held back. + bool m_selectors_stale = false; bool m_bake_in_progress = false; // When set, the true-displacement geometry is rebuilt on every parameter change (live), instead of @@ -636,11 +655,22 @@ private: // patch only), translucent (the preview stays visible through it) and rebuilt live during a // stroke. GLModel m_paint_overlay_glmodel; + // The islands selected in the UV editor, tinted on the model so the pane's selection can be seen + // in place. Rebuilt whenever the pane's selection differs from the one it was built for. + GLModel m_island_overlay_glmodel; + std::vector m_island_overlay_selection; + void rebuild_island_overlay(const std::vector &selection); + void render_island_overlay(); // Set on every paint event, cleared when the overlay is rebuilt in render_painter_gizmo(). Kept // separate from m_bump_preview_dirty so a stroke refreshes only the small painted patch per frame, bool m_paint_overlay_dirty = false; void rebuild_paint_overlay(); - void render_paint_overlay(); + void render_paint_overlay(GLModel &overlay); + // Every *other* layer's paint, muted, so all layers stay visible while one is edited. Rebuilt only when that + // paint, the active layer or the preview it is lifted onto changes (m_other_paint_key). + GLModel m_other_paint_glmodel; + std::string m_other_paint_key; + void rebuild_other_paint_overlay(); // Whether render_bump_preview_mesh() would actually draw something. Checked before the real volume // is hidden: with no layer, no texture or no shader the bump path draws nothing, and hiding the // volume for it left the model invisible. @@ -648,6 +678,12 @@ private: // Whether the current bump mesh carries a precomputed per-vertex uv (LSCM) that the shader // should sample at directly, rather than projecting in-shader. Set by rebuild_bump_preview_mesh(). bool m_bump_preview_uses_vertex_uv = false; + // The projection frame handed to the bump shader, captured when the mesh is built. Cylindrical and + // Spherical are reconstructed in the fragment shader (there is no per-vertex uv for them) and wrap + // around the whole patch, which no fragment can work out for itself. See layer_projection_frame(). + int m_bump_projection_mode = 0; + Vec3f m_bump_patch_center = Vec3f::Zero(); + Vec3f m_bump_patch_axis = Vec3f::UnitZ(); // The palette the fast preview's per-triangle filament indices were built against, captured when // the mesh was. Empty when the active layer is not colouring, which is what tells the shader to // fall back to the model's own colour. Held rather than re-read at draw time so the indices baked @@ -659,14 +695,23 @@ private: // the dragged island's vertices flagged, v_normal.y = 1) and then moved purely through the shader's // island_delta uniform - one uniform update per mouse move, no rebuild - so it tracks the cursor // as smoothly as Adjust placement. m_bump_active_chart is the dragged island (or -1); - // m_bump_active_vertex flags its base vertices; m_bump_baked_active_xf is that island's placement - // baked into the current mesh, against which the live delta is measured; m_bump_island_delta is the - // resulting final-uv-space affine handed to the shader (identity except mid-drag). + // m_bump_active_face flags the dragged islands' *triangles*, indexed by painted-patch face; + // m_bump_baked_active_xf is that island's placement baked into the current mesh, against which the + // live delta is measured; m_bump_island_delta is the resulting final-uv-space affine handed to the + // shader (identity except mid-drag). + // + // Per triangle rather than per vertex deliberately: a seam vertex belongs to every chart touching + // it, so flagging the dragged chart's base vertices also flagged the corners its neighbours use. + // island_active is an interpolated varying, so those neighbouring triangles then had island_delta + // applied too - dragging one island moved every adjacent island's texture while the editor, which + // is per chart, correctly moved only the one. A triangle belongs to exactly one chart. int m_bump_active_chart = -1; - std::vector m_bump_active_vertex; + std::vector m_bump_active_face; Eigen::Matrix m_bump_baked_active_xf = Eigen::Matrix::Identity(); Eigen::Matrix m_bump_island_delta = Eigen::Matrix::Identity(); - void compute_bump_active_vertices(const std::vector &charts); + // Flags `charts`' triangles in m_bump_active_face, sized to `patch_face_count` (the painted patch + // the bump mesh is being built from). Cleared if the unwrap carries no face map. + void compute_bump_active_faces(const std::vector &charts, size_t patch_face_count); // The set of islands the current UV-editor drag moves together: the pane's multi-selection unioned // with each selected island's join group (see build_island_move_set()). Populated at drag start and @@ -687,6 +732,19 @@ private: // the shader projects on its own. Shared by the bump preview and the UV-check overlay. std::vector compute_layer_vertex_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) const; + // The same, but three UVs per patch triangle (corner 0..2 of triangle i at 3i..3i+2). This is what + // the flat, unshared-vertex preview meshes actually want: under LSCM a seam vertex has a different + // UV in each island it borders, so collapsing to one per vertex handed a triangle at an unjoined + // seam its neighbour's placement - one visibly skewed triangle per face. Every other projection is + // single-valued per point, so there a corner's UV is just its vertex's. + std::vector compute_layer_corner_uvs(const indexed_triangle_set &patch, + const TextureDisplacementLayer &layer) const; + // The in-shader projection for `layer` (0 Triplanar, 1 Cylindrical, 2 Spherical) plus, for the two + // wrapping ones, the patch centroid and cylinder axis they wrap around - in the texture frame the + // shaders project in. Taken from the bake's own texture_displacement_patch_frame(), so a preview + // can never wrap around a different centre, or pick a different axis, than the bake will. + int layer_projection_frame(const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer, + Vec3f ¢er, Vec3f &axis) const; // `patch` with its vertices moved into world millimetres - the space the bake maps the texture in // (see build_texture_displacement()). Returned by value because the caller usually still needs the // original: the patch doubles as render geometry, which is drawn through the volume's own matrix. @@ -699,6 +757,10 @@ private: UVCheckMode m_uv_check_mode = UVCheckMode::None; GLModel m_uvcheck_glmodel; bool m_uvcheck_uses_vertex_uv = false; + // As m_bump_projection_mode and friends, for the Checker overlay. + int m_uvcheck_projection_mode = 0; + Vec3f m_uvcheck_patch_center = Vec3f::Zero(); + Vec3f m_uvcheck_patch_axis = Vec3f::UnitZ(); void rebuild_uvcheck_mesh(); void render_uvcheck_mesh(); diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 05df414ca5..f48ec6b28c 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -38,6 +38,8 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) TextureColorRequest *color = nullptr; if (!m_input.color.empty()) { color_request.quantize = GLGizmoTextureDisplacement::make_palette_quantizer(m_input.color.palette); + if (!m_input.color.palette_pure.empty()) + color_request.quantize_pure = GLGizmoTextureDisplacement::make_palette_quantizer(m_input.color.palette_pure); color_request.resolve = GLGizmoTextureDisplacement::make_mix_resolver( m_input.color.palette, m_input.color.mix_mode, m_input.color.layer_height, m_input.color.dither_cell_mm); @@ -79,6 +81,22 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept if (canceled || eptr || m_result.empty()) return; + // A bake that moved nothing - no layer could be sampled, or every sample was zero - must not be + // committed: committing is what clears the baked layers' paint, so the user would see the painted + // region simply vanish with no relief in its place and no idea why. Keep the paint and say so. + { + const indexed_triangle_set &out = m_result.its; + bool unchanged = out.indices.size() == m_input.base_mesh.indices.size() && + out.vertices.size() == m_input.base_mesh.vertices.size(); + for (size_t i = 0; unchanged && i < out.vertices.size(); ++i) + unchanged = (out.vertices[i] - m_input.base_mesh.vertices[i]).cwiseAbs().maxCoeff() < 1e-5f; + if (unchanged) { + show_error(nullptr, _u8L("The bake produced no displacement, so nothing was changed and the paint was kept. " + "Check that the painted layer has a texture and a non-zero depth.")); + return; + } + } + Plater *plater = wxGetApp().plater(); const auto commit = [this, plater]() { @@ -109,11 +127,21 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept // Clear the paint mask of every layer that was actually baked so a repeat bake (or the paint // overlay) doesn't act on triangles that no longer represent the same unbaked surface. The - // texture layer definitions themselves (and paint outside the baked area, if any) are left - // untouched so the user can keep sculpting with the same textures. - for (const TextureDisplacementLayer &layer : m_input.layers) - if (!layer.empty() && layer.slot >= 0 && layer.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) - volume->texture_displacement_facet(layer.slot).reset(); + // texture layer definitions themselves are left untouched so the user can keep sculpting with + // the same textures. + // + // A mask the bake did *not* consume only still means what it did if the topology is unchanged, + // which is true of the classic path (it moves existing vertices) but not of the one-run + // pipeline, which rebuilds and then simplifies the mesh. A mask left behind against the old + // topology is exactly what makes the gizmo reload an empty selector over a non-empty mask and + // then erase it on the next flush - see GLGizmoTextureDisplacement::update_from_model_object(). + const bool topology_changed = m_input.base_mesh.indices.size() != volume->mesh().its.indices.size(); + for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) { + const auto it = std::find_if(m_input.layers.begin(), m_input.layers.end(), + [slot](const TextureDisplacementLayer &l) { return l.slot == slot; }); + if (topology_changed || (it != m_input.layers.end() && !it->empty())) + volume->texture_displacement_facet(slot).reset(); + } ModelObject *object = volume->get_object(); if (object == nullptr) diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp index 013ea79750..1845314f0d 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.cpp @@ -24,6 +24,8 @@ void TextureDisplacementPreviewJob::process(Ctl &ctl) TextureColorRequest *color = nullptr; if (!m_input.color.empty()) { color_request.quantize = GLGizmoTextureDisplacement::make_palette_quantizer(m_input.color.palette); + if (!m_input.color.palette_pure.empty()) + color_request.quantize_pure = GLGizmoTextureDisplacement::make_palette_quantizer(m_input.color.palette_pure); color_request.resolve = GLGizmoTextureDisplacement::make_mix_resolver( m_input.color.palette, m_input.color.mix_mode, m_input.color.layer_height, m_input.color.dither_cell_mm); diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index e2f0147efb..3b9f0dac25 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -1313,54 +1313,42 @@ TEST_CASE("TextureDisplacement: edge flips lay a stepped field's wall along the // Automatic resolution (v2 pipeline) // --------------------------------------------------------------------------------------------- -TEST_CASE("TextureDisplacement: automatic resolution follows the texture's texel size and sharpness", "[TextureDisplacement]") +TEST_CASE("TextureDisplacement: automatic resolution follows the model's size like bumpmesh.com", "[TextureDisplacement]") { - // A 20 mm cube (diagonal 34.6 mm, so the edge may go up to 0.69 mm) with an 8 mm tile. + // A 20 mm cube: diagonal 34.64 mm, so diagonal / 250 = 0.1386 mm, rounded up to 0.14. const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); + TextureDisplacementLayer layer; + layer.image_data = make_checkerboard_png(16, 16); + layer.tiling_scale = 8.f; - SECTION("a hard-edged texture gets one texel per edge") + SECTION("edge from the diagonal, budget the standard 750 k") { - TextureDisplacementLayer layer; - layer.image_data = make_checkerboard_png(16, 16); // 2x2 texel checks: every other texel is a step - layer.tiling_scale = 8.f; // texel = 0.5 mm - const TextureDetail detail = analyze_texture_detail(layer); - CHECK(detail.sharp_fraction > 0.15f); - CHECK_THAT(detail.pixels_per_edge, WithinAbs(1.f, 1e-6f)); const V2Resolution rec = recommend_v2_resolution(cube, { layer }); - CHECK_THAT(rec.texel_mm, WithinAbs(0.5f, 1e-4f)); - CHECK_THAT(rec.edge_mm, WithinAbs(0.5f, 1e-4f)); - CHECK(rec.budget_k >= 10); - CHECK(rec.budget_k <= 2000); + CHECK_THAT(rec.edge_mm, WithinAbs(0.14f, 1e-4f)); + CHECK(rec.budget_k == 750); + CHECK_THAT(rec.texel_mm, WithinAbs(0.5f, 1e-4f)); // reported for the panel } - SECTION("a flat texture gets four texels per edge, within the model's clamp") + SECTION("the world transform scales the diagonal") { - TextureDisplacementLayer layer; - layer.image_data = make_flat_gray_png(128, 16, 16); - layer.tiling_scale = 8.f; // texel 0.5 mm x 4 = 2 mm, clamped to diagonal / 50 - const TextureDetail detail = analyze_texture_detail(layer); - CHECK_THAT(detail.pixels_per_edge, WithinAbs(4.f, 1e-6f)); - const V2Resolution rec = recommend_v2_resolution(cube, { layer }); - CHECK_THAT(rec.edge_mm, WithinAbs(0.70f, 0.011f)); // ceil(34.64 / 50 = 0.693) at 0.01 - } - - SECTION("the world transform scales the tile against the model") - { - TextureDisplacementLayer layer; - layer.image_data = make_checkerboard_png(16, 16); - layer.tiling_scale = 8.f; - // Scaled up 3x the cube is 60 mm; the texel is still 0.5 mm in world terms, so the edge holds. const V2Resolution rec = recommend_v2_resolution(cube, { layer }, Transform3d(Eigen::Scaling(3.0))); - CHECK_THAT(rec.edge_mm, WithinAbs(0.5f, 1e-4f)); - const V2Resolution plain = recommend_v2_resolution(cube, { layer }); - CHECK(rec.budget_k > plain.budget_k); // nine times the area wants more triangles + CHECK_THAT(rec.edge_mm, WithinAbs(0.42f, 1e-4f)); // 103.9 / 250 = 0.4157 -> 0.42 } - SECTION("no usable texture gives no recommendation") + SECTION("a tiny model stops at the 0.05 mm floor") { - TextureDisplacementLayer empty; - const V2Resolution rec = recommend_v2_resolution(cube, { empty }); - CHECK(rec.edge_mm == 0.f); + const indexed_triangle_set small = its_make_cube(2.f, 2.f, 2.f); + const V2Resolution rec = recommend_v2_resolution(small, { layer }); + CHECK_THAT(rec.edge_mm, WithinAbs(0.05f, 1e-4f)); + } + + SECTION("the texture's sharpness class is still measured") + { + const TextureDetail sharp = analyze_texture_detail(layer); + CHECK_THAT(sharp.pixels_per_edge, WithinAbs(1.f, 1e-6f)); + TextureDisplacementLayer flat; + flat.image_data = make_flat_gray_png(128, 16, 16); + CHECK_THAT(analyze_texture_detail(flat).pixels_per_edge, WithinAbs(4.f, 1e-6f)); } } @@ -1464,6 +1452,122 @@ static std::vector unwrap_charts_are_disks(const PatchUnwrap &u) return disks; } +TEST_CASE("TextureDisplacement: the default pipeline bakes an unwrap (LSCM) layer", "[TextureDisplacement]") +{ + // The one-run pipeline samples per point, and an unwrap has no per-point formula, so unwrap layers + // used to be dropped from it altogether: the bake moved nothing, and the job then cleared the paint + // as if it had baked - "paint, unwrap, bake, and the painted region just disappears". + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_flat_gray_png(255); + layer.depth_mm = 0.5f; + layer.tiling_scale = 10.f; + layer.projection_method = TextureProjectionMethod::LSCM; + + TextureDisplacementFacetsData facets; + facets[0] = paint_whole_mesh(cube); + + TextureDisplacementOptions options; + options.pipeline_v2 = true; + options.v2_refine_mm = 2.f; + options.v2_max_triangles_k = 0; + const indexed_triangle_set out = build_texture_displacement(cube, { layer }, facets, options); + REQUIRE(!out.vertices.empty()); + + // A flat white texture at depth 0.5 pushes the faces out by 0.5, so the bounding box grows on + // every side (face interiors move the full depth; only corners, moving along their blended normal, + // move less). Before the fix it stayed exactly [0, 10]. + Vec3f lo = out.vertices.front(), hi = lo; + for (const Vec3f &v : out.vertices) { + lo = lo.cwiseMin(v); + hi = hi.cwiseMax(v); + } + CHECK(lo.x() < -0.4f); + CHECK(hi.z() > 10.4f); +} + +TEST_CASE("Per-corner LSCM UVs give each triangle its own island's placement", "[TextureDisplacement]") +{ + // compute_lscm_uvs() has to collapse a seam vertex onto one chart, because displacement is + // per vertex. Everything that samples per *triangle* must not: a cube corner belongs to three + // faces, so the collapse handed a triangle at an unjoined seam a neighbouring island's placement. + // That showed up as one visibly skewed triangle per face, and as every island's texture following + // the lowest-numbered island whenever it was dragged. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const PatchUnwrap unwrap = compute_patch_unwrap(cube, 30.f, 0.f); + REQUIRE(unwrap.chart_count == 6); + // The map back to the patch's own triangle order, without which there are no per-corner UVs. + REQUIRE(unwrap.source_face.size() == unwrap.indices.size()); + + TextureDisplacementLayer layer; + layer.projection_method = TextureProjectionMethod::LSCM; + layer.lscm_seam_angle_deg = 30.f; + layer.islands = compute_connected_net(unwrap); + REQUIRE(layer.islands.size() == 6); + // Move one island by hand. Its neighbours must stay exactly where they were. + layer.islands[0].offset += Vec2f(37.f, -19.f); + + const std::vector corner = compute_lscm_corner_uvs(cube, layer); + REQUIRE(corner.size() == cube.indices.size() * 3); + + for (size_t t = 0; t < unwrap.indices.size(); ++t) { + const size_t f = size_t(unwrap.source_face[t]); + REQUIRE(f < cube.indices.size()); + // A triangle lies in exactly one chart, so any of its corners names that chart. + const int c = unwrap.vertex_chart[size_t(unwrap.indices[t][0])]; + for (int k = 0; k < 3; ++k) { + const Vec2f want = apply_island_transform(unwrap.uvs[size_t(unwrap.indices[t][k])], c, unwrap, layer.islands); + CHECK_THAT(corner[f * 3 + size_t(k)].x(), WithinAbs(want.x(), 1e-4)); + CHECK_THAT(corner[f * 3 + size_t(k)].y(), WithinAbs(want.y(), 1e-4)); + } + } + + // And the per-vertex path must disagree somewhere - otherwise this test proves nothing, because + // the bug it guards against is precisely that the two were the same thing. + const std::vector per_vertex = compute_lscm_uvs(cube, layer); + REQUIRE(per_vertex.size() == cube.vertices.size()); + bool differs = false; + for (size_t f = 0; f < cube.indices.size() && !differs; ++f) + for (int k = 0; k < 3; ++k) + if ((corner[f * 3 + size_t(k)] - per_vertex[size_t(cube.indices[f][k])]).norm() > 1e-3f) + differs = true; + CHECK(differs); +} + +TEST_CASE("The Cylindrical/Spherical patch frame is the bake's own, so a preview can share it", "[TextureDisplacement]") +{ + // The fast preview reconstructs these two projections in the fragment shader and needs the very + // centroid and axis the bake wraps around - a patch-only normal average would sometimes quantize + // to a different world axis and wrap the texture the other way round. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const std::vector normals = texture_displacement_vertex_normals(cube); + REQUIRE(normals.size() == cube.vertices.size()); + + // Just the +X face: its average normal is +X, so the cylinder axis must be a world axis + // perpendicular to it, and the centroid must sit on that face. + indexed_triangle_set face; + face.vertices = cube.vertices; + for (const stl_triangle_vertex_indices &t : cube.indices) { + const Vec3f n = (cube.vertices[size_t(t[1])] - cube.vertices[size_t(t[0])]) + .cross(cube.vertices[size_t(t[2])] - cube.vertices[size_t(t[0])]); + if (n.normalized().x() > 0.99f) + face.indices.push_back(t); + } + REQUIRE(face.indices.size() == 2); + + Vec3f center, axis, average_normal; + texture_displacement_patch_frame(face, normals, center, axis, average_normal); + CHECK_THAT(center.x(), WithinAbs(10., 1e-4)); + CHECK_THAT(std::abs(axis.x()), WithinAbs(0., 1e-4)); // never the face's own normal direction + CHECK_THAT(axis.norm(), WithinAbs(1., 1e-4)); + + // An empty patch must not divide by zero; it falls back to +Z. + texture_displacement_patch_frame(indexed_triangle_set{}, normals, center, axis, average_normal); + CHECK_THAT(center.norm(), WithinAbs(0., 1e-6)); + CHECK_THAT(axis.z(), WithinAbs(1., 1e-6)); +} + TEST_CASE("A cube unwraps into one island per face, laid out as a connected net", "[TextureDisplacement]") { const indexed_triangle_set cube = its_make_cube(10., 10., 10.); @@ -1595,3 +1699,267 @@ TEST_CASE("TextureDisplacement: moving the model about the plate does not move t max_diff = std::max(max_diff, (moved.vertices[i] - at_origin.vertices[i]).norm()); CHECK(max_diff < 1e-3f); } + +// Longest edge over the shortest altitude: 1.15 for an equilateral triangle, 2 for a right isosceles +// one, unbounded for a needle. +static float triangle_aspect(const Vec3f &a, const Vec3f &b, const Vec3f &c) +{ + const float longest = std::max({ (b - a).norm(), (c - b).norm(), (a - c).norm() }); + const float twice_area = (b - a).cross(c - a).norm(); + return twice_area > 0.f ? longest * longest / twice_area : std::numeric_limits::infinity(); +} + +TEST_CASE("TextureDisplacement: baking a second face leaves the first face's relief untouched", "[TextureDisplacement]") +{ + // Paint the top of a cube and bake, then paint the front of the *result* and bake again, the way + // the gizmo does (each bake replaces the mesh and clears the baked paint). The top is unpainted the + // second time round, so it is excluded from refinement and pinned by the displacement: every + // vertex of its relief must still be there, in the same number of triangles, and no needle may + // appear on it. + const indexed_triangle_set cube = subdivide_mesh_uniform(its_make_cube(20., 20., 20.), 2.f, 6); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_checkerboard_png(16, 16); + layer.tiling_scale = 5.f; + layer.depth_mm = 0.5f; + + TextureDisplacementOptions options; + options.pipeline_v2 = true; + options.v2_refine_mm = 0.5f; + options.v2_max_triangles_k = 0; // no simplification + + // Paints exactly the triangles whose three corners satisfy `on_face` - no brush spill. + const auto paint_where = [](const indexed_triangle_set &mesh, auto on_face) { + const TriangleMesh tm(mesh); + TriangleSelector selector(tm); + for (size_t f = 0; f < mesh.indices.size(); ++f) { + const stl_triangle_vertex_indices &t = mesh.indices[f]; + if (on_face(mesh.vertices[size_t(t[0])]) && on_face(mesh.vertices[size_t(t[1])]) && + on_face(mesh.vertices[size_t(t[2])])) + selector.set_facet(int(f), EnforcerBlockerType::ENFORCER); + } + return selector.serialize(); + }; + const auto on_top = [](const Vec3f &v) { return v.z() > 19.9f; }; + const auto on_front = [](const Vec3f &v) { return v.y() < 0.1f; }; + + // The top region of a result: its vertices, and its triangles' count and worst aspect ratio. + struct TopRegion + { + std::vector vertices; + size_t triangles = 0; + float max_aspect = 0.f; + }; + const auto top_region = [&on_top](const indexed_triangle_set &mesh) { + TopRegion r; + for (const Vec3f &v : mesh.vertices) + if (on_top(v)) + r.vertices.push_back(v); + for (const stl_triangle_vertex_indices &t : mesh.indices) { + const Vec3f &a = mesh.vertices[size_t(t[0])], &b = mesh.vertices[size_t(t[1])], &c = mesh.vertices[size_t(t[2])]; + if (!on_top(a) || !on_top(b) || !on_top(c)) + continue; + ++r.triangles; + r.max_aspect = std::max(r.max_aspect, triangle_aspect(a, b, c)); + } + return r; + }; + + TextureDisplacementFacetsData facets{}; + facets[0] = paint_where(cube, on_top); + const indexed_triangle_set first = build_texture_displacement(cube, { layer }, facets, options); + REQUIRE(first.indices.size() > cube.indices.size()); + const TopRegion top_before = top_region(first); + REQUIRE(top_before.triangles > 0); + // The relief really is there: the checkerboard raises part of the top by the full depth. + float top_z_max = 0.f; + for (const Vec3f &v : top_before.vertices) + top_z_max = std::max(top_z_max, v.z()); + REQUIRE(top_z_max > 20.3f); + REQUIRE(top_before.max_aspect < 20.f); + + // Only the front of the baked mesh is painted for the second bake. + facets[0] = paint_where(first, on_front); + REQUIRE_FALSE(facets[0].triangles_to_split.empty()); + + const auto check_top_untouched = [&](const indexed_triangle_set &second) { + REQUIRE_FALSE(second.indices.empty()); // an aborted bake returns {} + const TopRegion top_after = top_region(second); + + // Every vertex of the first relief still exists, at the same place. O(n*m) over a few + // thousand vertices each, restricted to the top region on both sides. + size_t missing = 0; + Vec3f first_missing = Vec3f::Zero(); + for (const Vec3f &v : top_before.vertices) { + bool found = false; + for (const Vec3f &w : top_after.vertices) + if ((w - v).squaredNorm() <= 1e-6f) { // within 1e-3 mm + found = true; + break; + } + if (!found) { + if (missing == 0) + first_missing = v; + ++missing; + } + } + INFO("first vertex of the top relief missing from the second bake: " << first_missing.transpose()); + CHECK(missing == 0); + + // Nothing was added to or taken from the top either - its triangles are excluded from the + // refinement, and a rim edge it shares with the front was already at the refine length. + CHECK(top_after.triangles == top_before.triangles); + + // And no needles: the worst triangle on the top is no worse than after the first bake. + INFO("worst top-face aspect ratio after the second bake: " << top_after.max_aspect + << ", after the first: " << top_before.max_aspect); + CHECK(top_after.max_aspect < 20.f); + }; + + SECTION("bake mode: no simplification") + { + check_top_untouched(build_texture_displacement(first, { layer }, facets, options)); + } + + SECTION("export mode: simplification and T-junction repair run over the excluded region too") + { + // A budget far below the mesh forces the decimation (the locked top alone is over it, so it + // only harvests flat faces) and with it the repair pass - the two stages that walk every face, + // excluded ones included. + TextureDisplacementOptions export_options = options; + export_options.v2_max_triangles_k = 1; + check_top_untouched(build_texture_displacement(first, { layer }, facets, export_options)); + } +} + +TEST_CASE("TextureDisplacement: a brush stroke smaller than a triangle displaces only the stroke", "[TextureDisplacement]") +{ + // A plain 12-triangle cube. The selector splits the top triangle under a 3 mm spherical brush, + // so the painted pieces are far smaller than the triangle. The one-run pipeline used to include + // the whole source triangle: the entire top face rose. + const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); + const TriangleMesh mesh(cube); + int top = -1; + for (size_t t = 0; t < cube.indices.size() && top < 0; ++t) { + const auto &f = cube.indices[t]; + if (cube.vertices[size_t(f[0])].z() > 19.9f && cube.vertices[size_t(f[1])].z() > 19.9f && + cube.vertices[size_t(f[2])].z() > 19.9f) + // The triangle that contains the face centre: the brush starts there. + for (int k = 0; k < 3; ++k) + if ((cube.vertices[size_t(f[k])] - Vec3f(10.f, 10.f, 20.f)).norm() < 15.f) + top = int(t); + } + REQUIRE(top >= 0); + TriangleSelector selector(mesh); + selector.select_patch(top, + TriangleSelector::SinglePointCursor::cursor_factory( + Vec3f(10.f, 10.f, 20.f), Vec3f(10.f, 10.f, 100.f), 3.f, TriangleSelector::CursorType::SPHERE, + Transform3d::Identity(), TriangleSelector::ClippingPlane()), + EnforcerBlockerType::ENFORCER, Transform3d::Identity(), /* triangle_splitting */ true); + TextureDisplacementFacetsData facets; + facets[0] = selector.serialize(); + REQUIRE(TriangleSelector::has_facets(facets[0], EnforcerBlockerType::ENFORCER)); + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_flat_gray_png(255, 8, 8); // uniform full height: every painted point rises + layer.tiling_scale = 4.f; + layer.depth_mm = 0.5f; + TextureDisplacementOptions options; + options.pipeline_v2 = true; + options.v2_refine_mm = 0.5f; + options.v2_max_triangles_k = 0; + const indexed_triangle_set out = build_texture_displacement(cube, { layer }, facets, options); + REQUIRE(out.indices.size() > cube.indices.size()); + + size_t raised_inside = 0, raised_outside = 0, outside = 0; + for (const Vec3f &v : out.vertices) { + if (v.z() < 19.9f) + continue; // not the top face + const float r = (Vec2f(v.x(), v.y()) - Vec2f(10.f, 10.f)).norm(); + if (r < 2.f && v.z() > 20.3f) + ++raised_inside; + if (r > 4.5f) { + ++outside; + if (v.z() > 20.01f) + ++raised_outside; + } + } + CHECK(raised_inside > 0); // the stroke itself is displaced + CHECK(outside > 0); + CHECK(raised_outside == 0); // the rest of the face, inside the same source triangle, is not +} + +TEST_CASE("TextureDisplacement: a texture of flat colours is told apart from a continuous one", "[TextureDisplacement]") +{ + // The verdict that decides whether a layer may use filament mixes: a checkerboard is two colours, + // a ramp spreads over every level. + TextureDisplacementLayer checker; + checker.image_data = make_checkerboard_png(32, 32); + CHECK(analyze_texture_detail(checker).flat_colors); + + std::vector ramp(64 * 64); + for (size_t y = 0; y < 64; ++y) + for (size_t x = 0; x < 64; ++x) + ramp[y * 64 + x] = uint8_t((x * 4 + y) & 255); + const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("texdisp_test_%%%%%%%%.png"); + REQUIRE(Slic3r::png::write_gray_to_file(tmp_path.string(), 64, 64, ramp)); + std::vector bytes; + { + std::ifstream ifs(tmp_path.string(), std::ios::binary); + bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp_path, ec); + TextureDisplacementLayer gradient; + gradient.image_data = std::make_shared>(std::move(bytes)); + const TextureDetail d = analyze_texture_detail(gradient); + CHECK_FALSE(d.flat_colors); + CHECK(d.flat_share < 0.85f); +} + +TEST_CASE("Each layer's texture is sampled only on its own painted area", "[TextureDisplacement]") +{ + // Two layers with different textures and depths, one painted on the cube's top, one on its -X side. + const indexed_triangle_set cube = its_make_cube(10., 10., 10.); + const TriangleMesh cube_mesh(cube); + const auto paint_facing = [&](const Vec3f &dir) { + TriangleSelector selector(cube_mesh); + for (int f = 0; f < int(cube.indices.size()); ++f) { + const stl_triangle_vertex_indices &t = cube.indices[size_t(f)]; + const Vec3f n = (cube.vertices[size_t(t[1])] - cube.vertices[size_t(t[0])]) + .cross(cube.vertices[size_t(t[2])] - cube.vertices[size_t(t[0])]) + .normalized(); + if (n.dot(dir) > 0.99f) + selector.set_facet(f, EnforcerBlockerType::ENFORCER); + } + return selector.serialize(); + }; + TextureDisplacementFacetsData facets{}; + facets[0] = paint_facing(Vec3f::UnitZ()); + facets[1] = paint_facing(-Vec3f::UnitX()); + + TextureDisplacementLayer top; + top.slot = 0; + top.depth_mm = 1.0f; + top.tiling_scale = 5.0f; + top.image_data = make_flat_gray_png(255); + TextureDisplacementLayer side = top; + side.slot = 1; + side.depth_mm = 0.5f; + side.image_data = make_flat_gray_png(64); + + const HeightFieldSampler both = make_combined_displacement_sampler(cube, { top, side }, facets); + const HeightFieldSampler top_only = make_combined_displacement_sampler(cube, { top }, facets); + const HeightFieldSampler side_only = make_combined_displacement_sampler(cube, { side }, facets); + REQUIRE(both); + REQUIRE(top_only); + REQUIRE(side_only); + + const Vec3f on_top(5.f, 5.f, 10.f), on_side(0.f, 5.f, 5.f), unpainted(5.f, 10.f, 5.f); + CHECK_THAT(both(on_top, Vec3f::UnitZ()), WithinAbs(top_only(on_top, Vec3f::UnitZ()), 1e-5f)); + CHECK_THAT(both(on_side, -Vec3f::UnitX()), WithinAbs(side_only(on_side, -Vec3f::UnitX()), 1e-5f)); + CHECK_THAT(both(unpainted, Vec3f::UnitY()), WithinAbs(0.f, 1e-6f)); +} From 8f853c0e22cff5a65dcf98a1229ef625832f57fb Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Mon, 21 Sep 2026 14:55:39 +0200 Subject: [PATCH 40/89] Faster slicing of colour-painted layers, texture panel tools row A layer split into ~1000 colour fragments (a colour texture baked over a large top face) made several per-fragment loops redo whole-layer ClipperLib work, so slicing took ~33 min; it now takes ~3 min with the same output. - make_fills: clip the layer's no-overlap area to each expolygon's box before intersecting - discover_vertical_shells: small-piece filter compares only against the nearby part of the layer - bridge_over_infill: whole-layer union/diff/intersections restricted to the candidate's neighbourhood; fill boundary expanded once per spacing; anchor tree built only from lines crossing the scan range; bbox pre-check in the collision test; limiting outline taken directly instead of through expand(..., 0.3 * flow.spacing()), which offsets by 0.135 scaled units (flow.spacing() is in mm) and only cost a whole-layer pass per candidate - Bake job: include GUI.hpp for show_error() - Texture panel: select/erase whole model next to the paint tools, brush size slider back on the same row --- src/libslic3r/Fill/Fill.cpp | 10 +- src/libslic3r/PrintObject.cpp | 115 +++++++++++++++--- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 27 ++-- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 1 + 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 28fabed8af..0515befe6a 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1409,7 +1409,15 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // Orca: Reuse the body origin used for bridge anchoring, resetting it for each surface. f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox)); - f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes); + // Only the part of the layer-wide no-overlap area under this expolygon matters, so clip it to the + // expolygon's box first (padded past the safety offset, which grows the clip side). The result is + // identical; the cost is not: a layer split into many small fills, e.g. by colour painting, + // otherwise intersects every one of them with the whole layer. + BoundingBox no_overlap_bbox = get_extents(expoly); + no_overlap_bbox.offset(SCALED_EPSILON); + f->no_overlap_expolygons = intersection_ex( + ClipperUtils::clip_clipper_polygons_with_subject_bbox(surface_fill.no_overlap_expolygons, no_overlap_bbox), + ExPolygons() = {expoly}, ApplySafetyOffset::Yes); if (params.symmetric_infill_y_axis) { params.symmetric_y_axis = f->extended_object_bounding_box().center().x(); expoly.symmetric_y(params.symmetric_y_axis); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 3b7e889472..3dadd4c9c0 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2580,15 +2580,34 @@ void PrintObject::discover_vertical_shells() // the in-model condition is there due to small sloping surfaces, e.g. top of the hull of the benchy // 2. the area does not fully cover an internal polygon // This is there mainly for a very thin parts, where the solid layers would be missing if the part area is quite small + // Both tests below compare a small piece against the whole layer. Done literally, that is + // quadratic in the number of pieces, which is what a layer split up by colour painting has, + // so each is restricted to the part of the layer near the piece with an identical result: + // object_volume is clipped to the piece's box, and only the internal polygons whose box meets + // the expanded piece take part in the count, since the others pass through the difference + // unchanged and add the same number to both sides of it. + std::vector internal_bboxes; + internal_bboxes.reserve(internal_volume.size()); + for (const Polygon &poly : internal_volume) + internal_bboxes.emplace_back(get_extents(poly)); regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(), - [&internal_volume, &min_perimeter_infill_spacing, + [&internal_volume, &internal_bboxes, &min_perimeter_infill_spacing, &object_volume](const ExPolygon &p) { - return (p.area() < min_perimeter_infill_spacing * scaled(1.5) || - (p.area() < min_perimeter_infill_spacing * scaled(8.0) && - diff(to_polygons(p), object_volume).empty())) && - diff(internal_volume, - expand(to_polygons(p), min_perimeter_infill_spacing)) - .size() >= internal_volume.size(); + const bool small = p.area() < min_perimeter_infill_spacing * scaled(1.5) || + (p.area() < min_perimeter_infill_spacing * scaled(8.0) && + diff(to_polygons(p), + ClipperUtils::clip_clipper_polygons_with_subject_bbox( + object_volume, get_extents(p).inflated(SCALED_EPSILON))) + .empty()); + if (!small) + return false; + const Polygons expanded = expand(to_polygons(p), min_perimeter_infill_spacing); + const BoundingBox bbox = get_extents(expanded); + Polygons nearby; + for (size_t i = 0; i < internal_volume.size(); ++i) + if (internal_bboxes[i].overlap(bbox)) + nearby.emplace_back(internal_volume[i]); + return diff(nearby, expanded).size() >= nearby.size(); }), regularized_shell.end()); } @@ -3159,6 +3178,16 @@ void PrintObject::bridge_over_infill() vertical_lines[i].b = Point{x, y_max}; } + // The vertical lines only span the bridged area's x range, so anchors entirely outside it can never be + // hit. Leaving them out gives the same intersections without building a tree over the whole layer's + // boundary for every bridge. + const coord_t scan_x_min = bb_x.min.x(); + const coord_t scan_x_max = bb_x.min.x() + coord_t(n_vlines) * scan_spacing; + anchors.erase(std::remove_if(anchors.begin(), anchors.end(), + [scan_x_min, scan_x_max](const Line &l) { + return std::max(l.a.x(), l.b.x()) < scan_x_min || std::min(l.a.x(), l.b.x()) > scan_x_max; + }), + anchors.end()); auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors)}; auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; @@ -3403,28 +3432,61 @@ void PrintObject::bridge_over_infill() std::vector expanded_surfaces; expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); + // The expanded fill boundary depends only on the bridging flow, and total_fill_area is not + // modified below, so build it once per spacing rather than once per candidate. A layer split + // into many candidates (e.g. by colour painting) otherwise repeats a layer-wide offset for each. + std::map boundary_by_spacing; + // expansion_area is a clean, non-overlapping set, so uniting it with a bridge or cutting a bridge + // out of it only changes the polygons near that bridge. The rest are passed through untouched + // instead of being fed to ClipperLib with the whole layer again for every candidate. + const auto split_near = [](const Polygons &polys, const BoundingBox &bbox, Polygons &far) { + Polygons near; + for (const Polygon &p : polys) + (get_extents(p).overlap(bbox) ? near : far).emplace_back(p); + return near; + }; for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { const auto ®ion_config = candidate.region->region().config(); const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve || region_config.sparse_infill_pattern == ipOctagramSpiral; const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); - area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); + // deep_infill_area and internal_unsupported_area cover the whole layer; only their part under + // this candidate can change the results, so they are clipped to its box first. + if (!area_to_be_bridge.empty()) + area_to_be_bridge = intersection(area_to_be_bridge, + ClipperUtils::clip_clipper_polygons_with_subject_bbox( + deep_infill_area, get_extents(area_to_be_bridge).inflated(SCALED_EPSILON))); area_to_be_bridge.erase(std::remove_if(area_to_be_bridge.begin(), area_to_be_bridge.end(), - [internal_unsupported_area](const Polygon &p) { - return intersection({p}, internal_unsupported_area).empty(); + [&internal_unsupported_area](const Polygon &p) { + return intersection({p}, ClipperUtils::clip_clipper_polygons_with_subject_bbox( + internal_unsupported_area, + get_extents(p).inflated(SCALED_EPSILON))) + .empty(); }), area_to_be_bridge.end()); - Polygons limiting_area = union_(area_to_be_bridge, expansion_area); - if (area_to_be_bridge.empty()) continue; - Polylines boundary_plines = to_polylines(expand(total_fill_area, 1.3 * flow.scaled_spacing())); + Polygons limiting_area; + const Polygons near_expansion = split_near(expansion_area, get_extents(area_to_be_bridge).inflated(SCALED_EPSILON), + limiting_area); + append(limiting_area, union_(area_to_be_bridge, near_expansion)); + + auto boundary_it = boundary_by_spacing.find(flow.scaled_spacing()); + if (boundary_it == boundary_by_spacing.end()) + boundary_it = boundary_by_spacing + .emplace(flow.scaled_spacing(), to_polylines(expand(total_fill_area, 1.3 * flow.scaled_spacing()))) + .first; + Polylines boundary_plines = boundary_it->second; { - Polylines limiting_plines = to_polylines(expand(limiting_area, 0.3*flow.spacing())); + // No offset here: flow.spacing() is in mm, so the expand(limiting_area, 0.3 * flow.spacing()) + // this used to be moved the outline by 0.135 scaled units - nothing beyond rounding - while + // costing a whole-layer ClipperLib pass for every candidate. limiting_area is already a clean + // union, so its own outline is the same boundary. + Polylines limiting_plines = to_polylines(limiting_area); boundary_plines.insert(boundary_plines.end(), limiting_plines.begin(), limiting_plines.end()); } @@ -3498,9 +3560,12 @@ void PrintObject::bridge_over_infill() // Check collision with other expanded surfaces { bool reconstruct = false; - Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing()); + Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing()); + const BoundingBox tmp_expanded_bbox = get_extents(tmp_expanded_area); for (const CandidateSurface &s : expanded_surfaces) { - if (!intersection(s.new_polys, tmp_expanded_area).empty()) { + // Surfaces whose boxes miss each other cannot intersect, which is most pairs on a busy layer. + if (get_extents(s.new_polys).overlap(tmp_expanded_bbox) && + !intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; reconstruct = true; break; @@ -3524,10 +3589,20 @@ void PrintObject::bridge_over_infill() bridging_area = union_(bridging_area, construct_anchored_polygon(bridging_area, to_lines(boundary_plines), flow, bridging_angle, scan_spacing, true)); } - bridging_area = intersection(bridging_area, limiting_area); - bridging_area = intersection(bridging_area, total_fill_area); - bridging_area = diff(bridging_area, total_top_area); - expansion_area = diff(expansion_area, bridging_area); + // Each of these meets one bridge with the whole layer, so the layer side is first cut down to the + // bridge's box (and expansion_area split as above); the result is the same. + if (!bridging_area.empty()) { + const BoundingBox bridging_bbox = get_extents(bridging_area).inflated(SCALED_EPSILON); + bridging_area = intersection(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(limiting_area, bridging_bbox)); + bridging_area = intersection(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(total_fill_area, bridging_bbox)); + bridging_area = diff(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(total_top_area, bridging_bbox)); + } + if (!bridging_area.empty()) { + Polygons kept; + const Polygons cut = split_near(expansion_area, get_extents(bridging_area).inflated(SCALED_EPSILON), kept); + append(kept, diff(cut, bridging_area)); + expansion_area = std::move(kept); + } #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_expanded_bridging" + std::to_string(r), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 34b47c1825..604c3786f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -5576,14 +5576,15 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_erase_mode = true; } - // ---- Tools: brush / face / connected area on the left, the whole-model actions on the right, and the - // active tool's own control on the line below ---- + // ---- Tools: brush / face / connected area, then the whole-model actions, then the active tool's own + // control filling the rest of the row ---- // "Face" and "Connected area" reuse the exact same selection machinery every other paint gizmo has // (single-facet click, and angle-limited flood fill respectively). { const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER; const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER; const bool is_area_mode = m_tool_type == ToolType::SMART_FILL; + const float row_y = ImGui::GetCursorPosY(); if (icon_toggle(801, "texture_displacement_brush.svg", is_brush_mode, icon_md, _L("Brush"), _L("Brush - paint over the surface by dragging"))) { m_tool_type = ToolType::BRUSH; @@ -5603,16 +5604,14 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_cursor_type = TriangleSelector::CursorType::POINTER; } - // Whole model: paint every face with the active layer, or clear its paint from all of them. Actions - // rather than tools, so they sit apart at the right end of the row. + // Whole model: paint every face with the active layer, or clear its paint from all of them. const wxString whole_na = busy ? _L("Wait for the bake to finish.") : active == nullptr ? _L("Add a layer first.") : wxString(); const wxString erase_na = !whole_na.empty() ? whole_na : !slot_painted(m_active_layer_slot) ? _L("The active layer has no paint yet.") : wxString(); - ImGui::SameLine(); - ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - (2.f * icon_md + gap_s))); + ImGui::SameLine(0.f, gap_s); if (icon_toggle(806, "texture_displacement_select_all.svg", false, icon_md, _L("Select whole model"), _L("Select whole model - paint every face of the model with the active layer"), whole_na)) select_whole_model(); @@ -5632,28 +5631,38 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_parent.set_as_dirty(); } + // The active tool's control fills the rest of the row, each part centred on the (taller) tool icons. + const float row_end = ImGui::GetWindowContentRegionMax().x; + const auto centre_on_row = [&](float h) { ImGui::SetCursorPosY(row_y + std::round((icon_md - h) * 0.5f)); }; + vsep(icon_md); if (is_brush_mode) { - ImGui::SetNextItemWidth(-(3.f * gap_s + 1.f + 2.f * icon_sm)); + ImGui::SetNextItemWidth(std::max(1.f, row_end - ImGui::GetCursorPosX() - (3.f * gap_s + 1.f + 2.f * icon_sm))); + centre_on_row(frame_h); ImGui::SliderFloat("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f mm", ImGuiSliderFlags_AlwaysClamp); hover_tip(m_desc.at("cursor_size")); vsep(icon_sm); const bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE; + centre_on_row(icon_sm); if (icon_toggle(804, "circle_paint.svg", is_circle, icon_sm, m_desc.at("circle"), _L("Circle - paints everything under the brush as seen from the camera"))) m_cursor_type = TriangleSelector::CursorType::CIRCLE; ImGui::SameLine(0.f, gap_s); + centre_on_row(icon_sm); if (icon_toggle(805, "menu_obj_sphere.svg", !is_circle, icon_sm, m_desc.at("sphere"), _L("Sphere - paints only within a ball around the point under the cursor"))) m_cursor_type = TriangleSelector::CursorType::SPHERE; } else if (is_area_mode) { - ImGui::SetNextItemWidth(-1.f); + ImGui::SetNextItemWidth(std::max(1.f, row_end - ImGui::GetCursorPosX())); + centre_on_row(frame_h); ImGui::SliderFloat("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, "%.0f°", ImGuiSliderFlags_AlwaysClamp); hover_tip(_u8L("Angle threshold - the fill stops at edges sharper than this")); } else { + centre_on_row(frame_h); ImGui::AlignTextToFramePadding(); - ImGui::TextDisabled("%s", _u8L("Click a triangle to paint it").c_str()); + ImGui::TextDisabled("%s", ellipsize(_u8L("Click a triangle to paint it"), + std::max(0.f, row_end - ImGui::GetCursorPosX())).c_str()); } } diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index f48ec6b28c..6e3976a297 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -6,6 +6,7 @@ #include "libslic3r/TriangleSelector.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" From ae7bf43fde5f203854f46ad5b6affbece4580819 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Mon, 21 Sep 2026 15:22:36 +0200 Subject: [PATCH 41/89] Run colour segmentation and vertical shells in parallel Same output, ~2.6 min for the LOTR map plate, was ~2.9. --- src/libslic3r/MultiMaterialSegmentation.cpp | 65 ++++++++++++--------- src/libslic3r/PrintObject.cpp | 33 ++++++++++- 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 6f80f7b759..57da8570ee 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1378,13 +1378,40 @@ static inline std::vector> segmentation_top_and_bottom_l return out; }; + // Projects a painted top or bottom face `ex` of layer `layer_idx` onto the shell layers below or above it (in + // `shell_layers`, nearest first), one more perimeter in on each, stopping at the first layer where nothing is left. + // The per-layer ClipperLib work is independent once the trimmed slices and the offsets have been walked in order, + // so it runs in parallel; the offsets are accumulated exactly as the sequential walk did. + const auto project_to_shells = [&input_expolygons](const ExPolygons &ex, size_t layer_idx, const std::vector &shell_layers, + const LayerColorStat &stat, std::vector &dst, size_t dst_offset) { + std::vector trimmed(shell_layers.size()); + std::vector offsets(shell_layers.size()); + ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; + float offset = 0.f; + for (size_t i = 0; i < shell_layers.size(); ++i) { + //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line + offset -= (stat.extrusion_spacing + stat.extrusion_width); + offsets[i] = offset; + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[shell_layers[i]]); + trimmed[i] = layer_slices_trimmed; + } + std::vector shells(shell_layers.size()); + tbb::parallel_for(size_t(0), shell_layers.size(), [&](size_t i) { + shells[i] = opening_ex(intersection_ex(ex, offset_ex(trimmed[i], offsets[i])), stat.small_region_threshold); + }); + for (size_t i = 0; i < shell_layers.size() && !shells[i].empty(); ++i) + append(dst[shell_layers[i] + dst_offset], std::move(shells[i])); + }; + tbb::parallel_for(tbb::blocked_range(0, num_layers, granularity), [&granularity, &num_layers, &num_facets_states, &layer_color_stat, &top_raw, &triangles_by_color_top, - &throw_on_cancel_callback, &input_expolygons, &bottom_raw, &triangles_by_color_bottom, + &throw_on_cancel_callback, &bottom_raw, &triangles_by_color_bottom, &project_to_shells, &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range &range) { size_t group_idx = range.begin() / granularity; size_t layer_idx_offset = (group_idx & 1) * num_layers; for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - for (size_t color_idx = 0; color_idx < num_facets_states; ++color_idx) { + // Each colour writes only its own vectors, so the colours run in parallel: a painted top or bottom face + // projects onto a single layer, which otherwise did all of its colours on one thread. + tbb::parallel_for(size_t(0), size_t(num_facets_states), [&](size_t color_idx) { throw_on_cancel_callback(); LayerColorStat stat = layer_color_stat(layer_idx, color_idx); if (std::vector &top = top_raw[color_idx]; ! top.empty() && ! top[layer_idx].empty()) @@ -1393,18 +1420,10 @@ static inline std::vector> segmentation_top_and_bottom_l top_ex = opening_ex(top_ex, stat.small_region_threshold); if (! top_ex.empty()) { append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex); - float offset = 0.f; - ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; - for (int last_idx = int(layer_idx) - 1; last_idx > std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) { - //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line - //offset -= stat.extrusion_width ; - offset -= (stat.extrusion_spacing + stat.extrusion_width); - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]); - ExPolygons last = opening_ex(intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold); - if (last.empty()) - break; - append(shell_triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last)); - } + std::vector shell_layers; + for (int last_idx = int(layer_idx) - 1; last_idx > std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) + shell_layers.emplace_back(size_t(last_idx)); + project_to_shells(top_ex, layer_idx, shell_layers, stat, shell_triangles_by_color_top[color_idx], layer_idx_offset); } } if (std::vector &bottom = bottom_raw[color_idx]; ! bottom.empty() && ! bottom[layer_idx].empty()) @@ -1413,21 +1432,13 @@ static inline std::vector> segmentation_top_and_bottom_l bottom_ex = opening_ex(bottom_ex, stat.small_region_threshold); if (! bottom_ex.empty()) { append(triangles_by_color_bottom[color_idx][layer_idx + layer_idx_offset], bottom_ex); - float offset = 0.f; - ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; - for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + stat.bottom_shell_layers, num_layers); ++last_idx) { - //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line - //offset -= stat.extrusion_width; - offset -= (stat.extrusion_spacing + stat.extrusion_width); - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]); - ExPolygons last = opening_ex(intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold); - if (last.empty()) - break; - append(shell_triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last)); - } + std::vector shell_layers; + for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + stat.bottom_shell_layers, num_layers); ++last_idx) + shell_layers.emplace_back(last_idx); + project_to_shells(bottom_ex, layer_idx, shell_layers, stat, shell_triangles_by_color_bottom[color_idx], layer_idx_offset); } } - } + }); } }); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 3dadd4c9c0..7101e1d4d7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -2304,6 +2305,18 @@ void PrintObject::discover_vertical_shells() BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } + // With one top/bottom cache for all regions, the shell and hole accumulation in the loop below depends on nothing + // region-specific but the shell settings and the external perimeter spacing, so a region sharing them with an earlier + // one reuses its result instead of repeating it: that accumulation is a union over several layers of top/bottom + // surfaces, and a multi-material print has a region per filament. + struct ShellAccumulation + { + std::array key; + Polygons shell; + Polygons holes; + }; + std::vector> shell_accumulations(top_bottom_surfaces_all_regions ? num_layers : 0); + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); if (region.config().ensure_vertical_shell_thickness.value != evstAll ) @@ -2348,7 +2361,7 @@ void PrintObject::discover_vertical_shells() grain_size = 1; tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), - [this, region_id, &cache_top_botom_regions] + [this, region_id, &cache_top_botom_regions, &shell_accumulations] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { @@ -2398,6 +2411,21 @@ void PrintObject::discover_vertical_shells() } } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + const std::array accumulation_key{ double(region_config.top_shell_layers.value), region_config.top_shell_thickness.value, + double(region_config.bottom_shell_layers.value), region_config.bottom_shell_thickness.value, + double(layerm->flow(frExternalPerimeter).scaled_spacing()) }; + std::vector *accumulations = shell_accumulations.empty() ? nullptr : &shell_accumulations[idx_layer]; + const auto reused = accumulations == nullptr ? nullptr : + [&]() -> const ShellAccumulation * { + for (const ShellAccumulation &a : *accumulations) + if (a.key == accumulation_key) + return &a; + return nullptr; + }(); + if (reused != nullptr) { + shell = reused->shell; + holes = reused->holes; + } else { polygons_append(holes, cache_top_botom_regions[idx_layer].holes); auto combine_holes = [&holes](const Polygons &holes2) { if (holes.empty() || holes2.empty()) @@ -2472,6 +2500,9 @@ void PrintObject::discover_vertical_shells() (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON)) combine_holes(cache_top_botom_regions[i].holes); } + if (accumulations != nullptr) + accumulations->push_back({ accumulation_key, shell, holes }); + } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); From 303efacec0320013a58247b9ca366ecb11076418 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Mon, 21 Sep 2026 16:40:22 +0200 Subject: [PATCH 42/89] Generate walls and split solid infill in parallel Same output, ~2.2 min for the LOTR map plate, was ~2.6. --- src/libslic3r/Fill/Fill.cpp | 33 +- src/libslic3r/MultiMaterialSegmentation.cpp | 17 +- src/libslic3r/PerimeterGenerator.cpp | 746 ++++++++++---------- 3 files changed, 417 insertions(+), 379 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 0515befe6a..56d54ef681 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -9,6 +9,8 @@ #include "../PrintConfig.hpp" #include "../Surface.hpp" +#include + #include "AABBTreeLines.hpp" #include "ExtrusionEntity.hpp" #include "Fill.hpp" @@ -630,24 +632,28 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n if (!line_based_pattern) { const coord_t scaled_spacing = scaled(fill.params.spacing); - for (const ExPolygon &expolygon : fill.expolygons) { + // Each expolygon is split on its own, so they run in parallel and are collected in their original order. + std::vector> split_parts(fill.expolygons.size()); // normal, narrow + tbb::parallel_for(size_t(0), fill.expolygons.size(), [&](size_t idx) { + const ExPolygon &expolygon = fill.expolygons[idx]; Polygons filled_area = to_polygons(expolygon); // "Core" area: open (erode+dilate) to drop thin features, then clamp back to the original polygon. Polygons inner_area = intersection(filled_area, opening(filled_area, scaled_spacing, scaled_spacing)); if (inner_area.empty()) { - narrow_infill.emplace_back(expolygon); - continue; + split_parts[idx].second.emplace_back(expolygon); + return; } ExPolygons inner_ex = union_ex(inner_area); ExPolygons expolys{expolygon}; - ExPolygons narrow_ex = diff_ex(expolys, inner_ex); - ExPolygons normal_ex = intersection_ex(expolys, inner_ex); - - append(normal_infill, normal_ex); // normal infill area - append(narrow_infill, narrow_ex); // narrow infill area + split_parts[idx].second = diff_ex(expolys, inner_ex); // narrow infill area + split_parts[idx].first = intersection_ex(expolys, inner_ex); // normal infill area + }); + for (auto &[normal_ex, narrow_ex] : split_parts) { + append(normal_infill, std::move(normal_ex)); + append(narrow_infill, std::move(narrow_ex)); } return; @@ -669,7 +675,10 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n } const double aligning_angle = -base_angle + PI; - for (const ExPolygon &expolygon : fill.expolygons) { + // Each expolygon is reconstructed on its own, so they run in parallel and are collected in their original order. + std::vector split_reconstructed(fill.expolygons.size()); + tbb::parallel_for(size_t(0), fill.expolygons.size(), [&](size_t expolygon_idx) { + const ExPolygon &expolygon = fill.expolygons[expolygon_idx]; Polygons filled_area = to_polygons(expolygon); polygons_rotate(filled_area, aligning_angle); BoundingBox bb = get_extents(filled_area); @@ -800,8 +809,10 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n } } - polygons_append(normal_fill_areas, reconstructed_area); - } + split_reconstructed[expolygon_idx] = std::move(reconstructed_area); + }); + for (Polygons &reconstructed_area : split_reconstructed) + polygons_append(normal_fill_areas, std::move(reconstructed_area)); polygons_rotate(normal_fill_areas, -aligning_angle); diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 57da8570ee..8b1d612b00 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1448,22 +1448,25 @@ static inline std::vector> segmentation_top_and_bottom_l &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { throw_on_cancel_callback(); - ExPolygons painted_exploys; - for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { + // The per-colour unions below are independent of each other, so they run in parallel (a painted top or + // bottom face puts all of its colours on one layer); whatever combines the colours stays in colour order. + const auto merge_colour_union = [&](size_t color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx + num_layers])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers])); self = union_ex(self); + }; + tbb::parallel_for(size_t(0), triangles_by_color_merged.size(), merge_colour_union); - append(painted_exploys, self); - } - + ExPolygons painted_exploys; + for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) + append(painted_exploys, triangles_by_color_merged[color_idx][layer_idx]); painted_exploys = union_ex(painted_exploys); //BBS: merge the top and bottom shell layers - for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { + tbb::parallel_for(size_t(0), triangles_by_color_merged.size(), [&](size_t color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; auto top_area = diff_ex(union_ex(shell_triangles_by_color_top[color_idx][layer_idx], @@ -1477,7 +1480,7 @@ static inline std::vector> segmentation_top_and_bottom_l append(self, top_area); append(self, bottom_area); self = union_ex(self); - } + }); // Trim one region by the other if some of the regions overlap. ExPolygons painted_regions; for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 9f6ec856ba..0468c3be91 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "libslic3r/AABBTreeLines.hpp" #include "Print.hpp" static const int overhang_sampling_number = 6; @@ -2456,421 +2458,443 @@ void PerimeterGenerator::process_arachne() const bool only_one_wall_first_layer = this->config->only_one_wall_first_layer && has_bottom_shell_layers(*this->config); // we need to process each island separately because we might have different // extra perimeters for each one - for (const Surface& surface : all_surfaces) { - coord_t bead_width_0 = ext_perimeter_spacing; - // detect how many perimeters must be generated for this island - int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops - int sparse_infill_density = this->config->sparse_infill_density.value; - if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0) // add alternating extra wall - loop_number++; + // Each island is independent up to its outputs, so they are generated in parallel - a layer split into thousands + // of islands (e.g. by colour painting) otherwise ran on one thread - and the outputs are then committed in the + // original island order, which is what the extra overhang perimeters (applied to the last island's loops and to + // all fill surfaces so far) depend on. + struct ArachneSurfaceResult + { + ExtrusionEntityCollection loops; + bool has_loops = false; + ExPolygons infill; + ExPolygons no_overlap; + }; + std::vector results(all_surfaces.size()); + tbb::parallel_for(tbb::blocked_range(0, all_surfaces.size()), [&](const tbb::blocked_range &range) { + for (size_t surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { + const Surface &surface = all_surfaces[surface_idx]; + ArachneSurfaceResult &result = results[surface_idx]; + coord_t bead_width_0 = ext_perimeter_spacing; + // detect how many perimeters must be generated for this island + int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops + int sparse_infill_density = this->config->sparse_infill_density.value; + if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0) // add alternating extra wall + loop_number++; - // Set the bottommost layer to be one wall - const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false; - if (is_bottom_layer && only_one_wall_first_layer) - loop_number = 0; + // Set the bottommost layer to be one wall + const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false; + if (is_bottom_layer && only_one_wall_first_layer) + loop_number = 0; - // Orca: set the topmost layer to be one wall according to the config - const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false; - if (is_topmost_layer && loop_number > 0 && only_one_wall_top) - loop_number = 0; + // Orca: set the topmost layer to be one wall according to the config + const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false; + if (is_topmost_layer && loop_number > 0 && only_one_wall_top) + loop_number = 0; - auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter; - // Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled. - ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), - apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing ) - : -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); + auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter; + // Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled. + ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), + apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing ) + : -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); - Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config); - // Set params is_top_or_bottom_layer for adjusting short-wall removal sensitivity. - input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false; + Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config); + // Set params is_top_or_bottom_layer for adjusting short-wall removal sensitivity. + input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false; - coord_t wall_0_inset = 0; - if (apply_precise_outer_wall) - wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2); + coord_t wall_0_inset = 0; + if (apply_precise_outer_wall) + wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2); - //PS: One wall top surface for Arachne - ExPolygons top_expolygons; - // Calculate how many inner loops remain when TopSurfaces is selected. - const int inner_loop_number = (only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1; + //PS: One wall top surface for Arachne + ExPolygons top_expolygons; + // Calculate how many inner loops remain when TopSurfaces is selected. + const int inner_loop_number = (only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1; - // Set one perimeter when TopSurfaces is selected. - if (only_one_wall_top && loop_number > 0) - loop_number = 0; + // Set one perimeter when TopSurfaces is selected. + if (only_one_wall_top && loop_number > 0) + loop_number = 0; - Arachne::WallToolPathsParams input_params_tmp = input_params; + Arachne::WallToolPathsParams input_params_tmp = input_params; - Polygons last_p = to_polygons(last); - Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1), - wall_0_inset, layer_height, input_params_tmp); - std::vector perimeters = wallToolPaths.getToolPaths(); - ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); + Polygons last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1), + wall_0_inset, layer_height, input_params_tmp); + std::vector perimeters = wallToolPaths.getToolPaths(); + ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); - // Check if there are some remaining perimeters to generate (the number of perimeters - // is greater than one together with enabled the single perimeter on top surface feature). - if (inner_loop_number >= 0) { - assert(upper_slices != nullptr); + // Check if there are some remaining perimeters to generate (the number of perimeters + // is greater than one together with enabled the single perimeter on top surface feature). + if (inner_loop_number >= 0) { + assert(upper_slices != nullptr); - // Infill contour bounding box. - BoundingBox infill_contour_bbox = get_extents(infill_contour); - infill_contour_bbox.offset(SCALED_EPSILON); + // Infill contour bounding box. + BoundingBox infill_contour_bbox = get_extents(infill_contour); + infill_contour_bbox.offset(SCALED_EPSILON); - coord_t perimeter_width = this->perimeter_flow.scaled_width(); + coord_t perimeter_width = this->perimeter_flow.scaled_width(); - // Get top ExPolygons from current infill contour. - Polygons upper_slices_clipped; - if (object_config->interface_shells) { - auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces); - upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, infill_contour_bbox); - } else - upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox); + // Get top ExPolygons from current infill contour. + Polygons upper_slices_clipped; + if (object_config->interface_shells) { + auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces); + upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, infill_contour_bbox); + } else + upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox); - top_expolygons = diff_ex(infill_contour, upper_slices_clipped); + top_expolygons = diff_ex(infill_contour, upper_slices_clipped); - if (!top_expolygons.empty()) { - if (lower_slices != nullptr) { - const float bridge_offset = float(std::max(ext_perimeter_spacing, perimeter_width)); - const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox); - const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset); + if (!top_expolygons.empty()) { + if (lower_slices != nullptr) { + const float bridge_offset = float(std::max(ext_perimeter_spacing, perimeter_width)); + const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox); + const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset); - // Remove bridges from top surface polygons. - top_expolygons = diff_ex(top_expolygons, current_slices_bridges); + // Remove bridges from top surface polygons. + top_expolygons = diff_ex(top_expolygons, current_slices_bridges); + } + + // Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line. + // ORCA: skip if the top surface area is smaller than "min_width_top_surface" + const float top_surface_min_width = std::max(float(ext_perimeter_spacing) / 4.f + scaled(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f); + // Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little. + // ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount, + // not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions + // due to thin lines being generated + top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85)); + + // Get final top ExPolygons (bridges were excluded above, so they stay walled). + top_expolygons = intersection_ex(top_expolygons, infill_contour); + + // ORCA: onion the real region (inside the outer wall) so the remaining walls follow the actual + // geometry, then cut away the parts over the top surface. Re-onioning the non-top complement + // instead - the fallback when there is no top fill - walls the top/non-top interface and rings + // top-surface islands with inner walls that don't exist when the feature is disabled. + const bool clip_walls_over_top = top_fill_replaces_inner_walls(*this->config); + const Polygons inner_region = to_polygons(offset_ex(clip_walls_over_top ? infill_contour + : diff_ex(infill_contour, top_expolygons), + wall_0_inset)); + Arachne::WallToolPaths inner_wall_tool_paths(inner_region, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp); + std::vector inner_perimeters = inner_wall_tool_paths.getToolPaths(); + + if (clip_walls_over_top) { + Polygons kept_over_top; + clip_inner_walls_over_top(inner_perimeters, top_expolygons, perimeter_width, kept_over_top); + // Route the top fill around the walls kept despite grazing the top. + if (! kept_over_top.empty()) + top_expolygons = diff_ex(top_expolygons, kept_over_top); + } + + // Recalculate indexes of inner perimeters before merging them: they come after the single outer wall. + if (!perimeters.empty()) + for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) + for (Arachne::ExtrusionLine &el : inner_perimeter) + ++el.inset_idx; + + perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end()); + infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour()); + } else { + // There is no top surface ExPolygon, so we call Arachne again with parameters + // like when the single perimeter feature is disabled. + Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp); + perimeters = no_single_perimeter_tool_paths.getToolPaths(); + infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour()); } - - // Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line. - // ORCA: skip if the top surface area is smaller than "min_width_top_surface" - const float top_surface_min_width = std::max(float(ext_perimeter_spacing) / 4.f + scaled(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f); - // Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little. - // ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount, - // not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions - // due to thin lines being generated - top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85)); - - // Get final top ExPolygons (bridges were excluded above, so they stay walled). - top_expolygons = intersection_ex(top_expolygons, infill_contour); - - // ORCA: onion the real region (inside the outer wall) so the remaining walls follow the actual - // geometry, then cut away the parts over the top surface. Re-onioning the non-top complement - // instead - the fallback when there is no top fill - walls the top/non-top interface and rings - // top-surface islands with inner walls that don't exist when the feature is disabled. - const bool clip_walls_over_top = top_fill_replaces_inner_walls(*this->config); - const Polygons inner_region = to_polygons(offset_ex(clip_walls_over_top ? infill_contour - : diff_ex(infill_contour, top_expolygons), - wall_0_inset)); - Arachne::WallToolPaths inner_wall_tool_paths(inner_region, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp); - std::vector inner_perimeters = inner_wall_tool_paths.getToolPaths(); - - if (clip_walls_over_top) { - Polygons kept_over_top; - clip_inner_walls_over_top(inner_perimeters, top_expolygons, perimeter_width, kept_over_top); - // Route the top fill around the walls kept despite grazing the top. - if (! kept_over_top.empty()) - top_expolygons = diff_ex(top_expolygons, kept_over_top); - } - - // Recalculate indexes of inner perimeters before merging them: they come after the single outer wall. - if (!perimeters.empty()) - for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) - for (Arachne::ExtrusionLine &el : inner_perimeter) - ++el.inset_idx; - - perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end()); - infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour()); - } else { - // There is no top surface ExPolygon, so we call Arachne again with parameters - // like when the single perimeter feature is disabled. - Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp); - perimeters = no_single_perimeter_tool_paths.getToolPaths(); - infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour()); } - } - //PS + //PS - loop_number = int(perimeters.size()) - 1; + loop_number = int(perimeters.size()) - 1; - #ifdef ARACHNE_DEBUG - { - static int iRun = 0; - export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour())); - } -#endif + #ifdef ARACHNE_DEBUG + { + static int iRun = 0; + export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour())); + } + #endif - // All closed ExtrusionLine should have the same the first and the last point. - // But in rare cases, Arachne produce ExtrusionLine marked as closed but without - // equal the first and the last point. - assert([&perimeters = std::as_const(perimeters)]() -> bool { - for (const Arachne::VariableWidthLines& perimeter : perimeters) - for (const Arachne::ExtrusionLine& el : perimeter) - if (el.is_closed && el.junctions.front().p != el.junctions.back().p) - return false; - return true; - }()); + // All closed ExtrusionLine should have the same the first and the last point. + // But in rare cases, Arachne produce ExtrusionLine marked as closed but without + // equal the first and the last point. + assert([&perimeters = std::as_const(perimeters)]() -> bool { + for (const Arachne::VariableWidthLines& perimeter : perimeters) + for (const Arachne::ExtrusionLine& el : perimeter) + if (el.is_closed && el.junctions.front().p != el.junctions.back().p) + return false; + return true; + }()); - int start_perimeter = int(perimeters.size()) - 1; - int end_perimeter = -1; - int direction = -1; + int start_perimeter = int(perimeters.size()) - 1; + int end_perimeter = -1; + int direction = -1; - bool is_outer_wall_first = - this->config->wall_sequence == WallSequence::OuterInner || - this->config->wall_sequence == WallSequence::InnerOuterInner; + bool is_outer_wall_first = + this->config->wall_sequence == WallSequence::OuterInner || + this->config->wall_sequence == WallSequence::InnerOuterInner; - if (layer_id == 0){ // disable inner outer inner algorithm after the first layer - is_outer_wall_first = - this->config->wall_sequence == WallSequence::OuterInner; - } - if (is_outer_wall_first) { - start_perimeter = 0; - end_perimeter = int(perimeters.size()); - direction = 1; - } - - std::vector all_extrusions; - for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) { - if (perimeters[perimeter_idx].empty()) - continue; - for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx]) - all_extrusions.emplace_back(&wall); - } - - // Find topological order with constraints from extrusions_constrains. - std::vector blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. - std::vector> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. - std::unordered_map map_extrusion_to_idx; - for (size_t idx = 0; idx < all_extrusions.size(); idx++) - map_extrusion_to_idx.emplace(all_extrusions[idx], idx); - - auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, is_outer_wall_first); - for (auto [before, after] : extrusions_constrains) { - auto after_it = map_extrusion_to_idx.find(after); - ++blocked[after_it->second]; - blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second); - } - - std::vector processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed. - Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position. - std::vector ordered_extrusions; // To store our result in. At the end we'll std::swap. - ordered_extrusions.reserve(all_extrusions.size()); - - while (ordered_extrusions.size() < all_extrusions.size()) { - size_t best_candidate = 0; - double best_distance_sqr = std::numeric_limits::max(); - bool is_best_closed = false; - - std::vector available_candidates; - for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) { - if (processed[candidate] || blocked[candidate]) - continue; // Not a valid candidate. - available_candidates.push_back(candidate); + if (layer_id == 0){ // disable inner outer inner algorithm after the first layer + is_outer_wall_first = + this->config->wall_sequence == WallSequence::OuterInner; + } + if (is_outer_wall_first) { + start_perimeter = 0; + end_perimeter = int(perimeters.size()); + direction = 1; } - std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool { - return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed; - }); - - for (const size_t candidate_path_idx : available_candidates) { - auto& path = all_extrusions[candidate_path_idx]; - - if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. - if (best_distance_sqr == std::numeric_limits::max()) { - best_candidate = candidate_path_idx; - is_best_closed = path->is_closed; - } + std::vector all_extrusions; + for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) { + if (perimeters[perimeter_idx].empty()) continue; + for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx]) + all_extrusions.emplace_back(&wall); + } + + // Find topological order with constraints from extrusions_constrains. + std::vector blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. + std::vector> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. + std::unordered_map map_extrusion_to_idx; + for (size_t idx = 0; idx < all_extrusions.size(); idx++) + map_extrusion_to_idx.emplace(all_extrusions[idx], idx); + + auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, is_outer_wall_first); + for (auto [before, after] : extrusions_constrains) { + auto after_it = map_extrusion_to_idx.find(after); + ++blocked[after_it->second]; + blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second); + } + + std::vector processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed. + Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position. + std::vector ordered_extrusions; // To store our result in. At the end we'll std::swap. + ordered_extrusions.reserve(all_extrusions.size()); + + while (ordered_extrusions.size() < all_extrusions.size()) { + size_t best_candidate = 0; + double best_distance_sqr = std::numeric_limits::max(); + bool is_best_closed = false; + + std::vector available_candidates; + for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) { + if (processed[candidate] || blocked[candidate]) + continue; // Not a valid candidate. + available_candidates.push_back(candidate); } - const Point candidate_position = path->junctions.front().p; - double distance_sqr = (current_position - candidate_position).cast().norm(); - if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far. - if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits::max()) || (!path->is_closed && !is_best_closed)) { - best_candidate = candidate_path_idx; - best_distance_sqr = distance_sqr; - is_best_closed = path->is_closed; + std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool { + return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed; + }); + + for (const size_t candidate_path_idx : available_candidates) { + auto& path = all_extrusions[candidate_path_idx]; + + if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. + if (best_distance_sqr == std::numeric_limits::max()) { + best_candidate = candidate_path_idx; + is_best_closed = path->is_closed; + } + continue; } + + const Point candidate_position = path->junctions.front().p; + double distance_sqr = (current_position - candidate_position).cast().norm(); + if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far. + if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits::max()) || (!path->is_closed && !is_best_closed)) { + best_candidate = candidate_path_idx; + best_distance_sqr = distance_sqr; + is_best_closed = path->is_closed; + } + } + } + + auto& best_path = all_extrusions[best_candidate]; + ordered_extrusions.push_back({ best_path, best_path->is_contour() }); + processed[best_candidate] = true; + for (size_t unlocked_idx : blocking[best_candidate]) + blocked[unlocked_idx]--; + + if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then. + if (best_path->is_closed) + current_position = best_path->junctions[0].p; //We end where we started. + else + current_position = best_path->junctions.back().p; //Pick the other end from where we started. } } - auto& best_path = all_extrusions[best_candidate]; - ordered_extrusions.push_back({ best_path, best_path->is_contour() }); - processed[best_candidate] = true; - for (size_t unlocked_idx : blocking[best_candidate]) - blocked[unlocked_idx]--; + // printf("New Layer: Layer ID %d\n",layer_id); //debug - new layer + if (this->config->wall_sequence == WallSequence::InnerOuterInner && layer_id > 0) { // only enable inner outer inner algorithm after first layer + if (ordered_extrusions.size() > 2) { // 3 walls minimum needed to do inner outer inner ordering + int position = 0; // index to run the re-ordering for multiple external perimeters in a single island. + int arr_i, arr_j = 0; // indexes to run through the walls in the for loops + int outer, first_internal, second_internal, max_internal, current_perimeter; // allocate index values + + // To address any remaining scenarios where the outer perimeter contour is not first on the list as arachne sometimes reorders the perimeters when clustering + // for OI mode that is used the basis for IOI + bringContoursToFront(ordered_extrusions); + std::vector reordered_extrusions; + + // Debug statement to print spacing values: + //printf("External threshold - Ext perimeter: %d Ext spacing: %d Int perimeter: %d Int spacing: %d\n", this->ext_perimeter_flow.scaled_width(),this->ext_perimeter_flow.scaled_spacing(),this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_spacing()); - if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then. - if (best_path->is_closed) - current_position = best_path->junctions[0].p; //We end where we started. - else - current_position = best_path->junctions.back().p; //Pick the other end from where we started. - } - } - - // printf("New Layer: Layer ID %d\n",layer_id); //debug - new layer - if (this->config->wall_sequence == WallSequence::InnerOuterInner && layer_id > 0) { // only enable inner outer inner algorithm after first layer - if (ordered_extrusions.size() > 2) { // 3 walls minimum needed to do inner outer inner ordering - int position = 0; // index to run the re-ordering for multiple external perimeters in a single island. - int arr_i, arr_j = 0; // indexes to run through the walls in the for loops - int outer, first_internal, second_internal, max_internal, current_perimeter; // allocate index values + // Get searching thresholds. For an external perimeter we take the external perimeter spacing/2 plus the internal perimeter spacing/2 and expand by the factor + // rounding errors. When precise wall is enabled, the external perimeter full spacing is used. + coord_t threshold_external = (apply_precise_outer_wall) + // Precise outer wall ⇒ use “full external spacing” + ? ( this->ext_perimeter_flow.scaled_spacing() + + this->perimeter_flow.scaled_spacing()/2.0 ) + // Normal ⇒ half ext spacing + half int spacing + : ( this->ext_perimeter_flow.scaled_spacing()/2.0 + + this->perimeter_flow.scaled_spacing()/2.0 ); - // To address any remaining scenarios where the outer perimeter contour is not first on the list as arachne sometimes reorders the perimeters when clustering - // for OI mode that is used the basis for IOI - bringContoursToFront(ordered_extrusions); - std::vector reordered_extrusions; + // For the intenal perimeter threshold, the distance is the internal perimeter spacing expanded by the factor to cover rounding errors. + coord_t threshold_internal = this->perimeter_flow.scaled_spacing(); - // Debug statement to print spacing values: - //printf("External threshold - Ext perimeter: %d Ext spacing: %d Int perimeter: %d Int spacing: %d\n", this->ext_perimeter_flow.scaled_width(),this->ext_perimeter_flow.scaled_spacing(),this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_spacing()); - - // Get searching thresholds. For an external perimeter we take the external perimeter spacing/2 plus the internal perimeter spacing/2 and expand by the factor - // rounding errors. When precise wall is enabled, the external perimeter full spacing is used. - coord_t threshold_external = (apply_precise_outer_wall) - // Precise outer wall ⇒ use “full external spacing” - ? ( this->ext_perimeter_flow.scaled_spacing() - + this->perimeter_flow.scaled_spacing()/2.0 ) - // Normal ⇒ half ext spacing + half int spacing - : ( this->ext_perimeter_flow.scaled_spacing()/2.0 - + this->perimeter_flow.scaled_spacing()/2.0 ); + // Re-order extrusions based on distance + // Alorithm will aggresively optimise for the appearance of the outermost perimeter + ordered_extrusions = reorderPerimetersByProximity(ordered_extrusions,threshold_external,threshold_internal ); + reordered_extrusions = ordered_extrusions; // copy them into the reordered extrusions vector to allow for IOI operations to be performed below without altering the base ordered extrusions list. - // For the intenal perimeter threshold, the distance is the internal perimeter spacing expanded by the factor to cover rounding errors. - coord_t threshold_internal = this->perimeter_flow.scaled_spacing(); - - // Re-order extrusions based on distance - // Alorithm will aggresively optimise for the appearance of the outermost perimeter - ordered_extrusions = reorderPerimetersByProximity(ordered_extrusions,threshold_external,threshold_internal ); - reordered_extrusions = ordered_extrusions; // copy them into the reordered extrusions vector to allow for IOI operations to be performed below without altering the base ordered extrusions list. - - // Now start the sandwich mode wall re-ordering using the reordered_extrusions as the basis - // scan to find the external perimeter, first internal, second internal and last perimeter in the island. - // We then advance the position index to move to the second island and continue until there are no more - // perimeters left. - while (position < reordered_extrusions.size()) { - outer = first_internal = second_internal = current_perimeter = -1; // initialise all index values to -1 - max_internal = reordered_extrusions.size()-1; // initialise the maximum internal perimeter to the last perimeter on the extrusion list - // run through the walls to get the index values that need re-ordering until the first one for each - // is found. Start at "position" index to enable the for loop to iterate for multiple external - // perimeters in a single island - // printf("Reorder Loop. Position %d, extrusion list size: %d, Outer index %d, inner index %d, second inner index %d\n", position, reordered_extrusions.size(),outer,first_internal,second_internal); - for (arr_i = position; arr_i < reordered_extrusions.size(); ++arr_i) { - // printf("Perimeter: extrusion inset index %d, ordered extrusions array position %d\n",reordered_extrusions[arr_i].extrusion->inset_idx, arr_i); - switch (reordered_extrusions[arr_i].extrusion->inset_idx) { - case 0: // external perimeter - if (outer == -1) - outer = arr_i; - break; - case 1: // first internal wall - if (first_internal==-1 && arr_i>outer && outer!=-1){ - first_internal = arr_i; - } - break; - case 2: // second internal wall - if (second_internal == -1 && arr_i > first_internal && outer!=-1){ - second_internal = arr_i; - } - break; - } - if(outer >-1 && first_internal>-1 && reordered_extrusions[arr_i].extrusion->inset_idx == 0){ // found a new external perimeter after we've found at least a first internal perimeter to re-order. - // This means we entered a new island. - arr_i=arr_i-1; //step back one perimeter - max_internal = arr_i; // new maximum internal perimeter is now this as we have found a new external perimeter, hence a new island. - break; // exit the for loop - } - } - - // printf("Layer ID %d, Outer index %d, inner index %d, second inner index %d, maximum internal perimeter %d \n",layer_id,outer,first_internal,second_internal, max_internal); - if (outer > -1 && first_internal > -1 && second_internal > -1) { // found all three perimeters to re-order? If not the perimeters will be processed outside in. - std::vector inner_outer_extrusions; // temporary array to hold extrusions for reordering - inner_outer_extrusions.resize(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations - // printf("Allocated array size %d, max_internal index %d, start position index %d \n",max_internal-position+1,max_internal,position); - - for (arr_j = max_internal; arr_j >=position; --arr_j){ // go inside out towards the external perimeter (perimeters in reverse order) and store all internal perimeters until the first one identified with inset index 2 - if(arr_j >= second_internal){ - //printf("Inside out loop: Mapped perimeter index %d to array position %d\n", arr_j, max_internal-arr_j); - inner_outer_extrusions[max_internal-arr_j] = reordered_extrusions[arr_j]; - current_perimeter++; + // Now start the sandwich mode wall re-ordering using the reordered_extrusions as the basis + // scan to find the external perimeter, first internal, second internal and last perimeter in the island. + // We then advance the position index to move to the second island and continue until there are no more + // perimeters left. + while (position < reordered_extrusions.size()) { + outer = first_internal = second_internal = current_perimeter = -1; // initialise all index values to -1 + max_internal = reordered_extrusions.size()-1; // initialise the maximum internal perimeter to the last perimeter on the extrusion list + // run through the walls to get the index values that need re-ordering until the first one for each + // is found. Start at "position" index to enable the for loop to iterate for multiple external + // perimeters in a single island + // printf("Reorder Loop. Position %d, extrusion list size: %d, Outer index %d, inner index %d, second inner index %d\n", position, reordered_extrusions.size(),outer,first_internal,second_internal); + for (arr_i = position; arr_i < reordered_extrusions.size(); ++arr_i) { + // printf("Perimeter: extrusion inset index %d, ordered extrusions array position %d\n",reordered_extrusions[arr_i].extrusion->inset_idx, arr_i); + switch (reordered_extrusions[arr_i].extrusion->inset_idx) { + case 0: // external perimeter + if (outer == -1) + outer = arr_i; + break; + case 1: // first internal wall + if (first_internal==-1 && arr_i>outer && outer!=-1){ + first_internal = arr_i; + } + break; + case 2: // second internal wall + if (second_internal == -1 && arr_i > first_internal && outer!=-1){ + second_internal = arr_i; + } + break; + } + if(outer >-1 && first_internal>-1 && reordered_extrusions[arr_i].extrusion->inset_idx == 0){ // found a new external perimeter after we've found at least a first internal perimeter to re-order. + // This means we entered a new island. + arr_i=arr_i-1; //step back one perimeter + max_internal = arr_i; // new maximum internal perimeter is now this as we have found a new external perimeter, hence a new island. + break; // exit the for loop } } + + // printf("Layer ID %d, Outer index %d, inner index %d, second inner index %d, maximum internal perimeter %d \n",layer_id,outer,first_internal,second_internal, max_internal); + if (outer > -1 && first_internal > -1 && second_internal > -1) { // found all three perimeters to re-order? If not the perimeters will be processed outside in. + std::vector inner_outer_extrusions; // temporary array to hold extrusions for reordering + inner_outer_extrusions.resize(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations + // printf("Allocated array size %d, max_internal index %d, start position index %d \n",max_internal-position+1,max_internal,position); - for (arr_j = position; arr_j < second_internal; ++arr_j){ // go outside in and map the remaining perimeters (external and first internal wall(s)) using the outside in wall order - // printf("Outside in loop: Mapped perimeter index %d to array position %d\n", arr_j, current_perimeter+1); - inner_outer_extrusions[++current_perimeter] = reordered_extrusions[arr_j]; + for (arr_j = max_internal; arr_j >=position; --arr_j){ // go inside out towards the external perimeter (perimeters in reverse order) and store all internal perimeters until the first one identified with inset index 2 + if(arr_j >= second_internal){ + //printf("Inside out loop: Mapped perimeter index %d to array position %d\n", arr_j, max_internal-arr_j); + inner_outer_extrusions[max_internal-arr_j] = reordered_extrusions[arr_j]; + current_perimeter++; + } + } + + for (arr_j = position; arr_j < second_internal; ++arr_j){ // go outside in and map the remaining perimeters (external and first internal wall(s)) using the outside in wall order + // printf("Outside in loop: Mapped perimeter index %d to array position %d\n", arr_j, current_perimeter+1); + inner_outer_extrusions[++current_perimeter] = reordered_extrusions[arr_j]; + } + + for(arr_j = position; arr_j <= max_internal; ++arr_j) // replace perimeter array with the new re-ordered array + ordered_extrusions[arr_j] = inner_outer_extrusions[arr_j-position]; } - - for(arr_j = position; arr_j <= max_internal; ++arr_j) // replace perimeter array with the new re-ordered array - ordered_extrusions[arr_j] = inner_outer_extrusions[arr_j-position]; + // go to the next perimeter from the current position to continue scanning for external walls in the same island + position = arr_i + 1; } - // go to the next perimeter from the current position to continue scanning for external walls in the same island - position = arr_i + 1; } } - } - bool steep_overhang_contour = false; - bool steep_overhang_hole = false; - if (!config->overhang_reverse) { - // Skip steep overhang detection no reverse is specified - steep_overhang_contour = true; - steep_overhang_hole = true; - } - if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) { - if (config->overhang_reverse) { - reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, - this->config->overhang_reverse_internal_only); + bool steep_overhang_contour = false; + bool steep_overhang_hole = false; + if (!config->overhang_reverse) { + // Skip steep overhang detection no reverse is specified + steep_overhang_contour = true; + steep_overhang_hole = true; + } + if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) { + if (config->overhang_reverse) { + reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, + this->config->overhang_reverse_internal_only); + } + defer_unsupported_loops(*this, extrusion_coll); + result.loops = std::move(extrusion_coll); + result.has_loops = true; } - defer_unsupported_loops(*this, extrusion_coll); - this->loops->append(extrusion_coll); - } - const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; + const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; - if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) - infill_contour.clear(); // Infill region is too small, so let's filter it out. + if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) + infill_contour.clear(); // Infill region is too small, so let's filter it out. - // create one more offset to be used as boundary for fill - // we offset by half the perimeter spacing (to get to the actual infill boundary) - // and then we offset back and forth by half the infill spacing to only consider the - // non-collapsing regions - coord_t inset = - (loop_number < 0) ? 0 : - (loop_number == 0) ? - // one loop - ext_perimeter_spacing : - // two or more loops? - perimeter_spacing; - coord_t top_inset = inset; + // create one more offset to be used as boundary for fill + // we offset by half the perimeter spacing (to get to the actual infill boundary) + // and then we offset back and forth by half the infill spacing to only consider the + // non-collapsing regions + coord_t inset = + (loop_number < 0) ? 0 : + (loop_number == 0) ? + // one loop + ext_perimeter_spacing : + // two or more loops? + perimeter_spacing; + coord_t top_inset = inset; - top_inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); - if(is_topmost_layer || is_bottom_layer) - inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); - else - inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale(inset)))); + top_inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); + if(is_topmost_layer || is_bottom_layer) + inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); + else + inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale(inset)))); - // simplify infill contours according to resolution - Polygons pp; - for (ExPolygon& ex : infill_contour) - ex.simplify_p(m_scaled_resolution, &pp); - ExPolygons not_filled_exp = union_ex(pp); - // collapse too narrow infill areas - const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); + // simplify infill contours according to resolution + Polygons pp; + for (ExPolygon& ex : infill_contour) + ex.simplify_p(m_scaled_resolution, &pp); + ExPolygons not_filled_exp = union_ex(pp); + // collapse too narrow infill areas + const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); - ExPolygons infill_exp = offset2_ex( - not_filled_exp, - float(-min_perimeter_infill_spacing / 2.), - float(inset + min_perimeter_infill_spacing / 2.)); - // append infill areas to fill_surfaces - if (!top_expolygons.empty()) { - infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset))); - } - this->fill_surfaces->append(infill_exp, stInternal); - - apply_extra_perimeters(infill_exp); - - // BBS: get the no-overlap infill expolygons - { - ExPolygons polyWithoutOverlap; - polyWithoutOverlap = offset2_ex( + ExPolygons infill_exp = offset2_ex( not_filled_exp, float(-min_perimeter_infill_spacing / 2.), - float(+min_perimeter_infill_spacing / 2.)); - if (!top_expolygons.empty()) - polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons); - this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end()); - } + float(inset + min_perimeter_infill_spacing / 2.)); + // append infill areas to fill_surfaces + if (!top_expolygons.empty()) { + infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset))); + } + result.infill = std::move(infill_exp); + + // BBS: get the no-overlap infill expolygons + { + ExPolygons polyWithoutOverlap; + polyWithoutOverlap = offset2_ex( + not_filled_exp, + float(-min_perimeter_infill_spacing / 2.), + float(+min_perimeter_infill_spacing / 2.)); + if (!top_expolygons.empty()) + polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons); + result.no_overlap = std::move(polyWithoutOverlap); + } + } + }); + for (ArachneSurfaceResult &result : results) { + if (result.has_loops) + this->loops->append(result.loops); + this->fill_surfaces->append(result.infill, stInternal); + apply_extra_perimeters(result.infill); + this->fill_no_overlap->insert(this->fill_no_overlap->end(), result.no_overlap.begin(), result.no_overlap.end()); } } From cfde702d092e19491886e681eabb2d048a172a01 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 22 Sep 2026 13:06:44 +0200 Subject: [PATCH 43/89] Slice fine texture relief without stalling A colour texture baked at 0.1 mm / 2000k made the top layers thousands of islands and slicing never finished. Colour segmentation runs per island, the merge subtracts piece by piece, the support check tests only nearby islands, and the travel ordering finds crossings through a grid. --- src/libslic3r/GCode/OrderingStrategies.cpp | 67 +++++++++- src/libslic3r/MultiMaterialSegmentation.cpp | 130 +++++++++++++++++--- src/libslic3r/Support/TreeSupport.cpp | 32 ++++- 3 files changed, 207 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/GCode/OrderingStrategies.cpp b/src/libslic3r/GCode/OrderingStrategies.cpp index 2ba9ae5490..d52a326e2e 100644 --- a/src/libslic3r/GCode/OrderingStrategies.cpp +++ b/src/libslic3r/GCode/OrderingStrategies.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -134,15 +135,79 @@ bool tsp_remove_crossings(std::vector& path, const Points& centers) return {std::numeric_limits::max(), std::numeric_limits::max()}; }; + // For many islands, the same scan with the edges binned in a uniform grid over their boxes, so each edge is only tested against the edges sharing a + // cell with it - two edges whose boxes overlap always do. It returns the same crossing as the all-pairs scan + // (smallest i, then smallest j), so the result is unchanged; with thousands of islands on a layer the all-pairs + // scan, repeated after every reversal, never finished. Rebuilding the grid costs more than it saves on small inputs. + constexpr size_t grid_min_size = 500; + BoundingBox extent; + for (size_t idx : path) + extent.merge(centers[idx]); + const int grid_n = std::clamp(int(std::sqrt(double(pn))), 1, 256); + const coord_t cell_w = std::max(1, (extent.max.x() - extent.min.x()) / grid_n + 1); + const coord_t cell_h = std::max(1, (extent.max.y() - extent.min.y()) / grid_n + 1); + const auto for_cells = [&](const Point& a, const Point& b, auto&& fn) { + const int x0 = int((std::min(a.x(), b.x()) - extent.min.x()) / cell_w), x1 = int((std::max(a.x(), b.x()) - extent.min.x()) / cell_w); + const int y0 = int((std::min(a.y(), b.y()) - extent.min.y()) / cell_h), y1 = int((std::max(a.y(), b.y()) - extent.min.y()) / cell_h); + for (int y = y0; y <= y1; ++y) + for (int x = x0; x <= x1; ++x) + fn(y * grid_n + x); + }; + std::vector> edge_cells(size_t(grid_n) * grid_n); + + auto find_crossing_grid = [&]() -> std::pair { + for (std::vector& cell : edge_cells) + cell.clear(); + for (size_t j = 0; j < n_edges; ++j) + for_cells(centers[path[j]], centers[path[(j + 1) % pn]], [&](int cell) { edge_cells[cell].emplace_back(j); }); + + for (size_t i = 0; i < n_edges; ++i) { + const Point& ai = centers[path[i]]; + const Point& bi = centers[path[(i + 1) % pn]]; + + size_t first_j = std::numeric_limits::max(); + for_cells(ai, bi, [&](int cell) { + for (size_t j : edge_cells[cell]) { + if (j < i + 2 || j >= first_j) continue; + // Skip the (0, pn-1) pair: edges (0,1) and (pn-1,0) share node 0. + if (i == 0 && j == pn - 1) continue; + + const Point& aj = centers[path[j]]; + const Point& bj = centers[path[(j + 1) % pn]]; + + if (!bboxes_overlap(ai, bi, aj, bj)) continue; + if (Geometry::segments_intersect(ai, bi, aj, bj)) + first_j = j; + } + }); + if (first_j != std::numeric_limits::max()) + return {i, first_j}; + } + return {std::numeric_limits::max(), std::numeric_limits::max()}; + }; + // Process crossings one at a time: find first, reverse it, restart scan. // Cap iterations to prevent infinite loops on collinear/overlapping segments. int max_iters = static_cast(pn * pn); bool improved = false; + // Reversing between two segments that only touch or overlap along a line need not remove the intersection, and on + // islands laid out on a regular grid (a tiled texture, an array of parts) the loop cycled through the same orderings + // until the pn * pn cap - effectively forever. Stop as soon as an ordering repeats: until then this is the same loop. + std::unordered_set seen_paths; + const auto path_hash = [&path]() { + uint64_t h = 1469598103934665603ull; // FNV-1a + for (size_t idx : path) + h = (h ^ uint64_t(idx)) * 1099511628211ull; + return h; + }; + seen_paths.insert(path_hash()); while (max_iters-- > 0) { - auto [ci, cj] = find_crossing(); + auto [ci, cj] = pn >= grid_min_size ? find_crossing_grid() : find_crossing(); if (ci == std::numeric_limits::max()) break; improved = true; std::reverse(path.begin() + ci + 1, path.begin() + cj + 1); + if (!seen_paths.insert(path_hash()).second) + break; } return improved; } diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 8b1d612b00..f7c7597d76 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1311,10 +1311,15 @@ static inline std::vector> segmentation_top_and_bottom_l } #endif // MM_SEGMENTATION_DEBUG_TOP_BOTTOM - // When the upper surface of an object is occluded, it should no longer be considered the upper surface + // When the upper surface of an object is occluded, it should no longer be considered the upper surface. + // Every (colour, layer) pair is trimmed on its own, so they all run at once: the painted faces of a finely + // textured part project hundreds of thousands of triangles onto one layer, which used to be trimmed serially. { - for (size_t extruder_idx = 0; extruder_idx < num_facets_states; ++extruder_idx) { - for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + const size_t occluded_pairs = num_facets_states * layers.size(); + tbb::parallel_for(tbb::blocked_range(0, occluded_pairs), [&](const tbb::blocked_range &range) { + for (size_t pair_idx = range.begin(); pair_idx < range.end(); ++pair_idx) { + const size_t extruder_idx = pair_idx / layers.size(); + const size_t layer_idx = pair_idx % layers.size(); if (!top_raw[extruder_idx].empty() && !top_raw[extruder_idx][layer_idx].empty() && layer_idx + 1 < layers.size()) { top_raw[extruder_idx][layer_idx] = diff(top_raw[extruder_idx][layer_idx], input_expolygons[layer_idx + 1]); } @@ -1322,7 +1327,7 @@ static inline std::vector> segmentation_top_and_bottom_l bottom_raw[extruder_idx][layer_idx] = diff(bottom_raw[extruder_idx][layer_idx], input_expolygons[layer_idx - 1]); } } - } + }); } std::vector> triangles_by_color_bottom(num_facets_states); @@ -1847,6 +1852,54 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } +// diff_ex(subject, clip), one subject ExPolygon at a time against the part of `clip` in its box. The pieces are disjoint, +// so the result is the same; but ClipperLib never gets a whole finely painted layer at once, where re-linking the holes +// of its PolyTree (FixupFirstLefts) is quadratic in the thousands of regions. +static ExPolygons diff_ex_by_piece(const ExPolygons &subject, const ExPolygons &clip) +{ + // A coarse grid over the clip's boxes, so each piece only looks at the clip regions near it. + std::vector clip_bboxes; + clip_bboxes.reserve(clip.size()); + BoundingBox extent; + for (const ExPolygon &expoly : clip) { + clip_bboxes.emplace_back(get_extents(expoly)); + extent.merge(clip_bboxes.back()); + } + constexpr int GRID = 64; + const Point size = extent.defined ? extent.size() : Point(1, 1); + const coord_t cell_w = std::max(1, size.x() / GRID + 1), cell_h = std::max(1, size.y() / GRID + 1); + const auto cells = [&](const BoundingBox &bb, auto &&fn) { + const int x0 = std::clamp(int((bb.min.x() - extent.min.x()) / cell_w), 0, GRID - 1), x1 = std::clamp(int((bb.max.x() - extent.min.x()) / cell_w), 0, GRID - 1); + const int y0 = std::clamp(int((bb.min.y() - extent.min.y()) / cell_h), 0, GRID - 1), y1 = std::clamp(int((bb.max.y() - extent.min.y()) / cell_h), 0, GRID - 1); + for (int y = y0; y <= y1; ++y) + for (int x = x0; x <= x1; ++x) + fn(y * GRID + x); + }; + std::vector> grid(GRID * GRID); + if (extent.defined) + for (size_t i = 0; i < clip.size(); ++i) + cells(clip_bboxes[i], [&](int cell) { grid[cell].emplace_back(i); }); + + std::vector pieces(subject.size()); + tbb::parallel_for(size_t(0), subject.size(), [&](size_t idx) { + const BoundingBox bbox = get_extents(subject[idx]).inflated(SCALED_EPSILON); + std::vector near; + if (extent.defined && bbox.overlap(extent)) { + cells(bbox, [&](int cell) { append(near, grid[cell]); }); + sort_remove_duplicates(near); + } + Polygons nearby_clip; + for (size_t i : near) + if (clip_bboxes[i].overlap(bbox)) + polygons_append(nearby_clip, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip[i], bbox)); + pieces[idx] = nearby_clip.empty() ? ExPolygons{ subject[idx] } : diff_ex(subject[idx], nearby_clip); + }); + ExPolygons out; + for (ExPolygons &piece : pieces) + append(out, std::move(piece)); + return out; +} + static std::vector> merge_segmented_layers(const std::vector> &segmented_regions, std::vector> &&top_and_bottom_layers, const size_t num_facets_states, @@ -1861,18 +1914,18 @@ static std::vector> merge_segmented_layers(const std::ve tbb::parallel_for(tbb::blocked_range(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_facets_states, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { assert(segmented_regions[layer_idx].size() == num_facets_states); + ExPolygons top_and_bottom_all_colours; + for (const std::vector &top_and_bottom_by_extruder : top_and_bottom_layers) + append(top_and_bottom_all_colours, top_and_bottom_by_extruder[layer_idx]); // Zero is skipped because it is the default color of the volume for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) { throw_on_cancel_callback(); if (!segmented_regions[layer_idx][extruder_id].empty()) { ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id]; - if (!top_and_bottom_layers.empty()) { - for (const std::vector &top_and_bottom_by_extruder : top_and_bottom_layers) { - if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty()) { - segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]); - } - } - } + // All colours at once: taking them one after another made every piece go through ClipperLib once per + // colour, and it is the same area either way. + if (!top_and_bottom_all_colours.empty() && !segmented_regions_trimmed.empty()) + segmented_regions_trimmed = diff_ex_by_piece(segmented_regions_trimmed, top_and_bottom_all_colours); segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); } @@ -2171,16 +2224,53 @@ std::vector> segmentation_by_painting(const PrintObject assert(!color_poly.empty()); assert(!color_poly.front().empty()); - if (has_layer_only_one_color(color_poly)) { - // If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer. - segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx]; - } else { - MMU_Graph graph = build_graph(layer_idx, color_poly); - remove_multiple_edges_in_vertices(graph, color_poly); - graph.remove_nodes_with_one_arc(); - segmented_regions[layer_idx] = extract_colored_segments(graph, num_facets_states); - //segmented_regions[layer_idx] = extract_colored_segments(color_poly, num_extruders, layer_idx); + // Each island (an ExPolygon with its holes) is segmented on its own. Any point of an island is closer to + // that island's contours than to any other island's - the way out crosses its own boundary first - so its + // Voronoi cells, and with them its colour regions, depend on nothing else. A layer cut through a fine relief + // has thousands of islands, and one Voronoi diagram over all of them degenerated into overlapping regions + // that every boolean afterwards had to untangle. Per island the diagrams stay small and the islands run in + // parallel; an island in a single colour needs no diagram at all. + const ExPolygons &islands = input_expolygons[layer_idx]; + std::vector> island_contours(islands.size()); // [first, last) into color_poly + { + // The same order EdgeGrid::Grid::create() lists the contours in, and so colorize_contours(). + size_t idx = 0; + for (size_t island_idx = 0; island_idx < islands.size(); ++island_idx) { + const size_t first = idx; + if (!islands[island_idx].contour.empty()) + ++idx; + for (const Polygon &hole : islands[island_idx].holes) + if (!hole.empty()) + ++idx; + island_contours[island_idx] = {first, idx}; + } + assert(idx == color_poly.size()); } + std::vector> island_regions(islands.size()); + tbb::parallel_for(size_t(0), islands.size(), [&](size_t island_idx) { + const auto [first, last] = island_contours[island_idx]; + if (first == last) + return; + const std::vector island_poly(color_poly.begin() + first, color_poly.begin() + last); + std::vector ®ions = island_regions[island_idx]; + if (has_layer_only_one_color(island_poly)) { + regions.assign(num_facets_states, ExPolygons()); + regions[size_t(island_poly.front().front().color)].emplace_back(islands[island_idx]); + } else { + MMU_Graph graph = build_graph(layer_idx, island_poly); + remove_multiple_edges_in_vertices(graph, island_poly); + graph.remove_nodes_with_one_arc(); + regions = extract_colored_segments(graph, num_facets_states); + // The faces of one colour tile it without overlapping; merged here, where an island is small, + // every later boolean gets a few regions instead of thousands of faces sharing their edges. + for (ExPolygons &faces : regions) + if (faces.size() > 1) + faces = union_ex(faces); + } + }); + for (std::vector ®ions : island_regions) + for (size_t color_idx = 0; color_idx < regions.size(); ++color_idx) + append(segmented_regions[layer_idx][color_idx], std::move(regions[color_idx])); #ifdef MM_SEGMENTATION_DEBUG_REGIONS export_regions_to_svg(debug_out_path("3-mm-regions-sides-%d-%d.svg", layer_idx, iRun), segmented_regions[layer_idx], input_expolygons[layer_idx]); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 519b6e826e..174c5e4fce 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -854,11 +854,41 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) if (is_auto(stype) && config_detect_sharp_tails) { // BBS detect sharp tail + // Each island is tested only against the lower islands whose box meets its own: overlaps() tries every + // pair, which on a layer cut through a fine relief (thousands of islands above thousands) never ends. + std::vector lower_bboxes; + lower_bboxes.reserve(lower_polys.size()); + for (const ExPolygon &lower : lower_polys) + lower_bboxes.emplace_back(get_extents(lower)); for (const ExPolygon& expoly : curr_polys) { bool is_sharp_tail = false; // 1. nothing below // this is a sharp tail region if it's floating and non-ignorable - if (!overlaps(offset_ex(expoly, 0.1 * extrusion_width_scaled), lower_polys)) { + const ExPolygons expanded = offset_ex(expoly, 0.1 * extrusion_width_scaled); + const BoundingBox bbox = get_extents(expanded); + ExPolygons lower_nearby; + for (size_t i = 0; i < lower_polys.size(); ++i) + if (lower_bboxes[i].overlap(bbox)) + lower_nearby.emplace_back(lower_polys[i]); + // As overlaps(expanded, lower_nearby), with each lower island cut to the island's box first: below + // a fine relief the lower layer is a few islands with thousands of holes, whose whole boundary + // was otherwise intersected again for every island above. + const auto overlaps_nearby = [&]() { + for (const ExPolygon &a : expanded) { + if (a.empty()) + continue; + const BoundingBox a_bbox = get_extents(a); + for (const ExPolygon &b : lower_nearby) { + if (b.empty() || !get_extents(b).overlap(a_bbox)) + continue; + const Polygons b_near = ClipperUtils::clip_clipper_polygons_with_subject_bbox(b, a_bbox.inflated(SCALED_EPSILON)); + if (!intersection_pl(to_polylines(b_near), a).empty() || b.contains(a.contour.points.front())) + return true; + } + } + return false; + }; + if (!overlaps_nearby()) { is_sharp_tail = !offset_ex(expoly, -0.1 * extrusion_width_scaled).empty(); } From 2f05dbf577dd27e0d74b8c63451fcc56fa4ab1a5 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Tue, 22 Sep 2026 09:39:11 -0400 Subject: [PATCH 44/89] Fix Snapmaker U1 profile defaults and extruder vectors Register high-flow filament defaults for every U1 nozzle variant in the wizard, and select compatible filaments for the standard and mixed-nozzle printers. Size the shared U1 extruder vectors to its four physical tools. Replace stale 3x flow claims with the source-specific filament guidance. Bump the Snapmaker bundle version. Co-authored-by: Codex --- resources/profiles/Snapmaker.json | 2 +- .../Snapmaker U1 (0.2 High Flow nozzle).json | 2 +- .../machine/Snapmaker U1 (0.2 nozzle).json | 8 ++++++- .../Snapmaker U1 (0.4 High Flow nozzle).json | 2 +- .../machine/Snapmaker U1 (0.4 nozzle).json | 8 ++++++- .../Snapmaker U1 (0.4+0.6 nozzle).json | 8 ++++++- .../Snapmaker U1 (0.6 High Flow nozzle).json | 2 +- .../machine/Snapmaker U1 (0.6 nozzle).json | 8 ++++++- .../Snapmaker U1 (0.8 High Flow nozzle).json | 2 +- .../machine/Snapmaker U1 (0.8 nozzle).json | 8 ++++++- .../Snapmaker/machine/Snapmaker U1.json | 2 +- .../profiles/Snapmaker/machine/fdm_U1.json | 24 ------------------- 12 files changed, 41 insertions(+), 35 deletions(-) diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index 7f2aaa2881..655e322af1 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.17", + "version": "02.04.00.18", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json index 6c3a2ff7ae..692b06c904 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json @@ -7,7 +7,7 @@ "instantiation": "true", "printer_model": "Snapmaker U1", "printer_variant": "0.2 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.2 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.2 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json index 46273a7f44..c492f67401 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json @@ -187,5 +187,11 @@ "machine_pause_gcode": "M600", "nozzle_volume": "143", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER={layer_num+1}", - "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 nozzle)" + "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json index cd54aae644..92bb62362b 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json @@ -7,7 +7,7 @@ "instantiation": "true", "printer_model": "Snapmaker U1", "printer_variant": "0.4 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.4 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.4 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json index d65d549d77..0092b268c3 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json @@ -190,5 +190,11 @@ "nozzle_volume": "143", "resonance_avoidance": "1", "min_resonance_avoidance_speed": "40", - "max_resonance_avoidance_speed": "90" + "max_resonance_avoidance_speed": "90", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1", + "Snapmaker PLA SnapSpeed @U1", + "Snapmaker PLA SnapSpeed @U1", + "Snapmaker PLA SnapSpeed @U1" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json index c9866329bd..3568305ce9 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json @@ -26,5 +26,11 @@ "0.6", "0.6" ], - "nozzle_type": "stainless_steel" + "nozzle_type": "stainless_steel", + "default_filament_profile": [ + "Generic PLA @System", + "Generic PLA @System", + "Generic PLA @System", + "Generic PLA @System" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json index 12f91c71cd..81844e4a64 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json @@ -7,7 +7,7 @@ "instantiation": "true", "printer_model": "Snapmaker U1", "printer_variant": "0.6 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.6 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.6 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json index b48d528074..1a528a6ff2 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json @@ -187,5 +187,11 @@ "machine_pause_gcode": "M600", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER={layer_num+1}", "nozzle_volume": "143", - "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 nozzle)" + "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json index 2f9ecca19e..2b357df990 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json @@ -7,7 +7,7 @@ "instantiation": "true", "printer_model": "Snapmaker U1", "printer_variant": "0.8 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.8 mm geometry, motion limits and G-code. Matching High Flow filament presets set the volumetric speed ceiling to 3x the matching non-HF filament at this diameter. This ceiling is not a measured flow capability. Calibrate for your nozzle and filament.", + "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.8 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json index e3ffac2bb8..d80a52ca48 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json @@ -187,5 +187,11 @@ "machine_pause_gcode": "M600", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER={layer_num+1}", "nozzle_volume": "143", - "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 nozzle)" + "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", + "default_filament_profile": [ + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1.json b/resources/profiles/Snapmaker/machine/Snapmaker U1.json index 0b5f8c8a93..532e3a22bf 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1.json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1.json @@ -5,7 +5,7 @@ "family": "Snapmaker", "url": "", "model_id": "797581801", - "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle;Snapmaker PLA SnapSpeed @U1;Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle;Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle;Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle;Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", "bed_model": "Snapmaker U1_bed.stl", "bed_texture": "Snapmaker U1_texture.svg", "nozzle_diameter": "0.2;0.4;0.4+0.6;0.6;0.8;0.2 High Flow;0.4 High Flow;0.6 High Flow;0.8 High Flow" diff --git a/resources/profiles/Snapmaker/machine/fdm_U1.json b/resources/profiles/Snapmaker/machine/fdm_U1.json index 85a94df1d8..bec96fc2c1 100644 --- a/resources/profiles/Snapmaker/machine/fdm_U1.json +++ b/resources/profiles/Snapmaker/machine/fdm_U1.json @@ -12,168 +12,144 @@ "0.32", "0.32", "0.32", - "0.32", "0.32" ], "min_layer_height": [ "0.08", "0.08", "0.08", - "0.08", "0.08" ], "deretraction_speed": [ "30", "30", "30", - "30", "30" ], "extruder_colour": [ "#FCE94F", "#FCE94F", "#FCE94F", - "#FCE94F", "#FCE94F" ], "extruder_offset": [ "0x0", "0x0", "0x0", - "0x0", "0x0" ], "long_retractions_when_cut": [ "0", "0", "0", - "0", "0" ], "nozzle_diameter": [ "0.4", "0.4", "0.4", - "0.4", "0.4" ], "retract_before_wipe": [ "70%", "70%", "70%", - "70%", "70%" ], "retract_length_toolchange": [ "2", "2", "2", - "2", "2" ], "retract_lift_above": [ "0", "0", "0", - "0", "0" ], "retract_lift_below": [ "0", "0", "0", - "0", "0" ], "retract_lift_enforce": [ "All Surfaces", "All Surfaces", "All Surfaces", - "All Surfaces", "All Surfaces" ], "retract_restart_extra": [ "0", "0", "0", - "0", "0" ], "retract_restart_extra_toolchange": [ "0", "0", "0", - "0", "0" ], "retract_when_changing_layer": [ "1", "1", "1", - "1", "1" ], "retraction_distances_when_cut": [ "18", "18", "18", - "18", "18" ], "retraction_length": [ "0.8", "0.8", "0.8", - "0.8", "0.8" ], "retraction_minimum_travel": [ "1", "1", "1", - "1", "1" ], "retraction_speed": [ "30", "30", "30", - "30", "30" ], "travel_slope": [ "3", "3", "3", - "3", "3" ], "wipe": [ "1", "1", "1", - "1", "1" ], "wipe_distance": [ "1", "1", "1", - "1", "1" ], "z_hop": [ "0.4", "0.4", "0.4", - "0.4", "0.4" ], "z_hop_types": [ "Normal Lift", "Normal Lift", "Normal Lift", - "Normal Lift", "Normal Lift" ], "purge_in_prime_tower": "0", From b3963b2a496ad5a17eb0666392c773717d1abd91 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 22 Sep 2026 16:43:53 +0200 Subject: [PATCH 45/89] Merge colour and top/bottom regions per island The merge took anything from 3 to 38 minutes at 0.1 mm / 2000k, now ~2.5. Every region is grouped with the islands it overlaps, so the result is the same. --- src/libslic3r/MultiMaterialSegmentation.cpp | 181 ++++++++++++++++---- src/libslic3r/PrintObject.cpp | 16 +- 2 files changed, 165 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index f7c7597d76..c7c2775a19 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -8,6 +8,7 @@ #include "MutablePolygon.hpp" #include "format.hpp" +#include #include #include @@ -1900,7 +1901,68 @@ static ExPolygons diff_ex_by_piece(const ExPolygons &subject, const ExPolygons & return out; } -static std::vector> merge_segmented_layers(const std::vector> &segmented_regions, +// Finds the islands (layer ExPolygons) a region piece overlaps. A top or bottom region is projected from the neighbouring +// layers and may reach past the island it belongs to, or over several islands. +class IslandLocator +{ +public: + explicit IslandLocator(const ExPolygons &islands) : m_islands(islands) + { + m_bboxes.reserve(islands.size()); + for (const ExPolygon &island : islands) { + m_bboxes.emplace_back(get_extents(island)); + m_extent.merge(m_bboxes.back()); + } + if (!m_extent.defined) + return; + const Point size = m_extent.size(); + m_cell_w = std::max(1, size.x() / GRID + 1); + m_cell_h = std::max(1, size.y() / GRID + 1); + m_grid.assign(GRID * GRID, {}); + for (size_t i = 0; i < m_bboxes.size(); ++i) + for_cells(m_bboxes[i], [&](int cell) { m_grid[cell].emplace_back(i); }); + } + + void find(const ExPolygon &piece, std::vector &out) const + { + out.clear(); + const BoundingBox bbox = get_extents(piece); + if (!m_extent.defined || !m_extent.overlap(bbox)) + return; + for_cells(bbox, [&](int cell) { + for (size_t i : m_grid[cell]) + if (m_bboxes[i].overlap(bbox)) + out.emplace_back(i); + }); + sort_remove_duplicates(out); + if (out.size() > 1) + out.erase(std::remove_if(out.begin(), out.end(), [&](size_t i) { + const BoundingBox common(m_bboxes[i].min.cwiseMax(bbox.min), m_bboxes[i].max.cwiseMin(bbox.max)); + return intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(piece, common.inflated(SCALED_EPSILON)), + ClipperUtils::clip_clipper_polygons_with_subject_bbox(m_islands[i], common.inflated(SCALED_EPSILON))).empty(); + }), out.end()); + } + +private: + static constexpr int GRID = 64; + template void for_cells(const BoundingBox &bb, Fn &&fn) const + { + const int x0 = std::clamp(int((bb.min.x() - m_extent.min.x()) / m_cell_w), 0, GRID - 1), x1 = std::clamp(int((bb.max.x() - m_extent.min.x()) / m_cell_w), 0, GRID - 1); + const int y0 = std::clamp(int((bb.min.y() - m_extent.min.y()) / m_cell_h), 0, GRID - 1), y1 = std::clamp(int((bb.max.y() - m_extent.min.y()) / m_cell_h), 0, GRID - 1); + for (int y = y0; y <= y1; ++y) + for (int x = x0; x <= x1; ++x) + fn(y * GRID + x); + } + + const ExPolygons &m_islands; + std::vector m_bboxes; + BoundingBox m_extent; + coord_t m_cell_w = 1, m_cell_h = 1; + std::vector> m_grid; +}; + +static std::vector> merge_segmented_layers(const std::vector &input_expolygons, + const std::vector> &segmented_regions, std::vector> &&top_and_bottom_layers, const size_t num_facets_states, const std::function &throw_on_cancel_callback) @@ -1911,33 +1973,91 @@ static std::vector> merge_segmented_layers(const std::ve assert(!top_and_bottom_layers.size() || num_facets_states == top_and_bottom_layers.size()); BOOST_LOG_TRIVIAL(debug) << "Print object segmentation - Merging segmented layers in parallel - Begin"; - tbb::parallel_for(tbb::blocked_range(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_facets_states, &throw_on_cancel_callback](const tbb::blocked_range &range) { + // Every region of a layer is merged together with the regions of the islands it overlaps, and the islands are further + // apart than the dimple removal below reaches, so this gives the same result as merging the layer at once. On a layer + // cut through a fine relief every region shares thousands of hole contours with every other, and ClipperLib, splitting + // and re-linking one huge polygon over and over, took anything up to half an hour for a layer; per island each operation + // stays the size of the island, and the islands run in parallel. + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { assert(segmented_regions[layer_idx].size() == num_facets_states); - ExPolygons top_and_bottom_all_colours; - for (const std::vector &top_and_bottom_by_extruder : top_and_bottom_layers) - append(top_and_bottom_all_colours, top_and_bottom_by_extruder[layer_idx]); - // Zero is skipped because it is the default color of the volume + throw_on_cancel_callback(); + // Group the islands joined by a region overlapping several of them; the last group takes the regions lying + // outside every island. + const ExPolygons &islands = input_expolygons[layer_idx]; + const IslandLocator locator(islands); + std::vector parent(islands.size() + 1); + std::iota(parent.begin(), parent.end(), 0); + const auto root = [&parent](size_t i) { + while (parent[i] != i) + i = parent[i] = parent[parent[i]]; + return i; + }; + // Islands of every piece: side regions of colours 1.., then top/bottom regions of colours 0.. + std::vector pieces; + for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) + for (const ExPolygon &piece : segmented_regions[layer_idx][extruder_id]) + pieces.emplace_back(&piece); + if (!top_and_bottom_layers.empty()) + for (size_t color_idx = 0; color_idx < num_facets_states; ++color_idx) + for (const ExPolygon &piece : top_and_bottom_layers[color_idx][layer_idx]) + pieces.emplace_back(&piece); + std::vector> overlapped(pieces.size()); + tbb::parallel_for(size_t(0), pieces.size(), [&](size_t i) { locator.find(*pieces[i], overlapped[i]); }); + std::vector piece_island(pieces.size()); + for (size_t i = 0; i < pieces.size(); ++i) { + piece_island[i] = overlapped[i].empty() ? islands.size() : overlapped[i].front(); + for (size_t island : overlapped[i]) + parent[root(island)] = root(piece_island[i]); + } + std::vector bucket_of(parent.size(), size_t(-1)); + size_t num_buckets = 0; + for (size_t i = 0; i < parent.size(); ++i) + if (size_t &b = bucket_of[root(i)]; b == size_t(-1)) + b = num_buckets++; + + // [bucket][colour] + std::vector> sides(num_buckets, std::vector(num_facets_states)); + std::vector> tops(num_buckets, std::vector(num_facets_states)); + size_t piece_idx = 0; + for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) + for (const ExPolygon &piece : segmented_regions[layer_idx][extruder_id]) + sides[bucket_of[root(piece_island[piece_idx++])]][extruder_id].emplace_back(piece); + if (!top_and_bottom_layers.empty()) + for (size_t color_idx = 0; color_idx < num_facets_states; ++color_idx) + for (const ExPolygon &piece : top_and_bottom_layers[color_idx][layer_idx]) + tops[bucket_of[root(piece_island[piece_idx++])]][color_idx].emplace_back(piece); + + // Side regions minus the top/bottom regions of every colour. + std::vector> merged(num_buckets, std::vector(num_facets_states)); + tbb::parallel_for(size_t(0), num_buckets, [&](size_t bucket) { + ExPolygons tops_all; + for (const ExPolygons &t : tops[bucket]) + append(tops_all, t); + for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) + if (!sides[bucket][extruder_id].empty()) + merged[bucket][extruder_id] = tops_all.empty() ? std::move(sides[bucket][extruder_id]) : + diff_ex_by_piece(sides[bucket][extruder_id], tops_all); + }); + + // Then this colour's top/bottom regions, with the dimples removed (#7235) when the layer has side regions left. for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) { - throw_on_cancel_callback(); - if (!segmented_regions[layer_idx][extruder_id].empty()) { - ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id]; - // All colours at once: taking them one after another made every piece go through ClipperLib once per - // colour, and it is the same area either way. - if (!top_and_bottom_all_colours.empty() && !segmented_regions_trimmed.empty()) - segmented_regions_trimmed = diff_ex_by_piece(segmented_regions_trimmed, top_and_bottom_all_colours); - - segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); - } - - if (!top_and_bottom_layers.empty() && !top_and_bottom_layers[extruder_id][layer_idx].empty()) { - bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty(); - append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); - - // Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers. - if (!was_top_and_bottom_empty) - segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), float(SCALED_EPSILON), -float(SCALED_EPSILON)); + if (top_and_bottom_layers.empty() || top_and_bottom_layers[extruder_id][layer_idx].empty()) { + for (size_t bucket = 0; bucket < num_buckets; ++bucket) + append(segmented_regions_merged[layer_idx][extruder_id - 1], std::move(merged[bucket][extruder_id])); + continue; } + bool was_top_and_bottom_empty = true; + for (size_t bucket = 0; bucket < num_buckets && was_top_and_bottom_empty; ++bucket) + was_top_and_bottom_empty = merged[bucket][extruder_id].empty(); + tbb::parallel_for(size_t(0), num_buckets, [&](size_t bucket) { + ExPolygons ®ion = merged[bucket][extruder_id]; + append(region, tops[bucket][extruder_id]); + if (!was_top_and_bottom_empty && !region.empty()) + region = offset2_ex(union_ex(region), float(SCALED_EPSILON), -float(SCALED_EPSILON)); + }); + for (size_t bucket = 0; bucket < num_buckets; ++bucket) + append(segmented_regions_merged[layer_idx][extruder_id - 1], std::move(merged[bucket][extruder_id])); } } }); // end of parallel_for @@ -2262,10 +2382,13 @@ std::vector> segmentation_by_painting(const PrintObject graph.remove_nodes_with_one_arc(); regions = extract_colored_segments(graph, num_facets_states); // The faces of one colour tile it without overlapping; merged here, where an island is small, - // every later boolean gets a few regions instead of thousands of faces sharing their edges. - for (ExPolygons &faces : regions) - if (faces.size() > 1) - faces = union_ex(faces); + // every later boolean gets a few regions instead of thousands of faces sharing their edges. An + // island with many holes keeps its faces: merged, each colour would be one region with thousands + // of holes, and subtracting from that is far slower than from the faces one at a time. + if (island_poly.size() <= 64) + for (ExPolygons &faces : regions) + if (faces.size() > 1) + faces = union_ex(faces); } }); for (std::vector ®ions : island_regions) @@ -2293,7 +2416,7 @@ std::vector> segmentation_by_painting(const PrintObject throw_on_cancel_callback(); } - std::vector> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), num_facets_states, throw_on_cancel_callback); + std::vector> segmented_regions_merged = merge_segmented_layers(input_expolygons, segmented_regions, std::move(top_and_bottom_layers), num_facets_states, throw_on_cancel_callback); throw_on_cancel_callback(); #ifdef MM_SEGMENTATION_DEBUG_REGIONS diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7101e1d4d7..84139273d9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1785,20 +1785,30 @@ void PrintObject::detect_surfaces_type() if (lower_layer) { // Only detect small cracks for non-first layer, because first layer should always be bottom const float small_crack_threshold = -layerm->flow(frExternalPerimeter).scaled_width() * 1.5; + // Only the bottom surfaces near a crack can take part: one that contains it must contain its box, + // and one whose box misses the grown crack is left unchanged by removing it. A layer cut through + // a fine relief has thousands of both, which made this loop quadratic. for (const auto& crack : cracks) { if (offset_ex(crack, small_crack_threshold).empty()) { // For small cracks, if it's part of a large bottom surface, then it should be added to bottom as well - if (std::any_of(bottom.begin(), bottom.end(), [&crack, small_crack_threshold](const Surface& s) { + const BoundingBox crack_bbox = get_extents(crack); + if (std::any_of(bottom.begin(), bottom.end(), [&crack, &crack_bbox, small_crack_threshold](const Surface& s) { const auto& se = s.expolygon; - return diff_ex(crack, se, ApplySafetyOffset::Yes).empty() + return get_extents(se).inflated(SCALED_EPSILON).contains(crack_bbox) + && diff_ex(crack, se, ApplySafetyOffset::Yes).empty() && se.area() > crack.area() * 2 && !offset_ex(diff_ex(se, crack), small_crack_threshold).empty(); })) continue; // Crack too small, leave it as part of the top surface, remove it from bottom surfaces + const ExPolygons grown_crack = offset_ex(crack, -small_crack_threshold); + const BoundingBox grown_bbox = get_extents(grown_crack); Surfaces bot_tmp; for (auto& b : bottom) { - surfaces_append(bot_tmp, diff_ex(b.expolygon, offset_ex(crack, -small_crack_threshold)), b.surface_type); + if (get_extents(b.expolygon).overlap(grown_bbox)) + surfaces_append(bot_tmp, diff_ex(b.expolygon, grown_crack), b.surface_type); + else + bot_tmp.emplace_back(std::move(b)); } bottom = std::move(bot_tmp); } From 5baefd8a4e7fc15fb46a7a677bc753f9b00f0709 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 22 Sep 2026 18:05:13 +0200 Subject: [PATCH 46/89] Tile the booleans on layers of many pieces ClipperLib slows down with the number of edges on a scan line, and a layer cut through a fine relief has tens of thousands of pieces. detect_surfaces_type ~50 s at 0.1 mm / 2000k, was ~145. --- src/libslic3r/ClipperUtils.cpp | 59 +++++++++ src/libslic3r/ClipperUtils.hpp | 5 + src/libslic3r/LayerRegion.cpp | 3 +- src/libslic3r/MultiMaterialSegmentation.cpp | 51 +------- src/libslic3r/PrintObject.cpp | 129 +++++++++++--------- tests/libslic3r/test_clipper_utils.cpp | 44 +++++++ 6 files changed, 181 insertions(+), 110 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index ea19eeb559..405ede60bd 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "ShortestPath.hpp" @@ -813,6 +815,63 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } + +static Slic3r::ExPolygons clipper_ex_by_piece(ClipperLib::ClipType clipType, const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) +{ + // The subject ExPolygons are split into tiles by the centres of their boxes, a few dozen per tile, and each tile is one + // ClipperLib call with the clip cut to the box of the tile's ExPolygons. + BoundingBox extent; + std::vector bboxes; + bboxes.reserve(subject.size()); + for (const ExPolygon &expoly : subject) { + bboxes.emplace_back(get_extents(expoly)); + extent.merge(bboxes.back()); + } + const int tiles = std::clamp(int(std::sqrt(double(subject.size()) / 32.)), 1, 32); + std::vector> members(size_t(tiles * tiles)); + std::vector tile_bboxes(members.size()); + if (extent.defined) { + const Point size = extent.size(); + const coord_t tile_w = std::max(1, size.x() / tiles + 1), tile_h = std::max(1, size.y() / tiles + 1); + for (size_t i = 0; i < subject.size(); ++i) { + const Point c = bboxes[i].center(); + const size_t tile = size_t(std::clamp(int((c.y() - extent.min.y()) / tile_h), 0, tiles - 1) * tiles + + std::clamp(int((c.x() - extent.min.x()) / tile_w), 0, tiles - 1)); + members[tile].emplace_back(i); + tile_bboxes[tile].merge(bboxes[i]); + } + } + std::vector clip_bboxes; + clip_bboxes.reserve(clip.size()); + for (const Polygon &polygon : clip) + clip_bboxes.emplace_back(get_extents(polygon)); + + std::vector out_tiles(members.size()); + tbb::parallel_for(size_t(0), members.size(), [&](size_t tile) { + if (members[tile].empty()) + return; + Slic3r::ExPolygons local_subject; + local_subject.reserve(members[tile].size()); + for (size_t i : members[tile]) + local_subject.emplace_back(subject[i]); + // Grown so that the cut edges of the clip stay clear of the subject, also after the safety offset. + const BoundingBox bbox = tile_bboxes[tile].inflated(SCALED_EPSILON); + Polygons local_clip; + for (size_t i = 0; i < clip.size(); ++i) + if (clip_bboxes[i].overlap(bbox)) + if (Polygon clipped = ClipperUtils::clip_clipper_polygon_with_subject_bbox(clip[i], bbox); ! clipped.empty()) + local_clip.emplace_back(std::move(clipped)); + out_tiles[tile] = _clipper_ex(clipType, ClipperUtils::ExPolygonsProvider(local_subject), ClipperUtils::PolygonsProvider(local_clip), do_safety_offset); + }); + Slic3r::ExPolygons out; + for (Slic3r::ExPolygons &out_tile : out_tiles) + append(out, std::move(out_tile)); + return out; +} +Slic3r::ExPolygons diff_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return clipper_ex_by_piece(ClipperLib::ctDifference, subject, clip, do_safety_offset); } +Slic3r::ExPolygons intersection_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return clipper_ex_by_piece(ClipperLib::ctIntersection, subject, clip, do_safety_offset); } // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type) { return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No, fill_type); } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 5ed161c27a..a2b43ceb70 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -518,6 +518,11 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +// diff_ex() / intersection_ex() of the subject split into tiles, each against only the part of the clip near it, the tiles in +// parallel. The same area as the operation on the whole subject when its ExPolygons do not overlap, and much faster for a +// subject of thousands of pieces spread over a layer: ClipperLib slows down with the number of edges crossing a scan line. +Slic3r::ExPolygons diff_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons intersection_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 22e0a26898..5dcf3baa31 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -72,10 +72,11 @@ void LayerRegion::slices_to_fill_surfaces_clipped() by_surface[size_t(surface.surface_type)].emplace_back(&surface); // Trim surfaces by the fill_boundaries. this->fill_surfaces.surfaces.clear(); + const Polygons fill_boundaries = to_polygons(this->fill_expolygons); for (size_t surface_type = 0; surface_type < size_t(stCount); ++ surface_type) { const SurfacesPtr &this_surfaces = by_surface[surface_type]; if (! this_surfaces.empty()) - this->fill_surfaces.append(intersection_ex(this_surfaces, this->fill_expolygons), SurfaceType(surface_type)); + this->fill_surfaces.append(intersection_ex_by_piece(to_expolygons(this_surfaces), fill_boundaries), SurfaceType(surface_type)); } } diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index c7c2775a19..320e8fa39b 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1853,53 +1853,6 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } -// diff_ex(subject, clip), one subject ExPolygon at a time against the part of `clip` in its box. The pieces are disjoint, -// so the result is the same; but ClipperLib never gets a whole finely painted layer at once, where re-linking the holes -// of its PolyTree (FixupFirstLefts) is quadratic in the thousands of regions. -static ExPolygons diff_ex_by_piece(const ExPolygons &subject, const ExPolygons &clip) -{ - // A coarse grid over the clip's boxes, so each piece only looks at the clip regions near it. - std::vector clip_bboxes; - clip_bboxes.reserve(clip.size()); - BoundingBox extent; - for (const ExPolygon &expoly : clip) { - clip_bboxes.emplace_back(get_extents(expoly)); - extent.merge(clip_bboxes.back()); - } - constexpr int GRID = 64; - const Point size = extent.defined ? extent.size() : Point(1, 1); - const coord_t cell_w = std::max(1, size.x() / GRID + 1), cell_h = std::max(1, size.y() / GRID + 1); - const auto cells = [&](const BoundingBox &bb, auto &&fn) { - const int x0 = std::clamp(int((bb.min.x() - extent.min.x()) / cell_w), 0, GRID - 1), x1 = std::clamp(int((bb.max.x() - extent.min.x()) / cell_w), 0, GRID - 1); - const int y0 = std::clamp(int((bb.min.y() - extent.min.y()) / cell_h), 0, GRID - 1), y1 = std::clamp(int((bb.max.y() - extent.min.y()) / cell_h), 0, GRID - 1); - for (int y = y0; y <= y1; ++y) - for (int x = x0; x <= x1; ++x) - fn(y * GRID + x); - }; - std::vector> grid(GRID * GRID); - if (extent.defined) - for (size_t i = 0; i < clip.size(); ++i) - cells(clip_bboxes[i], [&](int cell) { grid[cell].emplace_back(i); }); - - std::vector pieces(subject.size()); - tbb::parallel_for(size_t(0), subject.size(), [&](size_t idx) { - const BoundingBox bbox = get_extents(subject[idx]).inflated(SCALED_EPSILON); - std::vector near; - if (extent.defined && bbox.overlap(extent)) { - cells(bbox, [&](int cell) { append(near, grid[cell]); }); - sort_remove_duplicates(near); - } - Polygons nearby_clip; - for (size_t i : near) - if (clip_bboxes[i].overlap(bbox)) - polygons_append(nearby_clip, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip[i], bbox)); - pieces[idx] = nearby_clip.empty() ? ExPolygons{ subject[idx] } : diff_ex(subject[idx], nearby_clip); - }); - ExPolygons out; - for (ExPolygons &piece : pieces) - append(out, std::move(piece)); - return out; -} // Finds the islands (layer ExPolygons) a region piece overlaps. A top or bottom region is projected from the neighbouring // layers and may reach past the island it belongs to, or over several islands. @@ -2031,9 +1984,9 @@ static std::vector> merge_segmented_layers(const std::ve // Side regions minus the top/bottom regions of every colour. std::vector> merged(num_buckets, std::vector(num_facets_states)); tbb::parallel_for(size_t(0), num_buckets, [&](size_t bucket) { - ExPolygons tops_all; + Polygons tops_all; for (const ExPolygons &t : tops[bucket]) - append(tops_all, t); + polygons_append(tops_all, t); for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) if (!sides[bucket][extruder_id].empty()) merged[bucket][extruder_id] = tops_all.empty() ? std::move(sides[bucket][extruder_id]) : diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 84139273d9..57d21839fd 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -1723,7 +1724,7 @@ void PrintObject::detect_surfaces_type() if (upper_layer) { ExPolygons upper_slices = interface_shells ? diff_ex(layerm_slices_surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : - diff_ex(layerm_slices_surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); + diff_ex_by_piece(layerm_slices_surfaces, to_polygons(upper_layer->lslices), ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); } else { // if no upper layer, all surfaces of this one are solid @@ -1749,7 +1750,7 @@ void PrintObject::detect_surfaces_type() surfaces_append( bottom, opening_ex( - diff_ex(layerm_slices_surfaces, lower_layer->lslices, ApplySafetyOffset::Yes), + diff_ex_by_piece(layerm_slices_surfaces, to_polygons(lower_layer->lslices), ApplySafetyOffset::Yes), offset), surface_type_bottom_other); // if user requested internal shells, we need to identify surfaces @@ -1780,7 +1781,7 @@ void PrintObject::detect_surfaces_type() // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) if (! top.empty() && ! bottom.empty()) { - const auto cracks = intersection_ex(top, bottom); + const auto cracks = intersection_ex_by_piece(to_expolygons(top), to_polygons(bottom)); if (!cracks.empty()) { if (lower_layer) { // Only detect small cracks for non-first layer, because first layer should always be bottom const float small_crack_threshold = -layerm->flow(frExternalPerimeter).scaled_width() * 1.5; @@ -1815,9 +1816,9 @@ void PrintObject::detect_surfaces_type() } } - Polygons top_polygons = to_polygons(std::move(top)); + ExPolygons top_expolygons = to_expolygons(std::move(top)); top.clear(); - surfaces_append(top, diff_ex(top_polygons, bottom), stTop); + surfaces_append(top, diff_ex_by_piece(top_expolygons, to_polygons(bottom)), stTop); } } @@ -1908,7 +1909,7 @@ void PrintObject::detect_surfaces_type() { Polygons topbottom = to_polygons(top); polygons_append(topbottom, to_polygons(bottom)); - surfaces_append(surfaces_out, diff_ex(surfaces_prev_expolys, topbottom), stInternal); + surfaces_append(surfaces_out, diff_ex_by_piece(surfaces_prev_expolys, topbottom), stInternal); } surfaces_append(surfaces_out, std::move(top)); @@ -2248,10 +2249,10 @@ void PrintObject::discover_vertical_shells() // The "ensure vertical wall thickness" feature is not applicable to any of the regions. Quit. return; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; - //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(num_layers / 16, size_t(1)); + // One layer per task: on a layer cut through a fine relief the unions below take far longer than elsewhere, and a + // few such layers next to each other must not end up in one task. tbb::parallel_for( - tbb::blocked_range(0, num_layers, grain_size), + tbb::blocked_range(0, num_layers, 1), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const std::initializer_list surfaces_bottom { stBottom, stBottomBridge }; const size_t num_regions = this->num_printing_regions(); @@ -2259,56 +2260,66 @@ void PrintObject::discover_vertical_shells() m_print->throw_if_canceled(); const Layer &layer = *m_layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; - // Simulate single set of perimeters over all merged regions. - float perimeter_offset = 0.f; - float perimeter_min_spacing = FLT_MAX; + const auto top_bottom_expansion = [&layer](size_t region_id) { + return float(layer.m_regions[region_id]->flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff; + }; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - for (size_t region_id = 0; region_id < num_regions; ++ region_id) { - LayerRegion &layerm = *layer.m_regions[region_id]; - float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff; - // Top surfaces. - append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), top_bottom_expansion)); -// append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), top_bottom_expansion)); - // Bottom surfaces. - append(cache.bottom_surfaces, offset(layerm.slices.filter_by_types(surfaces_bottom), top_bottom_expansion)); -// append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom), top_bottom_expansion)); - // Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only. - // First find the maxium number of perimeters per region slice. - unsigned int perimeters = 0; - for (Surface &s : layerm.slices.surfaces) - perimeters = std::max(perimeters, s.extra_perimeters); - perimeters += layerm.region().config().wall_loops.value; - // Then calculate the infill offset. - if (perimeters > 0) { - Flow extflow = layerm.flow(frExternalPerimeter); - Flow flow = layerm.flow(frPerimeter); - perimeter_offset = std::max(perimeter_offset, - 0.5f * float(extflow.scaled_width() + extflow.scaled_spacing()) + (float(perimeters) - 1.f) * flow.scaled_spacing()); - perimeter_min_spacing = std::min(perimeter_min_spacing, float(std::min(extflow.scaled_spacing(), flow.scaled_spacing()))); - } - polygons_append(cache.holes, to_polygons(layerm.fill_expolygons)); - } - // Save some computing time by reducing the number of polygons. - cache.top_surfaces = union_(cache.top_surfaces); - cache.bottom_surfaces = union_(cache.bottom_surfaces); - // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print. - if (perimeter_offset > 0.) { - // The layer.lslices are forced to merge by expanding them first. - polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, - perimeter_offset - 0.3f * perimeter_min_spacing)); + // The top surfaces, the bottom surfaces and the holes are independent of each other. + tbb::parallel_invoke( + [&]() { + for (size_t region_id = 0; region_id < num_regions; ++ region_id) + append(cache.top_surfaces, offset(layer.m_regions[region_id]->slices.filter_by_type(stTop), top_bottom_expansion(region_id))); +// append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), top_bottom_expansion)); + // Save some computing time by reducing the number of polygons. + cache.top_surfaces = union_(cache.top_surfaces); + }, + [&]() { + for (size_t region_id = 0; region_id < num_regions; ++ region_id) + append(cache.bottom_surfaces, offset(layer.m_regions[region_id]->slices.filter_by_types(surfaces_bottom), top_bottom_expansion(region_id))); +// append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom), top_bottom_expansion)); + cache.bottom_surfaces = union_(cache.bottom_surfaces); + }, + [&]() { + // Simulate single set of perimeters over all merged regions. + float perimeter_offset = 0.f; + float perimeter_min_spacing = FLT_MAX; + for (size_t region_id = 0; region_id < num_regions; ++ region_id) { + const LayerRegion &layerm = *layer.m_regions[region_id]; + // Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only. + // First find the maxium number of perimeters per region slice. + unsigned int perimeters = 0; + for (const Surface &s : layerm.slices.surfaces) + perimeters = std::max(perimeters, s.extra_perimeters); + perimeters += layerm.region().config().wall_loops.value; + // Then calculate the infill offset. + if (perimeters > 0) { + Flow extflow = layerm.flow(frExternalPerimeter); + Flow flow = layerm.flow(frPerimeter); + perimeter_offset = std::max(perimeter_offset, + 0.5f * float(extflow.scaled_width() + extflow.scaled_spacing()) + (float(perimeters) - 1.f) * flow.scaled_spacing()); + perimeter_min_spacing = std::min(perimeter_min_spacing, float(std::min(extflow.scaled_spacing(), flow.scaled_spacing()))); + } + polygons_append(cache.holes, to_polygons(layerm.fill_expolygons)); + } + // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print. + if (perimeter_offset > 0.) { + // The layer.lslices are forced to merge by expanding them first. + polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, - perimeter_offset - 0.3f * perimeter_min_spacing)); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices)); - svg.draw(layer.lslices, "blue"); - svg.draw(union_ex(cache.holes), "red"); - svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); - svg.Close(); - } + { + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices)); + svg.draw(layer.lslices, "blue"); + svg.draw(union_ex(cache.holes), "red"); + svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); + svg.Close(); + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - } - cache.holes = union_(cache.holes); + } + cache.holes = union_(cache.holes); + }); } }); m_print->throw_if_canceled(); @@ -2606,11 +2617,8 @@ void PrintObject::discover_vertical_shells() Polygons object_volume; Polygons internal_volume; { - Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{}; - Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ? - to_polygons(m_layers[idx_layer + 1]->lslices) : - Polygons{}; - object_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); + if (idx_layer > 0 && idx_layer + 1 < m_layers.size()) + object_volume = to_polygons(intersection_ex_by_piece(m_layers[idx_layer - 1]->lslices, to_polygons(m_layers[idx_layer + 1]->lslices))); internal_volume = closing(polygonsInternal, SCALED_EPSILON); } @@ -2670,8 +2678,9 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the internal & internalvoid by the shell. - Slic3r::ExPolygons new_internal = diff_ex(layerm->fill_surfaces.filter_by_type(stInternal), regularized_shell); - Slic3r::ExPolygons new_internal_void = diff_ex(layerm->fill_surfaces.filter_by_type(stInternalVoid), regularized_shell); + const Polygons regularized_shell_polygons = to_polygons(regularized_shell); + Slic3r::ExPolygons new_internal = diff_ex_by_piece(to_expolygons(layerm->fill_surfaces.filter_by_type(stInternal)), regularized_shell_polygons); + Slic3r::ExPolygons new_internal_void = diff_ex_by_piece(to_expolygons(layerm->fill_surfaces.filter_by_type(stInternalVoid)), regularized_shell_polygons); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp index fe9961a771..c69c686521 100644 --- a/tests/libslic3r/test_clipper_utils.cpp +++ b/tests/libslic3r/test_clipper_utils.cpp @@ -299,3 +299,47 @@ TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") { REQUIRE(count_polys(output) == reference.size()); } } + +TEST_CASE("Tiled diff and intersection cover the same area as the plain calls", "[ClipperUtils]") { + // A grid of disjoint framed squares, enough of them to be split into several tiles. + const int n = 40; + const coord_t cell = scaled(2.), side = scaled(1.5), frame = scaled(0.3); + ExPolygons subject; + for (int y = 0; y < n; ++ y) + for (int x = 0; x < n; ++ x) { + const Point o(x * cell, y * cell); + ExPolygon square(Polygon({ o, o + Point(side, 0), o + Point(side, side), o + Point(0, side) })); + Polygon hole({ o + Point(frame, frame), o + Point(frame, side - frame), o + Point(side - frame, side - frame), o + Point(side - frame, frame) }); + square.holes.emplace_back(std::move(hole)); + subject.emplace_back(std::move(square)); + } + // Clip polygons crossing many squares, one of them large with holes of its own. + Polygons clip; + const coord_t span = n * cell; + for (int i = 0; i < 8; ++ i) { + const coord_t y0 = coord_t(i) * span / 8, y1 = y0 + scaled(0.9); + clip.emplace_back(Polygon({ Point(- cell, y0), Point(span, y0 + cell * 3), Point(span, y1 + cell * 3), Point(- cell, y1) })); + } + ExPolygon big(Polygon({ Point(span / 4, span / 4), Point(3 * span / 4, span / 4), Point(3 * span / 4, 3 * span / 4), Point(span / 4, 3 * span / 4) })); + for (int i = 0; i < 4; ++ i) { + const Point o(span / 4 + scaled(3.1) + i * scaled(9.7), span / 4 + scaled(5.3)); + big.holes.emplace_back(Polygon({ o, o + Point(0, scaled(20.)), o + Point(scaled(5.), scaled(20.)), o + Point(scaled(5.), 0) })); + } + polygons_append(clip, to_polygons(big)); + + const auto xor_area = [](const ExPolygons &a, const ExPolygons &b) { return area(diff_ex(a, b)) + area(diff_ex(b, a)); }; + const ApplySafetyOffset safety = GENERATE(ApplySafetyOffset::No, ApplySafetyOffset::Yes); + const double tolerance = double(scaled(0.001)) * double(span); + + const ExPolygons diff_plain = diff_ex(subject, clip, safety); + const ExPolygons diff_tiled = diff_ex_by_piece(subject, clip, safety); + REQUIRE(area(diff_plain) > 0.); + CHECK_THAT(area(diff_tiled), Catch::Matchers::WithinRel(area(diff_plain), 1e-9)); + CHECK(xor_area(diff_tiled, diff_plain) < tolerance); + + const ExPolygons intersection_plain = intersection_ex(subject, clip, safety); + const ExPolygons intersection_tiled = intersection_ex_by_piece(subject, clip, safety); + REQUIRE(area(intersection_plain) > 0.); + CHECK_THAT(area(intersection_tiled), Catch::Matchers::WithinRel(area(intersection_plain), 1e-9)); + CHECK(xor_area(intersection_tiled, intersection_plain) < tolerance); +} From d3485e8b6334114b1b0ef14d33edba783add0a6b Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 22 Sep 2026 19:19:54 +0200 Subject: [PATCH 47/89] Project painted faces onto the shell layers per tile Only the slices within the deepest shell offset decide the result, so the work is done per tile of the face. Top and bottom segmentation ~130 s at 0.1 mm / 2000k, was ~180. --- src/libslic3r/ClipperUtils.cpp | 64 +++++++++++---------- src/libslic3r/ClipperUtils.hpp | 10 ++++ src/libslic3r/MultiMaterialSegmentation.cpp | 50 +++++++++++----- 3 files changed, 81 insertions(+), 43 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 405ede60bd..51d9c0cea6 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -816,52 +816,58 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } -static Slic3r::ExPolygons clipper_ex_by_piece(ClipperLib::ClipType clipType, const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) -{ - // The subject ExPolygons are split into tiles by the centres of their boxes, a few dozen per tile, and each tile is one - // ClipperLib call with the clip cut to the box of the tile's ExPolygons. - BoundingBox extent; - std::vector bboxes; - bboxes.reserve(subject.size()); - for (const ExPolygon &expoly : subject) { - bboxes.emplace_back(get_extents(expoly)); - extent.merge(bboxes.back()); - } - const int tiles = std::clamp(int(std::sqrt(double(subject.size()) / 32.)), 1, 32); - std::vector> members(size_t(tiles * tiles)); - std::vector tile_bboxes(members.size()); - if (extent.defined) { +namespace ClipperUtils { + std::vector tile_expolygons(const ExPolygons &expolygons, size_t per_tile) + { + BoundingBox extent; + std::vector bboxes; + bboxes.reserve(expolygons.size()); + for (const ExPolygon &expoly : expolygons) { + bboxes.emplace_back(get_extents(expoly)); + extent.merge(bboxes.back()); + } + if (! extent.defined) + return {}; + const int tiles = std::clamp(int(std::sqrt(double(expolygons.size()) / double(std::max(per_tile, 1)))), 1, 32); const Point size = extent.size(); const coord_t tile_w = std::max(1, size.x() / tiles + 1), tile_h = std::max(1, size.y() / tiles + 1); - for (size_t i = 0; i < subject.size(); ++i) { - const Point c = bboxes[i].center(); - const size_t tile = size_t(std::clamp(int((c.y() - extent.min.y()) / tile_h), 0, tiles - 1) * tiles + - std::clamp(int((c.x() - extent.min.x()) / tile_w), 0, tiles - 1)); - members[tile].emplace_back(i); - tile_bboxes[tile].merge(bboxes[i]); + std::vector out(size_t(tiles * tiles)); + for (size_t i = 0; i < expolygons.size(); ++ i) { + const Point c = bboxes[i].center(); + ExPolygonsTile &tile = out[size_t(std::clamp(int((c.y() - extent.min.y()) / tile_h), 0, tiles - 1) * tiles + + std::clamp(int((c.x() - extent.min.x()) / tile_w), 0, tiles - 1))]; + tile.members.emplace_back(i); + tile.bbox.merge(bboxes[i]); } + out.erase(std::remove_if(out.begin(), out.end(), [](const ExPolygonsTile &tile) { return tile.members.empty(); }), out.end()); + return out; } +} + +static Slic3r::ExPolygons clipper_ex_by_piece(ClipperLib::ClipType clipType, const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) +{ + // A few dozen subject ExPolygons to a tile, each tile one ClipperLib call with the clip cut to the tile's box. + const std::vector tiles = ClipperUtils::tile_expolygons(subject, 32); std::vector clip_bboxes; clip_bboxes.reserve(clip.size()); for (const Polygon &polygon : clip) clip_bboxes.emplace_back(get_extents(polygon)); - std::vector out_tiles(members.size()); - tbb::parallel_for(size_t(0), members.size(), [&](size_t tile) { - if (members[tile].empty()) - return; + std::vector out_tiles(tiles.size()); + tbb::parallel_for(size_t(0), tiles.size(), [&](size_t tile_idx) { + const ClipperUtils::ExPolygonsTile &tile = tiles[tile_idx]; Slic3r::ExPolygons local_subject; - local_subject.reserve(members[tile].size()); - for (size_t i : members[tile]) + local_subject.reserve(tile.members.size()); + for (size_t i : tile.members) local_subject.emplace_back(subject[i]); // Grown so that the cut edges of the clip stay clear of the subject, also after the safety offset. - const BoundingBox bbox = tile_bboxes[tile].inflated(SCALED_EPSILON); + const BoundingBox bbox = tile.bbox.inflated(SCALED_EPSILON); Polygons local_clip; for (size_t i = 0; i < clip.size(); ++i) if (clip_bboxes[i].overlap(bbox)) if (Polygon clipped = ClipperUtils::clip_clipper_polygon_with_subject_bbox(clip[i], bbox); ! clipped.empty()) local_clip.emplace_back(std::move(clipped)); - out_tiles[tile] = _clipper_ex(clipType, ClipperUtils::ExPolygonsProvider(local_subject), ClipperUtils::PolygonsProvider(local_clip), do_safety_offset); + out_tiles[tile_idx] = _clipper_ex(clipType, ClipperUtils::ExPolygonsProvider(local_subject), ClipperUtils::PolygonsProvider(local_clip), do_safety_offset); }); Slic3r::ExPolygons out; for (Slic3r::ExPolygons &out_tile : out_tiles) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index a2b43ceb70..0c7b7f4294 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -2,6 +2,7 @@ #define slic3r_ClipperUtils_hpp_ #include "libslic3r.h" +#include "BoundingBox.hpp" #include "clipper.hpp" #include "ExPolygon.hpp" #include "Polygon.hpp" @@ -321,6 +322,15 @@ namespace ClipperUtils { [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox, const bool get_entire_polygons = false); [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox, const bool get_entire_polygons = false); + // Splits ExPolygons into tiles by the centres of their boxes, about `per_tile` of them to a tile, to run ClipperLib on a + // layer of many pieces tile by tile. Returns the non-empty tiles, each with the indices of its ExPolygons and their box. + struct ExPolygonsTile + { + BoundingBox bbox; + std::vector members; + }; + [[nodiscard]] std::vector tile_expolygons(const ExPolygons &expolygons, size_t per_tile); + } // Perform union of input polygons using the non-zero rule, convert to ExPolygons. diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 320e8fa39b..0798838b06 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1386,27 +1386,49 @@ static inline std::vector> segmentation_top_and_bottom_l // Projects a painted top or bottom face `ex` of layer `layer_idx` onto the shell layers below or above it (in // `shell_layers`, nearest first), one more perimeter in on each, stopping at the first layer where nothing is left. - // The per-layer ClipperLib work is independent once the trimmed slices and the offsets have been walked in order, - // so it runs in parallel; the offsets are accumulated exactly as the sequential walk did. + // Only the slices within the deepest offset of `ex` (three times that with the miter joins) decide the result, so the + // work is done per tile of `ex`'s ExPolygons on the slices cut to the tile's box grown by that much: the same result, but + // each ClipperLib call stays the size of a tile rather than of a layer cut through a fine relief, and the tiles run in + // parallel. const auto project_to_shells = [&input_expolygons](const ExPolygons &ex, size_t layer_idx, const std::vector &shell_layers, const LayerColorStat &stat, std::vector &dst, size_t dst_offset) { - std::vector trimmed(shell_layers.size()); - std::vector offsets(shell_layers.size()); - ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; - float offset = 0.f; + std::vector offsets(shell_layers.size()); + float offset = 0.f; for (size_t i = 0; i < shell_layers.size(); ++i) { //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line offset -= (stat.extrusion_spacing + stat.extrusion_width); - offsets[i] = offset; - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[shell_layers[i]]); - trimmed[i] = layer_slices_trimmed; + offsets[i] = offset; } - std::vector shells(shell_layers.size()); - tbb::parallel_for(size_t(0), shell_layers.size(), [&](size_t i) { - shells[i] = opening_ex(intersection_ex(ex, offset_ex(trimmed[i], offsets[i])), stat.small_region_threshold); + if (offsets.empty()) + return; + const coord_t reach = coord_t(std::ceil(DefaultMiterLimit * std::abs(offsets.back()))) + 10 * SCALED_EPSILON; + const std::vector tiles = ClipperUtils::tile_expolygons(ex, 16); + // [shell layer][tile] + std::vector> shells(shell_layers.size(), std::vector(tiles.size())); + tbb::parallel_for(size_t(0), tiles.size(), [&](size_t tile_idx) { + const ClipperUtils::ExPolygonsTile &tile = tiles[tile_idx]; + const BoundingBox bbox = tile.bbox.inflated(reach); + ExPolygons tile_ex; + tile_ex.reserve(tile.members.size()); + for (size_t i : tile.members) + tile_ex.emplace_back(ex[i]); + Polygons layer_slices_trimmed = ClipperUtils::clip_clipper_polygons_with_subject_bbox(input_expolygons[layer_idx], bbox); + for (size_t i = 0; i < shell_layers.size() && ! layer_slices_trimmed.empty(); ++i) { + const ExPolygons trimmed = intersection_ex(layer_slices_trimmed, ClipperUtils::clip_clipper_polygons_with_subject_bbox(input_expolygons[shell_layers[i]], bbox)); + shells[i][tile_idx] = opening_ex(intersection_ex(tile_ex, offset_ex(trimmed, offsets[i])), stat.small_region_threshold); + layer_slices_trimmed = to_polygons(trimmed); + } }); - for (size_t i = 0; i < shell_layers.size() && !shells[i].empty(); ++i) - append(dst[shell_layers[i] + dst_offset], std::move(shells[i])); + for (size_t i = 0; i < shell_layers.size(); ++i) { + bool empty = true; + for (ExPolygons &shell : shells[i]) + if (! shell.empty()) { + append(dst[shell_layers[i] + dst_offset], std::move(shell)); + empty = false; + } + if (empty) + break; + } }; tbb::parallel_for(tbb::blocked_range(0, num_layers, granularity), [&granularity, &num_layers, &num_facets_states, &layer_color_stat, &top_raw, &triangles_by_color_top, From e46eecc71683445f0946b1d3ac39c58958f6154e Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 22 Sep 2026 19:19:54 +0200 Subject: [PATCH 48/89] Move polygons instead of copying them on move MultiPoint had no rvalue constructor, so the derived move constructors bound to the const reference and copied; append reserved exactly, so collecting pieces one by one was quadratic. Colour segmentation ~3 s at 0.1 mm / 2000k, was ~40, and ordinary prints gain too. --- src/libslic3r/MultiPoint.hpp | 6 ++++-- src/libslic3r/Polygon.hpp | 4 ++-- src/libslic3r/Polyline.hpp | 4 ++-- src/libslic3r/Support/SupportMaterial.cpp | 2 +- src/libslic3r/Surface.hpp | 4 ++-- src/libslic3r/libslic3r.h | 8 ++++---- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 096230d22c..b4f8af0871 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -19,11 +19,13 @@ public: MultiPoint() {} MultiPoint(const MultiPoint &other) : points(other.points) {} - MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} + MultiPoint(MultiPoint &&other) noexcept : points(std::move(other.points)) {} MultiPoint(std::initializer_list list) : points(list) {} explicit MultiPoint(const Points &_points) : points(_points) {} + // Without it, the derived classes' move constructors passing std::move(points) here copied them. + explicit MultiPoint(Points &&_points) noexcept : points(std::move(_points)) {} MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; } - MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; } + MultiPoint& operator=(MultiPoint &&other) noexcept { points = std::move(other.points); return *this; } virtual ~MultiPoint() = default; void scale(double factor); void scale(double factor_x, double factor_y); diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 7d996055e5..d542d2d74a 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -27,7 +27,7 @@ public: explicit Polygon(const Points &points) : MultiPoint(points) {} Polygon(std::initializer_list points) : MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} - Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {} + Polygon(Polygon &&other) noexcept : MultiPoint(std::move(other.points)) {} static Polygon new_scale(const std::vector &points) { Polygon pgn; pgn.points.reserve(points.size()); @@ -36,7 +36,7 @@ public: return pgn; } Polygon& operator=(const Polygon &other) { points = other.points; return *this; } - Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } + Polygon& operator=(Polygon &&other) noexcept { points = std::move(other.points); return *this; } Point& operator[](Points::size_type idx) { return this->points[idx]; } const Point& operator[](Points::size_type idx) const { return this->points[idx]; } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index f52c3c9bbd..aa9282970e 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -20,7 +20,7 @@ class Polyline : public MultiPoint { public: Polyline() {}; Polyline(const Polyline& other) : MultiPoint(other.points), fitting_result(other.fitting_result) {} - Polyline(Polyline &&other) : MultiPoint(std::move(other.points)), fitting_result(std::move(other.fitting_result)) {} + Polyline(Polyline &&other) noexcept : MultiPoint(std::move(other.points)), fitting_result(std::move(other.fitting_result)) {} Polyline(std::initializer_list list) : MultiPoint(list) { fitting_result.clear(); } @@ -41,7 +41,7 @@ public: fitting_result = other.fitting_result; return *this; } - Polyline& operator=(Polyline&& other) { + Polyline& operator=(Polyline&& other) noexcept { points = std::move(other.points); fitting_result = std::move(other.fitting_result); return *this; diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index a955eb5ca7..ef18ed363f 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -929,9 +929,9 @@ public: ::fread(&y, sizeof(coord_t), 1, file); poly.points.emplace_back(Point(x * scale, y * scale)); } + printf("Polygon %d, area: %lf\n", i, area(poly.points)); if (which == -1 || which == i) m_support_polygons_deserialized.emplace_back(std::move(poly)); - printf("Polygon %d, area: %lf\n", i, area(poly.points)); } ::fread(&n_polygons, 4, 1, file); m_trimming_polygons_deserialized.reserve(n_polygons); diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp index b63c283251..f9737f8cf5 100644 --- a/src/libslic3r/Surface.hpp +++ b/src/libslic3r/Surface.hpp @@ -61,7 +61,7 @@ public: thickness(other.thickness), thickness_layers(other.thickness_layers), bridge_angle(other.bridge_angle), extra_perimeters(other.extra_perimeters) {}; - Surface(Surface &&rhs) + Surface(Surface &&rhs) noexcept : surface_type(rhs.surface_type), expolygon(std::move(rhs.expolygon)), thickness(rhs.thickness), thickness_layers(rhs.thickness_layers), bridge_angle(rhs.bridge_angle), extra_perimeters(rhs.extra_perimeters) @@ -87,7 +87,7 @@ public: return *this; } - Surface& operator=(Surface &&rhs) + Surface& operator=(Surface &&rhs) noexcept { surface_type = rhs.surface_type; expolygon = std::move(rhs.expolygon); diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index c339da566a..06fce9796d 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -162,10 +162,10 @@ inline void append(std::vector &dest, std::vector &&src) { if (dest.empty()) dest = std::move(src); - else { - dest.reserve(dest.size() + src.size()); - std::move(std::begin(src), std::end(src), std::back_inserter(dest)); - } + else + // insert() grows the capacity geometrically; reserving exactly the new size reallocated on every call, which + // made appending piece by piece quadratic. + dest.insert(dest.end(), std::make_move_iterator(src.begin()), std::make_move_iterator(src.end())); src.clear(); src.shrink_to_fit(); } From 3023ca0d3804ea31ad4ae621146b7ce25cbcb86e Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 22 Sep 2026 22:14:39 +0200 Subject: [PATCH 49/89] Run a layer's regions in parallel where they are independent detect_surfaces_type, process_external_surfaces and the vertical shells each waited on their own heaviest layer in turn. The LOTR map plate slices in ~10.5 min at 0.1 mm / 2000k, was ~11.5; ~87 s at normal settings, was ~97. --- src/libslic3r/PrintObject.cpp | 278 ++++++++++++++++++++-------------- 1 file changed, 161 insertions(+), 117 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 57d21839fd..f29881d8e4 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1666,7 +1666,9 @@ void PrintObject::detect_surfaces_type() bool interface_shells = ! spiral_mode && m_config.interface_shells.value; size_t num_layers = spiral_mode ? std::min(size_t(this->printing_region(0).config().bottom_shell_layers), m_layers.size()) : m_layers.size(); - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + // The regions of a layer do not see each other here, and a layer cut through a fine relief takes far longer than the + // others, so the regions run next to each other instead of one after another, each still over all layers. + tbb::parallel_for(size_t(0), this->num_printing_regions(), [&](size_t region_id) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (Layer *layer : m_layers) @@ -2086,29 +2088,31 @@ void PrintObject::detect_surfaces_type() } } ); - // ============================================================================================================== - // === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ============== - // === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ========== - // === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ========== - // ============================================================================================================== - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { - tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, region_id](const tbb::blocked_range &range) { - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { - Surfaces &surfs = m_layers[idx_layer]->m_regions[region_id]->slices.surfaces; - for (Surface &s : surfs) { - if (s.surface_type == stInternalAfterExternalBridge) { - s.surface_type = stBottomBridge; - } - } - } - } - ); - } } // ============================================================================================================== // === ORCA: End of second external bridge layer changes ======================================================= // ============================================================================================================== + }); // for each this->print->region_count + + // ============================================================================================================== + // === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ============== + // === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ========== + // === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ========== + // ============================================================================================================== + // Once all the regions have their second bridge layer, and before their slices are trimmed into fill surfaces below. + if ((this->config().enable_extra_bridge_layer.value == eblApplyToAll) || (this->config().enable_extra_bridge_layer.value == eblExternalBridgeOnly)) { + tbb::parallel_for(tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range &range) { + for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) + for (LayerRegion *layerm : m_layers[idx_layer]->regions()) + for (Surface &s : layerm->slices.surfaces) + if (s.surface_type == stInternalAfterExternalBridge) + s.surface_type = stBottomBridge; + }); + m_print->throw_if_canceled(); + } + + tbb::parallel_for(size_t(0), this->num_printing_regions(), [&](size_t region_id) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start"; // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. tbb::parallel_for( @@ -2125,7 +2129,7 @@ void PrintObject::detect_surfaces_type() }); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end"; - } // for each this->print->region_count + }); // Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) m_typed_slices = true; @@ -2192,8 +2196,10 @@ void PrintObject::process_external_surfaces() BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end"; } - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { - BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; + BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces in parallel - start"; + // The regions of a layer do not see each other here, and a layer cut through a fine relief takes far longer than the + // others, so the regions run next to each other instead of one after another, each still over all layers. + tbb::parallel_for(size_t(0), this->num_printing_regions(), [this, &surfaces_covered](size_t region_id) { tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, &surfaces_covered, region_id](const tbb::blocked_range& range) { @@ -2208,9 +2214,9 @@ void PrintObject::process_external_surfaces() } } ); - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end"; - } + }); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces in parallel - end"; } void PrintObject::discover_vertical_shells() @@ -2330,19 +2336,128 @@ void PrintObject::discover_vertical_shells() // region-specific but the shell settings and the external perimeter spacing, so a region sharing them with an earlier // one reuses its result instead of repeating it: that accumulation is a union over several layers of top/bottom // surfaces, and a multi-material print has a region per filament. + using AccumulationKey = std::array; struct ShellAccumulation { - std::array key; - Polygons shell; - Polygons holes; + AccumulationKey key; + Polygons shell; + Polygons holes; + }; + const auto accumulation_key = [](const PrintRegionConfig ®ion_config, const LayerRegion *layerm) { + return AccumulationKey{ double(region_config.top_shell_layers.value), region_config.top_shell_thickness.value, + double(region_config.bottom_shell_layers.value), region_config.bottom_shell_thickness.value, + double(layerm->flow(frExternalPerimeter).scaled_spacing()) }; + }; + const auto accumulate_shell = [this, &cache_top_botom_regions](size_t idx_layer, const PrintRegionConfig ®ion_config, + const LayerRegion *layerm, Polygons &shell, Polygons &holes) { + const Layer *layer = m_layers[idx_layer]; + polygons_append(holes, cache_top_botom_regions[idx_layer].holes); + auto combine_holes = [&holes](const Polygons &holes2) { + if (holes.empty() || holes2.empty()) + holes.clear(); + else + holes = intersection(holes, holes2); + }; + auto combine_shells = [&shell](const Polygons &shells2) { + if (shell.empty()) + shell = std::move(shells2); + else if (! shells2.empty()) { + polygons_append(shell, shells2); + // Running the union_ using the Clipper library piece by piece is cheaper + // than running the union_ all at once. + shell = union_(shell); + } + }; + static constexpr const bool one_more_layer_below_top_bottom_surfaces = false; + if (int n_top_layers = region_config.top_shell_layers.value; n_top_layers > 0) { + // Gather top regions projected to this layer. + coordf_t print_z = layer->print_z; + int i = int(idx_layer) + 1; + int itop = int(idx_layer) + n_top_layers; + bool at_least_one_top_projected = false; + for (; i < int(cache_top_botom_regions.size()) && + (i < itop || m_layers[i]->print_z - print_z < region_config.top_shell_thickness - EPSILON); + ++ i) { + at_least_one_top_projected = true; + const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; + combine_holes(cache.holes); + combine_shells(cache.top_surfaces); + } + if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) { + // Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the + // boundaries of solid layers are not anchored over/under perimeters, so lets fix it by adding at least one + // perimeter width of area + Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].top_surfaces, + layerm->flow(frExternalPerimeter).scaled_spacing()), + to_polygons(m_layers[i]->lslices)); + combine_shells(anchor_area); + } + + if (one_more_layer_below_top_bottom_surfaces) + if (i < int(cache_top_botom_regions.size()) && + (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_shell_thickness - EPSILON)) + combine_holes(cache_top_botom_regions[i].holes); + } + if (int n_bottom_layers = region_config.bottom_shell_layers.value; n_bottom_layers > 0) { + // Gather bottom regions projected to this layer. + coordf_t bottom_z = layer->bottom_z(); + int i = int(idx_layer) - 1; + int ibottom = int(idx_layer) - n_bottom_layers; + bool at_least_one_bottom_projected = false; + for (; i >= 0 && + (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_shell_thickness - EPSILON); + -- i) { + at_least_one_bottom_projected = true; + const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; + combine_holes(cache.holes); + combine_shells(cache.bottom_surfaces); + } + + if (!at_least_one_bottom_projected && i >= 0) { + Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].bottom_surfaces, + layerm->flow(frExternalPerimeter).scaled_spacing()), + to_polygons(m_layers[i]->lslices)); + combine_shells(anchor_area); + } + + if (one_more_layer_below_top_bottom_surfaces) + if (i >= 0 && + (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON)) + combine_holes(cache_top_botom_regions[i].holes); + } }; std::vector> shell_accumulations(top_bottom_surfaces_all_regions ? num_layers : 0); + if (! shell_accumulations.empty()) { + // Every (layer, key) pair is accumulated once, before the regions, so that nothing in the loop below is shared + // between them and they can run next to each other. + std::vector> todo; // layer, its slot, a region holding the key + for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer) { + std::vector &accumulations = shell_accumulations[idx_layer]; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + if (this->printing_region(region_id).config().ensure_vertical_shell_thickness.value != evstAll) + continue; + const LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id]; + const AccumulationKey key = accumulation_key(layerm->region().config(), layerm); + if (std::none_of(accumulations.begin(), accumulations.end(), [&key](const ShellAccumulation &a) { return a.key == key; })) { + todo.push_back({ idx_layer, accumulations.size(), region_id }); + accumulations.push_back({ key, {}, {} }); + } + } + } + tbb::parallel_for(size_t(0), todo.size(), [this, &todo, &shell_accumulations, &accumulate_shell](size_t i) { + m_print->throw_if_canceled(); + const LayerRegion *layerm = m_layers[todo[i][0]]->m_regions[todo[i][2]]; + ShellAccumulation &out = shell_accumulations[todo[i][0]][todo[i][1]]; + accumulate_shell(todo[i][0], layerm->region().config(), layerm, out.shell, out.holes); + }); + m_print->throw_if_canceled(); + } - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + const auto process_region = [&](size_t region_id) { const PrintRegion ®ion = this->printing_region(region_id); if (region.config().ensure_vertical_shell_thickness.value != evstAll ) // This region will be handled by discover_horizontal_shells(). - continue; + return; //FIXME Improve the heuristics for a grain size. size_t grain_size = std::max(num_layers / 16, size_t(1)); @@ -2382,7 +2497,7 @@ void PrintObject::discover_vertical_shells() grain_size = 1; tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), - [this, region_id, &cache_top_botom_regions, &shell_accumulations] + [this, region_id, &shell_accumulations, &accumulation_key, &accumulate_shell] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { @@ -2432,98 +2547,19 @@ void PrintObject::discover_vertical_shells() } } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - const std::array accumulation_key{ double(region_config.top_shell_layers.value), region_config.top_shell_thickness.value, - double(region_config.bottom_shell_layers.value), region_config.bottom_shell_thickness.value, - double(layerm->flow(frExternalPerimeter).scaled_spacing()) }; - std::vector *accumulations = shell_accumulations.empty() ? nullptr : &shell_accumulations[idx_layer]; - const auto reused = accumulations == nullptr ? nullptr : + const AccumulationKey key = accumulation_key(region_config, layerm); + const ShellAccumulation *reused = shell_accumulations.empty() ? nullptr : [&]() -> const ShellAccumulation * { - for (const ShellAccumulation &a : *accumulations) - if (a.key == accumulation_key) + for (const ShellAccumulation &a : shell_accumulations[idx_layer]) + if (a.key == key) return &a; return nullptr; }(); if (reused != nullptr) { shell = reused->shell; holes = reused->holes; - } else { - polygons_append(holes, cache_top_botom_regions[idx_layer].holes); - auto combine_holes = [&holes](const Polygons &holes2) { - if (holes.empty() || holes2.empty()) - holes.clear(); - else - holes = intersection(holes, holes2); - }; - auto combine_shells = [&shell](const Polygons &shells2) { - if (shell.empty()) - shell = std::move(shells2); - else if (! shells2.empty()) { - polygons_append(shell, shells2); - // Running the union_ using the Clipper library piece by piece is cheaper - // than running the union_ all at once. - shell = union_(shell); - } - }; - static constexpr const bool one_more_layer_below_top_bottom_surfaces = false; - if (int n_top_layers = region_config.top_shell_layers.value; n_top_layers > 0) { - // Gather top regions projected to this layer. - coordf_t print_z = layer->print_z; - int i = int(idx_layer) + 1; - int itop = int(idx_layer) + n_top_layers; - bool at_least_one_top_projected = false; - for (; i < int(cache_top_botom_regions.size()) && - (i < itop || m_layers[i]->print_z - print_z < region_config.top_shell_thickness - EPSILON); - ++ i) { - at_least_one_top_projected = true; - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - combine_holes(cache.holes); - combine_shells(cache.top_surfaces); - } - if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) { - // Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the - // boundaries of solid layers are not anchored over/under perimeters, so lets fix it by adding at least one - // perimeter width of area - Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].top_surfaces, - layerm->flow(frExternalPerimeter).scaled_spacing()), - to_polygons(m_layers[i]->lslices)); - combine_shells(anchor_area); - } - - if (one_more_layer_below_top_bottom_surfaces) - if (i < int(cache_top_botom_regions.size()) && - (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_shell_thickness - EPSILON)) - combine_holes(cache_top_botom_regions[i].holes); - } - if (int n_bottom_layers = region_config.bottom_shell_layers.value; n_bottom_layers > 0) { - // Gather bottom regions projected to this layer. - coordf_t bottom_z = layer->bottom_z(); - int i = int(idx_layer) - 1; - int ibottom = int(idx_layer) - n_bottom_layers; - bool at_least_one_bottom_projected = false; - for (; i >= 0 && - (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_shell_thickness - EPSILON); - -- i) { - at_least_one_bottom_projected = true; - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - combine_holes(cache.holes); - combine_shells(cache.bottom_surfaces); - } - - if (!at_least_one_bottom_projected && i >= 0) { - Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].bottom_surfaces, - layerm->flow(frExternalPerimeter).scaled_spacing()), - to_polygons(m_layers[i]->lslices)); - combine_shells(anchor_area); - } - - if (one_more_layer_below_top_bottom_surfaces) - if (i >= 0 && - (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON)) - combine_holes(cache_top_botom_regions[i].holes); - } - if (accumulations != nullptr) - accumulations->push_back({ accumulation_key, shell, holes }); - } + } else + accumulate_shell(idx_layer, region_config, layerm, shell, holes); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); @@ -2707,7 +2743,15 @@ void PrintObject::discover_vertical_shells() layerm->export_region_fill_surfaces_to_svg_debug("3_discover_vertical_shells-final"); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - } // for each region + }; // for each region + if (top_bottom_surfaces_all_regions) + // Nothing is shared between the regions, and a layer cut through a fine relief takes far longer than the others, + // so they run next to each other instead of one after another. + tbb::parallel_for(size_t(0), this->num_printing_regions(), process_region); + else + // Here every region fills the one top/bottom cache with its own surfaces first. + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) + process_region(region_id); } // void PrintObject::discover_vertical_shells() // #define DEBUG_BRIDGE_OVER_INFILL From 6e88ad7f52e3bc502e9cdb7d43eda8f67109a40c Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 02:03:02 +0200 Subject: [PATCH 50/89] Visit seam candidates as the search finds them Collecting every candidate within the radius into a vector cost more than the search itself. Same order, so the seams are unchanged; align_seam_points ~19.6 s at 0.1 mm / 2000k, was ~21. --- src/libslic3r/GCode/SeamPlacer.cpp | 30 ++++++++------ src/libslic3r/KDTreeIndirect.hpp | 30 ++++++++++++++ tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_kdtree.cpp | 66 ++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 tests/libslic3r/test_kdtree.cpp diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 35bc436d5a..8585f4bcd9 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1117,21 +1117,21 @@ std::optional> SeamPlacer::find_next_seam_in_layer( const size_t layer_idx, const float max_distance, const SeamPlacerImpl::SeamComparator &comparator) const { using namespace SeamPlacerImpl; - std::vector nearby_points_indices = find_nearby_points(*layers[layer_idx].points_tree, projected_position, - max_distance); - - if (nearby_points_indices.empty()) { - return {}; - } - - size_t best_nearby_point_index = nearby_points_indices[0]; - size_t nearest_point_index = nearby_points_indices[0]; - - // Now find best nearby point, nearest point, and corresponding indices - for (const size_t &nearby_point_index : nearby_points_indices) { + // Find the best nearby point and the nearest one. A layer of a fine relief has tens of thousands of candidates within + // the radius, so they are looked at as the search finds them rather than collected into a vector first. + constexpr size_t none = std::numeric_limits::max(); + size_t best_nearby_point_index = none; + size_t nearest_point_index = none; + visit_nearby_points(*layers[layer_idx].points_tree, projected_position, max_distance, + [&layers, &comparator, &projected_position, layer_idx, &best_nearby_point_index, &nearest_point_index] + (size_t nearby_point_index) { + if (best_nearby_point_index == none) { + // The first point found starts both, as the first of the collected ones did. + best_nearby_point_index = nearest_point_index = nearby_point_index; + } const SeamCandidate &point = layers[layer_idx].points[nearby_point_index]; if (point.perimeter.finalized) { - continue; // skip over finalized perimeters, try to find some that is not finalized + return; // skip over finalized perimeters, try to find some that is not finalized } if (comparator.is_first_better(point, layers[layer_idx].points[best_nearby_point_index], projected_position.head<2>()) @@ -1143,6 +1143,10 @@ std::optional> SeamPlacer::find_next_seam_in_layer( || layers[layer_idx].points[nearest_point_index].perimeter.finalized) { nearest_point_index = nearby_point_index; } + }); + + if (best_nearby_point_index == none) { + return {}; } const SeamCandidate &best_nearby_point = layers[layer_idx].points[best_nearby_point_index]; diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp index 37c10827b1..8b48ffc818 100644 --- a/src/libslic3r/KDTreeIndirect.hpp +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -313,6 +313,36 @@ std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const P return visitor.result; } +// Visits the points within max_distance of center, in the order find_nearby_points() would collect them, and hands +// each of them to `visitor_fn` instead of returning them all: a search over a dense set spends more on collecting the +// points into a vector than on the search itself, and its caller usually keeps only a few of them. +template +void visit_nearby_points(const KDTreeIndirectType &kdtree, const PointType ¢er, + const typename KDTreeIndirectType::CoordType &max_distance, VisitorFn visitor_fn) +{ + using CoordType = typename KDTreeIndirectType::CoordType; + + struct Visitor { + const KDTreeIndirectType &kdtree; + const PointType center; + const CoordType max_distance_squared; + VisitorFn visitor_fn; + + unsigned int operator()(size_t idx, size_t dimension) { + auto dist = CoordType(0); + for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) { + CoordType d = center[i] - kdtree.coordinate(idx, i); + dist += d * d; + } + if (dist < max_distance_squared) + visitor_fn(idx); + return kdtree.descent_mask(center[dimension], max_distance_squared, idx, dimension); + } + } visitor { kdtree, center, max_distance * max_distance, visitor_fn }; + + kdtree.visit(visitor); +} + template std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const PointType ¢er, const typename KDTreeIndirectType::CoordType& max_distance) diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 6479490ffa..015da4f905 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(${_TEST_NAME}_tests test_filament_mixer.cpp test_fill_plane_path.cpp test_geometry.cpp + test_kdtree.cpp test_multimaterial_segmentation.cpp test_placeholder_parser.cpp test_polygon.cpp diff --git a/tests/libslic3r/test_kdtree.cpp b/tests/libslic3r/test_kdtree.cpp new file mode 100644 index 0000000000..dbcf86674a --- /dev/null +++ b/tests/libslic3r/test_kdtree.cpp @@ -0,0 +1,66 @@ +#include + +#include +#include + +#include "libslic3r/KDTreeIndirect.hpp" +#include "libslic3r/Point.hpp" + +using namespace Slic3r; + +TEST_CASE("Visiting the nearby points gives what collecting them gives", "[KDTree]") { + std::mt19937 rng(19937); + std::uniform_real_distribution coord(-50.f, 50.f); + // Points in a box, so that a radius search returns anything from none of them to all of them. + std::vector points(2000); + for (Vec3f &p : points) + p = Vec3f(coord(rng), coord(rng), coord(rng)); + + auto coordinate = [&points](size_t idx, size_t dimension) { return points[idx](int(dimension)); }; + KDTreeIndirect<3, float, decltype(coordinate)> tree(coordinate); + std::vector indices(points.size()); + std::iota(indices.begin(), indices.end(), 0); + tree.build(indices); + + const float radius = GENERATE(0.5f, 5.f, 25.f, 200.f); + for (int i = 0; i < 20; ++ i) { + const Vec3f center(coord(rng), coord(rng), coord(rng)); + + const std::vector collected = find_nearby_points(tree, center, radius); + std::vector visited; + visit_nearby_points(tree, center, radius, [&visited](size_t idx) { visited.emplace_back(idx); }); + + // Same points, and in the same order: a caller that keeps the first of several equally good ones + // must get the same answer either way. + REQUIRE(visited == collected); + } +} + +TEST_CASE("A radius search returns every point within the radius and no other", "[KDTree]") { + std::mt19937 rng(2024); + std::uniform_real_distribution coord(-20.f, 20.f); + std::vector points(500); + for (Vec3f &p : points) + p = Vec3f(coord(rng), coord(rng), coord(rng)); + + auto coordinate = [&points](size_t idx, size_t dimension) { return points[idx](int(dimension)); }; + KDTreeIndirect<3, float, decltype(coordinate)> tree(coordinate); + std::vector indices(points.size()); + std::iota(indices.begin(), indices.end(), 0); + tree.build(indices); + + const Vec3f center(1.f, -2.f, 3.f); + const float radius = 7.f; + + std::vector expected; + for (size_t i = 0; i < points.size(); ++ i) + if ((points[i] - center).squaredNorm() < radius * radius) + expected.emplace_back(i); + + std::vector visited; + visit_nearby_points(tree, center, radius, [&visited](size_t idx) { visited.emplace_back(idx); }); + std::sort(visited.begin(), visited.end()); + + REQUIRE(! expected.empty()); + REQUIRE(visited == expected); +} From 9327e7770b621982a38a0071fb140f2416bcb001 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 09:51:08 +0200 Subject: [PATCH 51/89] Rename the fast preview to the shaded preview Its shader files, the name they are registered under, its members and the panel text now match what it does: shading only, no geometry. --- TEXTURE_DISPLACEMENT.md | 22 +- TEXTURE_DISPLACEMENT_GUIDE.md | 4 +- ...bump.fs => texture_displacement_shaded.fs} | 4 +- ...bump.vs => texture_displacement_shaded.vs} | 2 +- .../110/texture_displacement_uvcheck.fs | 4 +- ...bump.fs => texture_displacement_shaded.fs} | 14 +- ...bump.vs => texture_displacement_shaded.vs} | 2 +- .../140/texture_displacement_uvcheck.fs | 6 +- src/libslic3r/TextureDisplacement.cpp | 12 +- src/libslic3r/TextureDisplacement.hpp | 15 +- src/slic3r/GUI/GLShadersManager.cpp | 4 +- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 237 +++++++++--------- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 84 +++---- tests/libslic3r/test_texture_displacement.cpp | 20 +- 14 files changed, 214 insertions(+), 216 deletions(-) rename resources/shaders/110/{texture_displacement_bump.fs => texture_displacement_shaded.fs} (99%) rename resources/shaders/110/{texture_displacement_bump.vs => texture_displacement_shaded.vs} (90%) rename resources/shaders/140/{texture_displacement_bump.fs => texture_displacement_shaded.fs} (98%) rename resources/shaders/140/{texture_displacement_bump.vs => texture_displacement_shaded.vs} (97%) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index b557bad8db..93d668b68f 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -323,10 +323,10 @@ checkbox) that puts triangles where the *displaced surface actually bends*, not a linear **ramp** needs no extra vertices (linear interpolation is exact for a ramp); what needs them is **curvature** - the *second* derivative, not the gradient. So the extra predicate is a **chord-error** test: sample the combined displacement at the triangle's three edge midpoints *and its centroid* -(sampling the interior is what catches a bump sitting inside a triangle, the blind spot of an edge-only +(sampling the interior is what catches a hill sitting inside a triangle, the blind spot of an edge-only test) and take the largest departure from the flat triangle's barycentric interpolation. Refine while that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp ⇒ untouched; high on a -bump/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The +hill/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The per-triangle error is cached and recomputed only for the children of a split. Four knobs bracket it, and all four matter: @@ -351,9 +351,9 @@ heights are sampled lazily, so a small patch on a huge model never pays for the ### Fast preview (GPU-only, no CPU meshing) -`resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}`, registered as -`"texture_displacement_bump"`. Shades the *displaced* surface without moving geometry - active-layer -only, selected from the View row, and the default when the gizmo opens (`m_use_bump_preview = true`). +`resources/shaders/{110,140}/texture_displacement_shaded.{vs,fs}`, registered as +`"texture_displacement_shaded"`. Shades the *displaced* surface without moving geometry - active-layer +only, selected from the View row, and the default when the gizmo opens (`m_use_shaded_preview = true`). Vertex format is `GLModel::Geometry::EVertexLayout::P3N3T2`: `normal.x` carries the per-vertex paint weight (0/1), `normal.y` flags the UV island currently being dragged, and `tex_coord` carries a precomputed texture UV, so it can use `GLModel` normally instead of a hand-rolled VBO/VAO manager. @@ -366,7 +366,7 @@ normal from screen-space derivatives of position, not from a per-vertex normal. **Both preview meshes work in the patch's vertex space, not the mesh's.** Those agree only until a *brush* stroke splits a triangle: `get_facets_strict()` then appends the split vertices, so the patch -array is longer. `rebuild_bump_preview_mesh()` and `rebuild_uvcheck_mesh()` therefore index +array is longer. `rebuild_shaded_preview_mesh()` and `rebuild_uvcheck_mesh()` therefore index `patch.vertices` throughout. The weight buffer is rebuilt at the same cadence as the true-displacement preview (stroke-end/slider-release) but from the **live** `TriangleSelector` state, not the flushed model facets, so it does not lag by a full model round-trip. @@ -395,7 +395,7 @@ the bake's. captures the true on-screen rate of change however the chart is stretched. This path is also what makes the fast preview follow the UV editor: move an island and its uv - hence its shading - moves with it (the mesh rebuilds on drag-end, `on_island_edited(finished)` → `rebuild_preview()` → - `rebuild_bump_preview_mesh()`). The branch is uniform and the paint weight gates by multiply, so the + `rebuild_shaded_preview_mesh()`). The branch is uniform and the paint weight gates by multiply, so the texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv → the `det≈0` guard skips it (a localised preview-only artifact, never in the bake). @@ -425,7 +425,7 @@ flow. Two uniforms exist for this: `midlevel` (parallax needs the real height, n and `eye_model_pos` (the camera in the volume's local frame). Parallax cannot change the model's silhouette or cast shadows; the View row's Normal mode is one click -away for that. The LSCM path stays plain Mikkelsen bump - it has no closed-form uv, so there is no cheap +away for that. The LSCM path stays plain Mikkelsen normal perturbation - it has no closed-form uv, so there is no cheap way to re-project a marched position. One further approximation: the GPU sampler's wrap mode stands in for `tile_enabled`/`tile_method`, so with tiling *off* the GPU repeats where the CPU returns 0 outside `[0,1)`. @@ -558,8 +558,8 @@ gizmo via `CommandFn`; view-only ones (Frame, Snap) it handles directly. - `src/slic3r/GUI/UVEditorCanvas.hpp/.cpp` - the 2D UV unwrap viewer widget. - `src/slic3r/GUI/Plater.hpp/.cpp` - `uv_editor_canvas` member, AUI pane registration, `get_uv_editor_canvas()`/`show_uv_editor()`. -- `src/slic3r/GUI/GLShadersManager.cpp` - registers `"texture_displacement_bump"`. -- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` - the fast-preview shader. +- `src/slic3r/GUI/GLShadersManager.cpp` - registers `"texture_displacement_shaded"`. +- `resources/shaders/{110,140}/texture_displacement_shaded.{vs,fs}` - the fast-preview shader. - `src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp` - `PainterGizmoType::TEXTURE_DISPLACEMENT`. - `src/slic3r/GUI/Gizmos/GLGizmosManager.hpp/.cpp` - `EType::TextureDisplacement` registration. @@ -571,7 +571,7 @@ modes (table-driven), the lowest layer ignoring its blend mode, border displace/ smoothing and its mask guarantees, and adaptive subdivision: conformality (`every_edge_used_twice` on a partially-refined cube - an exact crack detector for a closed mesh), the target edge length actually being reached, the triangle budget capping the result without opening a crack, curvature-driven -refinement (a Gaussian bump refines at its centre, a linear ramp adds nothing), and the max-edge +refinement (a Gaussian hill refines at its centre, a linear ramp adds nothing), and the max-edge baseline. `BUILD_TESTS` is `OFF` in the checked-in build cache; flip it on to run them: diff --git a/TEXTURE_DISPLACEMENT_GUIDE.md b/TEXTURE_DISPLACEMENT_GUIDE.md index ac16c97c9e..3400449736 100644 --- a/TEXTURE_DISPLACEMENT_GUIDE.md +++ b/TEXTURE_DISPLACEMENT_GUIDE.md @@ -79,7 +79,7 @@ radio group; **Wireframe** is an independent toggle. Hover any icon for its tool | View | Meaning | |------|---------| | **Normal** | The true displaced geometry — exactly what **Bake** produces. Rebuilt in the background. | -| **Fast** | A GPU bump-shaded approximation of the *active layer only*. No real geometry movement — quick to update, not exact. Best while tuning or dragging islands. | +| **Fast** | A GPU shaded approximation of the *active layer only*. No real geometry movement — quick to update, not exact. Best while tuning or dragging islands. | | **Checker** | A test grid painted over the unwrap so you can see stretching (squares stay square where the map isn't distorted). | | **Distortion** | A blue→green→red heatmap of how much each area is compressed or stretched in UV space. Needs the **Unwrap (LSCM)** projection. | | **Wireframe** | Overlays the mesh edges (white). Independent of the view above; in **Normal** view it sits on the displaced surface. | @@ -315,7 +315,7 @@ Baking is the exact same algorithm as the **Normal** preview, so what you see is - **Not enough detail?** Subdivide or Remesh before painting fine textures. - **Inward cuts** (high Midlevel + big Depth) can self-intersect on thin walls or sharp concave corners — keep Depth modest there. -- **Fast vs Normal:** Fast preview shades a bump and shows only the active layer; use it for quick +- **Fast vs Normal:** Fast preview only shades the relief and shows only the active layer; use it for quick tuning and smooth UV dragging, but trust **Normal**/**Bake** for the exact result. - **Topology changes drop unbaked paint.** Subdivide-Apply, Remesh, and Simplify replace the mesh, and texture-displacement paint isn't remapped across that change (already-baked relief is unaffected). diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_shaded.fs similarity index 99% rename from resources/shaders/110/texture_displacement_bump.fs rename to resources/shaders/110/texture_displacement_shaded.fs index fa000cc7ba..4a2980cd64 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_shaded.fs @@ -1,6 +1,6 @@ #version 110 -// See resources/shaders/140/texture_displacement_bump.fs for full documentation; this is the +// See resources/shaders/140/texture_displacement_shaded.fs for full documentation; this is the // GLSL 1.10 compatibility variant (same logic, older syntax). #define INTENSITY_CORRECTION 0.6 @@ -309,7 +309,7 @@ void main() bool have_uv = false; if (use_vertex_uv) { - // Mikkelsen surface-gradient bump; see the 140 variant for the full rationale. Scale-exact + // Mikkelsen surface-gradient normal perturbation; see the 140 variant for the full rationale. Scale-exact // for a conformal LSCM map (no global 1/tiling assumption), and gated by the paint weight // via a multiply so the branch stays uniform (use_vertex_uv is a uniform). vec2 uv = (island_active > 0.5) diff --git a/resources/shaders/110/texture_displacement_bump.vs b/resources/shaders/110/texture_displacement_shaded.vs similarity index 90% rename from resources/shaders/110/texture_displacement_bump.vs rename to resources/shaders/110/texture_displacement_shaded.vs index 5e268ed346..468bdf56d9 100644 --- a/resources/shaders/110/texture_displacement_bump.vs +++ b/resources/shaders/110/texture_displacement_shaded.vs @@ -1,6 +1,6 @@ #version 110 -// See resources/shaders/140/texture_displacement_bump.vs for full documentation; this is the +// See resources/shaders/140/texture_displacement_shaded.vs for full documentation; this is the // GLSL 1.10 compatibility variant. uniform mat4 view_model_matrix; diff --git a/resources/shaders/110/texture_displacement_uvcheck.fs b/resources/shaders/110/texture_displacement_uvcheck.fs index 201bf38639..f9e998adc2 100644 --- a/resources/shaders/110/texture_displacement_uvcheck.fs +++ b/resources/shaders/110/texture_displacement_uvcheck.fs @@ -22,7 +22,7 @@ uniform vec2 uv_offset; uniform bool use_vertex_uv; // The in-shader projection (0 Triplanar, 1 Cylindrical, 2 Spherical) and the painted patch's frame the // wrapping ones wrap around, in the texture frame - the same uniforms, and the same formulas, as -// texture_displacement_bump.fs, so the checker reports the projection the bake will actually use. +// texture_displacement_shaded.fs, so the checker reports the projection the bake will actually use. uniform int projection_mode; uniform vec3 patch_center; uniform vec3 patch_axis; @@ -91,7 +91,7 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - // World space anchored at the volume's origin, like the bake and the bump preview. + // World space anchored at the volume's origin, like the bake and the shaded preview. vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); vec3 tex_pos = world_pos.xyz - tex_anchor; if (volume_mirrored) diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_shaded.fs similarity index 98% rename from resources/shaders/140/texture_displacement_bump.fs rename to resources/shaders/140/texture_displacement_shaded.fs index baf620933a..1d8bee10fa 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_shaded.fs @@ -1,7 +1,7 @@ #version 140 // Fast, geometry-free preview of texture displacement: perturbs the *shading* normal from the -// height texture's local gradient (a bump map), faded out by the per-vertex paint weight. The +// height texture's local gradient, faded out by the per-vertex paint weight. The // true, exact result is what "Bake" produces via libslic3r/TextureDisplacement.cpp on the CPU. // // The bake displaces each surface point along its normal by H = +/- depth_mm * (h(uv) - midlevel), @@ -13,7 +13,7 @@ // derivatives. Two things have to be right for the preview's apparent depth to match the bake's: // the tangent frame the gradient is expressed in, and the uv->mm scale that turns a texel // difference into a slope. Getting the scale wrong is a uniform flattening (a raw texel difference -// is dh over one texel step, not over one mm); getting the frame wrong tilts the bump along the +// is dh over one texel step, not over one mm); getting the frame wrong tilts the relief along the // wrong axes. // // Two projection paths: @@ -22,7 +22,7 @@ // formed analytically (there is a closed-form uv, so 1 uv unit is exactly tiling_scale mm). This // path also runs a parallax step before shading, see below. // -// Parallax. A pure bump map perturbs shading only, so the pattern is welded to the base surface: it +// Parallax. Perturbing the normal alone shades only, so the pattern is welded to the base surface: it // does not shift as the camera orbits and it does not get any deeper as depth_mm grows, which is // exactly when the preview stops reading as real geometry. The triplanar path therefore shades at the // point the *displaced* surface would show at this pixel rather than at the pixel's own base position. @@ -56,7 +56,7 @@ // assumption, which matters because an LSCM map is conformal, not isometric: the local mm-per-uv // varies across the chart, so a single global 1/tiling factor (what an earlier version used) got // the apparent depth wrong. This path is also what makes the fast preview follow the UV editor: -// move an island and its uv -- hence its bump -- moves with it. +// move an island and its uv -- hence its relief -- moves with it. #define INTENSITY_CORRECTION 0.6 @@ -124,7 +124,7 @@ uniform vec3 tex_anchor; // the volume's origin in world space: the tex uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM) // A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of // the island currently being dragged in the UV editor (island_active > 0.5). Identity when nothing is -// dragged, so this whole path is a no-op then. Lets a UV island drag move the bump on the model with +// dragged, so this whole path is a no-op then. Lets a UV island drag move the relief on the model with // only a uniform update uniform vec4 island_delta_lin; uniform vec2 island_delta_tr; @@ -377,7 +377,7 @@ void main() bool have_uv = false; if (use_vertex_uv) { - // Precomputed-uv (LSCM) path - Mikkelsen's surface-gradient bump ("Bump Mapping + // Precomputed-uv (LSCM) path - Mikkelsen's surface-gradient normal mapping ("Bump Mapping // Unparametrized Surfaces on the GPU"). The perturbed normal is derived straight from the // screen-space derivatives of the *sampled height* and the position, so it is scale-exact // with no uv->mm assumption at all - which is the whole point here: an LSCM map is conformal, @@ -388,7 +388,7 @@ void main() // use_vertex_uv is a uniform, so this whole branch is uniform control flow and the texture // derivatives are well defined; the paint weight gates the result by a plain multiply (k) // rather than a per-fragment branch, keeping it that way. - // The dragged island's uv rides a uniform affine so its bump moves without a rebuild; every + // The dragged island's uv rides a uniform affine so its relief moves without a rebuild; every // other vertex (island_active == 0) samples its baked uv unchanged. vec2 uv = (island_active > 0.5) ? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr diff --git a/resources/shaders/140/texture_displacement_bump.vs b/resources/shaders/140/texture_displacement_shaded.vs similarity index 97% rename from resources/shaders/140/texture_displacement_bump.vs rename to resources/shaders/140/texture_displacement_shaded.vs index 3cb82284a7..cd629acedc 100644 --- a/resources/shaders/140/texture_displacement_bump.vs +++ b/resources/shaders/140/texture_displacement_shaded.vs @@ -12,7 +12,7 @@ uniform vec4 clipping_plane; in vec3 v_position; // GLModel's P3N3T2 layout (position + normal + texcoord), reused so this mesh builds and renders // like any other GLModel rather than needing a bespoke vertex buffer. The two spare channels carry -// what the bump preview actually needs per vertex: +// what the shaded preview actually needs per vertex: // v_normal.x -- the active layer's paint weight, 0 (untouched) or 1 (painted). // v_normal.y -- 1 for a vertex of the island currently being dragged in the UV editor, else 0. // The fragment shader applies island_delta to those vertices' uv, so a UV drag is a diff --git a/resources/shaders/140/texture_displacement_uvcheck.fs b/resources/shaders/140/texture_displacement_uvcheck.fs index 583346aec0..e6fc8036da 100644 --- a/resources/shaders/140/texture_displacement_uvcheck.fs +++ b/resources/shaders/140/texture_displacement_uvcheck.fs @@ -8,7 +8,7 @@ // where the texture actually lands. // mode 1 - Distortion heatmap: the per-vertex area-distortion carried in `distortion`, blue // (compressed) -> green (ideal) -> red (stretched). -// Both are lit with the same cheap two-light diffuse the bump preview uses, so the surface still +// Both are lit with the same cheap two-light diffuse the shaded preview uses, so the surface still // reads as 3D. #define INTENSITY_CORRECTION 0.6 @@ -31,7 +31,7 @@ uniform vec2 uv_offset; uniform bool use_vertex_uv; // The in-shader projection (0 Triplanar, 1 Cylindrical, 2 Spherical) and the painted patch's frame the // wrapping ones wrap around, in the texture frame - the same uniforms, and the same formulas, as -// texture_displacement_bump.fs, so the checker reports the projection the bake will actually use. +// texture_displacement_shaded.fs, so the checker reports the projection the bake will actually use. uniform int projection_mode; uniform vec3 patch_center; uniform vec3 patch_axis; @@ -103,7 +103,7 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - // World space anchored at the volume's origin, like the bake and the bump preview. + // World space anchored at the volume's origin, like the bake and the shaded preview. vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); vec3 tex_pos = world_pos.xyz - tex_anchor; if (volume_mirrored) diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index f01faa9705..d24e334fa8 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -134,7 +134,7 @@ struct DecodedTextureCache }; DecodedTextureCache g_decoded_texture_cache; -// The smoothed copy is cached too, one per image: the thumbnail, the bump-preview height texture, the +// The smoothed copy is cached too, one per image: the thumbnail, the shaded-preview height texture, the // colour texture, the projector texture, the UV editor's background and the preview job all ask for // the same (image, smoothing) pair in the same frame while the Smoothing slider moves, and each of them // blurring its own copy is what froze the UI. Keyed like the raw cache; a different smoothing value @@ -428,10 +428,10 @@ V2Resolution recommend_v2_resolution(const indexed_triangle_set const std::vector &layers, const Transform3d &volume_to_world) { - // bumpmesh.com's defaults on model load: edge = diagonal / 250 in [0.05, 5] mm, budget 750 k. A - // texture-driven variant (BumpMesh's smart resolution) was measured to give better walls on step - // textures at 2-10x the bake time and up to 2 M output triangles; the user preferred the site's - // defaults. The texel size and sharpness are still reported for the panel. + // The defaults on model load: edge = diagonal / 250 in [0.05, 5] mm, budget 750 k. A texture-driven + // variant (resolution from the texture's own detail) was measured to give better walls on step + // textures at 2-10x the bake time and up to 2 M output triangles, and was not worth that; these + // defaults stayed. The texel size and sharpness are still reported for the panel. constexpr double EDGE_MIN = 0.05, EDGE_MAX = 5.0, DIAG_DIVISOR = 250.0; constexpr int BUDGET_K = 750; @@ -3480,7 +3480,7 @@ indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, // How far the *displaced* surface departs from the flat triangle, sampled across the WHOLE // triangle - the three edge midpoints and the centroid - not just one edge midpoint. Sampling the - // interior is what catches a bump that sits inside a triangle (the blind spot of an edge-only + // interior is what catches a hill that sits inside a triangle (the blind spot of an edge-only // test). Cached per triangle: it can only change when the triangle is split, and then both // children are fresh entries. std::vector tri_err; diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index 238422b5fe..d6b3a41548 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -420,10 +420,9 @@ struct TextureDisplacementOptions } }; -// How much detail a height texture carries, as BumpMesh's smart resolution measures it: central -// differences of the grey image, the mean gradient and the share of texels steeper than 30 grey -// levels, mapped to how many texels one mesh edge may span (1 for a hard-edged image, 4 for a smooth -// one). Cached per image, like the decode. +// How much detail a height texture carries: central differences of the grey image, the mean gradient +// and the share of texels steeper than 30 grey levels, mapped to how many texels one mesh edge may +// span (1 for a hard-edged image, 4 for a smooth one). Cached per image, like the decode. struct TextureDetail { float mean_gradient = 0.f; @@ -438,8 +437,8 @@ struct TextureDetail TextureDetail analyze_texture_detail(const TextureDisplacementLayer &layer); // The default pipeline's automatic resolution and budget, when the options leave them at "auto": -// bumpmesh.com's defaults - edge = the model's world-space diagonal / 250, clamped to [0.05, 5] mm and -// rounded up to 0.01; budget 750 k. The texel size of the finest layer and its sharpness class are +// edge = the model's world-space diagonal / 250, clamped to [0.05, 5] mm and rounded up to 0.01; +// budget 750 k. The texel size of the finest layer and its sharpness class are // reported alongside for the panel. `edge_mm` is 0 for an empty mesh. struct V2Resolution { @@ -946,10 +945,10 @@ indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, fl // departs from the flat triangle by more than `chord_tolerance_mm`, measured as the max // over the three edge midpoints AND the centroid of |sampled displacement - the flat // triangle's barycentric interpolation|. Sampling the interior, not just edge midpoints, -// is what catches a bump that sits inside a triangle. This is a *curvature* test: it is +// is what catches a hill that sits inside a triangle. This is a *curvature* test: it is // exactly zero on a plane or a linear ramp (barycentric interpolation is exact there, so // those stay coarse - the case a gradient criterion would over-refine) and large on a -// bump/ridge/noise. +// hill/ridge/noise. // `min_edge_length_mm` is a hard floor under both: no triangle whose longest edge is already at or // below it is ever refined, which is also what guarantees termination across a sharp texture step // (where the chord error never falls below the tolerance no matter how fine the mesh gets). diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 374dc2884f..47a9752c7b 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -104,8 +104,8 @@ std::pair GLShadersManager::init() valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }, { "FLIP_TRIANGLE_NORMALS"sv }); else valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }); - // Fast bump-map preview for the texture displacement gizmo (see libslic3r/TextureDisplacement.hpp). - valid &= append_shader("texture_displacement_bump", { prefix + "texture_displacement_bump.vs", prefix + "texture_displacement_bump.fs" }); + // Fast shaded preview for the texture displacement gizmo (see libslic3r/TextureDisplacement.hpp). + valid &= append_shader("texture_displacement_shaded", { prefix + "texture_displacement_shaded.vs", prefix + "texture_displacement_shaded.fs" }); // UV-check overlay for the same gizmo: a procedural checker or a distortion heatmap over the // painted patch, to sanity-check the unwrap. valid &= append_shader("texture_displacement_uvcheck", { prefix + "texture_displacement_uvcheck.vs", prefix + "texture_displacement_uvcheck.fs" }); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 604c3786f8..c2a19ba03a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -79,7 +79,7 @@ constexpr int THUMBNAIL_MAX_PX = 128; // Everything above is about drawing a ~48 px panel row, and none of it applies to the height texture // the fast-preview *shader* samples: that one is magnified across the model, not minified into a // row, and every texel it loses is relief the preview cannot show. It gets its own upload at (up to) -// this size, so the bump preview reads the same height field the bake does instead of a 128 px box +// this size, so the shaded preview reads the same height field the bake does instead of a 128 px box // blur of it - which is what made Fast look flatter and softer than the result it was previewing. constexpr int HEIGHT_TEX_MAX_PX = 2048; @@ -224,11 +224,11 @@ constexpr int PALETTE_LUT_EDGE = 24; // Ceiling on the printable palette, which bounds that fill cost (and the shader's uniform array). constexpr int PALETTE_MAX_ENTRIES = 64; -// Ceiling on the filaments the palette's entries can refer to (the bump shader's filament_rgb[]); +// Ceiling on the filaments the palette's entries can refer to (the shaded preview shader's filament_rgb[]); // mmu segmentation stops at Extruder16 anyway. constexpr int PALETTE_MAX_FILAMENTS = 16; -// sRGB (0..1) <-> CIELAB, D65. Exactly what the bump shader's srgb_to_lab() computes, so the CPU +// sRGB (0..1) <-> CIELAB, D65. Exactly what the preview shader's srgb_to_lab() computes, so the CPU // quantizer, the mixed-palette entries and the per-fragment preview all match in the same space. // Not slic3r/Utils/ColorSpaceConvert: its RGB2Lab wants 0..1 but its Lab2RGB hands back linear // values on a 0..100 scale, and the earlier code fed the former 0..255 and divided the latter by 255 - @@ -390,10 +390,10 @@ void GLGizmoTextureDisplacement::on_shutdown() { m_parent.toggle_model_objects_visibility(true); m_preview_glmodel.reset(); - m_bump_preview_glmodel.reset(); + m_shaded_preview_glmodel.reset(); m_paint_overlay_glmodel.reset(); m_paint_overlay_dirty = false; - // Any preview still in flight is superseded: bumping the shared counter makes it abort at its next + // Any preview still in flight is superseded: raising the shared counter makes it abort at its next // progress poll, and its completion handler then finds nothing to do. m_preview_generation->fetch_add(1); m_preview_job_pending = false; @@ -410,9 +410,9 @@ void GLGizmoTextureDisplacement::on_shutdown() m_subdivide_editing = false; m_subdivide_preview_tris = -1; m_subdivide_preview_glmodel.reset(); - m_bump_active_chart = -1; - m_bump_active_face.clear(); - m_bump_island_delta = Eigen::Matrix::Identity(); + m_shaded_active_chart = -1; + m_shaded_active_face.clear(); + m_shaded_island_delta = Eigen::Matrix::Identity(); m_island_drag_active = false; m_island_move_set.clear(); m_adjust_texture_mode = false; @@ -461,39 +461,38 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() // so it wins the depth test on the coincident (unpainted) surface - keeping the familiar // enforcer/blocker highlight for precise brush editing there. Where the surface has actually // been displaced, the raised preview geometry legitimately occludes the flat overlay - that - // visible bump is itself the "this is painted" indicator in that area. + // visible relief is itself the "this is painted" indicator in that area. // - // The bump preview is different: it never actually moves geometry (it's a shading trick), so + // The shaded preview is different: it never actually moves geometry (it only shades), so // its depth is identical to the overlay's *everywhere*, not just in the unpainted area - the - // depth-biased opaque overlay would win the depth test across the whole surface and hide the bump + // depth-biased opaque overlay would win the depth test across the whole surface and hide the relief // shading entirely. So render_triangles() is skipped for it. What is *not* skipped is - // render_paint_overlay(): leaving the bump shading as the only paint feedback meant a stroke that + // render_paint_overlay(): leaving the shading as the only paint feedback meant a stroke that // erased paint, or added it with no texture picked, changed nothing on screen until the whole // preview rebuilt at stroke end - and in the true-displacement view the opaque overlay is hidden // by the raised surface for the same reason. The translucent tint covers both cases. - // Coalesced bump rebuild from an in-progress UV island drag (see on_island_edited): done here, at + // Coalesced shaded-preview rebuild from an in-progress UV island drag (see on_island_edited): done here, at // most once per drawn frame, rather than synchronously in the UV canvas's mouse-move handler. - if (m_use_bump_preview && m_bump_preview_dirty) { - rebuild_bump_preview_mesh(); - m_bump_preview_dirty = false; + if (m_use_shaded_preview && m_shaded_preview_dirty) { + rebuild_shaded_preview_mesh(); + m_shaded_preview_dirty = false; } // Same coalescing for the paint tint, but on its own flag: a stroke marks this every mouse move - // (see on_mouse()) and it only costs the painted patch, whereas the bump mesh also carries every - // unpainted triangle of the volume and stays on the stroke-end cadence. + // (see on_mouse()) and it only costs the painted patch if (m_paint_overlay_dirty) { rebuild_paint_overlay(); m_paint_overlay_dirty = false; } rebuild_other_paint_overlay(); // a no-op unless another layer's paint, the active layer or the preview changed - // is_initialized() alone is not enough: render_bump_preview_mesh() also needs an active layer + // is_initialized() alone is not enough: render_shaded_preview_mesh() also needs an active layer // with a decoded texture and a compiled shader, and bails silently without them. Hiding the real - // volume for a bump pass that then draws nothing is what made the model vanish - most obviously + // volume for a shaded pass that then draws nothing is what made the model vanish - most obviously // with zero layers, but equally with a layer that has no texture picked yet. - const bool use_bump = m_use_bump_preview && m_bump_preview_glmodel.is_initialized() && bump_preview_ready(); - const bool use_true_preview = !use_bump && m_preview_glmodel.is_initialized(); + const bool use_shaded = m_use_shaded_preview && m_shaded_preview_glmodel.is_initialized() && shaded_preview_ready(); + const bool use_true_preview = !use_shaded && m_preview_glmodel.is_initialized(); // In Checker/Distortion mode the UV-check overlay *is* the surface visualization the user is // looking at, so the opaque paint-selection highlight must not be drawn on top of it - same - // reasoning as skipping it for the bump preview (see bug #12). Without this the painted area + // reasoning as skipping it for the shaded preview (see bug #12). Without this the painted area // covers the checker/heatmap and it can't be seen. const bool show_paint_overlay = m_uv_check_mode == UVCheckMode::None; @@ -501,14 +500,14 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() // put it back. Getting this wrong leaves an invisible model, so it is decided once, here, rather // than per branch below. m_parent.toggle_model_objects_visibility(true); - if (use_bump || use_true_preview) { + if (use_shaded || use_true_preview) { if (ModelVolume *mv = texture_volume()) m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), m_c->selection_info()->get_active_instance(), mv); } - if (use_bump) { - render_bump_preview_mesh(); + if (use_shaded) { + render_shaded_preview_mesh(); } else if (use_true_preview) { render_preview_mesh(); @@ -527,11 +526,11 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() if (show_paint_overlay) render_paint_overlay(m_other_paint_glmodel); - // The translucent paint tint. Needed in the bump view because the opaque highlight above is + // The translucent paint tint. Needed in the shaded view because the opaque highlight above is // skipped there, and in the true-displacement view because the displaced surface rises *above* // the undisplaced overlay geometry and hides it exactly where the relief is strongest - in both // cases leaving an erase stroke with no visible effect until the next full preview rebuild. - if (show_paint_overlay && (use_bump || use_true_preview)) + if (show_paint_overlay && (use_shaded || use_true_preview)) render_paint_overlay(m_paint_overlay_glmodel); // The UV editor's island selection, shown on the model. Polled here rather than pushed: the pane @@ -1123,9 +1122,9 @@ std::vector GLGizmoTextureDisplacement::compute_layer_corner_uvs(const in return corner; } -void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() +void GLGizmoTextureDisplacement::rebuild_shaded_preview_mesh() { - m_bump_preview_glmodel.reset(); + m_shaded_preview_glmodel.reset(); const ModelVolume *mv = texture_volume(); if (mv == nullptr || m_triangle_selectors.empty()) @@ -1141,13 +1140,13 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() // No "patch.vertices.size() == mesh vertex count" check here, and that is the point: a *brush* // stroke splits triangles, so the selector appends split vertices and the patch array is longer // than the mesh's. An earlier version bailed out on that as "shouldn't happen", which meant the - // bump model was never built while brushing and render_painter_gizmo() silently fell back to the + // shaded model was never built while brushing and render_painter_gizmo() silently fell back to the // Normal (true-displacement) preview - Fast looked broken for brush and fine for Face/Connected // area, because only the brush splits. Everything below indexes the patch's own vertex array, so // the extra vertices are simply carried through. // Unpainted triangles, so the surrounding surface still renders (the render path hides the real - // model in bump mode). get_facets_strict() returns the same vertex array whatever state is asked. + // model in shaded mode). get_facets_strict() returns the same vertex array whatever state is asked. const indexed_triangle_set rest = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::NONE); // For LSCM we hand the shader the finished per-vertex texture uv (island placement + tiling/ @@ -1159,22 +1158,22 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() // carry its own chart's UVs - see compute_layer_corner_uvs(). const TextureDisplacementLayer *active = active_layer(); std::vector corner_uv = active != nullptr ? compute_layer_corner_uvs(patch, *active) : std::vector{}; - m_bump_preview_uses_vertex_uv = corner_uv.size() == patch.indices.size() * 3; - if (!m_bump_preview_uses_vertex_uv) + m_shaded_preview_uses_vertex_uv = corner_uv.size() == patch.indices.size() * 3; + if (!m_shaded_preview_uses_vertex_uv) corner_uv.clear(); - m_bump_projection_mode = (active != nullptr && !m_bump_preview_uses_vertex_uv) ? - layer_projection_frame(patch, *active, m_bump_patch_center, m_bump_patch_axis) : 0; + m_shaded_projection_mode = (active != nullptr && !m_shaded_preview_uses_vertex_uv) ? + layer_projection_frame(patch, *active, m_shaded_patch_center, m_shaded_patch_axis) : 0; // Which triangles the in-flight UV drag moves. Computed here, against the very patch this mesh is // built from, so the flags can never be indexed by a different triangle count than they were sized // for (the drag starts from the flushed facet data, a brush stroke changes the live selector). - compute_bump_active_faces(m_bump_active_chart >= 0 ? m_island_move_set : std::vector{}, patch.indices.size()); + compute_shaded_active_faces(m_shaded_active_chart >= 0 ? m_island_move_set : std::vector{}, patch.indices.size()); // Colour is quantized per *fragment* in the shader now (see the .fs), so this mesh carries no // colour of its own - the palette and the colour texture are uniforms, and every pixel matches the // image rather than the facet it landed on. What the *bake* will produce, at facet resolution, is // what the Normal view shows. - m_bump_preview_palette = (active != nullptr && active->color_enabled) ? cached_palette() + m_shaded_preview_palette = (active != nullptr && active->color_enabled) ? cached_palette() : std::vector{}; GLModel::Geometry init_data; @@ -1186,7 +1185,7 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() // painted face of a raw cube has no strictly-interior vertex (all 8 are shared), so per-vertex // weighting would either bleed onto the neighbours (boundary weight 1) or vanish outright (boundary // weight 0, which is what made a single face show nothing). Duplicating vertices costs no shading - // quality here because the bump shader takes its surface normal from screen-space derivatives of + // quality here because the preview shader takes its surface normal from screen-space derivatives of // position (dFdx/dFdy), not from a per-vertex normal. normal.y flags the UV-editor island being // dragged so the shader can move just that island via the island_delta uniform. const size_t tri_total = patch.indices.size() + rest.indices.size(); @@ -1198,10 +1197,10 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() const stl_triangle_vertex_indices &tri = its.indices[f]; // One value for the whole triangle: island_active is an interpolated varying, so the three // corners have to agree or the shader moves part of a triangle and not the rest. - const float act = (painted && f < m_bump_active_face.size() && m_bump_active_face[f]) ? 1.f : 0.f; + const float act = (painted && f < m_shaded_active_face.size() && m_shaded_active_face[f]) ? 1.f : 0.f; for (int i = 0; i < 3; ++i) { const int idx = tri[i]; - const Vec2f uv = (painted && m_bump_preview_uses_vertex_uv) ? corner_uv[f * 3 + size_t(i)] + const Vec2f uv = (painted && m_shaded_preview_uses_vertex_uv) ? corner_uv[f * 3 + size_t(i)] : Vec2f::Zero(); init_data.add_vertex(its.vertices[size_t(idx)], Vec3f(weight, act, 0.f), uv); } @@ -1209,50 +1208,50 @@ void GLGizmoTextureDisplacement::rebuild_bump_preview_mesh() vcount += 3; } }; - emit_triangles(patch, 1.f, true); // painted -> bumped, and coloured by the shader - // Untouched surface: flat, so it still shows but isn't bumped - and uncoloured, which is what the + emit_triangles(patch, 1.f, true); // painted -> shaded as relief, and coloured by the shader + // Untouched surface: flat, so it still shows but carries no relief - and uncoloured, which is what the // bake leaves it as (EnforcerBlockerType::NONE, i.e. the volume's own filament). emit_triangles(rest, 0.f, false); - m_bump_preview_glmodel.init_from(std::move(init_data)); + m_shaded_preview_glmodel.init_from(std::move(init_data)); // GLModel::render() unconditionally re-sets the shader's "uniform_color" from this internal // color field right before drawing (see GLModel.cpp) - setting the uniform manually in - // render_bump_preview_mesh() would just get overwritten by it, so it must be set here instead. + // render_shaded_preview_mesh() would just get overwritten by it, so it must be set here instead. // GLModel::Geometry defaults to BLACK, which is exactly what showed up before this was added. - m_bump_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); + m_shaded_preview_glmodel.set_color(GLVolume::NEUTRAL_COLOR); // The mesh now reflects the islands' current placement, so any live drag delta is measured from // here: reset it to identity and record the dragged island's baked transform. - m_bump_island_delta = Eigen::Matrix::Identity(); + m_shaded_island_delta = Eigen::Matrix::Identity(); const TextureDisplacementLayer *al = active_layer(); - if (m_bump_active_chart >= 0 && al != nullptr) { + if (m_shaded_active_chart >= 0 && al != nullptr) { const std::vector> xf = uv_editor_island_transforms(*al); - m_bump_baked_active_xf = (size_t(m_bump_active_chart) < xf.size()) ? xf[size_t(m_bump_active_chart)] + m_shaded_baked_active_xf = (size_t(m_shaded_active_chart) < xf.size()) ? xf[size_t(m_shaded_active_chart)] : Eigen::Matrix::Identity(); } else { - m_bump_baked_active_xf = Eigen::Matrix::Identity(); + m_shaded_baked_active_xf = Eigen::Matrix::Identity(); } } -void GLGizmoTextureDisplacement::compute_bump_active_faces(const std::vector &charts, size_t patch_face_count) +void GLGizmoTextureDisplacement::compute_shaded_active_faces(const std::vector &charts, size_t patch_face_count) { - m_bump_active_face.clear(); + m_shaded_active_face.clear(); if (charts.empty() || patch_face_count == 0) return; const PatchUnwrap &u = m_uv_editor_unwrap; if (u.source_face.size() != u.indices.size()) return; - m_bump_active_face.assign(patch_face_count, 0); + m_shaded_active_face.assign(patch_face_count, 0); // Flag every triangle of every chart being moved. For a group/multi move that is more than one // chart, but since such a move is a pure translation the shader applies the same delta to them all // (see on_island_edited) - exactly the "joined islands move together" behaviour. for (size_t t = 0; t < u.indices.size(); ++t) { const int f = u.source_face[t]; const int v0 = u.indices[t][0]; // a triangle lies in one chart, so any corner names it - if (f < 0 || size_t(f) >= m_bump_active_face.size() || v0 < 0 || size_t(v0) >= u.vertex_chart.size()) + if (f < 0 || size_t(f) >= m_shaded_active_face.size() || v0 < 0 || size_t(v0) >= u.vertex_chart.size()) continue; if (std::find(charts.begin(), charts.end(), u.vertex_chart[size_t(v0)]) != charts.end()) - m_bump_active_face[size_t(f)] = 1; + m_shaded_active_face[size_t(f)] = 1; } } @@ -1313,11 +1312,11 @@ std::vector GLGizmoTextureDisplacement::build_island_move_set(const Texture return set; } -void GLGizmoTextureDisplacement::render_bump_preview_mesh() +void GLGizmoTextureDisplacement::render_shaded_preview_mesh() { const ModelObject *mo = m_c->selection_info()->model_object(); const ModelVolume *mv = texture_volume(); - if (mo == nullptr || mv == nullptr || !m_bump_preview_glmodel.is_initialized()) + if (mo == nullptr || mv == nullptr || !m_shaded_preview_glmodel.is_initialized()) return; const TextureDisplacementLayer *layer = active_layer(); @@ -1334,7 +1333,7 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0) return; - GLShaderProgram *shader = wxGetApp().get_shader("texture_displacement_bump"); + GLShaderProgram *shader = wxGetApp().get_shader("texture_displacement_shaded"); if (shader == nullptr) return; @@ -1401,14 +1400,14 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() shader->set_uniform("tex_anchor", Vec3f(tex_anchor.cast())); shader->set_uniform("eye_model_pos", Vec3f((camera.get_position() - tex_anchor).cast())); // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than - // projecting; see rebuild_bump_preview_mesh(). - shader->set_uniform("use_vertex_uv", m_bump_preview_uses_vertex_uv); + // projecting; see rebuild_shaded_preview_mesh(). + shader->set_uniform("use_vertex_uv", m_shaded_preview_uses_vertex_uv); // Cylindrical/Spherical wrap around the painted patch's own centre, which no fragment can derive: - // captured with the mesh (see rebuild_bump_preview_mesh()) and handed over here. 0 is the planar + // captured with the mesh (see rebuild_shaded_preview_mesh()) and handed over here. 0 is the planar // projection every other in-shader path uses. - shader->set_uniform("projection_mode", m_bump_projection_mode); - shader->set_uniform("patch_center", m_bump_patch_center); - shader->set_uniform("patch_axis", m_bump_patch_axis); + shader->set_uniform("projection_mode", m_shaded_projection_mode); + shader->set_uniform("patch_center", m_shaded_patch_center); + shader->set_uniform("patch_axis", m_shaded_patch_axis); // The filament palette the mesh's per-triangle indices refer to. Count 0 means "no layer is // colouring", and the shader keeps the model's own colour for every fragment. @@ -1416,13 +1415,13 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() // matched on the CPU because the quantization is per fragment here. const GLTexture *color_tex = get_layer_color_texture(*layer); const int palette_count = - (color_tex != nullptr) ? int(std::min(m_bump_preview_palette.size(), size_t(PALETTE_MAX_ENTRIES))) : 0; + (color_tex != nullptr) ? int(std::min(m_shaded_preview_palette.size(), size_t(PALETTE_MAX_ENTRIES))) : 0; shader->set_uniform("palette_count", palette_count); shader->set_uniform("has_color_tex", color_tex != nullptr); // A flat-colour image is matched against single filaments only, as the bake does. shader->set_uniform("pure_only", color_tex != nullptr && analyze_texture_detail(*layer).flat_colors); for (int i = 0; i < palette_count; ++i) { - const PaletteEntry &e = m_bump_preview_palette[size_t(i)]; + const PaletteEntry &e = m_shaded_preview_palette[size_t(i)]; const std::string idx = "[" + std::to_string(i) + "]"; shader->set_uniform(("palette_rgb" + idx).c_str(), e.rgb); shader->set_uniform(("palette_lab" + idx).c_str(), srgb_to_lab(e.rgb)); @@ -1434,7 +1433,7 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() } // The filaments those indices refer to, and the interleave the shader resolves a mix with - the // same inputs make_mix_resolver() gets, so the preview shows the pattern that prints rather than - // the mix's smooth average colour. m_palette_filaments is what m_bump_preview_palette was built from. + // the mix's smooth average colour. m_palette_filaments is what m_shaded_preview_palette was built from. const int filament_count = (palette_count > 0) ? int(std::min(m_palette_filaments.size(), size_t(PALETTE_MAX_FILAMENTS))) : 0; shader->set_uniform("filament_count", filament_count); @@ -1453,18 +1452,18 @@ void GLGizmoTextureDisplacement::render_bump_preview_mesh() } // The live UV-editor island drag rides this 2x3 affine (identity except mid-drag); only the flagged // island's vertices apply it, so a drag is a uniform update rather than a mesh rebuild. - const Eigen::Matrix &d = m_bump_island_delta; + const Eigen::Matrix &d = m_shaded_island_delta; shader->set_uniform("island_delta_lin", std::array{ d(0, 0), d(0, 1), d(1, 0), d(1, 1) }); shader->set_uniform("island_delta_tr", Vec2f(d(0, 2), d(1, 2))); - m_bump_preview_glmodel.render(); + m_shaded_preview_glmodel.render(); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); shader->stop_using(); } -bool GLGizmoTextureDisplacement::bump_preview_ready() const +bool GLGizmoTextureDisplacement::shaded_preview_ready() const { - // Mirrors render_bump_preview_mesh()'s own preconditions. Kept as a separate query because the - // caller has to know whether the bump pass will draw *before* it hides the real volume for it. + // Mirrors render_shaded_preview_mesh()'s own preconditions. Kept as a separate query because the + // caller has to know whether the shaded pass will draw *before* it hides the real volume for it. if (m_c->selection_info() == nullptr || m_c->selection_info()->model_object() == nullptr) return false; if (texture_volume() == nullptr) @@ -1472,12 +1471,12 @@ bool GLGizmoTextureDisplacement::bump_preview_ready() const const TextureDisplacementLayer *layer = active_layer(); if (layer == nullptr || layer->empty()) return false; - // The same texture render_bump_preview_mesh() will bind, not the panel thumbnail - the two are + // The same texture render_shaded_preview_mesh() will bind, not the panel thumbnail - the two are // separate caches and either can fail on its own. const GLTexture *tex = const_cast(this)->get_layer_height_texture(*layer); if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0) return false; - return wxGetApp().get_shader("texture_displacement_bump") != nullptr; + return wxGetApp().get_shader("texture_displacement_shaded") != nullptr; } // Appends a painted patch to an overlay, lifted onto the displaced surface where that has the base mesh's @@ -1502,7 +1501,7 @@ void GLGizmoTextureDisplacement::rebuild_other_paint_overlay() // displaced positions it is lifted onto. Compared every frame, rebuilt only when it differs. std::string key; if (mv != nullptr) { - key = std::to_string(mv->id().id) + ":" + std::to_string(m_active_layer_slot) + (m_use_bump_preview ? ":b:" : ":t:") + + key = std::to_string(mv->id().id) + ":" + std::to_string(m_active_layer_slot) + (m_use_shaded_preview ? ":b:" : ":t:") + std::to_string(reinterpret_cast(m_preview_its.vertices.data())) + ":" + std::to_string(m_preview_its.vertices.size()); for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) @@ -1517,7 +1516,7 @@ void GLGizmoTextureDisplacement::rebuild_other_paint_overlay() return; const std::vector *displaced = nullptr; - if (!m_use_bump_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() && + if (!m_use_shaded_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() && !m_preview_its.vertices.empty()) displaced = &m_preview_its.vertices; @@ -1559,7 +1558,7 @@ void GLGizmoTextureDisplacement::rebuild_paint_overlay() // the brush split live past the end of that array and keep their flat position; they sit on the // patch boundary, where the displacement is smallest anyway. const std::vector *displaced = nullptr; - if (!m_use_bump_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() && + if (!m_use_shaded_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() && !m_preview_its.vertices.empty()) displaced = &m_preview_its.vertices; @@ -1592,7 +1591,7 @@ void GLGizmoTextureDisplacement::render_paint_overlay(GLModel &overlay) shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); // Translucent, and pulled toward the camera so it wins the depth test against the coincident - // bump surface. Depth writes are off: this is a tint, and letting it own the depth buffer would + // shaded surface. Depth writes are off: this is a tint, and letting it own the depth buffer would // make the wireframe and seam overlays drawn after it fight with geometry that is not really // there. Blending is already enabled by render_painter_gizmo(). glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); @@ -1703,7 +1702,7 @@ void GLGizmoTextureDisplacement::rebuild_uvcheck_mesh() m_uvcheck_uses_vertex_uv = have_uvs; m_uvcheck_projection_mode = have_uvs ? 0 : layer_projection_frame(patch, *layer, m_uvcheck_patch_center, m_uvcheck_patch_axis); - // Per corner as well, for the same reason the bump mesh takes them: under LSCM a seam vertex has a + // Per corner as well: under LSCM a seam vertex has a // different uv in each island it borders, so the shared-vertex form drew one triangle per face from // a neighbouring island's placement. Only the *drawing* needs this; the distortion metric below is // a per-vertex average by construction and keeps using `uv`. @@ -1797,7 +1796,7 @@ void GLGizmoTextureDisplacement::render_uvcheck_mesh() const Matrix3d view_normal_matrix = camera.get_view_matrix().matrix().block(0, 0, 3, 3); shader->set_uniform("view_normal_matrix", view_normal_matrix); shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0); - shader->set_uniform("tex_anchor", Vec3f(trafo_matrix.translation().cast())); // see the bump shader + shader->set_uniform("tex_anchor", Vec3f(trafo_matrix.translation().cast())); // see the preview shader shader->set_uniform("mode", m_uv_check_mode == UVCheckMode::Distortion ? 1 : 0); shader->set_uniform("checker_freq", 4.f); // squares per texture tile shader->set_uniform("tiling_scale", layer->tiling_scale); @@ -1855,7 +1854,7 @@ void GLGizmoTextureDisplacement::rebuild_wireframe_overlay() if (its.indices.empty()) return; - // Building from the base mesh (bump/paint mode); its topology only changes on bake/subdivide, and + // Building from the base mesh (shaded/paint mode); its topology only changes on bake/subdivide, and // this runs on every rebuild_preview(), so rebuild only when the vertex count actually changes. if (m_wireframe_overlay_glmodel.is_initialized() && m_wireframe_overlay_vcount == its.vertices.size()) return; @@ -1871,9 +1870,9 @@ void GLGizmoTextureDisplacement::refresh_wireframe() } // The wireframe has to sit on whatever mesh is actually on screen. In the true-displacement view // that is the raised preview geometry (m_preview_its) - drawing the flat base mesh's edges there - // leaves them buried inside the bumps, which is why the wireframe "didn't show in real mode". In - // Fast (bump) mode or with nothing painted, the surface is the undisplaced base mesh. - if (!m_use_bump_preview && !m_preview_its.indices.empty()) + // leaves them buried inside the relief, which is why the wireframe "didn't show in real mode". In + // Fast (shaded) mode or with nothing painted, the surface is the undisplaced base mesh. + if (!m_use_shaded_preview && !m_preview_its.indices.empty()) build_wireframe_from_its(m_preview_its); else rebuild_wireframe_overlay(); @@ -1907,13 +1906,13 @@ void GLGizmoTextureDisplacement::render_wireframe_overlay() void GLGizmoTextureDisplacement::rebuild_preview() { - // Bumped first: any in-flight job's result (captured generation from before this call) will + // Raised first: any in-flight job's result (captured generation from before this call) will // now compare unequal to m_preview_generation and be discarded when it completes, even if it // finishes after the job queued below - and, since the counter is shared with the worker, that // job also notices mid-run and aborts rather than computing a result nobody will use. m_preview_generation->fetch_add(1); update_uv_editor(); - rebuild_bump_preview_mesh(); + rebuild_shaded_preview_mesh(); rebuild_paint_overlay(); rebuild_uvcheck_mesh(); rebuild_seam_overlay(); @@ -1939,7 +1938,7 @@ void GLGizmoTextureDisplacement::rebuild_preview() } // In Fast/paint modes the wireframe follows the base mesh and can be built now; the true-displacement // view's wireframe needs the displaced mesh, which only exists once the job below completes. - if (m_use_bump_preview) { + if (m_use_shaded_preview) { refresh_wireframe(); // Fast view: the shader *is* the preview, and m_preview_glmodel is never drawn. Running the // full CPU displacement anyway - which is what happened on every stroke and slider release - @@ -2040,7 +2039,7 @@ void GLGizmoTextureDisplacement::queue_preview_job() } if (m_preview_job_pending) { m_preview_job_pending = false; - if (!m_use_bump_preview) + if (!m_use_shaded_preview) queue_preview_job(); // no-ops if the gizmo has closed in the meantime } m_parent.set_as_dirty(); @@ -2310,12 +2309,12 @@ void GLGizmoTextureDisplacement::process_uv_commands() void GLGizmoTextureDisplacement::apply_view_mode(int mode) { - m_use_bump_preview = (mode == 1); + m_use_shaded_preview = (mode == 1); m_uv_check_mode = (mode == 2) ? UVCheckMode::Checker : (mode == 3) ? UVCheckMode::Distortion : UVCheckMode::None; rebuild_uvcheck_mesh(); - if (m_use_bump_preview) - rebuild_bump_preview_mesh(); + if (m_use_shaded_preview) + rebuild_shaded_preview_mesh(); else // The Fast view skips the CPU displacement entirely (see rebuild_preview()), so leaving it means // m_preview_glmodel may be stale or absent - ask for it now. @@ -2426,7 +2425,7 @@ void GLGizmoTextureDisplacement::run_uv_command(int cmd, float value) if (cmd == int(Command::SetBackground)) { // Height goes back to whichever of Normal / Fast was showing; Checker and Distortion are views of their own. const int background = std::clamp(int(std::lround(value)), 0, 2); - apply_view_mode(background == 1 ? 2 : background == 2 ? 3 : (m_use_bump_preview ? 1 : 0)); + apply_view_mode(background == 1 ? 2 : background == 2 ? 3 : (m_use_shaded_preview ? 1 : 0)); return; } @@ -2838,10 +2837,10 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse m_island_move_set = is_move ? build_island_move_set(*layer, island) : std::vector{ island }; // Set up the GPU drag: bake the mesh once (via the dirty flag), which is also what flags the // moved islands' triangles. From then on the drag is a uniform update, no rebuild - see - // render_bump_preview_mesh(). - m_bump_active_chart = island; - m_bump_island_delta = Eigen::Matrix::Identity(); - m_bump_preview_dirty = true; + // render_shaded_preview_mesh(). + m_shaded_active_chart = island; + m_shaded_island_delta = Eigen::Matrix::Identity(); + m_shaded_preview_dirty = true; } // Apply the edit. A move goes to every island in the moved set (same offset -> they translate as @@ -2865,10 +2864,10 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse if (finished) { m_island_drag_active = false; - m_bump_active_chart = -1; - m_bump_active_face.clear(); + m_shaded_active_chart = -1; + m_shaded_active_face.clear(); m_island_move_set.clear(); - m_bump_island_delta = Eigen::Matrix::Identity(); + m_shaded_island_delta = Eigen::Matrix::Identity(); rebuild_preview(); // the real displaced geometry moved: recompute it once, at the end } else { const std::vector> xf = uv_editor_island_transforms(*layer); @@ -2879,15 +2878,15 @@ void GLGizmoTextureDisplacement::on_island_edited(int island, const Vec2f &offse uv_canvas->set_island_transforms(xf); } // Move the island on the model live through the shader's island_delta uniform - no mesh - // rebuild. delta = F_current * F_baked^-1 in final-uv space (the bump mesh bakes F_baked; the - // shader applies delta to the flagged island's uv). The one rebuild that bakes the flags is + // rebuild. delta = F_current * F_baked^-1 in final-uv space. + // The one rebuild that bakes the flags is // scheduled at drag start above and consumed once per frame by render_painter_gizmo(). - if (m_use_bump_preview && m_bump_active_chart == island && size_t(island) < xf.size()) { + if (m_use_shaded_preview && m_shaded_active_chart == island && size_t(island) < xf.size()) { Eigen::Matrix3f cur = Eigen::Matrix3f::Identity(); cur.topRows<2>() = xf[size_t(island)]; Eigen::Matrix3f bak = Eigen::Matrix3f::Identity(); - bak.topRows<2>() = m_bump_baked_active_xf; - m_bump_island_delta = (cur * bak.inverse()).topRows<2>(); + bak.topRows<2>() = m_shaded_baked_active_xf; + m_shaded_island_delta = (cur * bak.inverse()).topRows<2>(); m_parent.set_as_dirty(); } } @@ -3260,12 +3259,12 @@ void GLGizmoTextureDisplacement::update_model_object() updated |= facet.set(*m_triangle_selectors[idx]); } - // The fast (bump) preview reads the live selector, so it has to be rebuilt after any stroke that + // The fast (shaded) preview reads the live selector, so it has to be rebuilt after any stroke that // flushes here - not only when set() reports a change. Rebuilding it via rebuild_preview() below // is gated on `updated`, which misses e.g. the first paint into a slot; marking it dirty makes the // render loop (render_painter_gizmo) rebuild it next frame regardless. Without this, fast preview - // now the default view - stayed blank until a full reload (select-whole-model / reopen). - m_bump_preview_dirty = true; + m_shaded_preview_dirty = true; if (updated) { const ModelObjectPtrs &mos = wxGetApp().model().objects; @@ -3337,7 +3336,7 @@ void GLGizmoTextureDisplacement::set_active_layer(int slot) // instead of leaving it pointing at the previous layer's (now stale) paint patch. if (m_adjust_texture_mode) update_adjust_anchor(); - // Refresh every preview/overlay (bump, UV editor, seams, ...) for the newly active layer. + // Refresh every preview/overlay (shaded, UV editor, seams, ...) for the newly active layer. rebuild_preview(); } @@ -4166,7 +4165,7 @@ TextureColorSettings GLGizmoTextureDisplacement::color_settings_for(const ModelV const std::vector &GLGizmoTextureDisplacement::cached_palette() { - // Rebuilt only when the loaded filaments or the mixing setting actually change. The bump preview + // Rebuilt only when the loaded filaments or the mixing setting actually change. The shaded preview // rebuilds on every paint stroke and the subdivide preview on every slider frame, and filling the // quantizer's lookup cube for a 64-entry palette is tens of milliseconds - paying that per stroke // is the difference between painting that keeps up and painting that stutters. @@ -4795,7 +4794,7 @@ GLTexture *GLGizmoTextureDisplacement::get_layer_thumbnail(const TextureDisplace // Reuses the already-decoded, already-cached grayscale pixels (see decode_height_texture()'s // own cache in TextureDisplacement.cpp) - only the gray-to-RGBA expansion and GPU upload below are - // new work. Rebuilt when the texture *or the smoothing* changes, so the fast/bump preview - which + // new work. Rebuilt when the texture *or the smoothing* changes, so the fast/shaded preview - which // samples this GPU texture directly - reflects the current smoothing rather than the raw image. std::unique_ptr texture = upload_height_thumbnail(decode_height_texture(layer)); if (!texture) @@ -4831,7 +4830,7 @@ GLTexture *GLGizmoTextureDisplacement::get_layer_height_texture(const TextureDis if (layer.empty()) return nullptr; - // One slot, not one per layer: the bump shader only ever shades the *active* layer, so a single + // One slot, not one per layer: the preview shader only ever shades the *active* layer, so a single // full-resolution upload is enough and the VRAM cost stays at one texture rather than eight. if (m_height_tex && m_height_tex_source == layer.image_data.get() && m_height_tex_smoothing == layer.smoothing) return m_height_tex.get(); @@ -5033,7 +5032,7 @@ void GLGizmoTextureDisplacement::show_debug_stage(int index) m_debug_stage = index; // Drawn through the ordinary true-displacement preview, so the Fast view - a shader trick over // the base mesh that never draws m_preview_glmodel - has to be left first. - m_use_bump_preview = false; + m_use_shaded_preview = false; m_preview_color_runs.clear(); m_preview_glmodel.reset(); @@ -5667,9 +5666,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } // ---- View: Normal / Fast / Checker / Distortion as one group, Wireframe on its own ---- - // The underlying state stays m_use_bump_preview + m_uv_check_mode. + // The underlying state stays m_use_shaded_preview + m_uv_check_mode. { - const int cur_mode = m_use_bump_preview ? 1 : + const int cur_mode = m_use_shaded_preview ? 1 : m_uv_check_mode == UVCheckMode::Checker ? 2 : m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0; int new_mode = cur_mode; @@ -5692,7 +5691,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float new_mode = 0; ImGui::SameLine(0.f, gap_s); if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, icon_md, _L("Fast"), - _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) + _L("Fast - a shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1; ImGui::SameLine(0.f, gap_s); if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, icon_md, _L("Checker"), @@ -6443,7 +6442,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (m_subdivide_feature) { if (float_row("##subdiv_detail", _L("Detail"), &m_subdivide_detail_mm, 0.001f, 1.f, "%.3f mm", true, 0.f)) preview_live(); - hover_tip(_u8L("How closely the mesh follows the texture's relief. Smaller captures finer bumps; " + hover_tip(_u8L("How closely the mesh follows the texture's relief. Smaller captures finer detail; " "larger only chases the big features.")); if (float_row("##subdiv_min", _L("Min edge"), &m_subdivide_min_edge_mm, 0.001f, 20.f, "%.3f mm", true, 0.f)) preview_live(); @@ -6528,7 +6527,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float _u8L("Refines the painted area to the target edge length and carries your paint onto " "the finer mesh. The rest of the model is left as it is.") : _u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked " - "paint on it (already-baked bumps are unaffected).")); + "paint on it (already-baked relief is unaffected).")); } // Remesh: even out uneven triangle sizes (CGAL isotropic remeshing). @@ -6569,7 +6568,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float hover_tip(_u8L("Rebuilds the whole model with triangles close to this edge length - splitting the big " "ones and merging the small ones - so displacement has an even density to work with. " "Replaces the geometry; your paint is carried onto the new triangles spatially, so it " - "survives (already-baked bumps are kept too).")); + "survives (already-baked relief is kept too).")); // Settings for the whole stack rather than one layer. Standard mode pins them instead of showing them. if (mv != nullptr) { @@ -6751,7 +6750,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); hover_tip(_u8L("Auto: the resolution follows the model's size and the budget is the standard " - "750 k, the same defaults as bumpmesh.com. Untick to set them by hand.")); + "750 k. Untick to set them by hand.")); ImGui::SameLine(); ImGui::SetNextItemWidth(x0 + panel_w - ImGui::GetCursorPosX()); float shown = auto_res ? rec.edge_mm : opts.v2_refine_mm; @@ -6764,7 +6763,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); if (auto_res && rec.edge_mm > 0.f) - hover_tip(Slic3r::format(_u8L("The model's diagonal / 250, as bumpmesh.com sets it. Budget %1% k."), rec.budget_k)); + hover_tip(Slic3r::format(_u8L("The model's diagonal / 250. Budget %1% k."), rec.budget_k)); else hover_tip(_u8L("Triangle edge length the painted area is refined to before displacement. " "Smaller carries finer texture detail and costs more triangles; the budget " diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 7ec7428c20..804c31821e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -303,7 +303,7 @@ private: GLTexture *get_layer_thumbnail(const TextureDisplacementLayer &layer); // The same texture at full resolution, for the fast-preview shader. One slot, shared by whichever - // layer is active, because that is the only one the bump shader ever shades. + // layer is active, because that is the only one the preview shader ever shades. GLTexture *get_layer_height_texture(const TextureDisplacementLayer &layer); // The layer's colour texture for the fast preview's per-fragment quantization. Null when the // layer is not colouring or its texture is grayscale. @@ -389,13 +389,13 @@ private: void render_preview_mesh(); // Alternate, GPU-only preview: perturbs shading normals from the active layer's height texture - // (a classic bump map) instead of actually moving vertices, using the - // resources/shaders/*/texture_displacement_bump.* shader. Faster than the true-displacement - // preview (no CPU meshing at all - just a per-vertex paint-weight buffer built at the same - // cadence as rebuild_preview()) but only shows the *active* layer, and any bump is a shading - // illusion, not real geometry - "Bake" always produces the true, exact result either way. - void rebuild_bump_preview_mesh(); - void render_bump_preview_mesh(); + // instead of actually moving vertices, using the resources/shaders/*/texture_displacement_shaded.* + // shader. Faster than the true-displacement preview (no CPU meshing at all - just a per-vertex + // paint-weight buffer built at the same cadence as rebuild_preview()) but only shows the *active* + // layer, and the relief it shows is a shading illusion, not real geometry - "Bake" always produces + // the true, exact result either way. + void rebuild_shaded_preview_mesh(); + void render_shaded_preview_mesh(); // Feeds the active layer's painted patch + LSCM unwrap (if it's using that projection method) // into Plater's docked UV-editor pane and shows it, or hides the pane if the active layer @@ -636,16 +636,16 @@ private: // mouse button driving the drag hasn't been released yet - see on_render_input_window(). bool m_preview_params_dirty = false; - // See rebuild_bump_preview_mesh()/render_bump_preview_mesh(). On by default: it is the cheap, + // See rebuild_shaded_preview_mesh()/render_shaded_preview_mesh(). On by default: it is the cheap, // instant-updating preview, so it is the better first impression while painting. The true- // displacement view (a background CPU remesh) is one click away in the View row when the user // wants an exact look at what Bake will produce. - bool m_use_bump_preview = true; - // Set from the UV editor's per-move island edits instead of rebuilding the (potentially large) bump + bool m_use_shaded_preview = true; + // Set from the UV editor's per-move island edits instead of rebuilding the (potentially large) shaded // mesh synchronously inside that mouse handler - doing the rebuild there stalled both the UV pane // and the 3D view. The rebuild is instead coalesced to once per 3D frame (render_painter_gizmo). - bool m_bump_preview_dirty = false; - GLModel m_bump_preview_glmodel; + bool m_shaded_preview_dirty = false; + GLModel m_shaded_preview_glmodel; // Translucent tint over the active layer's painted triangles, drawn on top of whichever preview // is showing. The base painter's own opaque paint highlight (render_triangles()) cannot be used @@ -662,7 +662,7 @@ private: void rebuild_island_overlay(const std::vector &selection); void render_island_overlay(); // Set on every paint event, cleared when the overlay is rebuilt in render_painter_gizmo(). Kept - // separate from m_bump_preview_dirty so a stroke refreshes only the small painted patch per frame, + // separate from m_shaded_preview_dirty so a stroke refreshes only the small painted patch per frame, bool m_paint_overlay_dirty = false; void rebuild_paint_overlay(); void render_paint_overlay(GLModel &overlay); @@ -671,33 +671,33 @@ private: GLModel m_other_paint_glmodel; std::string m_other_paint_key; void rebuild_other_paint_overlay(); - // Whether render_bump_preview_mesh() would actually draw something. Checked before the real volume - // is hidden: with no layer, no texture or no shader the bump path draws nothing, and hiding the + // Whether render_shaded_preview_mesh() would actually draw something. Checked before the real volume + // is hidden: with no layer, no texture or no shader the shaded path draws nothing, and hiding the // volume for it left the model invisible. - bool bump_preview_ready() const; - // Whether the current bump mesh carries a precomputed per-vertex uv (LSCM) that the shader - // should sample at directly, rather than projecting in-shader. Set by rebuild_bump_preview_mesh(). - bool m_bump_preview_uses_vertex_uv = false; - // The projection frame handed to the bump shader, captured when the mesh is built. Cylindrical and + bool shaded_preview_ready() const; + // Whether the current displacement mesh carries a precomputed per-vertex uv (LSCM) that the shader + // should sample at directly, rather than projecting in-shader. Set by rebuild_shaded_preview_mesh(). + bool m_shaded_preview_uses_vertex_uv = false; + // The projection frame handed to the preview shader, captured when the mesh is built. Cylindrical and // Spherical are reconstructed in the fragment shader (there is no per-vertex uv for them) and wrap // around the whole patch, which no fragment can work out for itself. See layer_projection_frame(). - int m_bump_projection_mode = 0; - Vec3f m_bump_patch_center = Vec3f::Zero(); - Vec3f m_bump_patch_axis = Vec3f::UnitZ(); + int m_shaded_projection_mode = 0; + Vec3f m_shaded_patch_center = Vec3f::Zero(); + Vec3f m_shaded_patch_axis = Vec3f::UnitZ(); // The palette the fast preview's per-triangle filament indices were built against, captured when // the mesh was. Empty when the active layer is not colouring, which is what tells the shader to // fall back to the model's own colour. Held rather than re-read at draw time so the indices baked // into the mesh can never be resolved against a different set of filaments than they were computed // from - loading a filament mid-session would otherwise recolour a stale preview at random. - std::vector m_bump_preview_palette; + std::vector m_shaded_preview_palette; - // GPU island drag: while an island is dragged in the UV editor, the bump mesh is baked once (with + // GPU island drag: while an island is dragged in the UV editor, the displacement mesh is baked once (with // the dragged island's vertices flagged, v_normal.y = 1) and then moved purely through the shader's // island_delta uniform - one uniform update per mouse move, no rebuild - so it tracks the cursor - // as smoothly as Adjust placement. m_bump_active_chart is the dragged island (or -1); - // m_bump_active_face flags the dragged islands' *triangles*, indexed by painted-patch face; - // m_bump_baked_active_xf is that island's placement baked into the current mesh, against which the - // live delta is measured; m_bump_island_delta is the resulting final-uv-space affine handed to the + // as smoothly as Adjust placement. m_shaded_active_chart is the dragged island (or -1); + // m_shaded_active_face flags the dragged islands' *triangles*, indexed by painted-patch face; + // m_shaded_baked_active_xf is that island's placement baked into the current mesh, against which the + // live delta is measured; m_shaded_island_delta is the resulting final-uv-space affine handed to the // shader (identity except mid-drag). // // Per triangle rather than per vertex deliberately: a seam vertex belongs to every chart touching @@ -705,13 +705,13 @@ private: // island_active is an interpolated varying, so those neighbouring triangles then had island_delta // applied too - dragging one island moved every adjacent island's texture while the editor, which // is per chart, correctly moved only the one. A triangle belongs to exactly one chart. - int m_bump_active_chart = -1; - std::vector m_bump_active_face; - Eigen::Matrix m_bump_baked_active_xf = Eigen::Matrix::Identity(); - Eigen::Matrix m_bump_island_delta = Eigen::Matrix::Identity(); - // Flags `charts`' triangles in m_bump_active_face, sized to `patch_face_count` (the painted patch - // the bump mesh is being built from). Cleared if the unwrap carries no face map. - void compute_bump_active_faces(const std::vector &charts, size_t patch_face_count); + int m_shaded_active_chart = -1; + std::vector m_shaded_active_face; + Eigen::Matrix m_shaded_baked_active_xf = Eigen::Matrix::Identity(); + Eigen::Matrix m_shaded_island_delta = Eigen::Matrix::Identity(); + // Flags `charts`' triangles in m_shaded_active_face, sized to `patch_face_count` (the painted patch + // the displacement mesh is being built from). Cleared if the unwrap carries no face map. + void compute_shaded_active_faces(const std::vector &charts, size_t patch_face_count); // The set of islands the current UV-editor drag moves together: the pane's multi-selection unioned // with each selected island's join group (see build_island_move_set()). Populated at drag start and @@ -729,7 +729,7 @@ private: // Final per-vertex texture uv for the projections the shader can't reconstruct itself - LSCM (an // unwrap) and ViewProjected (a projector plane the shader doesn't know). One entry per patch/base // vertex, already through apply_uv_transform(). Empty for Triplanar/Cylindrical/Spherical, which - // the shader projects on its own. Shared by the bump preview and the UV-check overlay. + // the shader projects on its own. Shared by the shaded preview and the UV-check overlay. std::vector compute_layer_vertex_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer) const; // The same, but three UVs per patch triangle (corner 0..2 of triangle i at 3i..3i+2). This is what @@ -757,7 +757,7 @@ private: UVCheckMode m_uv_check_mode = UVCheckMode::None; GLModel m_uvcheck_glmodel; bool m_uvcheck_uses_vertex_uv = false; - // As m_bump_projection_mode and friends, for the Checker overlay. + // As m_shaded_projection_mode and friends, for the Checker overlay. int m_uvcheck_projection_mode = 0; Vec3f m_uvcheck_patch_center = Vec3f::Zero(); Vec3f m_uvcheck_patch_axis = Vec3f::UnitZ(); @@ -798,14 +798,14 @@ private: bool m_wireframe_overlay = false; GLModel m_wireframe_overlay_glmodel; size_t m_wireframe_overlay_vcount = 0; // topology signature, so it rebuilds only on a real change - void rebuild_wireframe_overlay(); // from the base mesh (bump/paint mode) + void rebuild_wireframe_overlay(); // from the base mesh (shaded/paint mode) void build_wireframe_from_its(const indexed_triangle_set &its); // from an explicit mesh, no early-out void refresh_wireframe(); // pick base vs displaced source for the current view void render_wireframe_overlay(); // The displaced preview geometry the last preview job produced, kept so the wireframe overlay can be // drawn on the raised surface actually shown in the true-displacement view (#: "wireframe in real mode"). indexed_triangle_set m_preview_its; - // Bumped on every rebuild_preview() call; a background TextureDisplacementPreviewJob's result + // Raised on every rebuild_preview() call; a background TextureDisplacementPreviewJob's result // is only applied if this hasn't moved on since the job was queued (see rebuild_preview()), // so a burst of edits can't have an earlier, now-stale job clobber a later one's result. // @@ -831,7 +831,7 @@ private: // The smoothing each cached thumbnail was built at, so a smoothing change re-uploads it. std::array m_thumbnail_smoothing{}; - // Full-resolution height texture for the bump shader, keyed the same way (see + // Full-resolution height texture for the preview shader, keyed the same way (see // get_layer_height_texture()). A smoothing change re-uploads it, so the fast preview shows the // blur the bake will apply. std::unique_ptr m_height_tex; diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 6594ee804d..8a3d8ea778 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -694,10 +694,10 @@ TEST_CASE("TextureDisplacement: feature-adaptive subdivision follows curvature, (its.vertices[t[0]].y() + its.vertices[t[1]].y() + its.vertices[t[2]].y()) / 3.f); }; - SECTION("a sharp bump refines densely at its center and leaves flat corners coarse") + SECTION("a sharp hill refines densely at its center and leaves flat corners coarse") { - // A tight Gaussian bump at the sheet's center: strong curvature near (0.5, 0.5), flat far away. - HeightFieldSampler bump = [](const Vec3f &p, const Vec3f &) { + // A tight Gaussian hill at the sheet's center: strong curvature near (0.5, 0.5), flat far away. + HeightFieldSampler hill = [](const Vec3f &p, const Vec3f &) { const float r2 = (p.x() - 0.5f) * (p.x() - 0.5f) + (p.y() - 0.5f) * (p.y() - 0.5f); return 1.0f * std::exp(-r2 / 0.02f); }; @@ -706,12 +706,12 @@ TEST_CASE("TextureDisplacement: feature-adaptive subdivision follows curvature, // here - this isolates the *curvature* contribution (the grid already meets the baseline). std::vector source; const indexed_triangle_set out = - subdivide_mesh_adaptive(grid, region, /*max edge*/ 0.3f, 200000, &source, bump, /*tol*/ 0.02f, + subdivide_mesh_adaptive(grid, region, /*max edge*/ 0.3f, 200000, &source, hill, /*tol*/ 0.02f, /*min_edge*/ 0.01f); - CHECK(out.indices.size() > grid.indices.size()); // the bump forced real refinement + CHECK(out.indices.size() > grid.indices.size()); // the hill forced real refinement - // The largest triangle near the bump's center must be much smaller than the largest in a flat + // The largest triangle near the hill's center must be much smaller than the largest in a flat // corner - i.e. triangles went where the curvature is, not spread evenly. float near_max = 0.f, far_max = 0.f; for (const auto &t : out.indices) { @@ -1140,9 +1140,9 @@ TEST_CASE("TextureDisplacement: the step cutter turns a stepped field into walls TEST_CASE("TextureDisplacement: the step cutter passes a smooth field through untouched", "[TextureDisplacement]") { - // A wide bump: its mid-level contour runs through the sheet, but nowhere is it a step, so there is + // A wide hill: its mid-level contour runs through the sheet, but nowhere is it a step, so there is // nothing to cut - refinement is the right tool for it. - const HeightFieldSampler bump = [](const Vec3f &p, const Vec3f &) { + const HeightFieldSampler hill = [](const Vec3f &p, const Vec3f &) { const float r2 = (p.x() - 3.f) * (p.x() - 3.f) + (p.y() - 3.f) * (p.y() - 3.f); return 0.4f * std::exp(-r2 / 3.f); }; @@ -1151,7 +1151,7 @@ TEST_CASE("TextureDisplacement: the step cutter passes a smooth field through un std::vector source; size_t cuts = 0; - const indexed_triangle_set out = cut_mesh_at_steps(sheet, region, bump, 0.05f, 0.075f, 0.f, &source, &cuts); + const indexed_triangle_set out = cut_mesh_at_steps(sheet, region, hill, 0.05f, 0.075f, 0.f, &source, &cuts); CHECK(cuts == 0); CHECK(out.indices.size() == sheet.indices.size()); @@ -1363,7 +1363,7 @@ TEST_CASE("TextureDisplacement: edge flips lay a stepped field's wall along the // Automatic resolution (v2 pipeline) // --------------------------------------------------------------------------------------------- -TEST_CASE("TextureDisplacement: automatic resolution follows the model's size like bumpmesh.com", "[TextureDisplacement]") +TEST_CASE("TextureDisplacement: automatic resolution follows the model's size", "[TextureDisplacement]") { // A 20 mm cube: diagonal 34.64 mm, so diagonal / 250 = 0.1386 mm, rounded up to 0.14. const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); From 7fd12ed1ee4dcbf1c745f461a27b2309d223b630 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 09:51:17 +0200 Subject: [PATCH 52/89] Block undo and redo while a background job runs A job is queued against the model as it stands, so undoing underneath it left the bake landing on geometry it was never computed for. --- src/slic3r/GUI/Plater.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ae2ab9f84f..1c128a9933 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -7022,6 +7022,8 @@ struct Plater::priv void undo(); void redo(); + // True, and tells the user, while a background job is working on the model - see the definition. + bool undo_redo_blocked_by_job(); void undo_redo_to(size_t time_to_load); // BBS: backup @@ -14546,8 +14548,25 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info(); } +// A background job holds the model it is working on: the texture displacement bake, for one, hands its +// result to the volume when it finishes, and it was queued against the geometry as it was at the time. +// Undoing while it runs restores an older state under it - a different transform, a different mesh - +// and the result then lands on geometry it was never computed for. Undo and redo therefore wait for +// the job, and say so rather than doing nothing. +bool Plater::priv::undo_redo_blocked_by_job() +{ + if (m_worker.is_idle()) + return false; + notification_manager->push_notification(NotificationType::CustomNotification, + NotificationManager::NotificationLevel::RegularNotificationLevel, + _u8L("Cannot undo or redo while an operation is running. Stop it first.")); + return true; +} + void Plater::priv::undo() { + if (this->undo_redo_blocked_by_job()) + return; const std::vector &snapshots = this->undo_redo_stack().snapshots(); auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time())); // BBS: undo-redo until modify record @@ -14565,6 +14584,8 @@ void Plater::priv::undo() void Plater::priv::redo() { + if (this->undo_redo_blocked_by_job()) + return; const std::vector &snapshots = this->undo_redo_stack().snapshots(); auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time())); // BBS: undo-redo until modify record @@ -22382,8 +22403,9 @@ bool Plater::can_copy_to_clipboard() const return true; } -bool Plater::can_undo() const { return IsShown() && p->is_view3D_shown() && p->undo_redo_stack().has_undo_snapshot(); } -bool Plater::can_redo() const { return IsShown() && p->is_view3D_shown() && p->undo_redo_stack().has_redo_snapshot(); } +// The job check keeps the buttons in step with priv::undo()/redo(), which refuse while one runs. +bool Plater::can_undo() const { return IsShown() && p->is_view3D_shown() && p->m_worker.is_idle() && p->undo_redo_stack().has_undo_snapshot(); } +bool Plater::can_redo() const { return IsShown() && p->is_view3D_shown() && p->m_worker.is_idle() && p->undo_redo_stack().has_redo_snapshot(); } bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } //BBS bool Plater::can_fillcolor() const { return p->can_fillcolor(); } From 0a543644eff16db7f59accd5169298be33b8832c Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 10:26:24 +0200 Subject: [PATCH 53/89] Give each bake its own budget, and warn when it is short Relief preserved from an earlier bake is counted on top of the budget instead of eating into it: baking a second area went from ~214 k triangles to the ~1000 k it was given. When the resolution needs more triangles than the budget allows, it is said before the bake under Resolution and again afterwards. --- .../TextureBake/TextureBakePipeline.cpp | 35 ++++++++--- .../TextureBake/TextureBakePipeline.hpp | 12 +++- src/libslic3r/TextureDisplacement.cpp | 23 ++++++-- src/libslic3r/TextureDisplacement.hpp | 19 +++++- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 58 +++++++++++++++++++ .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 12 ++++ .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 17 +++++- .../GUI/Jobs/TextureDisplacementBakeJob.hpp | 2 + 8 files changed, 159 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index f59ed51163..37d0577b77 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -280,15 +280,12 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } // 4. Decimate - export only. A bake needs the face-parent map, which a collapse destroys. - std::vector parent = std::move(sub.face_parent_id); + std::vector parent = std::move(sub.face_parent_id); + const size_t displaced_before_decimate = displaced.triangle_count(); if (mode == PipelineMode::Export) { - // Only when the mesh is actually over budget. Harvesting flat faces on a mesh that already fits - // cost several times the decimation itself and degraded the relief it was handed; it now only - // runs as part of a decimation that has to happen anyway. The repair pass below keys off the - // same decision (it runs only when decimation did), so an under-budget bake skips both. - const bool needs_decimation = displaced.triangle_count() > settings.max_triangles; - if (needs_decimation) { - std::vector locked; + std::vector locked; + size_t preserved = 0; + { if (settings.preserve_untextured && !displaced.exclude_weight.empty()) { locked.assign(displaced.triangle_count(), 0); // The corner average, as the displacement stage judges it: after the flip stage's @@ -304,7 +301,21 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, for (size_t t = 0; t < locked.size() && t < soft_excluded.size(); ++t) if (soft_excluded[t]) locked[t] = 0; + preserved = size_t(std::count(locked.begin(), locked.end(), uint8_t(1))); } + } + // The budget is what this bake may spend on what it refines. Geometry it only preserves - the + // relief of an earlier bake, which this one does not paint - is counted on top of it: charged + // against the same budget, a second bake over a fresh area had to evict the first one's + // triangles to fit, so every bake after the first came out coarser than the one before. + const size_t target = settings.max_triangles + preserved; + const bool over_budget = displaced.triangle_count() > target; + // Only when the mesh is actually over budget: the decimation pass also welds the soup and is + // followed by the T-junction repair, and putting an under-budget bake through both changed the + // sliced result by a fifth even with the collapse tolerance at zero, i.e. with nothing + // collapsed. Harvesting flat faces on a mesh that already fits needs that path to leave the + // geometry alone first. + if (over_budget) { // Colour per face on the fine mesh, so colour boundaries become creases the collapse // respects. Excluded (unpainted) faces take no colour. std::vector face_color; @@ -322,14 +333,20 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } }); } - DecimateResult dec = decimate(displaced, settings.max_triangles, settings.harvest_flat, + const size_t before = displaced.triangle_count(); + DecimateResult dec = decimate(displaced, target, settings.harvest_flat, settings.harvest_tol, locked, [&](double f) { return report("decimate", f); }, face_color); result.locked_over_budget = dec.locked_over_budget; displaced = std::move(dec.geometry); lap("decimate", displaced, "over budget, simplified"); + BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: " << before << " -> " << displaced.triangle_count() + << " (budget " << target << ")"; parent.clear(); // no longer meaningful } + result.triangles_refined = displaced_before_decimate; + result.triangles_budget = target; + result.budget_limited = over_budget; if (!report("decimate", 1.0)) { result.canceled = true; return result; diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index d56a0d09dc..22cc962141 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -74,9 +74,11 @@ struct PipelineSettings std::function painted; // Export mode only. + // What this bake may spend on what it refines. Geometry it only preserves (see preserve_untextured) + // is counted on top of it, so an earlier bake's relief does not have to be evicted to fit this one. size_t max_triangles = 750'000; // Keep removing zero-cost flat faces past the target. Only applies when decimation runs, i.e. when - // the displaced mesh is over max_triangles - an under-budget mesh is never decimated. + // the displaced mesh is over the budget - an under-budget mesh is never decimated. bool harvest_flat = true; double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL; // Lock the untextured region against both regularization and decimation. @@ -107,6 +109,12 @@ struct PipelineResult bool locked_over_budget = false; size_t collapse_count = 0; bool canceled = false; + // What the refinement produced, before decimation, and the count it had to fit into (the budget + // plus the preserved geometry). budget_limited says the refined mesh did not fit: the result + // carries less of the texture than the resolution asked for, which is what a caller warns about. + size_t triangles_refined = 0; + size_t triangles_budget = 0; + bool budget_limited = false; }; // `debug`, when given and enabled, receives the mesh after every stage that ran - which is the only @@ -122,7 +130,7 @@ void clamp_below_bottom(TriSoup &geometry, float bottom_z); // Flatten the bed-contact surface by snapping positions within `tol` of the bottom plane onto it. // -// Gated, not unconditional: an unconditional band snap also flattens the undersides of texture bumps +// Gated, not unconditional: an unconditional band snap also flattens the undersides of texture relief // near the base, folding them coplanar into the bottom face. Folded faces overlap the plate, so edges // there pick up four incident faces - non-manifold edges and phantom shells on re-import. All copies // of a position move together, and the move is rejected if any incident triangle would go degenerate diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index d24e334fa8..ad3bf3f267 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2095,7 +2095,8 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set const DisplacementProgressFn &progress, const TextureColorRequest *color, bool flip_normals, - BakeStageRecorder *debug) + BakeStageRecorder *debug, + TextureBakeStats *stats) { HeightFieldSampler combined = make_combined_displacement_sampler(mesh, layers, facets_data); if (!combined) @@ -2282,6 +2283,12 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set if (result.canceled || result.geometry.empty()) return {}; + if (stats != nullptr) { + stats->triangles_refined = result.triangles_refined; + stats->triangles_out = result.geometry.triangle_count(); + stats->triangles_budget = result.triangles_budget; + stats->budget_limited = result.budget_limited; + } indexed_triangle_set out = TextureBake::to_indexed_triangle_set(result.geometry); if (out.indices.empty()) return mesh; @@ -2365,8 +2372,11 @@ static indexed_triangle_set build_texture_displacement_in_place( const DisplacementProgressFn &progress, const TextureColorRequest *color, bool flip_normals, - BakeStageRecorder *debug) + BakeStageRecorder *debug, + TextureBakeStats *stats) { + // The classic path moves the vertices the mesh already has, so there is no budget to report on. + (void) stats; // Returns true to keep going. An aborted run returns {} (see the header): an empty mesh is the // one result no caller can mistake for a finished bake and commit onto the volume. const auto report = [&progress](int percent) { return !progress || progress(percent); }; @@ -2400,7 +2410,7 @@ static indexed_triangle_set build_texture_displacement_in_place( if (options.pipeline_v2) return build_texture_displacement_v2(mesh, layers, facets_data, options, progress, color, flip_normals, - debug); + debug, stats); // Layers are combined in slot order, like stacked layers in an image editor: each one folds its // own displacement into the running total via its blend mode (see TextureBlendMode). @@ -2813,7 +2823,8 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set const DisplacementProgressFn &progress, const TextureColorRequest *color, const Transform3d &volume_to_world, - BakeStageRecorder *debug) + BakeStageRecorder *debug, + TextureBakeStats *stats) { // An untransformed volume on an untransformed instance is by far the common case, and the round // trip costs two matrix multiplies per vertex on a mesh that can carry millions of them - so take @@ -2823,7 +2834,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set const Transform3d frame = texture_displacement_bake_frame(volume_to_world); if (frame.matrix().isApprox(Transform3d::Identity().matrix())) return build_texture_displacement_in_place(base_mesh, layers, facets_data, options, progress, color, - false, debug); + false, debug, stats); const Transform3d to_local = frame.inverse(); // A mirroring placement leaves the positions correct but every winding-derived normal pointing @@ -2838,7 +2849,7 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set const size_t debug_mark = (debug != nullptr) ? debug->mark() : 0; indexed_triangle_set out = build_texture_displacement_in_place(world, layers, facets_data, options, - progress, color, mirrored, debug); + progress, color, mirrored, debug, stats); // Everything the bake recorded is in world millimetres, like `out` itself. The debug view draws in // the volume's local frame, so the stages are brought back the same way the result is. if (debug != nullptr) diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index d6b3a41548..bb39bc3266 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -820,6 +820,21 @@ void merge_small_color_regions(const indexed_triangle_set &mesh, std::vector &layers, const TextureDisplacementFacetsData &facets_data, @@ -829,7 +844,9 @@ indexed_triangle_set build_texture_displacement(const indexed_triangle_set const Transform3d &volume_to_world = Transform3d::Identity(), // When given and enabled, receives the mesh after each // stage, already brought back into `base_mesh`'s frame. - BakeStageRecorder *debug = nullptr); + BakeStageRecorder *debug = nullptr, + // When given, receives what the bake spent. + TextureBakeStats *stats = nullptr); // `volume`'s mesh coordinates -> world millimetres: its first instance's transform times its own. // The mesh is shared by every instance, so a multi-instance object can only be baked for one of diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index c2a19ba03a..62b23fde62 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -4114,6 +4114,44 @@ TextureDisplacementFacetsData GLGizmoTextureDisplacement::masks_after_subdivisio return out; } +double GLGizmoTextureDisplacement::painted_area_mm2(const ModelVolume &mv) +{ + // Keyed on the paint generation, which every stroke and every layer edit raises, so a panel that + // asks for this on every frame walks the mesh only when the answer can have changed. + const std::string key = std::to_string(mv.id().id) + ":" + std::to_string(mv.mesh().facets_count()) + ":" + + std::to_string(m_preview_generation->load()); + if (key == m_painted_area_key) + return m_painted_area_mm2; + + const TriangleMesh &mesh = mv.mesh(); + std::vector region; + double area = 0.; + if (collect_paint_region(mesh, facets_data_of(mv), region, nullptr)) { + // In the frame the bake refines in, so a scaled instance is measured at the size it prints at. + const Transform3d frame = texture_displacement_bake_frame(texture_displacement_volume_to_world(mv)); + const indexed_triangle_set &its = mesh.its; + for (size_t t = 0; t < its.indices.size() && t < region.size(); ++t) { + if ((region[t] & REFINE_PAINTED) == 0) + continue; + const stl_triangle_vertex_indices &tri = its.indices[t]; + const Vec3d a = frame * its.vertices[size_t(tri[0])].cast(); + const Vec3d b = frame * its.vertices[size_t(tri[1])].cast(); + const Vec3d c = frame * its.vertices[size_t(tri[2])].cast(); + area += 0.5 * (b - a).cross(c - a).norm(); + } + } + m_painted_area_mm2 = area; + m_painted_area_key = key; + return area; +} + +size_t GLGizmoTextureDisplacement::estimated_refined_triangles(const ModelVolume &mv, float edge_mm) +{ + if (edge_mm <= 0.f) + return 0; + return size_t(4.0 * painted_area_mm2(mv) / (double(edge_mm) * double(edge_mm))); +} + const V2Resolution &GLGizmoTextureDisplacement::v2_recommendation(const ModelVolume &mv) { // Rebuilt only when something it depends on changes: the surface area scan is O(triangles) and the @@ -6768,6 +6806,26 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float hover_tip(_u8L("Triangle edge length the painted area is refined to before displacement. " "Smaller carries finer texture detail and costs more triangles; the budget " "caps the result.")); + + // What this resolution costs over what is painted, against what the budget allows. Refining + // past the budget is not an error - the bake simplifies back down to it - but the result + // then carries less of the texture than the resolution asks for, and the only sign of that + // used to be a mesh that came out coarser than expected. Shown before the bake, so the + // answer is to change a number rather than to wait out a bake and redo it. + const float edge_now = auto_res ? rec.edge_mm : opts.v2_refine_mm; + const int budget_k = opts.v2_max_triangles_k < 0 ? rec.budget_k : opts.v2_max_triangles_k; + const size_t budget = size_t(std::max(0, budget_k)) * 1000; + const size_t needed = estimated_refined_triangles(*mv, edge_now); + // Only when it is clearly over: the estimate runs about 3% high where it matters and up to + // a third high at coarse resolutions, where the mesh's own triangles are already near the + // target, and a warning about a bake that would have fitted is worse than none. + if (budget > 0 && needed > budget * 5 / 4) { + const auto to_m = [](size_t n) { return double(n) / 1000000.; }; + m_imgui->warning_text(Slic3r::format(_u8L("This resolution needs about %1$.1f M triangles, " + "budget %2$.1f M - the bake will simplify back to " + "the budget and lose detail."), + to_m(needed), to_m(budget))); + } } else { m_imgui->text(_L("Triangles")); ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 804c31821e..47de461f2b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -611,6 +611,18 @@ private: // is exactly what this exists to find. bool m_debug_check_topology = true; + // World-millimetre area of everything painted on `mv`, cached on the mesh and the paint generation: + // it is what the refinement has to cover, so it is what says whether a budget can pay for a + // resolution. 0 when nothing is painted. + double painted_area_mm2(const ModelVolume &mv); + double m_painted_area_mm2 = 0.; + std::string m_painted_area_key; + // Triangles the refinement needs to reach `edge_mm` over that area. Bisection converges to four of + // them to a square of the target edge - measured within 3% on a test model over a 5x range of + // resolutions - but the real count depends on the shape of the triangles it starts from, so this + // is only ever used to say a budget is clearly too small, never as a promise of what will come out. + size_t estimated_refined_triangles(const ModelVolume &mv, float edge_mm); + // The default pipeline's automatic resolution/budget for the volume, cached on what it depends on. const V2Resolution &v2_recommendation(const ModelVolume &mv); V2Resolution m_v2_rec; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 6e3976a297..0fb9cefefc 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -3,6 +3,7 @@ #include #include "libslic3r/Model.hpp" +#include "libslic3r/format.hpp" #include "libslic3r/TriangleSelector.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" @@ -11,6 +12,7 @@ #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/Utils/UndoRedo.hpp" @@ -64,7 +66,7 @@ void TextureDisplacementBakeJob::process(Ctl &ctl) } return true; }, - color, m_input.volume_to_world)); + color, m_input.volume_to_world, nullptr, &m_stats)); // Always finish at 100: this is what closes the notification. Reported even on cancel, where // build_texture_displacement() returns an empty mesh and finalize() commits nothing. @@ -169,6 +171,19 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept } else { commit(); } + + // The refinement went finer than the budget could keep: the simplification had to take detail back + // out to fit, so what was baked carries less of the texture than the resolution asked for. Said + // here, with the numbers, because it is the only place that knows them - and not as an error + // dialog: the result is a usable mesh, just not the one the settings described. + if (m_stats.budget_limited) { + const auto millions = [](size_t n) { return double(n) / 1000000.; }; + wxGetApp().notification_manager()->push_notification( + NotificationType::CustomNotification, NotificationManager::NotificationLevel::WarningNotificationLevel, + Slic3r::format(_u8L("The triangle budget limited the detail: this resolution needs %1$.1f M triangles, " + "the budget kept %2$.1f M. Raise Budget or use a coarser Resolution for the full detail."), + millions(m_stats.triangles_refined), millions(m_stats.triangles_out))); + } } void queue_texture_displacement_bake(const ModelVolume &volume, const TextureColorSettings &color, diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp index 753dd20742..187df3d0e7 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp @@ -50,6 +50,8 @@ public: private: TextureDisplacementBakeInput m_input; TriangleMesh m_result; + // What the bake spent, for the message it leaves behind when the budget capped the detail. + TextureBakeStats m_stats; // Per triangle of m_result: the filament to print it in, as an EnforcerBlockerType value // (0 = leave alone). Empty unless a layer asked for colour. See TextureColorRequest. std::vector m_triangle_color; From 1b029ff8fa559d642311e83a9fb579a297e8691e Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 12:19:58 +0200 Subject: [PATCH 54/89] Simplify flat faces even when a bake fits its budget Nothing removed the even mesh over a texture's flat parts unless the budget was lowered until simplification had to run. Collapses stay under a thirty-second of the resolution: on the test map 387584 -> 369876 triangles, with the print unchanged (0.02% of the extrusion). An earlier attempt was reverted for a 21% heavier result. That was the colour despeckle switching itself off, not the collapsing: it keyed off the face parent map, which any collapse empties. --- .../TextureBake/TextureBakePipeline.cpp | 22 +++++++++++-------- .../TextureBake/TextureBakePipeline.hpp | 6 +++++ src/libslic3r/TextureDisplacement.cpp | 10 ++++++++- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 37d0577b77..45867c10bb 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -310,12 +310,13 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, // triangles to fit, so every bake after the first came out coarser than the one before. const size_t target = settings.max_triangles + preserved; const bool over_budget = displaced.triangle_count() > target; - // Only when the mesh is actually over budget: the decimation pass also welds the soup and is - // followed by the T-junction repair, and putting an under-budget bake through both changed the - // sliced result by a fifth even with the collapse tolerance at zero, i.e. with nothing - // collapsed. Harvesting flat faces on a mesh that already fits needs that path to leave the - // geometry alone first. - if (over_budget) { + // Flat faces are harvested whether or not the budget bites. Refinement is driven by the target + // edge length alone, so it leaves as fine a mesh over the flat parts of a texture as over its + // detail, and nothing else removes those: under its budget a bake kept every redundant triangle + // unless the budget was lowered until decimation had to run. Only collapses costing less than + // harvest_tol are taken, so this does not reach the relief. + const bool harvest_only = !over_budget && settings.harvest_flat && displaced.triangle_count() > 0; + if (over_budget || harvest_only) { // Colour per face on the fine mesh, so colour boundaries become creases the collapse // respects. Excluded (unpainted) faces take no colour. std::vector face_color; @@ -333,20 +334,23 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } }); } + // Harvesting alone is asked for by handing it the count it already has: nothing is then + // over the target, so the loop only ever pops collapses under the tolerance. const size_t before = displaced.triangle_count(); - DecimateResult dec = decimate(displaced, target, settings.harvest_flat, + DecimateResult dec = decimate(displaced, over_budget ? target : before, settings.harvest_flat, settings.harvest_tol, locked, [&](double f) { return report("decimate", f); }, face_color); result.locked_over_budget = dec.locked_over_budget; displaced = std::move(dec.geometry); - lap("decimate", displaced, "over budget, simplified"); + lap("decimate", displaced, over_budget ? "over budget, simplified" : "flat faces harvested"); BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: " << before << " -> " << displaced.triangle_count() - << " (budget " << target << ")"; + << (over_budget ? " (budget " : " (flat harvest, budget ") << target << ")"; parent.clear(); // no longer meaningful } result.triangles_refined = displaced_before_decimate; result.triangles_budget = target; result.budget_limited = over_budget; + result.simplified = over_budget; if (!report("decimate", 1.0)) { result.canceled = true; return result; diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index 22cc962141..54b40f46dd 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -115,6 +115,12 @@ struct PipelineResult size_t triangles_refined = 0; size_t triangles_budget = 0; bool budget_limited = false; + // The budget coarsened the mesh, so its triangles are no longer the fine, even ones the refinement + // laid down - they can be as large as the features on them, which a caller that post-processes per + // triangle (the colour despeckle) has to know. Harvesting flat faces does not count: what it + // removes carried nothing. Not the same question as "did a collapse run", which is why + // face_parent_id being empty is no longer used to answer it. + bool simplified = false; }; // `debug`, when given and enabled, receives the mesh after every stage that ran - which is the only diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index ad3bf3f267..00a66271cd 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2178,6 +2178,12 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set BOOST_LOG_TRIVIAL(info) << "TextureBake resolution: " << settings.refine_length << " mm" << (auto_edge ? " (auto)" : "") << ", budget " << settings.max_triangles / 1000 << " k" << (auto_budget ? " (auto)" : ""); + // How far a flat-face harvest may move the surface, tied to the resolution: what a bake refined to + // 1.4 mm edges can merge flat is not what one refined to 0.1 mm may touch. A thirty-second of the + // edge is well under a layer either way - at the default resolution it sheds about 5% of the + // triangles on a relief that covers the whole surface, and far more of one with flat ground in it - + // and the floor keeps a very fine resolution from spending the pass for nothing. + settings.harvest_tol = std::clamp(settings.refine_length / 32.0, 0.005, 0.05); settings.preserve_untextured = true; // Always on: relief driven under the plate is unprintable whichever pipeline produced it, so this // is no longer a choice the user has to make. Only geometry that ends up below the model's own @@ -2335,7 +2341,9 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set // noise. A simplified mesh is neither: its triangles are as large as the colour regions // themselves and already end on the colour boundaries, so a majority vote among three // neighbours would repaint whole features. Bake mode (no simplification) keeps it. - const bool simplified = result.face_parent_id.empty(); + // Not "did a collapse run": a flat-face harvest leaves the mesh as fine as it was, and + // reading it as simplified switched the despeckle off for every bake that harvested. + const bool simplified = result.simplified; despeckle_triangle_colors(out, palette, simplified ? 0 : color->despeckle_passes); merge_small_color_regions(out, palette, color->min_color_region_mm2); for (size_t i = 0; i < out.indices.size(); ++i) { From 9a2eadf9ccbb58ee2d3e72840f9b9ac47fc6a38e Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 13:16:56 +0200 Subject: [PATCH 55/89] Rewrite the texture displacement tooltips They described the implementation, and a few no longer matched the code. Each now says what the control does and in what units; five controls that had no tooltip have one. The catalogs need regenerating. --- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 265 +++++++++--------- 1 file changed, 134 insertions(+), 131 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 62b23fde62..6459fc9fd7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -5493,10 +5493,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::PushStyleColor(ImGuiCol_Text, on ? ImVec4(1.f, 1.f, 1.f, 1.f) : style.Colors[ImGuiCol_TextDisabled]); const bool clicked = ImGui::Button((labels[m] + "##panel_mode").c_str(), ImVec2(seg_w[m], frame_h)); ImGui::PopStyleColor(4); - hover_tip(m == 0 ? _u8L("Standard - paint, then press Bake: the mesh is remeshed, refined where the texture " - "bends, and displaced in one step.") : - _u8L("Pro - every mesh-preparation control is shown and you run Remesh, Subdivide and " - "Bake yourself, in the order you want.")); + hover_tip(m == 0 ? _u8L("Standard - pick a texture, paint where it goes, press Bake. Everything the " + "mesh needs is done for you in that one step.") : + _u8L("Pro - the mesh preparation is yours to run: Remesh, Subdivide and Bake as " + "separate steps, with every setting on show. For when Standard's result is " + "not what you wanted and you know why.")); if (clicked && !on) { m_panel_mode = m; if (!pro_mode()) { @@ -5725,15 +5726,17 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::SameLine(); ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + m_imgui->scaled(2.6f))); if (icon_toggle(701, "texture_displacement_real_preview.svg", cur_mode == 0, icon_md, _L("Normal"), - _L("Normal - the true displaced geometry (what Bake produces)"))) + _L("Normal - the real geometry, exactly what Bake will produce. Slower to update"))) new_mode = 0; ImGui::SameLine(0.f, gap_s); if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, icon_md, _L("Fast"), - _L("Fast - a shaded approximation of the active layer only; quick to update, not exact"))) + _L("Fast - the relief is only shaded on, not built, and only for the layer you are " + "editing. Updates instantly while you paint"))) new_mode = 1; ImGui::SameLine(0.f, gap_s); if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, icon_md, _L("Checker"), - _L("Checker - a test grid over the texture mapping; squares stay square where it does not stretch"))) + _L("Checker - a test grid instead of the texture. Where the squares stay square the " + "texture is undistorted; where they stretch, it will too"))) new_mode = 2; ImGui::SameLine(0.f, gap_s); if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, icon_md, _L("Distortion"), @@ -5750,8 +5753,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - auto_w)); if (ImGui::Checkbox((auto_label + "##auto_update").c_str(), &m_auto_update) && m_auto_update) rebuild_preview(); // catch up anything that changed while it was off - hover_tip(_u8L("Auto update - rebuild the displaced geometry as soon as anything changes (painting, textures, " - "sliders). Turn off to only rebuild when you release a slider, on very heavy models.")); + hover_tip(_u8L("Rebuilds the preview as soon as anything changes. Turn it off on a heavy model if painting " + "or dragging a slider starts to stutter - the preview then waits until you let go.")); if (new_mode != cur_mode) apply_view_mode(new_mode); @@ -5817,8 +5820,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::InvisibleButton("##grip", ImVec2(grip_w, h)); const bool hot = ImGui::IsItemHovered() || ImGui::IsItemActive(); draw_drag_icon(dl, p.x, p.y, grip_w, h, drag_tint(hot ? 255 : 150)); - hover_tip(_u8L("Drag onto another layer to reorder. The first layer is the base; each one after it blends onto " - "the layers before it.")); + hover_tip(_u8L("Drag onto another layer to reorder. The first layer sits on the bare surface; each one after " + "it adds its relief on top of the ones before.")); // ImGui's own preview is a tooltip holding whatever is submitted here. The panel draws a copy of the whole // layer row under the cursor instead (after the layer list), so that one is switched off. if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip)) { @@ -5893,7 +5896,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } else if (ImGui::Button("?##thumb", ImVec2(frame_h, frame_h))) { open_picker(layer.slot); } - hover_tip(_u8L("Change texture")); + hover_tip(_u8L("Pick a different image for this layer. The paint, depth and tiling stay as they are.")); { // The teal outline on the thumbnail is what marks this card as the layer being painted. const ImVec2 a = ImGui::GetItemRectMin(), b = ImGui::GetItemRectMax(); @@ -5911,7 +5914,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (ImGui::Selectable((shown + "##name").c_str(), false, 0, ImVec2(name_w, frame_h))) open_picker(layer.slot); ImGui::PopStyleColor(2); - hover_tip(_u8L("Change texture")); + hover_tip(_u8L("Pick a different image for this layer. The paint, depth and tiling stay as they are.")); if (!painted) { ImGui::SameLine(); ImGui::TextDisabled("%s", not_painted.c_str()); @@ -5942,10 +5945,15 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // The three controls nearly every layer needs; everything else waits behind "More settings". // Depth and tile size are logarithmic, so both ends of their wide ranges stay usable. m_preview_params_dirty |= float_row("##depth", _L("Depth"), &layer.depth_mm, 0.01f, 10.f, "%.3f mm", true, card_pad); - hover_tip(_u8L("How far the brightest part of the texture moves the surface.")); + hover_tip(_u8L("Height of the relief: how far white in the texture lifts the surface, in " + "millimetres. Black does not move it at all, unless Midlevel says otherwise.")); m_preview_params_dirty |= float_row("##tile_size", _L("Tile size"), &layer.tiling_scale, 0.2f, 200.f, "%.2f mm", true, card_pad); - hover_tip(_u8L("The size of one repeat of the texture on the model.")); + hover_tip(_u8L("How wide one copy of the texture is on the model. Smaller repeats the pattern " + "more often and makes its detail finer; with Tile off, this is the size of the " + "single copy.")); m_preview_params_dirty |= float_row("##rotation", _L("Rotation"), &layer.rotation_deg, 0.f, 360.f, "%.0f°", false, card_pad); + hover_tip(_u8L("Turns the texture on the surface, in degrees - for lining a pattern up with an " + "edge of the model.")); if (m_layer_expanded[size_t(layer.slot)]) { { @@ -5961,38 +5969,38 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // Midlevel: the height that means "don't move". At 0 the surface only ever bulges outwards; // at 0.5 mid-grey is neutral and darker texels cut inwards. m_preview_params_dirty |= float_row("##midlevel", _L("Midlevel"), &layer.midlevel, 0.f, 10.f, "%.2f", false, card_pad); - hover_tip(_u8L("The grey level that stays put. At 0 the texture can only push the surface " - "outwards. Raise it and anything darker cuts inwards instead, so one height " - "map both embosses and engraves -0.5 makes mid-grey neutral.\n\n" - "Cutting inwards can fold the surface through itself where normals converge: " - "inside a sharp concave corner, or through a thin wall. Keep Depth small " - "relative to the feature you are cutting into.")); + hover_tip(_u8L("Which grey stays where the surface already is. At 0 the texture only pushes " + "outwards; at 0.5 mid-grey stays put, so darker greys cut in and lighter ones " + "still push out - one image both embosses and engraves.\n\n" + "What cuts in has to fit: inside a sharp corner or through a thin wall, a deep " + "cut can pass through the other side.")); if (layer.midlevel > 0.f && layer.depth_mm > 1.f) m_imgui->warning_text(_L("Deep inward displacement may self-intersect.")); m_preview_params_dirty |= float_row("##smoothing", _L("Smoothing"), &layer.smoothing, 0.f, 1.f, "%.2f", false, card_pad); - hover_tip(_u8L("Blurs the height texture before it displaces the surface, rounding hard edges " - "and removing speckle without needing a softer source image. Affects the preview " - "and the bake alike.")); + hover_tip(_u8L("Blurs the image before it is used, which rounds off hard steps and removes " + "speckle from a noisy photo. Raise it if the relief looks harsh or grainy; it " + "costs fine detail.")); // Edge fade: the relief flattens toward the boundary of the painted area. m_preview_params_dirty |= ImGui::Checkbox((_u8L("Edge fade") + "##edge_smoothing").c_str(), &layer.edge_smoothing); - hover_tip(_u8L("Fade the relief to flat toward the edge of the painted area, so it blends into " - "the surrounding surface. A small amount only softens a thin band at the very " - "edge; the maximum flattens the whole painted face.")); + hover_tip(_u8L("Flattens the relief as it approaches the edge of the painted area, so it " + "blends into the bare surface instead of stopping at a step.")); ImGui::SameLine(); m_imgui->disabled_begin(!layer.edge_smoothing); ImGui::SetNextItemWidth(-card_pad); m_preview_params_dirty |= ImGui::SliderFloat("##edge_amount", &layer.edge_smoothing_amount, 0.02f, 1.f, "%.2f", ImGuiSliderFlags_AlwaysClamp); m_imgui->disabled_end(); - hover_tip(_u8L("How far the fade reaches into the painted area.")); + hover_tip(_u8L("How far in the fade reaches, as a share of the painted area. Small values " + "soften a narrow band at the edge; 1 flattens almost all of it.")); // Invert and Colours share a row. { const float x0 = ImGui::GetCursorPosX(); m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer.invert); - hover_tip(_u8L("Swap high and low: what stood out now cuts in.")); + hover_tip(_u8L("Turns the relief inside out: what stood out is cut in, and the other way " + "round. The same as using a negative of the image.")); ImGui::SameLine(); ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + 0.5f * (ImGui::GetWindowContentRegionMax().x - card_pad - x0))); @@ -6009,11 +6017,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->disabled_end(); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) m_imgui->tooltip(has_color ? - _u8L("Colours the painted area from the texture's own colours, as well as " - "displacing it. Each colour is matched to the closest printable " - "colour, and the result is written as multi-material paint - so the " - "area prints in those filaments. Everything you did not paint keeps " - "the object's own filament.") : + _u8L("Prints the painted area in the texture's colours as well as its " + "relief. Each colour is matched to the nearest of your loaded " + "filaments; anything you did not paint keeps the object's own.") : _u8L("This texture is a grayscale height map, so it has no colours to " "apply. Import a colour image to use this."), wrap_w); @@ -6024,10 +6030,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float TextureDisplacementOptions &opts = mv->texture_displacement_options; if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled)) m_preview_params_dirty = true; - hover_tip(_u8L("Interleave pairs of filaments to reach colours between them, so a few " - "filaments cover far more than a few colours. Used only on images with " - "continuous colour (photographs, gradients); a texture of flat colours " - "prints in single filaments either way. Off forces single filaments.")); + hover_tip(_u8L("Interleaves two filaments to fake the colours in between, so a handful " + "of filaments can cover a photo or a gradient. An image of flat colours " + "prints the same either way. Off uses one filament per area.")); if (opts.color_mix_enabled) { slider_label(_L("Mix by")); const std::string mix_z = _u8L("Layers"); @@ -6052,11 +6057,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } if (int_row("##color_despeckle", _L("Denoise"), &opts.color_despeckle, 0, 6, "%d", card_pad)) m_preview_params_dirty = true; - hover_tip(_u8L("Removes stray single triangles of the wrong colour, which is what " - "detail in the image finer than the mesh leaves behind. Each step " - "replaces a triangle's colour with the one most of its neighbours " - "have, so features wider than a triangle are kept. Raise it if the " - "result looks speckled; lower it if fine detail is being eaten.")); + hover_tip(_u8L("Cleans up single stray triangles of the wrong colour, which detail finer " + "than the mesh leaves behind. Raise it if the result looks speckled, " + "lower it if small features are being swallowed.")); } } @@ -6117,8 +6120,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_show_uv_editor = true; update_uv_editor(); } - hover_tip(_u8L("Show the UV editor pane, where the painted area is unwrapped, seams are marked and " - "islands are laid out.")); + hover_tip(_u8L("Opens the UV editor, where you can see how the texture is laid out over the " + "painted area, cut seams and move the pieces around.")); ImGui::SameLine(); ImGui::AlignTextToFramePadding(); if (m_uv_editor_unwrap.empty()) @@ -6140,10 +6143,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_preview_params_dirty = true; update_projector(); } - hover_tip(_u8L("Projects the texture straight onto the painted area from the direction " - "you are currently looking, like a slide projector. Faces angled away from " - "that direction will stretch - turn the model to where you want the " - "texture crisp, then capture.")); + hover_tip(_u8L("Projects the texture onto the painted area from where you are looking now, like " + "a slide projector. Faces turned away from you stretch, so line the view up with " + "the surface you care about first.")); if (ImGui::Checkbox(_u8L("Project only on visible").c_str(), &m_project_only_visible)) { if (m_project_only_visible && select_visible_faces() == 0) @@ -6152,21 +6154,20 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_preview_params_dirty = true; update_projector(); } - hover_tip(_u8L("Paints exactly the faces you can currently see - facing the camera and " - "not hidden behind anything else - and projects onto those. This replaces " - "the layer's painted area, and is re-applied each time you capture the view.")); + hover_tip(_u8L("Paints only the faces you can actually see right now - facing you and not hidden " + "behind anything - and projects onto those. Replaces what the layer had painted.")); bool projector_open = m_projector_frame != nullptr && m_projector_frame->IsShown(); if (ImGui::Checkbox(_u8L("Projection frame").c_str(), &projector_open)) show_projector(projector_open); - hover_tip(_u8L("Opens a see-through window you drag over the model. Whatever shows " - "through it is what the texture is projected onto, and the window's " - "border becomes the edge of the projection. Move and resize it to frame " - "the area you want, then press Apply.")); + hover_tip(_u8L("Opens a window you drag over the model. Whatever you can see through it is what " + "gets the texture, and its border becomes the edge of the projection.")); if (projector_open) { if (int_row("##projector_opacity", _L("Opacity"), &m_projector_opacity, 20, 255, "%d", card_pad)) m_projector_frame->set_opacity(m_projector_opacity); + hover_tip(_u8L("How solid the frame window looks while you place it. Lower to see the " + "model through it; it does not affect the result.")); if (m_imgui->button(_u8L("Apply projection frame"))) { const int painted_count = apply_projection_frame(); @@ -6176,10 +6177,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float else if (painted_count < 0) show_error(nullptr, _u8L("The frame could not be applied. Make sure it overlaps the 3D view.")); } - hover_tip(_u8L("Projects the texture through the frame from the direction you are " - "looking now, and paints the visible faces inside it. This replaces " - "the layer's painted area. The result is fixed to the model, so you " - "can orbit freely afterwards.")); + hover_tip(_u8L("Projects the texture through the frame from where you are looking now and " + "paints the faces inside it. Replaces what the layer had painted; the result " + "sticks to the model, so you can orbit afterwards.")); } if (layer.view_project_projective) { @@ -6200,7 +6200,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float { const float x0 = ImGui::GetCursorPosX(); m_preview_params_dirty |= ImGui::Checkbox((_u8L("Tile") + "##tile_enabled").c_str(), &layer.tile_enabled); - hover_tip(_u8L("Repeat the texture across the painted area. Off places it once, like a decal.")); + hover_tip(_u8L("Repeats the texture across the painted area. Off places one copy, like a decal, " + "at the size set by Tile size.")); ImGui::SameLine(); ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w)); const wxString tile_na = layer.tile_enabled ? wxString() : _L("Turn Tile on to choose how the texture repeats."); @@ -6244,11 +6245,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) m_imgui->tooltip(base_layer ? _u8L("The base layer has nothing beneath it to combine with, so it always adds.") : - _u8L("How this layer combines with the layers before it, wherever they overlap. " - "Add and Subtract pile relief on or carve it away. Multiply and Divide " - "scale the relief underneath, which makes this layer act as a mask over " - "it - for those two, Depth is a gain, and a depth of 1 mm on a white " - "part of the texture leaves the layers below unchanged."), + _u8L("What this layer does where it overlaps the ones below. Add and " + "Subtract pile relief on or carve it away. Multiply and Divide scale " + "what is underneath, which turns this layer into a mask over it - " + "there, Depth acts as a strength, and 1 mm leaves white areas " + "untouched."), wrap_w); ImGui::SameLine(0.f, gap_s); @@ -6342,7 +6343,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } else if (ImGui::Button("?##thumb", ImVec2(th, th))) { open_picker(layer.slot); } - hover_tip(_u8L("Change texture")); + hover_tip(_u8L("Pick a different image for this layer. The paint, depth and tiling stay as they are.")); ImGui::SameLine(); char depth_text[32]; @@ -6430,10 +6431,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result m_parent.set_as_dirty(); } - hover_tip(_u8L("Refine only where you have painted, down to a target edge length, instead of splitting " - "the whole model. Keeps the triangle count down on a big part with a small decal, and - " - "unlike whole-model subdivision - your paint is carried onto the finer mesh instead of " - "being cleared.")); + hover_tip(_u8L("Adds triangles only where you painted, instead of everywhere. On a large part with a small " + "decal that is the difference between thousands of triangles and millions, and your paint " + "survives the refinement.")); if (m_subdivide_adaptive) { if (m_subdivide_target_mm <= 0.f && mv != nullptr) { @@ -6459,9 +6459,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (ImGui::Checkbox(_u8L("Follow texture detail").c_str(), &m_subdivide_feature)) preview_live(); - hover_tip(_u8L("Put triangles only where the texture actually bends - dense over hills, ridges and " - "noise, sparse over flat areas and smooth slopes - instead of an even density " - "everywhere. Uses the combined displacement of all painted layers.")); + hover_tip(_u8L("Spends the triangles where the texture actually bends - packed along ridges and edges, " + "sparse over flat ground - instead of spreading them evenly. The same detail for fewer " + "triangles on most textures.")); // The edge-length target is a baseline in both modes. In feature mode it is what guarantees the // curvature test can actually see the texture: left too coarse, a big triangle over a fine @@ -6471,44 +6471,40 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float 0.001f, 20.f, "%.3f mm", true, 0.f)) preview_live(); hover_tip(m_subdivide_feature ? - _u8L("Nothing in the painted area stays coarser than this, even where the texture is " - "flat. Keep it near the size of the features you want picked up - too coarse and " - "fine detail can be missed entirely.") : - _u8L("Triangles in the painted area are split until every edge is at or below this " - "length. Smaller means finer detail and more triangles.")); + _u8L("No triangle in the painted area stays larger than this, even where the texture " + "is flat. Keep it near the size of the smallest feature you want to come out.") : + _u8L("Triangles in the painted area are split until none is larger than this. Smaller " + "means finer detail and, fast, a lot more triangles.")); if (m_subdivide_feature) { if (float_row("##subdiv_detail", _L("Detail"), &m_subdivide_detail_mm, 0.001f, 1.f, "%.3f mm", true, 0.f)) preview_live(); - hover_tip(_u8L("How closely the mesh follows the texture's relief. Smaller captures finer detail; " - "larger only chases the big features.")); + hover_tip(_u8L("How far the mesh may sit from the shape the texture describes, in millimetres. " + "Smaller follows fine detail and costs triangles; larger only chases the big " + "features.")); if (float_row("##subdiv_min", _L("Min edge"), &m_subdivide_min_edge_mm, 0.001f, 20.f, "%.3f mm", true, 0.f)) preview_live(); - hover_tip(_u8L("The finest triangle size refinement will ever produce. Smaller captures finer " - "relief; also stops runaway subdivision at a sharp texture step, where the " - "surface never becomes flat.")); + hover_tip(_u8L("A floor on triangle size, so a sharp step in the texture cannot be chased " + "forever. Lower it for finer relief, raise it if the count runs away at hard " + "edges.")); } // Applies in both adaptive sub-modes: it is about the step the bake puts at the paint's edge, which // exists whether or not "Follow texture detail" is on. 0 turns the band off. if (float_row("##subdiv_border", _L("Edge detail"), &m_subdivide_border_mm, 0.f, 5.f, "%.3f mm", false, 0.f)) preview_live(); - hover_tip(_u8L("Triangle size along the boundary of the painted area. The relief drops back to the " - "flat surface across that boundary, and the triangles spanning the drop are what you " - "see as a jagged rim around an unpainted region - smaller values make the outline " - "cleaner. Costs triangles along the outline only, not over the whole area. " - "0 turns it off.")); + hover_tip(_u8L("Triangle size along the outline of the painted area, where the relief drops back to the " + "bare surface. Lower it if that rim looks jagged; it only costs triangles along the " + "outline. 0 turns it off.")); // Only worth showing when a layer is actually colouring: with no colour there is no boundary for it // to refine and the control would do nothing whatever it is set to. if (mv != nullptr && any_layer_colors(*mv)) { if (float_row("##subdiv_color", _L("Colour detail"), &m_subdivide_color_mm, 0.f, 5.f, "%.3f mm", false, 0.f)) preview_live(); - hover_tip(_u8L("Triangle size along a boundary between two colours. Colour is assigned per " - "triangle, so an edge between two filaments can only be as clean as the " - "triangles along it - and the relief criteria cannot see it at all, because " - "the surface is perfectly smooth across a change of colour. Costs triangles " - "along the boundaries only. 0 turns it off.")); + hover_tip(_u8L("Triangle size where two colours meet. Each triangle prints in one filament, so a " + "colour edge can only be as sharp as the triangles along it - and nothing else " + "refines there, since the surface is flat across a change of colour. 0 turns it off.")); } ImGui::TextDisabled("%s", _u8L("Triangle budget: set with Triangles, below.").c_str()); @@ -6520,9 +6516,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one m_parent.set_as_dirty(); } - hover_tip(_u8L("How many times to split every triangle into four. Each step roughly quadruples the " - "triangle count, so there are enough vertices for the height texture to displace. " - "0 means no subdivision.")); + hover_tip(_u8L("Splits every triangle of the model into four, this many times over. Each step " + "quadruples the count - 3 turns 100 k triangles into 6.4 M - so prefer refining " + "only the painted area unless you need the whole model finer.")); } { @@ -6543,7 +6539,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_parent.set_as_dirty(); } m_imgui->disabled_end(); - hover_tip(_u8L("Shows the subdivided mesh as a wireframe without changing the model.")); + hover_tip(_u8L("Shows what the refinement would produce as a wireframe, without touching the model.")); ImGui::SameLine(); m_imgui->disabled_begin(!subdivide_ready || busy); if (ImGui::Button(_u8L("Subdivide").c_str(), ImVec2(half, 0.f))) { @@ -6585,7 +6581,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_remesh_target_edge_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)), 0.1f, 20.f) : 1.f; } float_row("##remesh_edge", _L("Target edge"), &m_remesh_target_edge_mm, 0.1f, 20.f, "%.2f mm", true, 0.f); - hover_tip(_u8L("The triangle edge length the whole model is rebuilt with.")); + hover_tip(_u8L("Triangle size the whole model is rebuilt with, in millimetres. Displacement works best on an " + "even mesh; this is what makes one out of an uneven import.")); ImGui::Checkbox((_u8L("Keep sharp edges") + "##remesh_sharp").c_str(), &m_remesh_keep_sharp_edges); hover_tip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it " @@ -6596,8 +6593,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::SetNextItemWidth(-1.f); ImGui::SliderFloat("##remesh_sharp_angle", &m_remesh_sharp_angle_deg, 5.f, 90.f, "%.0f°", ImGuiSliderFlags_AlwaysClamp); m_imgui->disabled_end(); - hover_tip(_u8L("Edges bent by more than this count as hard features and are kept. Lower keeps " - "more detail but leaves more of the mesh untouched; higher remeshes more freely.")); + hover_tip(_u8L("How sharp a fold has to be, in degrees, before the remesher treats it as an edge worth " + "keeping. Lower protects more of the model's shape; higher rebuilds more of it evenly.")); m_imgui->disabled_begin(mv == nullptr || busy); if (ImGui::Button(_u8L("Remesh").c_str())) @@ -6615,40 +6612,38 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float heading(_L("Result")); m_preview_params_dirty |= ImGui::Checkbox(_u8L("Displace up to the border").c_str(), &opts.displace_border); - hover_tip(_u8L("Move the outermost ring of painted vertices as well, so the relief runs all the " - "way to the edge of the painted area. Turn this off to hold that ring flat, which " - "keeps the displacement strictly inside the paint but flattens the pattern right " - "at the border.")); + hover_tip(_u8L("Lets the relief run right to the edge of the painted area. Turn it off to hold that " + "outer ring flat, which keeps the displacement strictly inside your paint but flattens " + "the pattern at the border.")); m_preview_params_dirty |= ImGui::Checkbox(_u8L("Smooth result").c_str(), &opts.smooth_enabled); - hover_tip(_u8L("Relax the displaced surface after the textures have been applied, to round off " - "the hard steps a bitmap height map leaves behind. Only the vertices the " - "displacement moved are touched. This is geometry smoothing - the per-layer " - "Smoothing slider instead softens the height map before it is sampled.")); + hover_tip(_u8L("Smooths the geometry after the texture has been applied, to take the hard steps out of a " + "low-resolution image. Only what the displacement moved is touched. The Smoothing slider " + "on a layer is a different thing: it blurs the image before it is used.")); if (opts.smooth_enabled) { float percent = opts.smooth_strength * 100.f; if (float_row("##dispsmooth", _L("Strength"), &percent, 1.f, 100.f, "%.0f %%", false, 0.f)) { opts.smooth_strength = std::clamp(percent / 100.f, 0.01f, 1.f); m_preview_params_dirty = true; } + hover_tip(_u8L("How far each pass pulls a vertex towards its neighbours. High values round the " + "relief off quickly; low values need more passes but keep more of the detail.")); if (int_row("##dispsmoothit", _L("Passes"), &opts.smooth_iterations, 1, 10, "%d", 0.f)) m_preview_params_dirty = true; - hover_tip(_u8L("How many relaxation passes to run. More passes reach further across the " - "surface; strength controls how much each one moves a vertex.")); + hover_tip(_u8L("How many smoothing passes to run. More passes spread the smoothing further across " + "the surface; Strength decides how much each one moves.")); m_preview_params_dirty |= ImGui::Checkbox(_u8L("Ignore outer ring").c_str(), &opts.smooth_skip_border); - hover_tip(_u8L("Leave the outermost ring of painted vertices out of the smoothing. Their " - "neighbours outside the paint never move, so relaxing them drags the relief " - "back down and the pattern comes out half-melted right at the edge. Turn this " - "off only if you want that edge softened on purpose.")); + hover_tip(_u8L("Keeps the outer ring of the painted area out of the smoothing. Its neighbours " + "outside the paint never move, so smoothing it drags the relief down and leaves the " + "pattern half-melted at the border.")); // The settings above ride along with Preview/Bake. This button is for geometry that has *already* // been baked, where there is no displacement left to fold the smoothing into. if (m_imgui->button(_u8L("Smooth baked mesh now"))) smooth_model(); - hover_tip(_u8L("Apply the smoothing above to the model's real geometry right now, using the " - "painted area to decide what to touch. For relief that is already baked in - " - "unbaked displacement is smoothed by Bake itself.")); + hover_tip(_u8L("Applies the smoothing above to the model itself, right now, within the painted area. " + "For relief that is already baked in - anything not yet baked is smoothed by Bake.")); } } } @@ -6683,8 +6678,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_preview_params_dirty = true; } m_imgui->disabled_end(); - hover_tip(_u8L("Triangles to simplify down to after displacement, in thousands. Automatic " - "with the resolution; 0 turns simplification off.")); + hover_tip(_u8L("How many thousand triangles this bake may spend on the area you painted. " + "Relief already baked into the rest of the model is kept on top of it, so a " + "second bake somewhere else gets the same budget as the first. Raise it if the " + "warning below Resolution says the detail will not fit; 0 keeps every triangle " + "the refinement produced, however many that is.")); } if (SHOW_PIPELINE_DEV_CONTROLS && opts.pipeline_v2) { // Keeping the relief above the plate is not a checkbox: it is unconditional, in both pipelines @@ -6787,8 +6785,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_parent.set_as_dirty(); } m_imgui->disabled_end(); - hover_tip(_u8L("Auto: the resolution follows the model's size and the budget is the standard " - "750 k. Untick to set them by hand.")); + hover_tip(_u8L("On: the resolution follows the size of the model and the budget is left at its " + "default. Untick to set both yourself - worth doing for a texture much finer or " + "much coarser than the part it sits on.")); ImGui::SameLine(); ImGui::SetNextItemWidth(x0 + panel_w - ImGui::GetCursorPosX()); float shown = auto_res ? rec.edge_mm : opts.v2_refine_mm; @@ -6801,11 +6800,15 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); if (auto_res && rec.edge_mm > 0.f) - hover_tip(Slic3r::format(_u8L("The model's diagonal / 250. Budget %1% k."), rec.budget_k)); + hover_tip(Slic3r::format(_u8L("Chosen from the size of the model: %1$.2f mm triangles, " + "budget %2% k. Untick Auto to set them yourself."), + rec.edge_mm, rec.budget_k)); else - hover_tip(_u8L("Triangle edge length the painted area is refined to before displacement. " - "Smaller carries finer texture detail and costs more triangles; the budget " - "caps the result.")); + hover_tip(_u8L("How fine the mesh is made under the paint, in millimetres. It has to be " + "smaller than the detail you want out of the texture - a 0.5 mm groove needs " + "triangles well under 0.5 mm. Smaller costs triangles fast: halving it needs " + "four times as many, and once they no longer fit the budget the bake simplifies " + "back down and the detail goes with it.")); // What this resolution costs over what is painted, against what the budget allows. Refining // past the budget is not an error - the bake simplifies back down to it - but the result @@ -6839,9 +6842,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_parent.set_as_dirty(); } m_imgui->disabled_end(); - hover_tip(_u8L("How many thousand triangles the refinement may add. It always splits the " - "worst-fitting triangle first, so a run that uses the whole budget has still spent " - "it where it shows most - raise this if the result still looks too coarse.")); + hover_tip(_u8L("How many thousand triangles the refinement may add to the model. The triangle " + "that fits worst is always split first, so even a run that spends the lot has " + "spent it where it shows most. Raise it if the relief still looks coarse.")); } const float button_h = std::round(frame_h * 1.25f); @@ -6849,11 +6852,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (busy) { if (ImGui::Button((_u8L("Stop") + "##stop").c_str(), ImVec2(third, button_h))) wxGetApp().plater()->get_ui_job_worker().cancel_all(); - hover_tip(_u8L("Stop the running bake. A step it has already finished stays on the model and can be undone.")); + hover_tip(_u8L("Stops the bake. Whatever it had already finished stays on the model, and can be undone.")); } else { if (ImGui::Button((_u8L("Close") + "##close").c_str(), ImVec2(third, button_h))) m_parent.reset_all_gizmos(); - hover_tip(_u8L("Close the tool without baking. Paint and layers are kept with the model.")); + hover_tip(_u8L("Closes the tool without baking. Your paint, layers and settings stay with the model.")); } ImGui::SameLine(); From b3216b7f6af3e5365fcfdab481d87e98d60f9edf Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 13:22:57 +0200 Subject: [PATCH 56/89] Wrap the budget warning, count triangles in thousands Unwrapped, it ran past the edge of the panel and pulled the layout with it. Counts under a million now read in thousands, so a 119 k budget is no longer shown as 0.1 M. --- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 28 +++++++++++++------ .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 12 +++++--- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 6459fc9fd7..f24a3125e7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -5974,8 +5974,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "still push out - one image both embosses and engraves.\n\n" "What cuts in has to fit: inside a sharp corner or through a thin wall, a deep " "cut can pass through the other side.")); - if (layer.midlevel > 0.f && layer.depth_mm > 1.f) + if (layer.midlevel > 0.f && layer.depth_mm > 1.f) { + ImGui::PushTextWrapPos(content_rx - card_pad); m_imgui->warning_text(_L("Deep inward displacement may self-intersect.")); + ImGui::PopTextWrapPos(); + } m_preview_params_dirty |= float_row("##smoothing", _L("Smoothing"), &layer.smoothing, 0.f, 1.f, "%.2f", false, card_pad); hover_tip(_u8L("Blurs the image before it is used, which rounds off hard steps and removes " @@ -6823,11 +6826,18 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // a third high at coarse resolutions, where the mesh's own triangles are already near the // target, and a warning about a bake that would have fitted is worse than none. if (budget > 0 && needed > budget * 5 / 4) { - const auto to_m = [](size_t n) { return double(n) / 1000000.; }; - m_imgui->warning_text(Slic3r::format(_u8L("This resolution needs about %1$.1f M triangles, " - "budget %2$.1f M - the bake will simplify back to " - "the budget and lose detail."), - to_m(needed), to_m(budget))); + // Thousands under a million: a 119 k budget shown as "0.1 M" says nothing. + const auto count = [](size_t n) { + return n >= 1000000 ? Slic3r::format("%1$.1f M", double(n) / 1000000.) : + Slic3r::format("%1% k", (n + 500) / 1000); + }; + // Wrapped to the panel, like the note under the buttons: unwrapped text runs past the + // panel's edge and takes the window's width with it. + ImGui::PushTextWrapPos(x0 + panel_w); + m_imgui->warning_text(Slic3r::format(_u8L("Needs about %1% triangles, budget %2% - the bake " + "will simplify back down and lose detail."), + count(needed), count(budget))); + ImGui::PopTextWrapPos(); } } else { m_imgui->text(_L("Triangles")); @@ -6898,8 +6908,10 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // What Bake will produce, and which layers it will skip. if (mv != nullptr) { - std::string note = v2 ? Slic3r::format(_u8L("Model: %1% k triangles - the bake refines to the resolution above, up to its budget"), base_k) : - pro_mode() ? Slic3r::format(_u8L("Bake moves existing vertices - the model stays at %1% k triangles"), base_k) : + const std::string base_count = base_k > 0 ? Slic3r::format(_u8L("%1% k"), base_k) : + std::to_string(mv->mesh().facets_count()); + std::string note = v2 ? Slic3r::format(_u8L("Model: %1% triangles - the bake refines to the resolution above, up to its budget"), base_count) : + pro_mode() ? Slic3r::format(_u8L("Bake moves existing vertices - the model stays at %1% triangles"), base_count) : Slic3r::format(_u8L("Bake result up to ~%1% k triangles"), base_k + m_subdivide_budget_k); if (mv->is_texture_displacement_painted()) { std::vector skipped; diff --git a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp index 0fb9cefefc..077b11cbc5 100644 --- a/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp +++ b/src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp @@ -177,12 +177,16 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept // here, with the numbers, because it is the only place that knows them - and not as an error // dialog: the result is a usable mesh, just not the one the settings described. if (m_stats.budget_limited) { - const auto millions = [](size_t n) { return double(n) / 1000000.; }; + // Thousands under a million, so a small budget is not reported as "0.1 M". + const auto count = [](size_t n) { + return n >= 1000000 ? Slic3r::format("%1$.1f M", double(n) / 1000000.) : + Slic3r::format("%1% k", (n + 500) / 1000); + }; wxGetApp().notification_manager()->push_notification( NotificationType::CustomNotification, NotificationManager::NotificationLevel::WarningNotificationLevel, - Slic3r::format(_u8L("The triangle budget limited the detail: this resolution needs %1$.1f M triangles, " - "the budget kept %2$.1f M. Raise Budget or use a coarser Resolution for the full detail."), - millions(m_stats.triangles_refined), millions(m_stats.triangles_out))); + Slic3r::format(_u8L("The triangle budget limited the detail: this resolution needs %1% triangles, " + "the budget kept %2%. Raise Budget or use a coarser Resolution for the full detail."), + count(m_stats.triangles_refined), count(m_stats.triangles_out))); } } From 56092f0367fe6db00e286d024654c797d7603992 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 14:51:07 +0200 Subject: [PATCH 57/89] Fix the Windows build: near and far are macros there bridge_over_infill's helper for splitting polygons by proximity named its locals near and far. The Windows headers define both as macros that expand to nothing, so "Polygons near;" declared nothing and the uses of it did not compile. Renamed; no behaviour change. --- src/libslic3r/PrintObject.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f29881d8e4..d26bb142cb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -3533,11 +3533,13 @@ void PrintObject::bridge_over_infill() // expansion_area is a clean, non-overlapping set, so uniting it with a bridge or cutting a bridge // out of it only changes the polygons near that bridge. The rest are passed through untouched // instead of being fed to ClipperLib with the whole layer again for every candidate. - const auto split_near = [](const Polygons &polys, const BoundingBox &bbox, Polygons &far) { - Polygons near; + // Not `near`/`far`: the Windows headers still define those as macros, and they expand to + // nothing, which turns the declaration below into an empty one. + const auto split_near = [](const Polygons &polys, const BoundingBox &bbox, Polygons &rest) { + Polygons nearby; for (const Polygon &p : polys) - (get_extents(p).overlap(bbox) ? near : far).emplace_back(p); - return near; + (get_extents(p).overlap(bbox) ? nearby : rest).emplace_back(p); + return nearby; }; for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { const auto ®ion_config = candidate.region->region().config(); From 4504f315ee6461882dd777f703ce9c36e785f2ef Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 16:16:46 +0200 Subject: [PATCH 58/89] Include what the new code uses The Windows build stopped on test_kdtree.cpp: it calls std::iota without including , which libstdc++ happens to pull in anyway. Added there, and the same for and / where the recent changes rely on them being included by something else. --- src/libslic3r/ClipperUtils.cpp | 2 ++ src/libslic3r/GCode/SeamPlacer.cpp | 1 + tests/libslic3r/test_kdtree.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 51d9c0cea6..abb0433076 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include #include diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 8585f4bcd9..5d7377e242 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "libslic3r/AABBTreeLines.hpp" diff --git a/tests/libslic3r/test_kdtree.cpp b/tests/libslic3r/test_kdtree.cpp index dbcf86674a..679a1465a7 100644 --- a/tests/libslic3r/test_kdtree.cpp +++ b/tests/libslic3r/test_kdtree.cpp @@ -1,5 +1,6 @@ #include +#include #include #include From c4a19bff1d8a198e950132e507206794d8b19078 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 18:01:46 +0200 Subject: [PATCH 59/89] Give an empty patch an upright frame texture_displacement_patch_frame picks the world axis least aligned with the patch's average normal. With no patch there is no normal either, and the axis came out of the +Z fallback for it - so it was +X, while its test asked for the upright frame the fallback is meant to give. An empty patch now answers +Z outright. --- src/libslic3r/TextureDisplacement.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index 00a66271cd..f3f902e821 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2890,6 +2890,12 @@ void texture_displacement_patch_frame(const indexed_triangle_set &patch, const s } average_normal = (normal_sum.norm() > 1e-8f) ? Vec3f(normal_sum.normalized()) : Vec3f::UnitZ(); center = (count > 0) ? Vec3f(centroid_sum / float(count)) : Vec3f::Zero(); + if (count == 0) { + // Nothing to measure: an upright cylinder about the origin, rather than the axis least aligned + // with a normal that is itself only a fallback. + axis = Vec3f::UnitZ(); + return; + } // The world axis least aligned with the average normal - perpendicular to the outward radial // normal, as a cylinder's own axis would be. From 14ee1e3d3aebdab5497ce2aeeeb627d6785c9020 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 23 Sep 2026 22:53:45 +0200 Subject: [PATCH 60/89] Fix the Windows crash on a truncated PNG libpng reports a corrupt image by longjmp()ing to the jump buffer the decoder sets. Both decoders set theirs in a frame holding a PNGDescr and a vector, and with exceptions enabled MSVC unwinds the stack as part of longjmp - so returning from that frame afterwards crashed. A truncated texture segfaulted the Windows test run and was fine everywhere else. The calls that can fail now sit in two helpers that own nothing but pointers, and the C++ objects stay in the callers' frames. --- src/libslic3r/PNGReadWrite.cpp | 69 ++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/PNGReadWrite.cpp b/src/libslic3r/PNGReadWrite.cpp index adc41a164f..9f6a8b739d 100644 --- a/src/libslic3r/PNGReadWrite.cpp +++ b/src/libslic3r/PNGReadWrite.cpp @@ -61,6 +61,38 @@ static void png_read_callback(png_struct *png_ptr, png_error(png_ptr, "PNG data is truncated"); } +// libpng reports a corrupt or truncated image by longjmp()ing back to the jump buffer set with +// setjmp(). The frame it lands in must own nothing that needs destroying: with exceptions enabled +// MSVC unwinds the stack as part of longjmp, and returning from a frame unwound that way crashes - +// which is what a truncated texture did on Windows while working everywhere else. So the calls that +// can fail live in these two helpers, which hold nothing but pointers, and every C++ object the +// decoders need stays in their own frames. +static bool png_read_header_guarded(png_struct *png, png_info *info, IStream *in_buf, int sig_bytes) +{ + if (setjmp(png_jmpbuf(png))) + return false; + + png_set_read_fn(png, static_cast(in_buf), png_read_callback); + // Tell that we have already read the first bytes to check the signature + png_set_sig_bytes(png, sig_bytes); + png_read_info(png, info); + return true; +} + +// `bottom_up` fills the buffer last row first, which is the order the colour decoder hands back. +static bool png_read_rows_guarded(png_struct *png, png_info *info, png_bytep dst, size_t rows, size_t rowbytes, + bool bottom_up, bool read_end) +{ + if (setjmp(png_jmpbuf(png))) + return false; + + for (size_t i = 0; i < rows; ++i) + png_read_row(png, dst + (bottom_up ? rows - 1 - i : i) * rowbytes, nullptr); + if (read_end) + png_read_end(png, info); + return true; +} + bool decode_png(IStream &in_buf, ImageGreyscale &out_img) { static const constexpr int PNG_SIG_BYTES = 8; @@ -79,18 +111,9 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img) dsc.info = png_create_info_struct(dsc.png); if(!dsc.info) return false; - // libpng reports a corrupt or truncated image by longjmp()ing back here. Without a jump buffer - // it abort()s the whole process instead. - if (setjmp(png_jmpbuf(dsc.png))) + if (!png_read_header_guarded(dsc.png, dsc.info, &in_buf, PNG_SIG_BYTES)) return false; - png_set_read_fn(dsc.png, static_cast(&in_buf), png_read_callback); - - // Tell that we have already read the first bytes to check the signature - png_set_sig_bytes(dsc.png, PNG_SIG_BYTES); - - png_read_info(dsc.png, dsc.info); - out_img.cols = png_get_image_width(dsc.png, dsc.info); out_img.rows = png_get_image_height(dsc.png, dsc.info); size_t color_type = png_get_color_type(dsc.png, dsc.info); @@ -101,11 +124,8 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img) out_img.buf.resize(out_img.rows * out_img.cols); - auto readbuf = static_cast(out_img.buf.data()); - for (size_t r = 0; r < out_img.rows; ++r) - png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr); - - return true; + return png_read_rows_guarded(dsc.png, dsc.info, static_cast(out_img.buf.data()), out_img.rows, + out_img.cols, /* bottom_up */ false, /* read_end */ false); } bool decode_colored_png(IStream &in_buf, ImageColorscale &out_img) @@ -135,19 +155,11 @@ bool decode_colored_png(IStream &in_buf, ImageColorscale &out_img) return false; } - // See decode_png(). - if (setjmp(png_jmpbuf(dsc.png))) { + if (!png_read_header_guarded(dsc.png, dsc.info, &in_buf, PNG_SIG_BYTES)) { BOOST_LOG_TRIVIAL(error) << "decode_colored_png: corrupt or truncated PNG data"; return false; } - png_set_read_fn(dsc.png, static_cast(&in_buf), png_read_callback); - - // Tell that we have already read the first bytes to check the signature - png_set_sig_bytes(dsc.png, PNG_SIG_BYTES); - - png_read_info(dsc.png, dsc.info); - out_img.cols = png_get_image_width(dsc.png, dsc.info); out_img.rows = png_get_image_height(dsc.png, dsc.info); size_t color_type = png_get_color_type(dsc.png, dsc.info); @@ -175,13 +187,12 @@ bool decode_colored_png(IStream &in_buf, ImageColorscale &out_img) int interlace_type = png_get_interlace_type(dsc.png, dsc.info); BOOST_LOG_TRIVIAL(info) << boost::format("filter_type %1%, compression_type %2%, interlace_type %3%, rowbytes %4%")%filter_type %compression_type %interlace_type %rowbytes; - auto readbuf = static_cast(out_img.buf.data()); - for (size_t r = out_img.rows; r > 0; r--) - { - png_read_row(dsc.png, readbuf + (r - 1) * rowbytes, nullptr); + if (!png_read_rows_guarded(dsc.png, dsc.info, static_cast(out_img.buf.data()), out_img.rows, rowbytes, + /* bottom_up */ true, /* read_end */ true)) { + BOOST_LOG_TRIVIAL(error) << "decode_colored_png: corrupt or truncated PNG data"; + return false; } - png_read_end(dsc.png, dsc.info); png_destroy_read_struct(&dsc.png, &dsc.info, NULL); return true; From 517286c93d93088dd53e5866021d5e720be7dc98 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Thu, 24 Sep 2026 08:50:54 -0400 Subject: [PATCH 61/89] Apply consistent output filenames to all Snapmaker U1 presets Extend the existing U1 filename format to the base process preset and let Benchy presets inherit it. Bump the Snapmaker catalog version. Co-authored-by: Codex --- resources/profiles/Snapmaker.json | 2 +- .../process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json | 1 - resources/profiles/Snapmaker/process/fdm_process_U1.json | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index 655e322af1..8abdc0287b 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.18", + "version": "02.04.00.19", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ diff --git a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json index 2dcf03d540..c7d7abf58a 100644 --- a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json @@ -29,7 +29,6 @@ "detect_overhang_wall": "0", "reduce_crossing_wall": "0", "ensure_vertical_shell_thickness": "none", - "filename_format": "testBenchy_{print_time}.gcode", "sparse_infill_density": "10%", "sparse_infill_pattern": "alignedrectilinear", "wall_generator": "classic", diff --git a/resources/profiles/Snapmaker/process/fdm_process_U1.json b/resources/profiles/Snapmaker/process/fdm_process_U1.json index 4e0d7bf6e6..c05db3013a 100644 --- a/resources/profiles/Snapmaker/process/fdm_process_U1.json +++ b/resources/profiles/Snapmaker/process/fdm_process_U1.json @@ -28,7 +28,7 @@ "interface_shells": "0", "detect_overhang_wall": "0", "reduce_infill_retraction": "0", - "filename_format": "{input_filename_base}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_no_support_extruder]}_{int(total_weight*10) / 10.0}g_{print_time}.gcode", "wall_loops": "2", "inner_wall_line_width": "0.45", "inner_wall_speed": "40", From 7766c2e003ccac9bdf08c204bc315c4dc566476f Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Fri, 25 Sep 2026 10:31:04 +0200 Subject: [PATCH 62/89] Test that a baked texture still slices The bake is covered in the libslic3r suite, but nothing carried its result through the slicer - where a mesh it cannot use shows up as a crash or as a print with no toolpaths, which is what was reported on macOS and Linux. Paints a cube, bakes it as the gizmo commits it (mesh replaced, hull recomputed, paint cleared), slices it and checks the G-code has toolpaths and layers. Once under its budget and once capped, since a capped bake also goes through the simplification and the repair. --- tests/fff_print/CMakeLists.txt | 1 + tests/fff_print/test_texture_displacement.cpp | 149 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 tests/fff_print/test_texture_displacement.cpp diff --git a/tests/fff_print/CMakeLists.txt b/tests/fff_print/CMakeLists.txt index b52694e793..77d2576bd9 100644 --- a/tests/fff_print/CMakeLists.txt +++ b/tests/fff_print/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(${_TEST_NAME}_tests test_skirt_brim.cpp test_slicing_pipeline_hook.cpp test_support_material.cpp + test_texture_displacement.cpp test_tree_support.cpp test_trianglemesh.cpp test_wipe.cpp diff --git a/tests/fff_print/test_texture_displacement.cpp b/tests/fff_print/test_texture_displacement.cpp new file mode 100644 index 0000000000..459a42d185 --- /dev/null +++ b/tests/fff_print/test_texture_displacement.cpp @@ -0,0 +1,149 @@ +#include + +#include +#include + +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/PNGReadWrite.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TriangleSelector.hpp" + +#include "test_helpers.hpp" + +using namespace Slic3r; +using namespace Slic3r::Test; + +// What a bake leaves behind has to survive the slicer. The bake itself is covered in the libslic3r +// suite; these tests carry its result through Print::process and the G-code, which is where a mesh +// the slicer cannot use shows up - as a crash, or as a print with nothing in it. +namespace { + +// A striped height map, so the relief has real steps in it rather than a flat offset. +std::shared_ptr> stripes_png(size_t w = 32, size_t h = 32) +{ + std::vector pixels(w * h); + for (size_t y = 0; y < h; ++y) + for (size_t x = 0; x < w; ++x) + pixels[y * w + x] = ((x / 4) % 2 == 0) ? 20 : 235; + + const boost::filesystem::path tmp_path = boost::filesystem::temp_directory_path() / + boost::filesystem::unique_path("texdisp_slice_%%%%%%%%.png"); + REQUIRE(Slic3r::png::write_gray_to_file(tmp_path.string(), w, h, pixels)); + std::vector bytes; + { + std::ifstream ifs(tmp_path.string(), std::ios::binary); + bytes.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + } + boost::system::error_code ec; + boost::filesystem::remove(tmp_path, ec); + REQUIRE_FALSE(bytes.empty()); + return std::make_shared>(std::move(bytes)); +} + +// Paints every facet of `volume` with the texture layer in slot 0, as painting over the whole model +// does, and gives the layer a relief to bake. +void paint_whole_volume(ModelVolume &volume, float depth_mm, float tile_mm) +{ + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = stripes_png(); + layer.depth_mm = depth_mm; + layer.tiling_scale = tile_mm; + volume.texture_displacement_layers = { layer }; + + TriangleSelector selector(volume.mesh()); + for (int i = 0; i < int(volume.mesh().its.indices.size()); ++i) + selector.set_facet(i, EnforcerBlockerType::ENFORCER); + volume.texture_displacement_facet(0).set(selector); +} + +// The bake as the gizmo's job commits it: the mesh is replaced, the hull recomputed and the paint of +// the baked layer cleared. `budget_k` and `resolution_mm` stand in for the two panel controls. +size_t bake_into_volume(ModelVolume &volume, int budget_k, float resolution_mm) +{ + TextureDisplacementOptions options; + options.pipeline_v2 = true; + options.v2_refine_mm = resolution_mm; + options.v2_max_triangles_k = budget_k; + + TextureDisplacementFacetsData facets; + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + facets[size_t(i)] = volume.texture_displacement_facet(i).get_data(); + + TriangleMesh baked(build_texture_displacement(volume.mesh().its, volume.texture_displacement_layers, facets, options)); + REQUIRE_FALSE(baked.empty()); + + volume.set_mesh(std::move(baked)); + volume.set_new_unique_id(); + volume.calculate_convex_hull(); + for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) + volume.texture_displacement_facet(i).reset(); + return volume.mesh().its.indices.size(); +} + +// Extruding moves in the G-code: what tells a print with toolpaths from one without. +size_t count_extrusions(const std::string &gcode_text) +{ + size_t count = 0; + for (size_t pos = 0; (pos = gcode_text.find("\nG1 ", pos)) != std::string::npos; ++pos) { + const size_t eol = gcode_text.find('\n', pos + 1); + if (gcode_text.find(" E", pos) < eol) + ++count; + } + return count; +} + +} // namespace + +TEST_CASE("A baked texture slices into a print with toolpaths", "[TextureDisplacement]") +{ + Model model = Slic3r::Test::model("cube", Test::cube(20.)); + ModelVolume &volume = *model.objects.front()->volumes.front(); + paint_whole_volume(volume, /* depth */ 0.4f, /* tile */ 8.f); + + // Coarse on purpose: this test is about the result reaching the slicer, not about how fine it is. + const size_t baked_triangles = bake_into_volume(volume, /* budget */ 60, /* resolution */ 0.8f); + CHECK(baked_triangles > Test::cube(20.).its.indices.size()); // the relief added geometry + + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({ { "layer_height", "0.2" }, { "initial_layer_print_height", "0.2" } }); + + Print print; + Model sliced_model; + init_print({ volume.mesh() }, print, sliced_model, config); + REQUIRE(print.objects().size() == 1); + + print.process(); + const std::string gcode_text = Test::gcode(print); + + REQUIRE_FALSE(gcode_text.empty()); + CHECK(count_extrusions(gcode_text) > 1000); + CHECK(print.objects().front()->layer_count() > 10); +} + +TEST_CASE("A baked texture slices the same whether or not the budget capped it", "[TextureDisplacement]") +{ + // The budget decides how much of the relief survives, and a capped bake goes through the + // simplification and the repair that follow it. Both have to leave a mesh the slicer can print. + const int budget_k = GENERATE(10, 400); + + Model model = Slic3r::Test::model("cube", Test::cube(20.)); + ModelVolume &volume = *model.objects.front()->volumes.front(); + paint_whole_volume(volume, /* depth */ 0.4f, /* tile */ 8.f); + bake_into_volume(volume, budget_k, /* resolution */ 0.5f); + + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({ { "layer_height", "0.2" }, { "initial_layer_print_height", "0.2" } }); + + Print print; + Model sliced_model; + init_print({ volume.mesh() }, print, sliced_model, config); + print.process(); + + const std::string gcode_text = Test::gcode(print); + REQUIRE_FALSE(gcode_text.empty()); + CHECK(count_extrusions(gcode_text) > 1000); +} From 2fcf2222b6d899f05ac2448e00293583f0110046 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Sun, 27 Sep 2026 14:14:28 -0400 Subject: [PATCH 63/89] Refresh filament controls after loading printer presets Synchronize the plater filament controls after preset loading, even when the internal filament list already matches the nozzle count. Co-authored-by: Codex --- src/slic3r/GUI/GUI_App.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a41001f9e0..0cac129e78 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -8874,6 +8874,11 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch if (active_preset_combox) tab->reactive_preset_combo_box(); } + // Preset loading can resize the filament list without an extruder-count change event. + // Refresh the controls even when the list already matches the printer's nozzle count. + if (printer_technology == ptFFF) + this->plater()->on_filament_count_change(preset_bundle->filament_presets.size()); + // BBS: model config for (Tab *tab : model_tabs_list) if (tab->supports_printer_technology(printer_technology)) { From 6f62fe374c1bdf161100a43f1b1f031f9afe1a6c Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 29 Sep 2026 10:38:48 +0200 Subject: [PATCH 64/89] Move the slicing optimizations to their own branch They stand on their own: nothing in the texture displacement feature calls them, and nothing in them knows about textures. Reviewing a slicer-wide parallelization next to a new gizmo helped neither. They now live in perf/slicing-optimizations, based on current main. --- src/libslic3r/ClipperUtils.cpp | 67 -- src/libslic3r/ClipperUtils.hpp | 15 - src/libslic3r/Fill/Fill.cpp | 43 +- src/libslic3r/GCode/OrderingStrategies.cpp | 67 +- src/libslic3r/GCode/SeamPlacer.cpp | 31 +- src/libslic3r/KDTreeIndirect.hpp | 30 - src/libslic3r/LayerRegion.cpp | 3 +- src/libslic3r/MultiMaterialSegmentation.cpp | 346 ++------- src/libslic3r/MultiPoint.hpp | 6 +- src/libslic3r/PerimeterGenerator.cpp | 750 ++++++++++---------- src/libslic3r/Polygon.hpp | 4 +- src/libslic3r/Polyline.hpp | 4 +- src/libslic3r/PrintObject.cpp | 543 +++++--------- src/libslic3r/Support/SupportMaterial.cpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 32 +- src/libslic3r/Surface.hpp | 4 +- src/libslic3r/libslic3r.h | 8 +- tests/libslic3r/CMakeLists.txt | 1 - tests/libslic3r/test_clipper_utils.cpp | 44 -- tests/libslic3r/test_kdtree.cpp | 67 -- 20 files changed, 662 insertions(+), 1405 deletions(-) delete mode 100644 tests/libslic3r/test_kdtree.cpp diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index abb0433076..ea19eeb559 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1,11 +1,7 @@ -#include -#include #include #include #include -#include - #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "ShortestPath.hpp" @@ -817,69 +813,6 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } - -namespace ClipperUtils { - std::vector tile_expolygons(const ExPolygons &expolygons, size_t per_tile) - { - BoundingBox extent; - std::vector bboxes; - bboxes.reserve(expolygons.size()); - for (const ExPolygon &expoly : expolygons) { - bboxes.emplace_back(get_extents(expoly)); - extent.merge(bboxes.back()); - } - if (! extent.defined) - return {}; - const int tiles = std::clamp(int(std::sqrt(double(expolygons.size()) / double(std::max(per_tile, 1)))), 1, 32); - const Point size = extent.size(); - const coord_t tile_w = std::max(1, size.x() / tiles + 1), tile_h = std::max(1, size.y() / tiles + 1); - std::vector out(size_t(tiles * tiles)); - for (size_t i = 0; i < expolygons.size(); ++ i) { - const Point c = bboxes[i].center(); - ExPolygonsTile &tile = out[size_t(std::clamp(int((c.y() - extent.min.y()) / tile_h), 0, tiles - 1) * tiles + - std::clamp(int((c.x() - extent.min.x()) / tile_w), 0, tiles - 1))]; - tile.members.emplace_back(i); - tile.bbox.merge(bboxes[i]); - } - out.erase(std::remove_if(out.begin(), out.end(), [](const ExPolygonsTile &tile) { return tile.members.empty(); }), out.end()); - return out; - } -} - -static Slic3r::ExPolygons clipper_ex_by_piece(ClipperLib::ClipType clipType, const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) -{ - // A few dozen subject ExPolygons to a tile, each tile one ClipperLib call with the clip cut to the tile's box. - const std::vector tiles = ClipperUtils::tile_expolygons(subject, 32); - std::vector clip_bboxes; - clip_bboxes.reserve(clip.size()); - for (const Polygon &polygon : clip) - clip_bboxes.emplace_back(get_extents(polygon)); - - std::vector out_tiles(tiles.size()); - tbb::parallel_for(size_t(0), tiles.size(), [&](size_t tile_idx) { - const ClipperUtils::ExPolygonsTile &tile = tiles[tile_idx]; - Slic3r::ExPolygons local_subject; - local_subject.reserve(tile.members.size()); - for (size_t i : tile.members) - local_subject.emplace_back(subject[i]); - // Grown so that the cut edges of the clip stay clear of the subject, also after the safety offset. - const BoundingBox bbox = tile.bbox.inflated(SCALED_EPSILON); - Polygons local_clip; - for (size_t i = 0; i < clip.size(); ++i) - if (clip_bboxes[i].overlap(bbox)) - if (Polygon clipped = ClipperUtils::clip_clipper_polygon_with_subject_bbox(clip[i], bbox); ! clipped.empty()) - local_clip.emplace_back(std::move(clipped)); - out_tiles[tile_idx] = _clipper_ex(clipType, ClipperUtils::ExPolygonsProvider(local_subject), ClipperUtils::PolygonsProvider(local_clip), do_safety_offset); - }); - Slic3r::ExPolygons out; - for (Slic3r::ExPolygons &out_tile : out_tiles) - append(out, std::move(out_tile)); - return out; -} -Slic3r::ExPolygons diff_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) - { return clipper_ex_by_piece(ClipperLib::ctDifference, subject, clip, do_safety_offset); } -Slic3r::ExPolygons intersection_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) - { return clipper_ex_by_piece(ClipperLib::ctIntersection, subject, clip, do_safety_offset); } // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type) { return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No, fill_type); } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 0c7b7f4294..5ed161c27a 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -2,7 +2,6 @@ #define slic3r_ClipperUtils_hpp_ #include "libslic3r.h" -#include "BoundingBox.hpp" #include "clipper.hpp" #include "ExPolygon.hpp" #include "Polygon.hpp" @@ -322,15 +321,6 @@ namespace ClipperUtils { [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox, const bool get_entire_polygons = false); [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox, const bool get_entire_polygons = false); - // Splits ExPolygons into tiles by the centres of their boxes, about `per_tile` of them to a tile, to run ClipperLib on a - // layer of many pieces tile by tile. Returns the non-empty tiles, each with the indices of its ExPolygons and their box. - struct ExPolygonsTile - { - BoundingBox bbox; - std::vector members; - }; - [[nodiscard]] std::vector tile_expolygons(const ExPolygons &expolygons, size_t per_tile); - } // Perform union of input polygons using the non-zero rule, convert to ExPolygons. @@ -528,11 +518,6 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); -// diff_ex() / intersection_ex() of the subject split into tiles, each against only the part of the clip near it, the tiles in -// parallel. The same area as the operation on the whole subject when its ExPolygons do not overlap, and much faster for a -// subject of thousands of pieces spread over a layer: ClipperLib slows down with the number of edges crossing a scan line. -Slic3r::ExPolygons diff_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); -Slic3r::ExPolygons intersection_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 56d54ef681..28fabed8af 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -9,8 +9,6 @@ #include "../PrintConfig.hpp" #include "../Surface.hpp" -#include - #include "AABBTreeLines.hpp" #include "ExtrusionEntity.hpp" #include "Fill.hpp" @@ -632,28 +630,24 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n if (!line_based_pattern) { const coord_t scaled_spacing = scaled(fill.params.spacing); - // Each expolygon is split on its own, so they run in parallel and are collected in their original order. - std::vector> split_parts(fill.expolygons.size()); // normal, narrow - tbb::parallel_for(size_t(0), fill.expolygons.size(), [&](size_t idx) { - const ExPolygon &expolygon = fill.expolygons[idx]; + for (const ExPolygon &expolygon : fill.expolygons) { Polygons filled_area = to_polygons(expolygon); // "Core" area: open (erode+dilate) to drop thin features, then clamp back to the original polygon. Polygons inner_area = intersection(filled_area, opening(filled_area, scaled_spacing, scaled_spacing)); if (inner_area.empty()) { - split_parts[idx].second.emplace_back(expolygon); - return; + narrow_infill.emplace_back(expolygon); + continue; } ExPolygons inner_ex = union_ex(inner_area); ExPolygons expolys{expolygon}; - split_parts[idx].second = diff_ex(expolys, inner_ex); // narrow infill area - split_parts[idx].first = intersection_ex(expolys, inner_ex); // normal infill area - }); - for (auto &[normal_ex, narrow_ex] : split_parts) { - append(normal_infill, std::move(normal_ex)); - append(narrow_infill, std::move(narrow_ex)); + ExPolygons narrow_ex = diff_ex(expolys, inner_ex); + ExPolygons normal_ex = intersection_ex(expolys, inner_ex); + + append(normal_infill, normal_ex); // normal infill area + append(narrow_infill, narrow_ex); // narrow infill area } return; @@ -675,10 +669,7 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n } const double aligning_angle = -base_angle + PI; - // Each expolygon is reconstructed on its own, so they run in parallel and are collected in their original order. - std::vector split_reconstructed(fill.expolygons.size()); - tbb::parallel_for(size_t(0), fill.expolygons.size(), [&](size_t expolygon_idx) { - const ExPolygon &expolygon = fill.expolygons[expolygon_idx]; + for (const ExPolygon &expolygon : fill.expolygons) { Polygons filled_area = to_polygons(expolygon); polygons_rotate(filled_area, aligning_angle); BoundingBox bb = get_extents(filled_area); @@ -809,10 +800,8 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n } } - split_reconstructed[expolygon_idx] = std::move(reconstructed_area); - }); - for (Polygons &reconstructed_area : split_reconstructed) - polygons_append(normal_fill_areas, std::move(reconstructed_area)); + polygons_append(normal_fill_areas, reconstructed_area); + } polygons_rotate(normal_fill_areas, -aligning_angle); @@ -1420,15 +1409,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // Orca: Reuse the body origin used for bridge anchoring, resetting it for each surface. f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox)); - // Only the part of the layer-wide no-overlap area under this expolygon matters, so clip it to the - // expolygon's box first (padded past the safety offset, which grows the clip side). The result is - // identical; the cost is not: a layer split into many small fills, e.g. by colour painting, - // otherwise intersects every one of them with the whole layer. - BoundingBox no_overlap_bbox = get_extents(expoly); - no_overlap_bbox.offset(SCALED_EPSILON); - f->no_overlap_expolygons = intersection_ex( - ClipperUtils::clip_clipper_polygons_with_subject_bbox(surface_fill.no_overlap_expolygons, no_overlap_bbox), - ExPolygons() = {expoly}, ApplySafetyOffset::Yes); + f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes); if (params.symmetric_infill_y_axis) { params.symmetric_y_axis = f->extended_object_bounding_box().center().x(); expoly.symmetric_y(params.symmetric_y_axis); diff --git a/src/libslic3r/GCode/OrderingStrategies.cpp b/src/libslic3r/GCode/OrderingStrategies.cpp index d52a326e2e..2ba9ae5490 100644 --- a/src/libslic3r/GCode/OrderingStrategies.cpp +++ b/src/libslic3r/GCode/OrderingStrategies.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -135,79 +134,15 @@ bool tsp_remove_crossings(std::vector& path, const Points& centers) return {std::numeric_limits::max(), std::numeric_limits::max()}; }; - // For many islands, the same scan with the edges binned in a uniform grid over their boxes, so each edge is only tested against the edges sharing a - // cell with it - two edges whose boxes overlap always do. It returns the same crossing as the all-pairs scan - // (smallest i, then smallest j), so the result is unchanged; with thousands of islands on a layer the all-pairs - // scan, repeated after every reversal, never finished. Rebuilding the grid costs more than it saves on small inputs. - constexpr size_t grid_min_size = 500; - BoundingBox extent; - for (size_t idx : path) - extent.merge(centers[idx]); - const int grid_n = std::clamp(int(std::sqrt(double(pn))), 1, 256); - const coord_t cell_w = std::max(1, (extent.max.x() - extent.min.x()) / grid_n + 1); - const coord_t cell_h = std::max(1, (extent.max.y() - extent.min.y()) / grid_n + 1); - const auto for_cells = [&](const Point& a, const Point& b, auto&& fn) { - const int x0 = int((std::min(a.x(), b.x()) - extent.min.x()) / cell_w), x1 = int((std::max(a.x(), b.x()) - extent.min.x()) / cell_w); - const int y0 = int((std::min(a.y(), b.y()) - extent.min.y()) / cell_h), y1 = int((std::max(a.y(), b.y()) - extent.min.y()) / cell_h); - for (int y = y0; y <= y1; ++y) - for (int x = x0; x <= x1; ++x) - fn(y * grid_n + x); - }; - std::vector> edge_cells(size_t(grid_n) * grid_n); - - auto find_crossing_grid = [&]() -> std::pair { - for (std::vector& cell : edge_cells) - cell.clear(); - for (size_t j = 0; j < n_edges; ++j) - for_cells(centers[path[j]], centers[path[(j + 1) % pn]], [&](int cell) { edge_cells[cell].emplace_back(j); }); - - for (size_t i = 0; i < n_edges; ++i) { - const Point& ai = centers[path[i]]; - const Point& bi = centers[path[(i + 1) % pn]]; - - size_t first_j = std::numeric_limits::max(); - for_cells(ai, bi, [&](int cell) { - for (size_t j : edge_cells[cell]) { - if (j < i + 2 || j >= first_j) continue; - // Skip the (0, pn-1) pair: edges (0,1) and (pn-1,0) share node 0. - if (i == 0 && j == pn - 1) continue; - - const Point& aj = centers[path[j]]; - const Point& bj = centers[path[(j + 1) % pn]]; - - if (!bboxes_overlap(ai, bi, aj, bj)) continue; - if (Geometry::segments_intersect(ai, bi, aj, bj)) - first_j = j; - } - }); - if (first_j != std::numeric_limits::max()) - return {i, first_j}; - } - return {std::numeric_limits::max(), std::numeric_limits::max()}; - }; - // Process crossings one at a time: find first, reverse it, restart scan. // Cap iterations to prevent infinite loops on collinear/overlapping segments. int max_iters = static_cast(pn * pn); bool improved = false; - // Reversing between two segments that only touch or overlap along a line need not remove the intersection, and on - // islands laid out on a regular grid (a tiled texture, an array of parts) the loop cycled through the same orderings - // until the pn * pn cap - effectively forever. Stop as soon as an ordering repeats: until then this is the same loop. - std::unordered_set seen_paths; - const auto path_hash = [&path]() { - uint64_t h = 1469598103934665603ull; // FNV-1a - for (size_t idx : path) - h = (h ^ uint64_t(idx)) * 1099511628211ull; - return h; - }; - seen_paths.insert(path_hash()); while (max_iters-- > 0) { - auto [ci, cj] = pn >= grid_min_size ? find_crossing_grid() : find_crossing(); + auto [ci, cj] = find_crossing(); if (ci == std::numeric_limits::max()) break; improved = true; std::reverse(path.begin() + ci + 1, path.begin() + cj + 1); - if (!seen_paths.insert(path_hash()).second) - break; } return improved; } diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 04d2360f0e..d527ec6aa9 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -1179,21 +1178,21 @@ std::optional> SeamPlacer::find_next_seam_in_layer( const size_t layer_idx, const float max_distance, const SeamPlacerImpl::SeamComparator &comparator) const { using namespace SeamPlacerImpl; - // Find the best nearby point and the nearest one. A layer of a fine relief has tens of thousands of candidates within - // the radius, so they are looked at as the search finds them rather than collected into a vector first. - constexpr size_t none = std::numeric_limits::max(); - size_t best_nearby_point_index = none; - size_t nearest_point_index = none; - visit_nearby_points(*layers[layer_idx].points_tree, projected_position, max_distance, - [&layers, &comparator, &projected_position, layer_idx, &best_nearby_point_index, &nearest_point_index] - (size_t nearby_point_index) { - if (best_nearby_point_index == none) { - // The first point found starts both, as the first of the collected ones did. - best_nearby_point_index = nearest_point_index = nearby_point_index; - } + std::vector nearby_points_indices = find_nearby_points(*layers[layer_idx].points_tree, projected_position, + max_distance); + + if (nearby_points_indices.empty()) { + return {}; + } + + size_t best_nearby_point_index = nearby_points_indices[0]; + size_t nearest_point_index = nearby_points_indices[0]; + + // Now find best nearby point, nearest point, and corresponding indices + for (const size_t &nearby_point_index : nearby_points_indices) { const SeamCandidate &point = layers[layer_idx].points[nearby_point_index]; if (point.perimeter.finalized) { - return; // skip over finalized perimeters, try to find some that is not finalized + continue; // skip over finalized perimeters, try to find some that is not finalized } if (comparator.is_first_better(point, layers[layer_idx].points[best_nearby_point_index], projected_position.head<2>()) @@ -1205,10 +1204,6 @@ std::optional> SeamPlacer::find_next_seam_in_layer( || layers[layer_idx].points[nearest_point_index].perimeter.finalized) { nearest_point_index = nearby_point_index; } - }); - - if (best_nearby_point_index == none) { - return {}; } const SeamCandidate &best_nearby_point = layers[layer_idx].points[best_nearby_point_index]; diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp index 8b48ffc818..37c10827b1 100644 --- a/src/libslic3r/KDTreeIndirect.hpp +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -313,36 +313,6 @@ std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const P return visitor.result; } -// Visits the points within max_distance of center, in the order find_nearby_points() would collect them, and hands -// each of them to `visitor_fn` instead of returning them all: a search over a dense set spends more on collecting the -// points into a vector than on the search itself, and its caller usually keeps only a few of them. -template -void visit_nearby_points(const KDTreeIndirectType &kdtree, const PointType ¢er, - const typename KDTreeIndirectType::CoordType &max_distance, VisitorFn visitor_fn) -{ - using CoordType = typename KDTreeIndirectType::CoordType; - - struct Visitor { - const KDTreeIndirectType &kdtree; - const PointType center; - const CoordType max_distance_squared; - VisitorFn visitor_fn; - - unsigned int operator()(size_t idx, size_t dimension) { - auto dist = CoordType(0); - for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) { - CoordType d = center[i] - kdtree.coordinate(idx, i); - dist += d * d; - } - if (dist < max_distance_squared) - visitor_fn(idx); - return kdtree.descent_mask(center[dimension], max_distance_squared, idx, dimension); - } - } visitor { kdtree, center, max_distance * max_distance, visitor_fn }; - - kdtree.visit(visitor); -} - template std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const PointType ¢er, const typename KDTreeIndirectType::CoordType& max_distance) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 1dbdafa292..6f868ba095 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -72,11 +72,10 @@ void LayerRegion::slices_to_fill_surfaces_clipped() by_surface[size_t(surface.surface_type)].emplace_back(&surface); // Trim surfaces by the fill_boundaries. this->fill_surfaces.surfaces.clear(); - const Polygons fill_boundaries = to_polygons(this->fill_expolygons); for (size_t surface_type = 0; surface_type < size_t(stCount); ++ surface_type) { const SurfacesPtr &this_surfaces = by_surface[surface_type]; if (! this_surfaces.empty()) - this->fill_surfaces.append(intersection_ex_by_piece(to_expolygons(this_surfaces), fill_boundaries), SurfaceType(surface_type)); + this->fill_surfaces.append(intersection_ex(this_surfaces, this->fill_expolygons), SurfaceType(surface_type)); } } diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 0798838b06..6f80f7b759 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -8,7 +8,6 @@ #include "MutablePolygon.hpp" #include "format.hpp" -#include #include #include @@ -1312,15 +1311,10 @@ static inline std::vector> segmentation_top_and_bottom_l } #endif // MM_SEGMENTATION_DEBUG_TOP_BOTTOM - // When the upper surface of an object is occluded, it should no longer be considered the upper surface. - // Every (colour, layer) pair is trimmed on its own, so they all run at once: the painted faces of a finely - // textured part project hundreds of thousands of triangles onto one layer, which used to be trimmed serially. + // When the upper surface of an object is occluded, it should no longer be considered the upper surface { - const size_t occluded_pairs = num_facets_states * layers.size(); - tbb::parallel_for(tbb::blocked_range(0, occluded_pairs), [&](const tbb::blocked_range &range) { - for (size_t pair_idx = range.begin(); pair_idx < range.end(); ++pair_idx) { - const size_t extruder_idx = pair_idx / layers.size(); - const size_t layer_idx = pair_idx % layers.size(); + for (size_t extruder_idx = 0; extruder_idx < num_facets_states; ++extruder_idx) { + for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { if (!top_raw[extruder_idx].empty() && !top_raw[extruder_idx][layer_idx].empty() && layer_idx + 1 < layers.size()) { top_raw[extruder_idx][layer_idx] = diff(top_raw[extruder_idx][layer_idx], input_expolygons[layer_idx + 1]); } @@ -1328,7 +1322,7 @@ static inline std::vector> segmentation_top_and_bottom_l bottom_raw[extruder_idx][layer_idx] = diff(bottom_raw[extruder_idx][layer_idx], input_expolygons[layer_idx - 1]); } } - }); + } } std::vector> triangles_by_color_bottom(num_facets_states); @@ -1384,62 +1378,13 @@ static inline std::vector> segmentation_top_and_bottom_l return out; }; - // Projects a painted top or bottom face `ex` of layer `layer_idx` onto the shell layers below or above it (in - // `shell_layers`, nearest first), one more perimeter in on each, stopping at the first layer where nothing is left. - // Only the slices within the deepest offset of `ex` (three times that with the miter joins) decide the result, so the - // work is done per tile of `ex`'s ExPolygons on the slices cut to the tile's box grown by that much: the same result, but - // each ClipperLib call stays the size of a tile rather than of a layer cut through a fine relief, and the tiles run in - // parallel. - const auto project_to_shells = [&input_expolygons](const ExPolygons &ex, size_t layer_idx, const std::vector &shell_layers, - const LayerColorStat &stat, std::vector &dst, size_t dst_offset) { - std::vector offsets(shell_layers.size()); - float offset = 0.f; - for (size_t i = 0; i < shell_layers.size(); ++i) { - //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line - offset -= (stat.extrusion_spacing + stat.extrusion_width); - offsets[i] = offset; - } - if (offsets.empty()) - return; - const coord_t reach = coord_t(std::ceil(DefaultMiterLimit * std::abs(offsets.back()))) + 10 * SCALED_EPSILON; - const std::vector tiles = ClipperUtils::tile_expolygons(ex, 16); - // [shell layer][tile] - std::vector> shells(shell_layers.size(), std::vector(tiles.size())); - tbb::parallel_for(size_t(0), tiles.size(), [&](size_t tile_idx) { - const ClipperUtils::ExPolygonsTile &tile = tiles[tile_idx]; - const BoundingBox bbox = tile.bbox.inflated(reach); - ExPolygons tile_ex; - tile_ex.reserve(tile.members.size()); - for (size_t i : tile.members) - tile_ex.emplace_back(ex[i]); - Polygons layer_slices_trimmed = ClipperUtils::clip_clipper_polygons_with_subject_bbox(input_expolygons[layer_idx], bbox); - for (size_t i = 0; i < shell_layers.size() && ! layer_slices_trimmed.empty(); ++i) { - const ExPolygons trimmed = intersection_ex(layer_slices_trimmed, ClipperUtils::clip_clipper_polygons_with_subject_bbox(input_expolygons[shell_layers[i]], bbox)); - shells[i][tile_idx] = opening_ex(intersection_ex(tile_ex, offset_ex(trimmed, offsets[i])), stat.small_region_threshold); - layer_slices_trimmed = to_polygons(trimmed); - } - }); - for (size_t i = 0; i < shell_layers.size(); ++i) { - bool empty = true; - for (ExPolygons &shell : shells[i]) - if (! shell.empty()) { - append(dst[shell_layers[i] + dst_offset], std::move(shell)); - empty = false; - } - if (empty) - break; - } - }; - tbb::parallel_for(tbb::blocked_range(0, num_layers, granularity), [&granularity, &num_layers, &num_facets_states, &layer_color_stat, &top_raw, &triangles_by_color_top, - &throw_on_cancel_callback, &bottom_raw, &triangles_by_color_bottom, &project_to_shells, + &throw_on_cancel_callback, &input_expolygons, &bottom_raw, &triangles_by_color_bottom, &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range &range) { size_t group_idx = range.begin() / granularity; size_t layer_idx_offset = (group_idx & 1) * num_layers; for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - // Each colour writes only its own vectors, so the colours run in parallel: a painted top or bottom face - // projects onto a single layer, which otherwise did all of its colours on one thread. - tbb::parallel_for(size_t(0), size_t(num_facets_states), [&](size_t color_idx) { + for (size_t color_idx = 0; color_idx < num_facets_states; ++color_idx) { throw_on_cancel_callback(); LayerColorStat stat = layer_color_stat(layer_idx, color_idx); if (std::vector &top = top_raw[color_idx]; ! top.empty() && ! top[layer_idx].empty()) @@ -1448,10 +1393,18 @@ static inline std::vector> segmentation_top_and_bottom_l top_ex = opening_ex(top_ex, stat.small_region_threshold); if (! top_ex.empty()) { append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex); - std::vector shell_layers; - for (int last_idx = int(layer_idx) - 1; last_idx > std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) - shell_layers.emplace_back(size_t(last_idx)); - project_to_shells(top_ex, layer_idx, shell_layers, stat, shell_triangles_by_color_top[color_idx], layer_idx_offset); + float offset = 0.f; + ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; + for (int last_idx = int(layer_idx) - 1; last_idx > std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) { + //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line + //offset -= stat.extrusion_width ; + offset -= (stat.extrusion_spacing + stat.extrusion_width); + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]); + ExPolygons last = opening_ex(intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold); + if (last.empty()) + break; + append(shell_triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last)); + } } } if (std::vector &bottom = bottom_raw[color_idx]; ! bottom.empty() && ! bottom[layer_idx].empty()) @@ -1460,13 +1413,21 @@ static inline std::vector> segmentation_top_and_bottom_l bottom_ex = opening_ex(bottom_ex, stat.small_region_threshold); if (! bottom_ex.empty()) { append(triangles_by_color_bottom[color_idx][layer_idx + layer_idx_offset], bottom_ex); - std::vector shell_layers; - for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + stat.bottom_shell_layers, num_layers); ++last_idx) - shell_layers.emplace_back(last_idx); - project_to_shells(bottom_ex, layer_idx, shell_layers, stat, shell_triangles_by_color_bottom[color_idx], layer_idx_offset); + float offset = 0.f; + ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; + for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + stat.bottom_shell_layers, num_layers); ++last_idx) { + //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line + //offset -= stat.extrusion_width; + offset -= (stat.extrusion_spacing + stat.extrusion_width); + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]); + ExPolygons last = opening_ex(intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold); + if (last.empty()) + break; + append(shell_triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last)); + } } } - }); + } } }); @@ -1476,25 +1437,22 @@ static inline std::vector> segmentation_top_and_bottom_l &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { throw_on_cancel_callback(); - // The per-colour unions below are independent of each other, so they run in parallel (a painted top or - // bottom face puts all of its colours on one layer); whatever combines the colours stays in colour order. - const auto merge_colour_union = [&](size_t color_idx) { + ExPolygons painted_exploys; + for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx + num_layers])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers])); self = union_ex(self); - }; - tbb::parallel_for(size_t(0), triangles_by_color_merged.size(), merge_colour_union); - ExPolygons painted_exploys; - for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) - append(painted_exploys, triangles_by_color_merged[color_idx][layer_idx]); + append(painted_exploys, self); + } + painted_exploys = union_ex(painted_exploys); //BBS: merge the top and bottom shell layers - tbb::parallel_for(size_t(0), triangles_by_color_merged.size(), [&](size_t color_idx) { + for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; auto top_area = diff_ex(union_ex(shell_triangles_by_color_top[color_idx][layer_idx], @@ -1508,7 +1466,7 @@ static inline std::vector> segmentation_top_and_bottom_l append(self, top_area); append(self, bottom_area); self = union_ex(self); - }); + } // Trim one region by the other if some of the regions overlap. ExPolygons painted_regions; for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { @@ -1875,69 +1833,7 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } - -// Finds the islands (layer ExPolygons) a region piece overlaps. A top or bottom region is projected from the neighbouring -// layers and may reach past the island it belongs to, or over several islands. -class IslandLocator -{ -public: - explicit IslandLocator(const ExPolygons &islands) : m_islands(islands) - { - m_bboxes.reserve(islands.size()); - for (const ExPolygon &island : islands) { - m_bboxes.emplace_back(get_extents(island)); - m_extent.merge(m_bboxes.back()); - } - if (!m_extent.defined) - return; - const Point size = m_extent.size(); - m_cell_w = std::max(1, size.x() / GRID + 1); - m_cell_h = std::max(1, size.y() / GRID + 1); - m_grid.assign(GRID * GRID, {}); - for (size_t i = 0; i < m_bboxes.size(); ++i) - for_cells(m_bboxes[i], [&](int cell) { m_grid[cell].emplace_back(i); }); - } - - void find(const ExPolygon &piece, std::vector &out) const - { - out.clear(); - const BoundingBox bbox = get_extents(piece); - if (!m_extent.defined || !m_extent.overlap(bbox)) - return; - for_cells(bbox, [&](int cell) { - for (size_t i : m_grid[cell]) - if (m_bboxes[i].overlap(bbox)) - out.emplace_back(i); - }); - sort_remove_duplicates(out); - if (out.size() > 1) - out.erase(std::remove_if(out.begin(), out.end(), [&](size_t i) { - const BoundingBox common(m_bboxes[i].min.cwiseMax(bbox.min), m_bboxes[i].max.cwiseMin(bbox.max)); - return intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(piece, common.inflated(SCALED_EPSILON)), - ClipperUtils::clip_clipper_polygons_with_subject_bbox(m_islands[i], common.inflated(SCALED_EPSILON))).empty(); - }), out.end()); - } - -private: - static constexpr int GRID = 64; - template void for_cells(const BoundingBox &bb, Fn &&fn) const - { - const int x0 = std::clamp(int((bb.min.x() - m_extent.min.x()) / m_cell_w), 0, GRID - 1), x1 = std::clamp(int((bb.max.x() - m_extent.min.x()) / m_cell_w), 0, GRID - 1); - const int y0 = std::clamp(int((bb.min.y() - m_extent.min.y()) / m_cell_h), 0, GRID - 1), y1 = std::clamp(int((bb.max.y() - m_extent.min.y()) / m_cell_h), 0, GRID - 1); - for (int y = y0; y <= y1; ++y) - for (int x = x0; x <= x1; ++x) - fn(y * GRID + x); - } - - const ExPolygons &m_islands; - std::vector m_bboxes; - BoundingBox m_extent; - coord_t m_cell_w = 1, m_cell_h = 1; - std::vector> m_grid; -}; - -static std::vector> merge_segmented_layers(const std::vector &input_expolygons, - const std::vector> &segmented_regions, +static std::vector> merge_segmented_layers(const std::vector> &segmented_regions, std::vector> &&top_and_bottom_layers, const size_t num_facets_states, const std::function &throw_on_cancel_callback) @@ -1948,91 +1844,33 @@ static std::vector> merge_segmented_layers(const std::ve assert(!top_and_bottom_layers.size() || num_facets_states == top_and_bottom_layers.size()); BOOST_LOG_TRIVIAL(debug) << "Print object segmentation - Merging segmented layers in parallel - Begin"; - // Every region of a layer is merged together with the regions of the islands it overlaps, and the islands are further - // apart than the dimple removal below reaches, so this gives the same result as merging the layer at once. On a layer - // cut through a fine relief every region shares thousands of hole contours with every other, and ClipperLib, splitting - // and re-linking one huge polygon over and over, took anything up to half an hour for a layer; per island each operation - // stays the size of the island, and the islands run in parallel. - tbb::parallel_for(tbb::blocked_range(0, num_layers), [&](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_facets_states, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { assert(segmented_regions[layer_idx].size() == num_facets_states); - throw_on_cancel_callback(); - // Group the islands joined by a region overlapping several of them; the last group takes the regions lying - // outside every island. - const ExPolygons &islands = input_expolygons[layer_idx]; - const IslandLocator locator(islands); - std::vector parent(islands.size() + 1); - std::iota(parent.begin(), parent.end(), 0); - const auto root = [&parent](size_t i) { - while (parent[i] != i) - i = parent[i] = parent[parent[i]]; - return i; - }; - // Islands of every piece: side regions of colours 1.., then top/bottom regions of colours 0.. - std::vector pieces; - for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) - for (const ExPolygon &piece : segmented_regions[layer_idx][extruder_id]) - pieces.emplace_back(&piece); - if (!top_and_bottom_layers.empty()) - for (size_t color_idx = 0; color_idx < num_facets_states; ++color_idx) - for (const ExPolygon &piece : top_and_bottom_layers[color_idx][layer_idx]) - pieces.emplace_back(&piece); - std::vector> overlapped(pieces.size()); - tbb::parallel_for(size_t(0), pieces.size(), [&](size_t i) { locator.find(*pieces[i], overlapped[i]); }); - std::vector piece_island(pieces.size()); - for (size_t i = 0; i < pieces.size(); ++i) { - piece_island[i] = overlapped[i].empty() ? islands.size() : overlapped[i].front(); - for (size_t island : overlapped[i]) - parent[root(island)] = root(piece_island[i]); - } - std::vector bucket_of(parent.size(), size_t(-1)); - size_t num_buckets = 0; - for (size_t i = 0; i < parent.size(); ++i) - if (size_t &b = bucket_of[root(i)]; b == size_t(-1)) - b = num_buckets++; - - // [bucket][colour] - std::vector> sides(num_buckets, std::vector(num_facets_states)); - std::vector> tops(num_buckets, std::vector(num_facets_states)); - size_t piece_idx = 0; - for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) - for (const ExPolygon &piece : segmented_regions[layer_idx][extruder_id]) - sides[bucket_of[root(piece_island[piece_idx++])]][extruder_id].emplace_back(piece); - if (!top_and_bottom_layers.empty()) - for (size_t color_idx = 0; color_idx < num_facets_states; ++color_idx) - for (const ExPolygon &piece : top_and_bottom_layers[color_idx][layer_idx]) - tops[bucket_of[root(piece_island[piece_idx++])]][color_idx].emplace_back(piece); - - // Side regions minus the top/bottom regions of every colour. - std::vector> merged(num_buckets, std::vector(num_facets_states)); - tbb::parallel_for(size_t(0), num_buckets, [&](size_t bucket) { - Polygons tops_all; - for (const ExPolygons &t : tops[bucket]) - polygons_append(tops_all, t); - for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) - if (!sides[bucket][extruder_id].empty()) - merged[bucket][extruder_id] = tops_all.empty() ? std::move(sides[bucket][extruder_id]) : - diff_ex_by_piece(sides[bucket][extruder_id], tops_all); - }); - - // Then this colour's top/bottom regions, with the dimples removed (#7235) when the layer has side regions left. + // Zero is skipped because it is the default color of the volume for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) { - if (top_and_bottom_layers.empty() || top_and_bottom_layers[extruder_id][layer_idx].empty()) { - for (size_t bucket = 0; bucket < num_buckets; ++bucket) - append(segmented_regions_merged[layer_idx][extruder_id - 1], std::move(merged[bucket][extruder_id])); - continue; + throw_on_cancel_callback(); + if (!segmented_regions[layer_idx][extruder_id].empty()) { + ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id]; + if (!top_and_bottom_layers.empty()) { + for (const std::vector &top_and_bottom_by_extruder : top_and_bottom_layers) { + if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty()) { + segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]); + } + } + } + + segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); + } + + if (!top_and_bottom_layers.empty() && !top_and_bottom_layers[extruder_id][layer_idx].empty()) { + bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty(); + append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + + // Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers. + if (!was_top_and_bottom_empty) + segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), float(SCALED_EPSILON), -float(SCALED_EPSILON)); } - bool was_top_and_bottom_empty = true; - for (size_t bucket = 0; bucket < num_buckets && was_top_and_bottom_empty; ++bucket) - was_top_and_bottom_empty = merged[bucket][extruder_id].empty(); - tbb::parallel_for(size_t(0), num_buckets, [&](size_t bucket) { - ExPolygons ®ion = merged[bucket][extruder_id]; - append(region, tops[bucket][extruder_id]); - if (!was_top_and_bottom_empty && !region.empty()) - region = offset2_ex(union_ex(region), float(SCALED_EPSILON), -float(SCALED_EPSILON)); - }); - for (size_t bucket = 0; bucket < num_buckets; ++bucket) - append(segmented_regions_merged[layer_idx][extruder_id - 1], std::move(merged[bucket][extruder_id])); } } }); // end of parallel_for @@ -2319,56 +2157,16 @@ std::vector> segmentation_by_painting(const PrintObject assert(!color_poly.empty()); assert(!color_poly.front().empty()); - // Each island (an ExPolygon with its holes) is segmented on its own. Any point of an island is closer to - // that island's contours than to any other island's - the way out crosses its own boundary first - so its - // Voronoi cells, and with them its colour regions, depend on nothing else. A layer cut through a fine relief - // has thousands of islands, and one Voronoi diagram over all of them degenerated into overlapping regions - // that every boolean afterwards had to untangle. Per island the diagrams stay small and the islands run in - // parallel; an island in a single colour needs no diagram at all. - const ExPolygons &islands = input_expolygons[layer_idx]; - std::vector> island_contours(islands.size()); // [first, last) into color_poly - { - // The same order EdgeGrid::Grid::create() lists the contours in, and so colorize_contours(). - size_t idx = 0; - for (size_t island_idx = 0; island_idx < islands.size(); ++island_idx) { - const size_t first = idx; - if (!islands[island_idx].contour.empty()) - ++idx; - for (const Polygon &hole : islands[island_idx].holes) - if (!hole.empty()) - ++idx; - island_contours[island_idx] = {first, idx}; - } - assert(idx == color_poly.size()); + if (has_layer_only_one_color(color_poly)) { + // If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer. + segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx]; + } else { + MMU_Graph graph = build_graph(layer_idx, color_poly); + remove_multiple_edges_in_vertices(graph, color_poly); + graph.remove_nodes_with_one_arc(); + segmented_regions[layer_idx] = extract_colored_segments(graph, num_facets_states); + //segmented_regions[layer_idx] = extract_colored_segments(color_poly, num_extruders, layer_idx); } - std::vector> island_regions(islands.size()); - tbb::parallel_for(size_t(0), islands.size(), [&](size_t island_idx) { - const auto [first, last] = island_contours[island_idx]; - if (first == last) - return; - const std::vector island_poly(color_poly.begin() + first, color_poly.begin() + last); - std::vector ®ions = island_regions[island_idx]; - if (has_layer_only_one_color(island_poly)) { - regions.assign(num_facets_states, ExPolygons()); - regions[size_t(island_poly.front().front().color)].emplace_back(islands[island_idx]); - } else { - MMU_Graph graph = build_graph(layer_idx, island_poly); - remove_multiple_edges_in_vertices(graph, island_poly); - graph.remove_nodes_with_one_arc(); - regions = extract_colored_segments(graph, num_facets_states); - // The faces of one colour tile it without overlapping; merged here, where an island is small, - // every later boolean gets a few regions instead of thousands of faces sharing their edges. An - // island with many holes keeps its faces: merged, each colour would be one region with thousands - // of holes, and subtracting from that is far slower than from the faces one at a time. - if (island_poly.size() <= 64) - for (ExPolygons &faces : regions) - if (faces.size() > 1) - faces = union_ex(faces); - } - }); - for (std::vector ®ions : island_regions) - for (size_t color_idx = 0; color_idx < regions.size(); ++color_idx) - append(segmented_regions[layer_idx][color_idx], std::move(regions[color_idx])); #ifdef MM_SEGMENTATION_DEBUG_REGIONS export_regions_to_svg(debug_out_path("3-mm-regions-sides-%d-%d.svg", layer_idx, iRun), segmented_regions[layer_idx], input_expolygons[layer_idx]); @@ -2391,7 +2189,7 @@ std::vector> segmentation_by_painting(const PrintObject throw_on_cancel_callback(); } - std::vector> segmented_regions_merged = merge_segmented_layers(input_expolygons, segmented_regions, std::move(top_and_bottom_layers), num_facets_states, throw_on_cancel_callback); + std::vector> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), num_facets_states, throw_on_cancel_callback); throw_on_cancel_callback(); #ifdef MM_SEGMENTATION_DEBUG_REGIONS diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index b4f8af0871..096230d22c 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -19,13 +19,11 @@ public: MultiPoint() {} MultiPoint(const MultiPoint &other) : points(other.points) {} - MultiPoint(MultiPoint &&other) noexcept : points(std::move(other.points)) {} + MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} MultiPoint(std::initializer_list list) : points(list) {} explicit MultiPoint(const Points &_points) : points(_points) {} - // Without it, the derived classes' move constructors passing std::move(points) here copied them. - explicit MultiPoint(Points &&_points) noexcept : points(std::move(_points)) {} MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; } - MultiPoint& operator=(MultiPoint &&other) noexcept { points = std::move(other.points); return *this; } + MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; } virtual ~MultiPoint() = default; void scale(double factor); void scale(double factor_x, double factor_y); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 4fec834171..1f1f1289e5 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include "libslic3r/AABBTreeLines.hpp" #include "Print.hpp" static const int overhang_sampling_number = 6; @@ -2483,443 +2481,421 @@ void PerimeterGenerator::process_arachne() const bool only_one_wall_first_layer = this->config->only_one_wall_first_layer && has_bottom_shell_layers(*this->config); // we need to process each island separately because we might have different // extra perimeters for each one - // Each island is independent up to its outputs, so they are generated in parallel - a layer split into thousands - // of islands (e.g. by colour painting) otherwise ran on one thread - and the outputs are then committed in the - // original island order, which is what the extra overhang perimeters (applied to the last island's loops and to - // all fill surfaces so far) depend on. - struct ArachneSurfaceResult - { - ExtrusionEntityCollection loops; - bool has_loops = false; - ExPolygons infill; - ExPolygons no_overlap; - }; - std::vector results(all_surfaces.size()); - tbb::parallel_for(tbb::blocked_range(0, all_surfaces.size()), [&](const tbb::blocked_range &range) { - for (size_t surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { - const Surface &surface = all_surfaces[surface_idx]; - ArachneSurfaceResult &result = results[surface_idx]; - coord_t bead_width_0 = ext_perimeter_spacing; - // detect how many perimeters must be generated for this island - int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops - int sparse_infill_density = this->config->sparse_infill_density.value; - if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0) // add alternating extra wall - loop_number++; + for (const Surface& surface : all_surfaces) { + coord_t bead_width_0 = ext_perimeter_spacing; + // detect how many perimeters must be generated for this island + int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops + int sparse_infill_density = this->config->sparse_infill_density.value; + if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0) // add alternating extra wall + loop_number++; - // Set the bottommost layer to be one wall - const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false; - if (is_bottom_layer && only_one_wall_first_layer) - loop_number = 0; + // Set the bottommost layer to be one wall + const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false; + if (is_bottom_layer && only_one_wall_first_layer) + loop_number = 0; - // Orca: set the topmost layer to be one wall according to the config - const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false; - if (is_topmost_layer && loop_number > 0 && only_one_wall_top) - loop_number = 0; + // Orca: set the topmost layer to be one wall according to the config + const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false; + if (is_topmost_layer && loop_number > 0 && only_one_wall_top) + loop_number = 0; - auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter; - // Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled. - ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), - apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing ) - : -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); + auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter; + // Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled. + ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), + apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing ) + : -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); - Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config); - // Set params is_top_or_bottom_layer for adjusting short-wall removal sensitivity. - input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false; + Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config); + // Set params is_top_or_bottom_layer for adjusting short-wall removal sensitivity. + input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false; - coord_t wall_0_inset = 0; - if (apply_precise_outer_wall) - wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2); + coord_t wall_0_inset = 0; + if (apply_precise_outer_wall) + wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2); - //PS: One wall top surface for Arachne - ExPolygons top_expolygons; - // Calculate how many inner loops remain when TopSurfaces is selected. - const int inner_loop_number = (only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1; + //PS: One wall top surface for Arachne + ExPolygons top_expolygons; + // Calculate how many inner loops remain when TopSurfaces is selected. + const int inner_loop_number = (only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1; - // Set one perimeter when TopSurfaces is selected. - if (only_one_wall_top && loop_number > 0) - loop_number = 0; + // Set one perimeter when TopSurfaces is selected. + if (only_one_wall_top && loop_number > 0) + loop_number = 0; - Arachne::WallToolPathsParams input_params_tmp = input_params; + Arachne::WallToolPathsParams input_params_tmp = input_params; - Polygons last_p = to_polygons(last); - Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1), - wall_0_inset, layer_height, input_params_tmp); - std::vector perimeters = wallToolPaths.getToolPaths(); - ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); + Polygons last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1), + wall_0_inset, layer_height, input_params_tmp); + std::vector perimeters = wallToolPaths.getToolPaths(); + ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); - // Check if there are some remaining perimeters to generate (the number of perimeters - // is greater than one together with enabled the single perimeter on top surface feature). - if (inner_loop_number >= 0) { - assert(upper_slices != nullptr); + // Check if there are some remaining perimeters to generate (the number of perimeters + // is greater than one together with enabled the single perimeter on top surface feature). + if (inner_loop_number >= 0) { + assert(upper_slices != nullptr); - // Infill contour bounding box. - BoundingBox infill_contour_bbox = get_extents(infill_contour); - infill_contour_bbox.offset(SCALED_EPSILON); + // Infill contour bounding box. + BoundingBox infill_contour_bbox = get_extents(infill_contour); + infill_contour_bbox.offset(SCALED_EPSILON); - coord_t perimeter_width = this->perimeter_flow.scaled_width(); + coord_t perimeter_width = this->perimeter_flow.scaled_width(); - // Get top ExPolygons from current infill contour. - Polygons upper_slices_clipped; - if (object_config->interface_shells) { - auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces); - upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, infill_contour_bbox); - } else - upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox); + // Get top ExPolygons from current infill contour. + Polygons upper_slices_clipped; + if (object_config->interface_shells) { + auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces); + upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, infill_contour_bbox); + } else + upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox); - top_expolygons = diff_ex(infill_contour, upper_slices_clipped); + top_expolygons = diff_ex(infill_contour, upper_slices_clipped); - if (!top_expolygons.empty()) { - if (lower_slices != nullptr) { - const float bridge_offset = float(std::max(ext_perimeter_spacing, perimeter_width)); - const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox); - const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset); + if (!top_expolygons.empty()) { + if (lower_slices != nullptr) { + const float bridge_offset = float(std::max(ext_perimeter_spacing, perimeter_width)); + const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox); + const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset); - // Remove bridges from top surface polygons. - top_expolygons = diff_ex(top_expolygons, current_slices_bridges); - } - - // Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line. - // ORCA: skip if the top surface area is smaller than "min_width_top_surface" - const float top_surface_min_width = std::max(float(ext_perimeter_spacing) / 4.f + scaled(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f); - // Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little. - // ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount, - // not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions - // due to thin lines being generated - top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85)); - - // Get final top ExPolygons (bridges were excluded above, so they stay walled). - top_expolygons = intersection_ex(top_expolygons, infill_contour); - - // ORCA: onion the real region (inside the outer wall) so the remaining walls follow the actual - // geometry, then cut away the parts over the top surface. Re-onioning the non-top complement - // instead - the fallback when there is no top fill - walls the top/non-top interface and rings - // top-surface islands with inner walls that don't exist when the feature is disabled. - const bool clip_walls_over_top = top_fill_replaces_inner_walls(*this->config); - const Polygons inner_region = to_polygons(offset_ex(clip_walls_over_top ? infill_contour - : diff_ex(infill_contour, top_expolygons), - wall_0_inset)); - Arachne::WallToolPaths inner_wall_tool_paths(inner_region, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp); - std::vector inner_perimeters = inner_wall_tool_paths.getToolPaths(); - - if (clip_walls_over_top) { - Polygons kept_over_top; - clip_inner_walls_over_top(inner_perimeters, top_expolygons, perimeter_width, kept_over_top); - // Route the top fill around the walls kept despite grazing the top. - if (! kept_over_top.empty()) - top_expolygons = diff_ex(top_expolygons, kept_over_top); - } - - // Recalculate indexes of inner perimeters before merging them: they come after the single outer wall. - if (!perimeters.empty()) - for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) - for (Arachne::ExtrusionLine &el : inner_perimeter) - ++el.inset_idx; - - perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end()); - infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour()); - } else { - // There is no top surface ExPolygon, so we call Arachne again with parameters - // like when the single perimeter feature is disabled. - Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp); - perimeters = no_single_perimeter_tool_paths.getToolPaths(); - infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour()); + // Remove bridges from top surface polygons. + top_expolygons = diff_ex(top_expolygons, current_slices_bridges); } + + // Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line. + // ORCA: skip if the top surface area is smaller than "min_width_top_surface" + const float top_surface_min_width = std::max(float(ext_perimeter_spacing) / 4.f + scaled(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f); + // Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little. + // ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount, + // not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions + // due to thin lines being generated + top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85)); + + // Get final top ExPolygons (bridges were excluded above, so they stay walled). + top_expolygons = intersection_ex(top_expolygons, infill_contour); + + // ORCA: onion the real region (inside the outer wall) so the remaining walls follow the actual + // geometry, then cut away the parts over the top surface. Re-onioning the non-top complement + // instead - the fallback when there is no top fill - walls the top/non-top interface and rings + // top-surface islands with inner walls that don't exist when the feature is disabled. + const bool clip_walls_over_top = top_fill_replaces_inner_walls(*this->config); + const Polygons inner_region = to_polygons(offset_ex(clip_walls_over_top ? infill_contour + : diff_ex(infill_contour, top_expolygons), + wall_0_inset)); + Arachne::WallToolPaths inner_wall_tool_paths(inner_region, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp); + std::vector inner_perimeters = inner_wall_tool_paths.getToolPaths(); + + if (clip_walls_over_top) { + Polygons kept_over_top; + clip_inner_walls_over_top(inner_perimeters, top_expolygons, perimeter_width, kept_over_top); + // Route the top fill around the walls kept despite grazing the top. + if (! kept_over_top.empty()) + top_expolygons = diff_ex(top_expolygons, kept_over_top); + } + + // Recalculate indexes of inner perimeters before merging them: they come after the single outer wall. + if (!perimeters.empty()) + for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) + for (Arachne::ExtrusionLine &el : inner_perimeter) + ++el.inset_idx; + + perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end()); + infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour()); + } else { + // There is no top surface ExPolygon, so we call Arachne again with parameters + // like when the single perimeter feature is disabled. + Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp); + perimeters = no_single_perimeter_tool_paths.getToolPaths(); + infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour()); } - //PS + } + //PS - loop_number = int(perimeters.size()) - 1; + loop_number = int(perimeters.size()) - 1; - #ifdef ARACHNE_DEBUG - { - static int iRun = 0; - export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour())); - } - #endif + #ifdef ARACHNE_DEBUG + { + static int iRun = 0; + export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour())); + } +#endif - // All closed ExtrusionLine should have the same the first and the last point. - // But in rare cases, Arachne produce ExtrusionLine marked as closed but without - // equal the first and the last point. - assert([&perimeters = std::as_const(perimeters)]() -> bool { - for (const Arachne::VariableWidthLines& perimeter : perimeters) - for (const Arachne::ExtrusionLine& el : perimeter) - if (el.is_closed && el.junctions.front().p != el.junctions.back().p) - return false; - return true; - }()); + // All closed ExtrusionLine should have the same the first and the last point. + // But in rare cases, Arachne produce ExtrusionLine marked as closed but without + // equal the first and the last point. + assert([&perimeters = std::as_const(perimeters)]() -> bool { + for (const Arachne::VariableWidthLines& perimeter : perimeters) + for (const Arachne::ExtrusionLine& el : perimeter) + if (el.is_closed && el.junctions.front().p != el.junctions.back().p) + return false; + return true; + }()); - int start_perimeter = int(perimeters.size()) - 1; - int end_perimeter = -1; - int direction = -1; + int start_perimeter = int(perimeters.size()) - 1; + int end_perimeter = -1; + int direction = -1; - bool is_outer_wall_first = - this->config->wall_sequence == WallSequence::OuterInner || - this->config->wall_sequence == WallSequence::InnerOuterInner; + bool is_outer_wall_first = + this->config->wall_sequence == WallSequence::OuterInner || + this->config->wall_sequence == WallSequence::InnerOuterInner; - if (layer_id == 0){ // disable inner outer inner algorithm after the first layer - is_outer_wall_first = - this->config->wall_sequence == WallSequence::OuterInner; - } - if (is_outer_wall_first) { - start_perimeter = 0; - end_perimeter = int(perimeters.size()); - direction = 1; + if (layer_id == 0){ // disable inner outer inner algorithm after the first layer + is_outer_wall_first = + this->config->wall_sequence == WallSequence::OuterInner; + } + if (is_outer_wall_first) { + start_perimeter = 0; + end_perimeter = int(perimeters.size()); + direction = 1; + } + + std::vector all_extrusions; + for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) { + if (perimeters[perimeter_idx].empty()) + continue; + for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx]) + all_extrusions.emplace_back(&wall); + } + + // Find topological order with constraints from extrusions_constrains. + std::vector blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. + std::vector> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. + std::unordered_map map_extrusion_to_idx; + for (size_t idx = 0; idx < all_extrusions.size(); idx++) + map_extrusion_to_idx.emplace(all_extrusions[idx], idx); + + auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, is_outer_wall_first); + for (auto [before, after] : extrusions_constrains) { + auto after_it = map_extrusion_to_idx.find(after); + ++blocked[after_it->second]; + blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second); + } + + std::vector processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed. + Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position. + std::vector ordered_extrusions; // To store our result in. At the end we'll std::swap. + ordered_extrusions.reserve(all_extrusions.size()); + + while (ordered_extrusions.size() < all_extrusions.size()) { + size_t best_candidate = 0; + double best_distance_sqr = std::numeric_limits::max(); + bool is_best_closed = false; + + std::vector available_candidates; + for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) { + if (processed[candidate] || blocked[candidate]) + continue; // Not a valid candidate. + available_candidates.push_back(candidate); } - std::vector all_extrusions; - for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) { - if (perimeters[perimeter_idx].empty()) + std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool { + return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed; + }); + + for (const size_t candidate_path_idx : available_candidates) { + auto& path = all_extrusions[candidate_path_idx]; + + if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. + if (best_distance_sqr == std::numeric_limits::max()) { + best_candidate = candidate_path_idx; + is_best_closed = path->is_closed; + } continue; - for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx]) - all_extrusions.emplace_back(&wall); - } - - // Find topological order with constraints from extrusions_constrains. - std::vector blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. - std::vector> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. - std::unordered_map map_extrusion_to_idx; - for (size_t idx = 0; idx < all_extrusions.size(); idx++) - map_extrusion_to_idx.emplace(all_extrusions[idx], idx); - - auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, is_outer_wall_first); - for (auto [before, after] : extrusions_constrains) { - auto after_it = map_extrusion_to_idx.find(after); - ++blocked[after_it->second]; - blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second); - } - - std::vector processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed. - Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position. - std::vector ordered_extrusions; // To store our result in. At the end we'll std::swap. - ordered_extrusions.reserve(all_extrusions.size()); - - while (ordered_extrusions.size() < all_extrusions.size()) { - size_t best_candidate = 0; - double best_distance_sqr = std::numeric_limits::max(); - bool is_best_closed = false; - - std::vector available_candidates; - for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) { - if (processed[candidate] || blocked[candidate]) - continue; // Not a valid candidate. - available_candidates.push_back(candidate); } - std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool { - return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed; - }); - - for (const size_t candidate_path_idx : available_candidates) { - auto& path = all_extrusions[candidate_path_idx]; - - if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. - if (best_distance_sqr == std::numeric_limits::max()) { - best_candidate = candidate_path_idx; - is_best_closed = path->is_closed; - } - continue; - } - - const Point candidate_position = path->junctions.front().p; - double distance_sqr = (current_position - candidate_position).cast().norm(); - if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far. - if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits::max()) || (!path->is_closed && !is_best_closed)) { - best_candidate = candidate_path_idx; - best_distance_sqr = distance_sqr; - is_best_closed = path->is_closed; - } + const Point candidate_position = path->junctions.front().p; + double distance_sqr = (current_position - candidate_position).cast().norm(); + if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far. + if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits::max()) || (!path->is_closed && !is_best_closed)) { + best_candidate = candidate_path_idx; + best_distance_sqr = distance_sqr; + is_best_closed = path->is_closed; } } - - auto& best_path = all_extrusions[best_candidate]; - ordered_extrusions.push_back({ best_path, best_path->is_contour() }); - processed[best_candidate] = true; - for (size_t unlocked_idx : blocking[best_candidate]) - blocked[unlocked_idx]--; - - if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then. - if (best_path->is_closed) - current_position = best_path->junctions[0].p; //We end where we started. - else - current_position = best_path->junctions.back().p; //Pick the other end from where we started. - } } - // printf("New Layer: Layer ID %d\n",layer_id); //debug - new layer - if (this->config->wall_sequence == WallSequence::InnerOuterInner && layer_id > 0) { // only enable inner outer inner algorithm after first layer - if (ordered_extrusions.size() > 2) { // 3 walls minimum needed to do inner outer inner ordering - int position = 0; // index to run the re-ordering for multiple external perimeters in a single island. - int arr_i, arr_j = 0; // indexes to run through the walls in the for loops - int outer, first_internal, second_internal, max_internal, current_perimeter; // allocate index values - - // To address any remaining scenarios where the outer perimeter contour is not first on the list as arachne sometimes reorders the perimeters when clustering - // for OI mode that is used the basis for IOI - bringContoursToFront(ordered_extrusions); - std::vector reordered_extrusions; - - // Debug statement to print spacing values: - //printf("External threshold - Ext perimeter: %d Ext spacing: %d Int perimeter: %d Int spacing: %d\n", this->ext_perimeter_flow.scaled_width(),this->ext_perimeter_flow.scaled_spacing(),this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_spacing()); + auto& best_path = all_extrusions[best_candidate]; + ordered_extrusions.push_back({ best_path, best_path->is_contour() }); + processed[best_candidate] = true; + for (size_t unlocked_idx : blocking[best_candidate]) + blocked[unlocked_idx]--; - // Get searching thresholds. For an external perimeter we take the external perimeter spacing/2 plus the internal perimeter spacing/2 and expand by the factor - // rounding errors. When precise wall is enabled, the external perimeter full spacing is used. - coord_t threshold_external = (apply_precise_outer_wall) - // Precise outer wall ⇒ use “full external spacing” - ? ( this->ext_perimeter_flow.scaled_spacing() - + this->perimeter_flow.scaled_spacing()/2.0 ) - // Normal ⇒ half ext spacing + half int spacing - : ( this->ext_perimeter_flow.scaled_spacing()/2.0 - + this->perimeter_flow.scaled_spacing()/2.0 ); + if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then. + if (best_path->is_closed) + current_position = best_path->junctions[0].p; //We end where we started. + else + current_position = best_path->junctions.back().p; //Pick the other end from where we started. + } + } + + // printf("New Layer: Layer ID %d\n",layer_id); //debug - new layer + if (this->config->wall_sequence == WallSequence::InnerOuterInner && layer_id > 0) { // only enable inner outer inner algorithm after first layer + if (ordered_extrusions.size() > 2) { // 3 walls minimum needed to do inner outer inner ordering + int position = 0; // index to run the re-ordering for multiple external perimeters in a single island. + int arr_i, arr_j = 0; // indexes to run through the walls in the for loops + int outer, first_internal, second_internal, max_internal, current_perimeter; // allocate index values - // For the intenal perimeter threshold, the distance is the internal perimeter spacing expanded by the factor to cover rounding errors. - coord_t threshold_internal = this->perimeter_flow.scaled_spacing(); + // To address any remaining scenarios where the outer perimeter contour is not first on the list as arachne sometimes reorders the perimeters when clustering + // for OI mode that is used the basis for IOI + bringContoursToFront(ordered_extrusions); + std::vector reordered_extrusions; - // Re-order extrusions based on distance - // Alorithm will aggresively optimise for the appearance of the outermost perimeter - ordered_extrusions = reorderPerimetersByProximity(ordered_extrusions,threshold_external,threshold_internal ); - reordered_extrusions = ordered_extrusions; // copy them into the reordered extrusions vector to allow for IOI operations to be performed below without altering the base ordered extrusions list. + // Debug statement to print spacing values: + //printf("External threshold - Ext perimeter: %d Ext spacing: %d Int perimeter: %d Int spacing: %d\n", this->ext_perimeter_flow.scaled_width(),this->ext_perimeter_flow.scaled_spacing(),this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_spacing()); + + // Get searching thresholds. For an external perimeter we take the external perimeter spacing/2 plus the internal perimeter spacing/2 and expand by the factor + // rounding errors. When precise wall is enabled, the external perimeter full spacing is used. + coord_t threshold_external = (apply_precise_outer_wall) + // Precise outer wall ⇒ use “full external spacing” + ? ( this->ext_perimeter_flow.scaled_spacing() + + this->perimeter_flow.scaled_spacing()/2.0 ) + // Normal ⇒ half ext spacing + half int spacing + : ( this->ext_perimeter_flow.scaled_spacing()/2.0 + + this->perimeter_flow.scaled_spacing()/2.0 ); - // Now start the sandwich mode wall re-ordering using the reordered_extrusions as the basis - // scan to find the external perimeter, first internal, second internal and last perimeter in the island. - // We then advance the position index to move to the second island and continue until there are no more - // perimeters left. - while (position < reordered_extrusions.size()) { - outer = first_internal = second_internal = current_perimeter = -1; // initialise all index values to -1 - max_internal = reordered_extrusions.size()-1; // initialise the maximum internal perimeter to the last perimeter on the extrusion list - // run through the walls to get the index values that need re-ordering until the first one for each - // is found. Start at "position" index to enable the for loop to iterate for multiple external - // perimeters in a single island - // printf("Reorder Loop. Position %d, extrusion list size: %d, Outer index %d, inner index %d, second inner index %d\n", position, reordered_extrusions.size(),outer,first_internal,second_internal); - for (arr_i = position; arr_i < reordered_extrusions.size(); ++arr_i) { - // printf("Perimeter: extrusion inset index %d, ordered extrusions array position %d\n",reordered_extrusions[arr_i].extrusion->inset_idx, arr_i); - switch (reordered_extrusions[arr_i].extrusion->inset_idx) { - case 0: // external perimeter - if (outer == -1) - outer = arr_i; - break; - case 1: // first internal wall - if (first_internal==-1 && arr_i>outer && outer!=-1){ - first_internal = arr_i; - } - break; - case 2: // second internal wall - if (second_internal == -1 && arr_i > first_internal && outer!=-1){ - second_internal = arr_i; - } - break; - } - if(outer >-1 && first_internal>-1 && reordered_extrusions[arr_i].extrusion->inset_idx == 0){ // found a new external perimeter after we've found at least a first internal perimeter to re-order. - // This means we entered a new island. - arr_i=arr_i-1; //step back one perimeter - max_internal = arr_i; // new maximum internal perimeter is now this as we have found a new external perimeter, hence a new island. - break; // exit the for loop - } - } - - // printf("Layer ID %d, Outer index %d, inner index %d, second inner index %d, maximum internal perimeter %d \n",layer_id,outer,first_internal,second_internal, max_internal); - if (outer > -1 && first_internal > -1 && second_internal > -1) { // found all three perimeters to re-order? If not the perimeters will be processed outside in. - std::vector inner_outer_extrusions; // temporary array to hold extrusions for reordering - inner_outer_extrusions.resize(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations - // printf("Allocated array size %d, max_internal index %d, start position index %d \n",max_internal-position+1,max_internal,position); - - for (arr_j = max_internal; arr_j >=position; --arr_j){ // go inside out towards the external perimeter (perimeters in reverse order) and store all internal perimeters until the first one identified with inset index 2 - if(arr_j >= second_internal){ - //printf("Inside out loop: Mapped perimeter index %d to array position %d\n", arr_j, max_internal-arr_j); - inner_outer_extrusions[max_internal-arr_j] = reordered_extrusions[arr_j]; - current_perimeter++; + // For the intenal perimeter threshold, the distance is the internal perimeter spacing expanded by the factor to cover rounding errors. + coord_t threshold_internal = this->perimeter_flow.scaled_spacing(); + + // Re-order extrusions based on distance + // Alorithm will aggresively optimise for the appearance of the outermost perimeter + ordered_extrusions = reorderPerimetersByProximity(ordered_extrusions,threshold_external,threshold_internal ); + reordered_extrusions = ordered_extrusions; // copy them into the reordered extrusions vector to allow for IOI operations to be performed below without altering the base ordered extrusions list. + + // Now start the sandwich mode wall re-ordering using the reordered_extrusions as the basis + // scan to find the external perimeter, first internal, second internal and last perimeter in the island. + // We then advance the position index to move to the second island and continue until there are no more + // perimeters left. + while (position < reordered_extrusions.size()) { + outer = first_internal = second_internal = current_perimeter = -1; // initialise all index values to -1 + max_internal = reordered_extrusions.size()-1; // initialise the maximum internal perimeter to the last perimeter on the extrusion list + // run through the walls to get the index values that need re-ordering until the first one for each + // is found. Start at "position" index to enable the for loop to iterate for multiple external + // perimeters in a single island + // printf("Reorder Loop. Position %d, extrusion list size: %d, Outer index %d, inner index %d, second inner index %d\n", position, reordered_extrusions.size(),outer,first_internal,second_internal); + for (arr_i = position; arr_i < reordered_extrusions.size(); ++arr_i) { + // printf("Perimeter: extrusion inset index %d, ordered extrusions array position %d\n",reordered_extrusions[arr_i].extrusion->inset_idx, arr_i); + switch (reordered_extrusions[arr_i].extrusion->inset_idx) { + case 0: // external perimeter + if (outer == -1) + outer = arr_i; + break; + case 1: // first internal wall + if (first_internal==-1 && arr_i>outer && outer!=-1){ + first_internal = arr_i; } - } - - for (arr_j = position; arr_j < second_internal; ++arr_j){ // go outside in and map the remaining perimeters (external and first internal wall(s)) using the outside in wall order - // printf("Outside in loop: Mapped perimeter index %d to array position %d\n", arr_j, current_perimeter+1); - inner_outer_extrusions[++current_perimeter] = reordered_extrusions[arr_j]; - } - - for(arr_j = position; arr_j <= max_internal; ++arr_j) // replace perimeter array with the new re-ordered array - ordered_extrusions[arr_j] = inner_outer_extrusions[arr_j-position]; + break; + case 2: // second internal wall + if (second_internal == -1 && arr_i > first_internal && outer!=-1){ + second_internal = arr_i; + } + break; + } + if(outer >-1 && first_internal>-1 && reordered_extrusions[arr_i].extrusion->inset_idx == 0){ // found a new external perimeter after we've found at least a first internal perimeter to re-order. + // This means we entered a new island. + arr_i=arr_i-1; //step back one perimeter + max_internal = arr_i; // new maximum internal perimeter is now this as we have found a new external perimeter, hence a new island. + break; // exit the for loop } - // go to the next perimeter from the current position to continue scanning for external walls in the same island - position = arr_i + 1; } + + // printf("Layer ID %d, Outer index %d, inner index %d, second inner index %d, maximum internal perimeter %d \n",layer_id,outer,first_internal,second_internal, max_internal); + if (outer > -1 && first_internal > -1 && second_internal > -1) { // found all three perimeters to re-order? If not the perimeters will be processed outside in. + std::vector inner_outer_extrusions; // temporary array to hold extrusions for reordering + inner_outer_extrusions.resize(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations + // printf("Allocated array size %d, max_internal index %d, start position index %d \n",max_internal-position+1,max_internal,position); + + for (arr_j = max_internal; arr_j >=position; --arr_j){ // go inside out towards the external perimeter (perimeters in reverse order) and store all internal perimeters until the first one identified with inset index 2 + if(arr_j >= second_internal){ + //printf("Inside out loop: Mapped perimeter index %d to array position %d\n", arr_j, max_internal-arr_j); + inner_outer_extrusions[max_internal-arr_j] = reordered_extrusions[arr_j]; + current_perimeter++; + } + } + + for (arr_j = position; arr_j < second_internal; ++arr_j){ // go outside in and map the remaining perimeters (external and first internal wall(s)) using the outside in wall order + // printf("Outside in loop: Mapped perimeter index %d to array position %d\n", arr_j, current_perimeter+1); + inner_outer_extrusions[++current_perimeter] = reordered_extrusions[arr_j]; + } + + for(arr_j = position; arr_j <= max_internal; ++arr_j) // replace perimeter array with the new re-ordered array + ordered_extrusions[arr_j] = inner_outer_extrusions[arr_j-position]; + } + // go to the next perimeter from the current position to continue scanning for external walls in the same island + position = arr_i + 1; } } + } - bool steep_overhang_contour = false; - bool steep_overhang_hole = false; - if (!config->overhang_reverse) { - // Skip steep overhang detection no reverse is specified - steep_overhang_contour = true; - steep_overhang_hole = true; - } - if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) { - if (config->overhang_reverse) { - reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, - this->config->overhang_reverse_internal_only); - } - defer_unsupported_loops(*this, extrusion_coll); - result.loops = std::move(extrusion_coll); - result.has_loops = true; + bool steep_overhang_contour = false; + bool steep_overhang_hole = false; + if (!config->overhang_reverse) { + // Skip steep overhang detection no reverse is specified + steep_overhang_contour = true; + steep_overhang_hole = true; + } + if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) { + if (config->overhang_reverse) { + reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, + this->config->overhang_reverse_internal_only); } + defer_unsupported_loops(*this, extrusion_coll); + this->loops->append(extrusion_coll); + } - const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; + const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; - if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) - infill_contour.clear(); // Infill region is too small, so let's filter it out. + if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) + infill_contour.clear(); // Infill region is too small, so let's filter it out. - // create one more offset to be used as boundary for fill - // we offset by half the perimeter spacing (to get to the actual infill boundary) - // and then we offset back and forth by half the infill spacing to only consider the - // non-collapsing regions - coord_t inset = - (loop_number < 0) ? 0 : - (loop_number == 0) ? - // one loop - ext_perimeter_spacing : - // two or more loops? - perimeter_spacing; - coord_t top_inset = inset; + // create one more offset to be used as boundary for fill + // we offset by half the perimeter spacing (to get to the actual infill boundary) + // and then we offset back and forth by half the infill spacing to only consider the + // non-collapsing regions + coord_t inset = + (loop_number < 0) ? 0 : + (loop_number == 0) ? + // one loop + ext_perimeter_spacing : + // two or more loops? + perimeter_spacing; + coord_t top_inset = inset; - top_inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); - if(is_topmost_layer || is_bottom_layer) - inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); - else - inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale(inset)))); + top_inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); + if(is_topmost_layer || is_bottom_layer) + inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale(inset)))); + else + inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale(inset)))); - // simplify infill contours according to resolution - Polygons pp; - for (ExPolygon& ex : infill_contour) - ex.simplify_p(m_scaled_resolution, &pp); - ExPolygons not_filled_exp = union_ex(pp); - // collapse too narrow infill areas - const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); + // simplify infill contours according to resolution + Polygons pp; + for (ExPolygon& ex : infill_contour) + ex.simplify_p(m_scaled_resolution, &pp); + ExPolygons not_filled_exp = union_ex(pp); + // collapse too narrow infill areas + const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); - ExPolygons infill_exp = offset2_ex( + ExPolygons infill_exp = offset2_ex( + not_filled_exp, + float(-min_perimeter_infill_spacing / 2.), + float(inset + min_perimeter_infill_spacing / 2.)); + // append infill areas to fill_surfaces + if (!top_expolygons.empty()) { + infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset))); + } + this->fill_surfaces->append(infill_exp, stInternal); + + apply_extra_perimeters(infill_exp); + + // BBS: get the no-overlap infill expolygons + { + ExPolygons polyWithoutOverlap; + polyWithoutOverlap = offset2_ex( not_filled_exp, float(-min_perimeter_infill_spacing / 2.), - float(inset + min_perimeter_infill_spacing / 2.)); - // append infill areas to fill_surfaces - if (!top_expolygons.empty()) { - infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset))); - } - result.infill = std::move(infill_exp); - - // BBS: get the no-overlap infill expolygons - { - ExPolygons polyWithoutOverlap; - polyWithoutOverlap = offset2_ex( - not_filled_exp, - float(-min_perimeter_infill_spacing / 2.), - float(+min_perimeter_infill_spacing / 2.)); - if (!top_expolygons.empty()) - polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons); - result.no_overlap = std::move(polyWithoutOverlap); - } - } - }); - for (ArachneSurfaceResult &result : results) { - if (result.has_loops) - this->loops->append(result.loops); - this->fill_surfaces->append(result.infill, stInternal); - apply_extra_perimeters(result.infill); - this->fill_no_overlap->insert(this->fill_no_overlap->end(), result.no_overlap.begin(), result.no_overlap.end()); + float(+min_perimeter_infill_spacing / 2.)); + if (!top_expolygons.empty()) + polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons); + this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end()); + } } } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 7e0d88a720..34ca214cf3 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -27,7 +27,7 @@ public: explicit Polygon(const Points &points) : MultiPoint(points) {} Polygon(std::initializer_list points) : MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} - Polygon(Polygon &&other) noexcept : MultiPoint(std::move(other.points)) {} + Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {} static Polygon new_scale(const std::vector &points) { Polygon pgn; pgn.points.reserve(points.size()); @@ -36,7 +36,7 @@ public: return pgn; } Polygon& operator=(const Polygon &other) { points = other.points; return *this; } - Polygon& operator=(Polygon &&other) noexcept { points = std::move(other.points); return *this; } + Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } Point& operator[](Points::size_type idx) { return this->points[idx]; } const Point& operator[](Points::size_type idx) const { return this->points[idx]; } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index aa9282970e..f52c3c9bbd 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -20,7 +20,7 @@ class Polyline : public MultiPoint { public: Polyline() {}; Polyline(const Polyline& other) : MultiPoint(other.points), fitting_result(other.fitting_result) {} - Polyline(Polyline &&other) noexcept : MultiPoint(std::move(other.points)), fitting_result(std::move(other.fitting_result)) {} + Polyline(Polyline &&other) : MultiPoint(std::move(other.points)), fitting_result(std::move(other.fitting_result)) {} Polyline(std::initializer_list list) : MultiPoint(list) { fitting_result.clear(); } @@ -41,7 +41,7 @@ public: fitting_result = other.fitting_result; return *this; } - Polyline& operator=(Polyline&& other) noexcept { + Polyline& operator=(Polyline&& other) { points = std::move(other.points); fitting_result = std::move(other.fitting_result); return *this; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d26bb142cb..3b7e889472 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -43,7 +42,6 @@ #include #include -#include #include #include @@ -1666,9 +1664,7 @@ void PrintObject::detect_surfaces_type() bool interface_shells = ! spiral_mode && m_config.interface_shells.value; size_t num_layers = spiral_mode ? std::min(size_t(this->printing_region(0).config().bottom_shell_layers), m_layers.size()) : m_layers.size(); - // The regions of a layer do not see each other here, and a layer cut through a fine relief takes far longer than the - // others, so the regions run next to each other instead of one after another, each still over all layers. - tbb::parallel_for(size_t(0), this->num_printing_regions(), [&](size_t region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (Layer *layer : m_layers) @@ -1726,7 +1722,7 @@ void PrintObject::detect_surfaces_type() if (upper_layer) { ExPolygons upper_slices = interface_shells ? diff_ex(layerm_slices_surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : - diff_ex_by_piece(layerm_slices_surfaces, to_polygons(upper_layer->lslices), ApplySafetyOffset::Yes); + diff_ex(layerm_slices_surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); } else { // if no upper layer, all surfaces of this one are solid @@ -1752,7 +1748,7 @@ void PrintObject::detect_surfaces_type() surfaces_append( bottom, opening_ex( - diff_ex_by_piece(layerm_slices_surfaces, to_polygons(lower_layer->lslices), ApplySafetyOffset::Yes), + diff_ex(layerm_slices_surfaces, lower_layer->lslices, ApplySafetyOffset::Yes), offset), surface_type_bottom_other); // if user requested internal shells, we need to identify surfaces @@ -1783,44 +1779,34 @@ void PrintObject::detect_surfaces_type() // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) if (! top.empty() && ! bottom.empty()) { - const auto cracks = intersection_ex_by_piece(to_expolygons(top), to_polygons(bottom)); + const auto cracks = intersection_ex(top, bottom); if (!cracks.empty()) { if (lower_layer) { // Only detect small cracks for non-first layer, because first layer should always be bottom const float small_crack_threshold = -layerm->flow(frExternalPerimeter).scaled_width() * 1.5; - // Only the bottom surfaces near a crack can take part: one that contains it must contain its box, - // and one whose box misses the grown crack is left unchanged by removing it. A layer cut through - // a fine relief has thousands of both, which made this loop quadratic. for (const auto& crack : cracks) { if (offset_ex(crack, small_crack_threshold).empty()) { // For small cracks, if it's part of a large bottom surface, then it should be added to bottom as well - const BoundingBox crack_bbox = get_extents(crack); - if (std::any_of(bottom.begin(), bottom.end(), [&crack, &crack_bbox, small_crack_threshold](const Surface& s) { + if (std::any_of(bottom.begin(), bottom.end(), [&crack, small_crack_threshold](const Surface& s) { const auto& se = s.expolygon; - return get_extents(se).inflated(SCALED_EPSILON).contains(crack_bbox) - && diff_ex(crack, se, ApplySafetyOffset::Yes).empty() + return diff_ex(crack, se, ApplySafetyOffset::Yes).empty() && se.area() > crack.area() * 2 && !offset_ex(diff_ex(se, crack), small_crack_threshold).empty(); })) continue; // Crack too small, leave it as part of the top surface, remove it from bottom surfaces - const ExPolygons grown_crack = offset_ex(crack, -small_crack_threshold); - const BoundingBox grown_bbox = get_extents(grown_crack); Surfaces bot_tmp; for (auto& b : bottom) { - if (get_extents(b.expolygon).overlap(grown_bbox)) - surfaces_append(bot_tmp, diff_ex(b.expolygon, grown_crack), b.surface_type); - else - bot_tmp.emplace_back(std::move(b)); + surfaces_append(bot_tmp, diff_ex(b.expolygon, offset_ex(crack, -small_crack_threshold)), b.surface_type); } bottom = std::move(bot_tmp); } } } - ExPolygons top_expolygons = to_expolygons(std::move(top)); + Polygons top_polygons = to_polygons(std::move(top)); top.clear(); - surfaces_append(top, diff_ex_by_piece(top_expolygons, to_polygons(bottom)), stTop); + surfaces_append(top, diff_ex(top_polygons, bottom), stTop); } } @@ -1911,7 +1897,7 @@ void PrintObject::detect_surfaces_type() { Polygons topbottom = to_polygons(top); polygons_append(topbottom, to_polygons(bottom)); - surfaces_append(surfaces_out, diff_ex_by_piece(surfaces_prev_expolys, topbottom), stInternal); + surfaces_append(surfaces_out, diff_ex(surfaces_prev_expolys, topbottom), stInternal); } surfaces_append(surfaces_out, std::move(top)); @@ -2088,31 +2074,29 @@ void PrintObject::detect_surfaces_type() } } ); + // ============================================================================================================== + // === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ============== + // === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ========== + // === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ========== + // ============================================================================================================== + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { + tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, region_id](const tbb::blocked_range &range) { + for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { + Surfaces &surfs = m_layers[idx_layer]->m_regions[region_id]->slices.surfaces; + for (Surface &s : surfs) { + if (s.surface_type == stInternalAfterExternalBridge) { + s.surface_type = stBottomBridge; + } + } + } + } + ); + } } // ============================================================================================================== // === ORCA: End of second external bridge layer changes ======================================================= // ============================================================================================================== - }); // for each this->print->region_count - - // ============================================================================================================== - // === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ============== - // === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ========== - // === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ========== - // ============================================================================================================== - // Once all the regions have their second bridge layer, and before their slices are trimmed into fill surfaces below. - if ((this->config().enable_extra_bridge_layer.value == eblApplyToAll) || (this->config().enable_extra_bridge_layer.value == eblExternalBridgeOnly)) { - tbb::parallel_for(tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range &range) { - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) - for (LayerRegion *layerm : m_layers[idx_layer]->regions()) - for (Surface &s : layerm->slices.surfaces) - if (s.surface_type == stInternalAfterExternalBridge) - s.surface_type = stBottomBridge; - }); - m_print->throw_if_canceled(); - } - - tbb::parallel_for(size_t(0), this->num_printing_regions(), [&](size_t region_id) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start"; // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. tbb::parallel_for( @@ -2129,7 +2113,7 @@ void PrintObject::detect_surfaces_type() }); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end"; - }); + } // for each this->print->region_count // Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) m_typed_slices = true; @@ -2196,10 +2180,8 @@ void PrintObject::process_external_surfaces() BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end"; } - BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces in parallel - start"; - // The regions of a layer do not see each other here, and a layer cut through a fine relief takes far longer than the - // others, so the regions run next to each other instead of one after another, each still over all layers. - tbb::parallel_for(size_t(0), this->num_printing_regions(), [this, &surfaces_covered](size_t region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { + BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, &surfaces_covered, region_id](const tbb::blocked_range& range) { @@ -2214,9 +2196,9 @@ void PrintObject::process_external_surfaces() } } ); - }); - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces in parallel - end"; + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - end"; + } } void PrintObject::discover_vertical_shells() @@ -2255,10 +2237,10 @@ void PrintObject::discover_vertical_shells() // The "ensure vertical wall thickness" feature is not applicable to any of the regions. Quit. return; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; - // One layer per task: on a layer cut through a fine relief the unions below take far longer than elsewhere, and a - // few such layers next to each other must not end up in one task. + //FIXME Improve the heuristics for a grain size. + size_t grain_size = std::max(num_layers / 16, size_t(1)); tbb::parallel_for( - tbb::blocked_range(0, num_layers, 1), + tbb::blocked_range(0, num_layers, grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const std::initializer_list surfaces_bottom { stBottom, stBottomBridge }; const size_t num_regions = this->num_printing_regions(); @@ -2266,198 +2248,67 @@ void PrintObject::discover_vertical_shells() m_print->throw_if_canceled(); const Layer &layer = *m_layers[idx_layer]; DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer]; - const auto top_bottom_expansion = [&layer](size_t region_id) { - return float(layer.m_regions[region_id]->flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff; - }; + // Simulate single set of perimeters over all merged regions. + float perimeter_offset = 0.f; + float perimeter_min_spacing = FLT_MAX; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING static size_t debug_idx = 0; ++ debug_idx; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - // The top surfaces, the bottom surfaces and the holes are independent of each other. - tbb::parallel_invoke( - [&]() { - for (size_t region_id = 0; region_id < num_regions; ++ region_id) - append(cache.top_surfaces, offset(layer.m_regions[region_id]->slices.filter_by_type(stTop), top_bottom_expansion(region_id))); -// append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), top_bottom_expansion)); - // Save some computing time by reducing the number of polygons. - cache.top_surfaces = union_(cache.top_surfaces); - }, - [&]() { - for (size_t region_id = 0; region_id < num_regions; ++ region_id) - append(cache.bottom_surfaces, offset(layer.m_regions[region_id]->slices.filter_by_types(surfaces_bottom), top_bottom_expansion(region_id))); -// append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom), top_bottom_expansion)); - cache.bottom_surfaces = union_(cache.bottom_surfaces); - }, - [&]() { - // Simulate single set of perimeters over all merged regions. - float perimeter_offset = 0.f; - float perimeter_min_spacing = FLT_MAX; - for (size_t region_id = 0; region_id < num_regions; ++ region_id) { - const LayerRegion &layerm = *layer.m_regions[region_id]; - // Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only. - // First find the maxium number of perimeters per region slice. - unsigned int perimeters = 0; - for (const Surface &s : layerm.slices.surfaces) - perimeters = std::max(perimeters, s.extra_perimeters); - perimeters += layerm.region().config().wall_loops.value; - // Then calculate the infill offset. - if (perimeters > 0) { - Flow extflow = layerm.flow(frExternalPerimeter); - Flow flow = layerm.flow(frPerimeter); - perimeter_offset = std::max(perimeter_offset, - 0.5f * float(extflow.scaled_width() + extflow.scaled_spacing()) + (float(perimeters) - 1.f) * flow.scaled_spacing()); - perimeter_min_spacing = std::min(perimeter_min_spacing, float(std::min(extflow.scaled_spacing(), flow.scaled_spacing()))); - } - polygons_append(cache.holes, to_polygons(layerm.fill_expolygons)); - } - // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print. - if (perimeter_offset > 0.) { - // The layer.lslices are forced to merge by expanding them first. - polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, - perimeter_offset - 0.3f * perimeter_min_spacing)); + for (size_t region_id = 0; region_id < num_regions; ++ region_id) { + LayerRegion &layerm = *layer.m_regions[region_id]; + float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff; + // Top surfaces. + append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), top_bottom_expansion)); +// append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), top_bottom_expansion)); + // Bottom surfaces. + append(cache.bottom_surfaces, offset(layerm.slices.filter_by_types(surfaces_bottom), top_bottom_expansion)); +// append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom), top_bottom_expansion)); + // Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only. + // First find the maxium number of perimeters per region slice. + unsigned int perimeters = 0; + for (Surface &s : layerm.slices.surfaces) + perimeters = std::max(perimeters, s.extra_perimeters); + perimeters += layerm.region().config().wall_loops.value; + // Then calculate the infill offset. + if (perimeters > 0) { + Flow extflow = layerm.flow(frExternalPerimeter); + Flow flow = layerm.flow(frPerimeter); + perimeter_offset = std::max(perimeter_offset, + 0.5f * float(extflow.scaled_width() + extflow.scaled_spacing()) + (float(perimeters) - 1.f) * flow.scaled_spacing()); + perimeter_min_spacing = std::min(perimeter_min_spacing, float(std::min(extflow.scaled_spacing(), flow.scaled_spacing()))); + } + polygons_append(cache.holes, to_polygons(layerm.fill_expolygons)); + } + // Save some computing time by reducing the number of polygons. + cache.top_surfaces = union_(cache.top_surfaces); + cache.bottom_surfaces = union_(cache.bottom_surfaces); + // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print. + if (perimeter_offset > 0.) { + // The layer.lslices are forced to merge by expanding them first. + polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, - perimeter_offset - 0.3f * perimeter_min_spacing)); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices)); - svg.draw(layer.lslices, "blue"); - svg.draw(union_ex(cache.holes), "red"); - svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); - svg.Close(); - } + { + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices)); + svg.draw(layer.lslices, "blue"); + svg.draw(union_ex(cache.holes), "red"); + svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); + svg.Close(); + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - } - cache.holes = union_(cache.holes); - }); + } + cache.holes = union_(cache.holes); } }); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } - // With one top/bottom cache for all regions, the shell and hole accumulation in the loop below depends on nothing - // region-specific but the shell settings and the external perimeter spacing, so a region sharing them with an earlier - // one reuses its result instead of repeating it: that accumulation is a union over several layers of top/bottom - // surfaces, and a multi-material print has a region per filament. - using AccumulationKey = std::array; - struct ShellAccumulation - { - AccumulationKey key; - Polygons shell; - Polygons holes; - }; - const auto accumulation_key = [](const PrintRegionConfig ®ion_config, const LayerRegion *layerm) { - return AccumulationKey{ double(region_config.top_shell_layers.value), region_config.top_shell_thickness.value, - double(region_config.bottom_shell_layers.value), region_config.bottom_shell_thickness.value, - double(layerm->flow(frExternalPerimeter).scaled_spacing()) }; - }; - const auto accumulate_shell = [this, &cache_top_botom_regions](size_t idx_layer, const PrintRegionConfig ®ion_config, - const LayerRegion *layerm, Polygons &shell, Polygons &holes) { - const Layer *layer = m_layers[idx_layer]; - polygons_append(holes, cache_top_botom_regions[idx_layer].holes); - auto combine_holes = [&holes](const Polygons &holes2) { - if (holes.empty() || holes2.empty()) - holes.clear(); - else - holes = intersection(holes, holes2); - }; - auto combine_shells = [&shell](const Polygons &shells2) { - if (shell.empty()) - shell = std::move(shells2); - else if (! shells2.empty()) { - polygons_append(shell, shells2); - // Running the union_ using the Clipper library piece by piece is cheaper - // than running the union_ all at once. - shell = union_(shell); - } - }; - static constexpr const bool one_more_layer_below_top_bottom_surfaces = false; - if (int n_top_layers = region_config.top_shell_layers.value; n_top_layers > 0) { - // Gather top regions projected to this layer. - coordf_t print_z = layer->print_z; - int i = int(idx_layer) + 1; - int itop = int(idx_layer) + n_top_layers; - bool at_least_one_top_projected = false; - for (; i < int(cache_top_botom_regions.size()) && - (i < itop || m_layers[i]->print_z - print_z < region_config.top_shell_thickness - EPSILON); - ++ i) { - at_least_one_top_projected = true; - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - combine_holes(cache.holes); - combine_shells(cache.top_surfaces); - } - if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) { - // Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the - // boundaries of solid layers are not anchored over/under perimeters, so lets fix it by adding at least one - // perimeter width of area - Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].top_surfaces, - layerm->flow(frExternalPerimeter).scaled_spacing()), - to_polygons(m_layers[i]->lslices)); - combine_shells(anchor_area); - } - - if (one_more_layer_below_top_bottom_surfaces) - if (i < int(cache_top_botom_regions.size()) && - (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_shell_thickness - EPSILON)) - combine_holes(cache_top_botom_regions[i].holes); - } - if (int n_bottom_layers = region_config.bottom_shell_layers.value; n_bottom_layers > 0) { - // Gather bottom regions projected to this layer. - coordf_t bottom_z = layer->bottom_z(); - int i = int(idx_layer) - 1; - int ibottom = int(idx_layer) - n_bottom_layers; - bool at_least_one_bottom_projected = false; - for (; i >= 0 && - (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_shell_thickness - EPSILON); - -- i) { - at_least_one_bottom_projected = true; - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - combine_holes(cache.holes); - combine_shells(cache.bottom_surfaces); - } - - if (!at_least_one_bottom_projected && i >= 0) { - Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].bottom_surfaces, - layerm->flow(frExternalPerimeter).scaled_spacing()), - to_polygons(m_layers[i]->lslices)); - combine_shells(anchor_area); - } - - if (one_more_layer_below_top_bottom_surfaces) - if (i >= 0 && - (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON)) - combine_holes(cache_top_botom_regions[i].holes); - } - }; - std::vector> shell_accumulations(top_bottom_surfaces_all_regions ? num_layers : 0); - if (! shell_accumulations.empty()) { - // Every (layer, key) pair is accumulated once, before the regions, so that nothing in the loop below is shared - // between them and they can run next to each other. - std::vector> todo; // layer, its slot, a region holding the key - for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer) { - std::vector &accumulations = shell_accumulations[idx_layer]; - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { - if (this->printing_region(region_id).config().ensure_vertical_shell_thickness.value != evstAll) - continue; - const LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id]; - const AccumulationKey key = accumulation_key(layerm->region().config(), layerm); - if (std::none_of(accumulations.begin(), accumulations.end(), [&key](const ShellAccumulation &a) { return a.key == key; })) { - todo.push_back({ idx_layer, accumulations.size(), region_id }); - accumulations.push_back({ key, {}, {} }); - } - } - } - tbb::parallel_for(size_t(0), todo.size(), [this, &todo, &shell_accumulations, &accumulate_shell](size_t i) { - m_print->throw_if_canceled(); - const LayerRegion *layerm = m_layers[todo[i][0]]->m_regions[todo[i][2]]; - ShellAccumulation &out = shell_accumulations[todo[i][0]][todo[i][1]]; - accumulate_shell(todo[i][0], layerm->region().config(), layerm, out.shell, out.holes); - }); - m_print->throw_if_canceled(); - } - - const auto process_region = [&](size_t region_id) { + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); if (region.config().ensure_vertical_shell_thickness.value != evstAll ) // This region will be handled by discover_horizontal_shells(). - return; + continue; //FIXME Improve the heuristics for a grain size. size_t grain_size = std::max(num_layers / 16, size_t(1)); @@ -2497,7 +2348,7 @@ void PrintObject::discover_vertical_shells() grain_size = 1; tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), - [this, region_id, &shell_accumulations, &accumulation_key, &accumulate_shell] + [this, region_id, &cache_top_botom_regions] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { @@ -2547,19 +2398,80 @@ void PrintObject::discover_vertical_shells() } } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - const AccumulationKey key = accumulation_key(region_config, layerm); - const ShellAccumulation *reused = shell_accumulations.empty() ? nullptr : - [&]() -> const ShellAccumulation * { - for (const ShellAccumulation &a : shell_accumulations[idx_layer]) - if (a.key == key) - return &a; - return nullptr; - }(); - if (reused != nullptr) { - shell = reused->shell; - holes = reused->holes; - } else - accumulate_shell(idx_layer, region_config, layerm, shell, holes); + polygons_append(holes, cache_top_botom_regions[idx_layer].holes); + auto combine_holes = [&holes](const Polygons &holes2) { + if (holes.empty() || holes2.empty()) + holes.clear(); + else + holes = intersection(holes, holes2); + }; + auto combine_shells = [&shell](const Polygons &shells2) { + if (shell.empty()) + shell = std::move(shells2); + else if (! shells2.empty()) { + polygons_append(shell, shells2); + // Running the union_ using the Clipper library piece by piece is cheaper + // than running the union_ all at once. + shell = union_(shell); + } + }; + static constexpr const bool one_more_layer_below_top_bottom_surfaces = false; + if (int n_top_layers = region_config.top_shell_layers.value; n_top_layers > 0) { + // Gather top regions projected to this layer. + coordf_t print_z = layer->print_z; + int i = int(idx_layer) + 1; + int itop = int(idx_layer) + n_top_layers; + bool at_least_one_top_projected = false; + for (; i < int(cache_top_botom_regions.size()) && + (i < itop || m_layers[i]->print_z - print_z < region_config.top_shell_thickness - EPSILON); + ++ i) { + at_least_one_top_projected = true; + const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; + combine_holes(cache.holes); + combine_shells(cache.top_surfaces); + } + if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) { + // Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the + // boundaries of solid layers are not anchored over/under perimeters, so lets fix it by adding at least one + // perimeter width of area + Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].top_surfaces, + layerm->flow(frExternalPerimeter).scaled_spacing()), + to_polygons(m_layers[i]->lslices)); + combine_shells(anchor_area); + } + + if (one_more_layer_below_top_bottom_surfaces) + if (i < int(cache_top_botom_regions.size()) && + (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_shell_thickness - EPSILON)) + combine_holes(cache_top_botom_regions[i].holes); + } + if (int n_bottom_layers = region_config.bottom_shell_layers.value; n_bottom_layers > 0) { + // Gather bottom regions projected to this layer. + coordf_t bottom_z = layer->bottom_z(); + int i = int(idx_layer) - 1; + int ibottom = int(idx_layer) - n_bottom_layers; + bool at_least_one_bottom_projected = false; + for (; i >= 0 && + (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_shell_thickness - EPSILON); + -- i) { + at_least_one_bottom_projected = true; + const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; + combine_holes(cache.holes); + combine_shells(cache.bottom_surfaces); + } + + if (!at_least_one_bottom_projected && i >= 0) { + Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].bottom_surfaces, + layerm->flow(frExternalPerimeter).scaled_spacing()), + to_polygons(m_layers[i]->lslices)); + combine_shells(anchor_area); + } + + if (one_more_layer_below_top_bottom_surfaces) + if (i >= 0 && + (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON)) + combine_holes(cache_top_botom_regions[i].holes); + } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); @@ -2653,8 +2565,11 @@ void PrintObject::discover_vertical_shells() Polygons object_volume; Polygons internal_volume; { - if (idx_layer > 0 && idx_layer + 1 < m_layers.size()) - object_volume = to_polygons(intersection_ex_by_piece(m_layers[idx_layer - 1]->lslices, to_polygons(m_layers[idx_layer + 1]->lslices))); + Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{}; + Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ? + to_polygons(m_layers[idx_layer + 1]->lslices) : + Polygons{}; + object_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); internal_volume = closing(polygonsInternal, SCALED_EPSILON); } @@ -2665,34 +2580,15 @@ void PrintObject::discover_vertical_shells() // the in-model condition is there due to small sloping surfaces, e.g. top of the hull of the benchy // 2. the area does not fully cover an internal polygon // This is there mainly for a very thin parts, where the solid layers would be missing if the part area is quite small - // Both tests below compare a small piece against the whole layer. Done literally, that is - // quadratic in the number of pieces, which is what a layer split up by colour painting has, - // so each is restricted to the part of the layer near the piece with an identical result: - // object_volume is clipped to the piece's box, and only the internal polygons whose box meets - // the expanded piece take part in the count, since the others pass through the difference - // unchanged and add the same number to both sides of it. - std::vector internal_bboxes; - internal_bboxes.reserve(internal_volume.size()); - for (const Polygon &poly : internal_volume) - internal_bboxes.emplace_back(get_extents(poly)); regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(), - [&internal_volume, &internal_bboxes, &min_perimeter_infill_spacing, + [&internal_volume, &min_perimeter_infill_spacing, &object_volume](const ExPolygon &p) { - const bool small = p.area() < min_perimeter_infill_spacing * scaled(1.5) || - (p.area() < min_perimeter_infill_spacing * scaled(8.0) && - diff(to_polygons(p), - ClipperUtils::clip_clipper_polygons_with_subject_bbox( - object_volume, get_extents(p).inflated(SCALED_EPSILON))) - .empty()); - if (!small) - return false; - const Polygons expanded = expand(to_polygons(p), min_perimeter_infill_spacing); - const BoundingBox bbox = get_extents(expanded); - Polygons nearby; - for (size_t i = 0; i < internal_volume.size(); ++i) - if (internal_bboxes[i].overlap(bbox)) - nearby.emplace_back(internal_volume[i]); - return diff(nearby, expanded).size() >= nearby.size(); + return (p.area() < min_perimeter_infill_spacing * scaled(1.5) || + (p.area() < min_perimeter_infill_spacing * scaled(8.0) && + diff(to_polygons(p), object_volume).empty())) && + diff(internal_volume, + expand(to_polygons(p), min_perimeter_infill_spacing)) + .size() >= internal_volume.size(); }), regularized_shell.end()); } @@ -2714,9 +2610,8 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the internal & internalvoid by the shell. - const Polygons regularized_shell_polygons = to_polygons(regularized_shell); - Slic3r::ExPolygons new_internal = diff_ex_by_piece(to_expolygons(layerm->fill_surfaces.filter_by_type(stInternal)), regularized_shell_polygons); - Slic3r::ExPolygons new_internal_void = diff_ex_by_piece(to_expolygons(layerm->fill_surfaces.filter_by_type(stInternalVoid)), regularized_shell_polygons); + Slic3r::ExPolygons new_internal = diff_ex(layerm->fill_surfaces.filter_by_type(stInternal), regularized_shell); + Slic3r::ExPolygons new_internal_void = diff_ex(layerm->fill_surfaces.filter_by_type(stInternalVoid), regularized_shell); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { @@ -2743,15 +2638,7 @@ void PrintObject::discover_vertical_shells() layerm->export_region_fill_surfaces_to_svg_debug("3_discover_vertical_shells-final"); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - }; // for each region - if (top_bottom_surfaces_all_regions) - // Nothing is shared between the regions, and a layer cut through a fine relief takes far longer than the others, - // so they run next to each other instead of one after another. - tbb::parallel_for(size_t(0), this->num_printing_regions(), process_region); - else - // Here every region fills the one top/bottom cache with its own surfaces first. - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) - process_region(region_id); + } // for each region } // void PrintObject::discover_vertical_shells() // #define DEBUG_BRIDGE_OVER_INFILL @@ -3272,16 +3159,6 @@ void PrintObject::bridge_over_infill() vertical_lines[i].b = Point{x, y_max}; } - // The vertical lines only span the bridged area's x range, so anchors entirely outside it can never be - // hit. Leaving them out gives the same intersections without building a tree over the whole layer's - // boundary for every bridge. - const coord_t scan_x_min = bb_x.min.x(); - const coord_t scan_x_max = bb_x.min.x() + coord_t(n_vlines) * scan_spacing; - anchors.erase(std::remove_if(anchors.begin(), anchors.end(), - [scan_x_min, scan_x_max](const Line &l) { - return std::max(l.a.x(), l.b.x()) < scan_x_min || std::min(l.a.x(), l.b.x()) > scan_x_max; - }), - anchors.end()); auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors)}; auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; @@ -3526,63 +3403,28 @@ void PrintObject::bridge_over_infill() std::vector expanded_surfaces; expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); - // The expanded fill boundary depends only on the bridging flow, and total_fill_area is not - // modified below, so build it once per spacing rather than once per candidate. A layer split - // into many candidates (e.g. by colour painting) otherwise repeats a layer-wide offset for each. - std::map boundary_by_spacing; - // expansion_area is a clean, non-overlapping set, so uniting it with a bridge or cutting a bridge - // out of it only changes the polygons near that bridge. The rest are passed through untouched - // instead of being fed to ClipperLib with the whole layer again for every candidate. - // Not `near`/`far`: the Windows headers still define those as macros, and they expand to - // nothing, which turns the declaration below into an empty one. - const auto split_near = [](const Polygons &polys, const BoundingBox &bbox, Polygons &rest) { - Polygons nearby; - for (const Polygon &p : polys) - (get_extents(p).overlap(bbox) ? nearby : rest).emplace_back(p); - return nearby; - }; for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { const auto ®ion_config = candidate.region->region().config(); const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve || region_config.sparse_infill_pattern == ipOctagramSpiral; const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); - // deep_infill_area and internal_unsupported_area cover the whole layer; only their part under - // this candidate can change the results, so they are clipped to its box first. - if (!area_to_be_bridge.empty()) - area_to_be_bridge = intersection(area_to_be_bridge, - ClipperUtils::clip_clipper_polygons_with_subject_bbox( - deep_infill_area, get_extents(area_to_be_bridge).inflated(SCALED_EPSILON))); + area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); area_to_be_bridge.erase(std::remove_if(area_to_be_bridge.begin(), area_to_be_bridge.end(), - [&internal_unsupported_area](const Polygon &p) { - return intersection({p}, ClipperUtils::clip_clipper_polygons_with_subject_bbox( - internal_unsupported_area, - get_extents(p).inflated(SCALED_EPSILON))) - .empty(); + [internal_unsupported_area](const Polygon &p) { + return intersection({p}, internal_unsupported_area).empty(); }), area_to_be_bridge.end()); + Polygons limiting_area = union_(area_to_be_bridge, expansion_area); + if (area_to_be_bridge.empty()) continue; - Polygons limiting_area; - const Polygons near_expansion = split_near(expansion_area, get_extents(area_to_be_bridge).inflated(SCALED_EPSILON), - limiting_area); - append(limiting_area, union_(area_to_be_bridge, near_expansion)); - - auto boundary_it = boundary_by_spacing.find(flow.scaled_spacing()); - if (boundary_it == boundary_by_spacing.end()) - boundary_it = boundary_by_spacing - .emplace(flow.scaled_spacing(), to_polylines(expand(total_fill_area, 1.3 * flow.scaled_spacing()))) - .first; - Polylines boundary_plines = boundary_it->second; + Polylines boundary_plines = to_polylines(expand(total_fill_area, 1.3 * flow.scaled_spacing())); { - // No offset here: flow.spacing() is in mm, so the expand(limiting_area, 0.3 * flow.spacing()) - // this used to be moved the outline by 0.135 scaled units - nothing beyond rounding - while - // costing a whole-layer ClipperLib pass for every candidate. limiting_area is already a clean - // union, so its own outline is the same boundary. - Polylines limiting_plines = to_polylines(limiting_area); + Polylines limiting_plines = to_polylines(expand(limiting_area, 0.3*flow.spacing())); boundary_plines.insert(boundary_plines.end(), limiting_plines.begin(), limiting_plines.end()); } @@ -3656,12 +3498,9 @@ void PrintObject::bridge_over_infill() // Check collision with other expanded surfaces { bool reconstruct = false; - Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing()); - const BoundingBox tmp_expanded_bbox = get_extents(tmp_expanded_area); + Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { - // Surfaces whose boxes miss each other cannot intersect, which is most pairs on a busy layer. - if (get_extents(s.new_polys).overlap(tmp_expanded_bbox) && - !intersection(s.new_polys, tmp_expanded_area).empty()) { + if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; reconstruct = true; break; @@ -3685,20 +3524,10 @@ void PrintObject::bridge_over_infill() bridging_area = union_(bridging_area, construct_anchored_polygon(bridging_area, to_lines(boundary_plines), flow, bridging_angle, scan_spacing, true)); } - // Each of these meets one bridge with the whole layer, so the layer side is first cut down to the - // bridge's box (and expansion_area split as above); the result is the same. - if (!bridging_area.empty()) { - const BoundingBox bridging_bbox = get_extents(bridging_area).inflated(SCALED_EPSILON); - bridging_area = intersection(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(limiting_area, bridging_bbox)); - bridging_area = intersection(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(total_fill_area, bridging_bbox)); - bridging_area = diff(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(total_top_area, bridging_bbox)); - } - if (!bridging_area.empty()) { - Polygons kept; - const Polygons cut = split_near(expansion_area, get_extents(bridging_area).inflated(SCALED_EPSILON), kept); - append(kept, diff(cut, bridging_area)); - expansion_area = std::move(kept); - } + bridging_area = intersection(bridging_area, limiting_area); + bridging_area = intersection(bridging_area, total_fill_area); + bridging_area = diff(bridging_area, total_top_area); + expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_expanded_bridging" + std::to_string(r), diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index ef18ed363f..a955eb5ca7 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -929,9 +929,9 @@ public: ::fread(&y, sizeof(coord_t), 1, file); poly.points.emplace_back(Point(x * scale, y * scale)); } - printf("Polygon %d, area: %lf\n", i, area(poly.points)); if (which == -1 || which == i) m_support_polygons_deserialized.emplace_back(std::move(poly)); + printf("Polygon %d, area: %lf\n", i, area(poly.points)); } ::fread(&n_polygons, 4, 1, file); m_trimming_polygons_deserialized.reserve(n_polygons); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 174c5e4fce..519b6e826e 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -854,41 +854,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) if (is_auto(stype) && config_detect_sharp_tails) { // BBS detect sharp tail - // Each island is tested only against the lower islands whose box meets its own: overlaps() tries every - // pair, which on a layer cut through a fine relief (thousands of islands above thousands) never ends. - std::vector lower_bboxes; - lower_bboxes.reserve(lower_polys.size()); - for (const ExPolygon &lower : lower_polys) - lower_bboxes.emplace_back(get_extents(lower)); for (const ExPolygon& expoly : curr_polys) { bool is_sharp_tail = false; // 1. nothing below // this is a sharp tail region if it's floating and non-ignorable - const ExPolygons expanded = offset_ex(expoly, 0.1 * extrusion_width_scaled); - const BoundingBox bbox = get_extents(expanded); - ExPolygons lower_nearby; - for (size_t i = 0; i < lower_polys.size(); ++i) - if (lower_bboxes[i].overlap(bbox)) - lower_nearby.emplace_back(lower_polys[i]); - // As overlaps(expanded, lower_nearby), with each lower island cut to the island's box first: below - // a fine relief the lower layer is a few islands with thousands of holes, whose whole boundary - // was otherwise intersected again for every island above. - const auto overlaps_nearby = [&]() { - for (const ExPolygon &a : expanded) { - if (a.empty()) - continue; - const BoundingBox a_bbox = get_extents(a); - for (const ExPolygon &b : lower_nearby) { - if (b.empty() || !get_extents(b).overlap(a_bbox)) - continue; - const Polygons b_near = ClipperUtils::clip_clipper_polygons_with_subject_bbox(b, a_bbox.inflated(SCALED_EPSILON)); - if (!intersection_pl(to_polylines(b_near), a).empty() || b.contains(a.contour.points.front())) - return true; - } - } - return false; - }; - if (!overlaps_nearby()) { + if (!overlaps(offset_ex(expoly, 0.1 * extrusion_width_scaled), lower_polys)) { is_sharp_tail = !offset_ex(expoly, -0.1 * extrusion_width_scaled).empty(); } diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp index f9737f8cf5..b63c283251 100644 --- a/src/libslic3r/Surface.hpp +++ b/src/libslic3r/Surface.hpp @@ -61,7 +61,7 @@ public: thickness(other.thickness), thickness_layers(other.thickness_layers), bridge_angle(other.bridge_angle), extra_perimeters(other.extra_perimeters) {}; - Surface(Surface &&rhs) noexcept + Surface(Surface &&rhs) : surface_type(rhs.surface_type), expolygon(std::move(rhs.expolygon)), thickness(rhs.thickness), thickness_layers(rhs.thickness_layers), bridge_angle(rhs.bridge_angle), extra_perimeters(rhs.extra_perimeters) @@ -87,7 +87,7 @@ public: return *this; } - Surface& operator=(Surface &&rhs) noexcept + Surface& operator=(Surface &&rhs) { surface_type = rhs.surface_type; expolygon = std::move(rhs.expolygon); diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 06fce9796d..c339da566a 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -162,10 +162,10 @@ inline void append(std::vector &dest, std::vector &&src) { if (dest.empty()) dest = std::move(src); - else - // insert() grows the capacity geometrically; reserving exactly the new size reallocated on every call, which - // made appending piece by piece quadratic. - dest.insert(dest.end(), std::make_move_iterator(src.begin()), std::make_move_iterator(src.end())); + else { + dest.reserve(dest.size() + src.size()); + std::move(std::begin(src), std::end(src), std::back_inserter(dest)); + } src.clear(); src.shrink_to_fit(); } diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 1c6f14bc8e..fcc257f14d 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -28,7 +28,6 @@ add_executable(${_TEST_NAME}_tests test_filament_mixer.cpp test_fill_plane_path.cpp test_geometry.cpp - test_kdtree.cpp test_multimaterial_segmentation.cpp test_placeholder_parser.cpp test_polygon.cpp diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp index c69c686521..fe9961a771 100644 --- a/tests/libslic3r/test_clipper_utils.cpp +++ b/tests/libslic3r/test_clipper_utils.cpp @@ -299,47 +299,3 @@ TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") { REQUIRE(count_polys(output) == reference.size()); } } - -TEST_CASE("Tiled diff and intersection cover the same area as the plain calls", "[ClipperUtils]") { - // A grid of disjoint framed squares, enough of them to be split into several tiles. - const int n = 40; - const coord_t cell = scaled(2.), side = scaled(1.5), frame = scaled(0.3); - ExPolygons subject; - for (int y = 0; y < n; ++ y) - for (int x = 0; x < n; ++ x) { - const Point o(x * cell, y * cell); - ExPolygon square(Polygon({ o, o + Point(side, 0), o + Point(side, side), o + Point(0, side) })); - Polygon hole({ o + Point(frame, frame), o + Point(frame, side - frame), o + Point(side - frame, side - frame), o + Point(side - frame, frame) }); - square.holes.emplace_back(std::move(hole)); - subject.emplace_back(std::move(square)); - } - // Clip polygons crossing many squares, one of them large with holes of its own. - Polygons clip; - const coord_t span = n * cell; - for (int i = 0; i < 8; ++ i) { - const coord_t y0 = coord_t(i) * span / 8, y1 = y0 + scaled(0.9); - clip.emplace_back(Polygon({ Point(- cell, y0), Point(span, y0 + cell * 3), Point(span, y1 + cell * 3), Point(- cell, y1) })); - } - ExPolygon big(Polygon({ Point(span / 4, span / 4), Point(3 * span / 4, span / 4), Point(3 * span / 4, 3 * span / 4), Point(span / 4, 3 * span / 4) })); - for (int i = 0; i < 4; ++ i) { - const Point o(span / 4 + scaled(3.1) + i * scaled(9.7), span / 4 + scaled(5.3)); - big.holes.emplace_back(Polygon({ o, o + Point(0, scaled(20.)), o + Point(scaled(5.), scaled(20.)), o + Point(scaled(5.), 0) })); - } - polygons_append(clip, to_polygons(big)); - - const auto xor_area = [](const ExPolygons &a, const ExPolygons &b) { return area(diff_ex(a, b)) + area(diff_ex(b, a)); }; - const ApplySafetyOffset safety = GENERATE(ApplySafetyOffset::No, ApplySafetyOffset::Yes); - const double tolerance = double(scaled(0.001)) * double(span); - - const ExPolygons diff_plain = diff_ex(subject, clip, safety); - const ExPolygons diff_tiled = diff_ex_by_piece(subject, clip, safety); - REQUIRE(area(diff_plain) > 0.); - CHECK_THAT(area(diff_tiled), Catch::Matchers::WithinRel(area(diff_plain), 1e-9)); - CHECK(xor_area(diff_tiled, diff_plain) < tolerance); - - const ExPolygons intersection_plain = intersection_ex(subject, clip, safety); - const ExPolygons intersection_tiled = intersection_ex_by_piece(subject, clip, safety); - REQUIRE(area(intersection_plain) > 0.); - CHECK_THAT(area(intersection_tiled), Catch::Matchers::WithinRel(area(intersection_plain), 1e-9)); - CHECK(xor_area(intersection_tiled, intersection_plain) < tolerance); -} diff --git a/tests/libslic3r/test_kdtree.cpp b/tests/libslic3r/test_kdtree.cpp deleted file mode 100644 index 679a1465a7..0000000000 --- a/tests/libslic3r/test_kdtree.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include - -#include -#include -#include - -#include "libslic3r/KDTreeIndirect.hpp" -#include "libslic3r/Point.hpp" - -using namespace Slic3r; - -TEST_CASE("Visiting the nearby points gives what collecting them gives", "[KDTree]") { - std::mt19937 rng(19937); - std::uniform_real_distribution coord(-50.f, 50.f); - // Points in a box, so that a radius search returns anything from none of them to all of them. - std::vector points(2000); - for (Vec3f &p : points) - p = Vec3f(coord(rng), coord(rng), coord(rng)); - - auto coordinate = [&points](size_t idx, size_t dimension) { return points[idx](int(dimension)); }; - KDTreeIndirect<3, float, decltype(coordinate)> tree(coordinate); - std::vector indices(points.size()); - std::iota(indices.begin(), indices.end(), 0); - tree.build(indices); - - const float radius = GENERATE(0.5f, 5.f, 25.f, 200.f); - for (int i = 0; i < 20; ++ i) { - const Vec3f center(coord(rng), coord(rng), coord(rng)); - - const std::vector collected = find_nearby_points(tree, center, radius); - std::vector visited; - visit_nearby_points(tree, center, radius, [&visited](size_t idx) { visited.emplace_back(idx); }); - - // Same points, and in the same order: a caller that keeps the first of several equally good ones - // must get the same answer either way. - REQUIRE(visited == collected); - } -} - -TEST_CASE("A radius search returns every point within the radius and no other", "[KDTree]") { - std::mt19937 rng(2024); - std::uniform_real_distribution coord(-20.f, 20.f); - std::vector points(500); - for (Vec3f &p : points) - p = Vec3f(coord(rng), coord(rng), coord(rng)); - - auto coordinate = [&points](size_t idx, size_t dimension) { return points[idx](int(dimension)); }; - KDTreeIndirect<3, float, decltype(coordinate)> tree(coordinate); - std::vector indices(points.size()); - std::iota(indices.begin(), indices.end(), 0); - tree.build(indices); - - const Vec3f center(1.f, -2.f, 3.f); - const float radius = 7.f; - - std::vector expected; - for (size_t i = 0; i < points.size(); ++ i) - if ((points[i] - center).squaredNorm() < radius * radius) - expected.emplace_back(i); - - std::vector visited; - visit_nearby_points(tree, center, radius, [&visited](size_t idx) { visited.emplace_back(idx); }); - std::sort(visited.begin(), visited.end()); - - REQUIRE(! expected.empty()); - REQUIRE(visited == expected); -} From ea49b4851e2c2f96ac101af60194725408373548 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Tue, 29 Sep 2026 07:26:38 -0400 Subject: [PATCH 65/89] Make U1 filament notes readable without commit references Co-authored-by: Codex --- .../filament/Generic PETG @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Generic PETG @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic PETG @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic PETG @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Generic PETG HF @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Generic PETG HF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic PETG HF @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic PETG HF @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Generic PLA @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Generic PLA @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic PLA @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic PLA @U1 0.8 High Flow nozzle.json | 2 +- .../Generic PLA High Speed @U1 0.2 High Flow nozzle.json | 2 +- .../Generic PLA High Speed @U1 0.4 High Flow nozzle.json | 2 +- .../Generic PLA High Speed @U1 0.6 High Flow nozzle.json | 2 +- .../Generic PLA High Speed @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Generic TPU @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Generic TPU @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Generic TPU @U1 0.8 High Flow nozzle.json | 2 +- .../Polymaker General PLA Family @U1 0.4 High Flow nozzle.json | 2 +- .../Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json | 2 +- .../Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json | 2 +- .../Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json | 2 +- .../Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json | 2 +- .../Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json | 2 +- .../Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json | 2 +- .../Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json | 2 +- .../Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json | 2 +- .../Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json | 2 +- .../Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json | 2 +- .../Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json | 2 +- .../Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json | 2 +- .../Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json | 2 +- .../Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json | 2 +- .../Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json | 2 +- .../filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json | 2 +- 80 files changed, 80 insertions(+), 80 deletions(-) diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json index 69a948e993..b3774299ab 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json @@ -17,6 +17,6 @@ "1" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json index c0ea593f87..3d2d110ab8 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json index dbb184c629..4c39a6b53f 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json index f34b489862..7700ec94a9 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json index bf10c3f60a..7afbd3ad2f 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json @@ -17,6 +17,6 @@ "1" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json index f6c8c07549..6e79495a5d 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "16" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json index a9a0fad842..e2734fdbe7 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "16" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json index ec6e6fb489..21c39efbb2 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "16" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json index 541068fb5a..e1ebd81f1c 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "11" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11 mm^3/s, from Snorca standard preset Generic PETG-CF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11 mm^3/s, from Snorca standard preset Generic PETG-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json index 5533276d17..0bc1021008 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "11.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json index e351a4ca4e..acdb9f92c9 100644 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "11.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json index 68231acfb7..4c60340703 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json @@ -17,6 +17,6 @@ "1.6" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Generic PLA @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Generic PLA @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json index 2baa380948..04f5b81b65 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json index 10c26c9d84..e9cd941ccb 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json index e718fafcf4..0df240dcd0 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json index 86932032b6..228901f1cf 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json @@ -17,6 +17,6 @@ "2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json index af222bcf59..e016e53d1a 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "18" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json index 69d900ad3f..348db5014d 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "18" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json index 7aab3575f0..f377e30f7b 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "18" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json index edcf2ba1b4..53fc87bb9b 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json @@ -17,6 +17,6 @@ "2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json index c3670ffc51..980840da51 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "7.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json index 9bb5ac2b2e..d3e0aced6d 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "7.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json index cf4f4fc493..450b030227 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "7.5" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json index 85ca08b766..07af2d1a24 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json index af044f19ea..aa2ae79dc5 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json index 3aba0b6dc9..ddeec0f5bd 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "12" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json index 665559b2f7..83ff889f31 100644 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json @@ -17,6 +17,6 @@ "3.2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json index 475ad1725c..cd745b81b6 100644 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json @@ -17,6 +17,6 @@ "3.2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json index 3e5a21716f..589302d895 100644 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json @@ -17,6 +17,6 @@ "3.2" ], "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json index 2c0f8a6936..22401d9ca5 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Polymaker General PLA Family @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Polymaker General PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json index c51dce398d..0c9dfe4271 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Silk PLA Family @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Silk PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json index 724ab002c2..2eac1cc0c2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Tough PLA Family @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Tough PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json index 0de983e448..fb361728c6 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json @@ -45,6 +45,6 @@ "280" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ABS @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ABS @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json index 212e9b1044..b5609dc056 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json @@ -45,6 +45,6 @@ "0.02" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ASA @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ASA @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json index 6fded62f26..c86ce97a3a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json index 5b562ef932..232d346113 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json @@ -63,6 +63,6 @@ "0.035" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 30 mm^3/s, from Snorca HF preset Snapmaker PETG HF (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 30 mm^3/s, from Snorca HF preset Snapmaker PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json index 1bb8fca6dd..80eb645ad2 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json index b95ecd4911..10321dcf95 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json index eec95f80bf..6c67be5752 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "1" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json index 1a17739f69..c25ea01897 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json @@ -15,7 +15,7 @@ "0.05" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 10 mm^3/s, from Snorca HF preset Snapmaker PETG Translucent @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 10 mm^3/s, from Snorca HF preset Snapmaker PETG Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "10" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json index 0fceb6ef5a..675e07357b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "16" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json index 26749c6ecf..d8771ee472 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "16" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json index 6c06c1c4bf..c5473497d8 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json @@ -60,6 +60,6 @@ "0.028" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PETG-CF @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PETG-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json index 3d2e16dc7f..86cd96747b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "15" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Snapmaker PLA Basic @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Snapmaker PLA Basic @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json index 1a63ff75cc..f408b23651 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json @@ -24,7 +24,7 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 15 mm^3/s, from Snorca HF preset Snapmaker PLA Full Spectrum @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 15 mm^3/s, from Snorca HF preset Snapmaker PLA Full Spectrum @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "15" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json index b2f4efcfb1..70c4b76b98 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json @@ -21,6 +21,6 @@ "0.023" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PLA Glow @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PLA Glow @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json index 3b2e3541c4..f9f65c0f64 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json @@ -27,6 +27,6 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json index 45bf88f8b5..6a39076a6f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json @@ -39,6 +39,6 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA Matte @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA Matte @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json index 2430b54a9d..6c471d4f08 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json @@ -27,6 +27,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json index 38aed6438a..d076c056a5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json @@ -27,6 +27,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json index d8eba5a227..a3715598dd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json @@ -12,6 +12,6 @@ "2" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json index 96869e7af9..23626faf6a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json @@ -18,6 +18,6 @@ "Slope Lift" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Silk @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Silk @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json index e71fc44138..b4f510bfec 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json @@ -12,6 +12,6 @@ "10" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json index 4b8d8be438..4919383540 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json @@ -12,6 +12,6 @@ "10" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json index dd492516fb..4b3e1405bb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json index 03be272261..48a4044180 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json @@ -21,6 +21,6 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA SnapSpeed @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA SnapSpeed @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json index 8bb018713c..92c7b1388d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json index 6af5875213..20db16271a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json index 66ea29ff0c..30c289604f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.2 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "1.6" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json index dd3134ef2b..d8ab413ff1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json @@ -12,7 +12,7 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Translucent @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "12" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json index 647971457c..08ef19a9cb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "12" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json index ef2bcbe096..a2e1b4dc16 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "12" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json index 5f3d8d6cc7..47fa100cd1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json @@ -21,6 +21,6 @@ "0.024" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA Wood @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA Wood @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json index b30036af55..9fc8c36369 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json @@ -18,6 +18,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json index 9bbf33e51b..5300cdbb96 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json @@ -15,7 +15,7 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "20" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json index 310efaefe2..b156c65012 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json @@ -33,6 +33,6 @@ "0.02" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA-CF @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json index 3fecd0e77c..28e15cb5eb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json index 0f5a73930a..7c62ffd8ed 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json @@ -24,6 +24,6 @@ "1" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json index ca38fd6675..ab3a76e43d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json @@ -21,6 +21,6 @@ "0.6" ], "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer snorca/main b1831e5dcb for 0.4 mm nozzles. Volumetric speed ceiling: 6 mm^3/s, from Snorca HF preset Snapmaker PVA @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 6 mm^3/s, from Snorca HF preset Snapmaker PVA @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json index e5ef681dc3..b3f8c6db87 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json @@ -12,7 +12,7 @@ "0.9506" ], "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "6" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json index 693f9a4c27..b18972c81f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json @@ -12,7 +12,7 @@ "0.9312" ], "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from snorca/main b1831e5dcb. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ "6" diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json index 1accbf432d..61b7ca0026 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "3.2" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json index 51edd1e01f..0e85a008d6 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json @@ -12,6 +12,6 @@ "3.2" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json index 847f0ed8d5..bd3a88d1cd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json @@ -12,6 +12,6 @@ "3.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.5 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.5 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json index 0b0d178f90..f0b49ea018 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "9" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 9 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 9 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json index 887c94ee7b..198b8de558 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json @@ -12,6 +12,6 @@ "12" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json index cb976d3246..28374dc75a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json @@ -12,6 +12,6 @@ "16" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json index afaefa2460..c976fd3bc9 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json @@ -12,6 +12,6 @@ "10.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.4 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json index 00dd85f773..98b3b6a564 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json @@ -12,6 +12,6 @@ "10.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.6 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json index cb7d2fa74f..8f49d839c3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json @@ -12,6 +12,6 @@ "10.5" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.8 nozzle (snorca/main b1831e5dcb). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } From a46e29c21e3e561cea9ba0c8a7e2a36e1b50438f Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Tue, 29 Sep 2026 07:31:37 -0400 Subject: [PATCH 66/89] Update breakaway support flow for HF 0.4 --- ...er Breakaway Support For PLA @U1 0.4 High Flow nozzle.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json index 9d8922829a..32fd557a65 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json @@ -9,9 +9,9 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_max_volumetric_speed": [ - "12" + "18" ], "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker Support For PLA @U1 0.4 nozzle. Its base identifies the material as Snapmaker Breakaway Support. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } From 7cbea5f454c2fb7493bf76ae12aa14d56f557afa Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Tue, 29 Sep 2026 08:24:37 -0400 Subject: [PATCH 67/89] Consolidate U1 nozzle variants while preserving filament tuning Support variant-specific cooling, pressure advance, purge, ramming and temperature ranges. Keep shared U1 values as singletons and verify element-zero fallback for missing variant entries. Preserve preset migration aliases and validate high-flow selections using their declared nozzle volumes. Co-authored-by: Codex --- resources/profiles/Snapmaker.json | 710 ++---------------- ...ron ASA-CF08 @U1 0.4 High Flow nozzle.json | 17 - ...on PA12-CF10 @U1 0.4 High Flow nozzle.json | 17 - ...ron PA6-CF20 @U1 0.4 High Flow nozzle.json | 17 - ...on PA612-ESD @U1 0.4 High Flow nozzle.json | 17 - ...ron PETG-ESD @U1 0.4 High Flow nozzle.json | 17 - ...ron PPS-GF20 @U1 0.4 High Flow nozzle.json | 17 - ...Generic PETG @U1 0.2 High Flow nozzle.json | 22 - .../filament/Generic PETG @U1 0.2 nozzle.json | 24 + ...Generic PETG @U1 0.4 High Flow nozzle.json | 22 - .../filament/Generic PETG @U1 0.4 nozzle.json | 24 + ...Generic PETG @U1 0.6 High Flow nozzle.json | 22 - .../filament/Generic PETG @U1 0.6 nozzle.json | 24 + ...Generic PETG @U1 0.8 High Flow nozzle.json | 22 - .../filament/Generic PETG @U1 0.8 nozzle.json | 24 + ...eric PETG HF @U1 0.2 High Flow nozzle.json | 22 - .../Generic PETG HF @U1 0.2 nozzle.json | 24 + ...eric PETG HF @U1 0.4 High Flow nozzle.json | 22 - .../Generic PETG HF @U1 0.4 nozzle.json | 24 + ...eric PETG HF @U1 0.6 High Flow nozzle.json | 22 - .../Generic PETG HF @U1 0.6 nozzle.json | 24 + ...eric PETG HF @U1 0.8 High Flow nozzle.json | 22 - .../Generic PETG HF @U1 0.8 nozzle.json | 24 + ...eric PETG-CF @U1 0.4 High Flow nozzle.json | 22 - .../Generic PETG-CF @U1 0.4 nozzle.json | 24 + ...eric PETG-CF @U1 0.6 High Flow nozzle.json | 22 - .../Generic PETG-CF @U1 0.6 nozzle.json | 20 + ...eric PETG-CF @U1 0.8 High Flow nozzle.json | 22 - .../Generic PETG-CF @U1 0.8 nozzle.json | 20 + .../Generic PLA @U1 0.2 High Flow nozzle.json | 22 - .../filament/Generic PLA @U1 0.2 nozzle.json | 24 + .../Generic PLA @U1 0.4 High Flow nozzle.json | 22 - .../filament/Generic PLA @U1 0.4 nozzle.json | 23 + .../Generic PLA @U1 0.6 High Flow nozzle.json | 22 - .../filament/Generic PLA @U1 0.6 nozzle.json | 23 + .../Generic PLA @U1 0.8 High Flow nozzle.json | 22 - .../filament/Generic PLA @U1 0.8 nozzle.json | 23 + ...A High Speed @U1 0.2 High Flow nozzle.json | 22 - ...Generic PLA High Speed @U1 0.2 nozzle.json | 24 + ...A High Speed @U1 0.4 High Flow nozzle.json | 22 - ...Generic PLA High Speed @U1 0.4 nozzle.json | 23 + ...A High Speed @U1 0.6 High Flow nozzle.json | 22 - ...Generic PLA High Speed @U1 0.6 nozzle.json | 23 + ...A High Speed @U1 0.8 High Flow nozzle.json | 22 - ...Generic PLA High Speed @U1 0.8 nozzle.json | 23 + ...ric PLA Silk @U1 0.2 High Flow nozzle.json | 22 - .../Generic PLA Silk @U1 0.2 nozzle.json | 24 + ...ric PLA Silk @U1 0.4 High Flow nozzle.json | 22 - .../Generic PLA Silk @U1 0.4 nozzle.json | 20 + ...ric PLA Silk @U1 0.6 High Flow nozzle.json | 22 - .../Generic PLA Silk @U1 0.6 nozzle.json | 20 + ...ric PLA Silk @U1 0.8 High Flow nozzle.json | 22 - .../Generic PLA Silk @U1 0.8 nozzle.json | 20 + ...neric PLA-CF @U1 0.4 High Flow nozzle.json | 22 - .../Generic PLA-CF @U1 0.4 nozzle.json | 23 + ...neric PLA-CF @U1 0.6 High Flow nozzle.json | 22 - .../Generic PLA-CF @U1 0.6 nozzle.json | 23 + ...neric PLA-CF @U1 0.8 High Flow nozzle.json | 22 - .../Generic PLA-CF @U1 0.8 nozzle.json | 23 + .../Generic TPU @U1 0.4 High Flow nozzle.json | 22 - .../filament/Generic TPU @U1 0.4 nozzle.json | 20 + .../Generic TPU @U1 0.6 High Flow nozzle.json | 22 - .../filament/Generic TPU @U1 0.6 nozzle.json | 20 + .../Generic TPU @U1 0.8 High Flow nozzle.json | 22 - .../filament/Generic TPU @U1 0.8 nozzle.json | 20 + ...nchroma CoPE @U1 0.4 High Flow nozzle.json | 17 - ...anchroma PLA @U1 0.4 High Flow nozzle.json | 17 - ...LA Celestial @U1 0.4 High Flow nozzle.json | 17 - ...a PLA Galaxy @U1 0.4 High Flow nozzle.json | 17 - ...oma PLA Glow @U1 0.4 High Flow nozzle.json | 17 - ...PLA Luminous @U1 0.4 High Flow nozzle.json | 17 - ...a PLA Marble @U1 0.4 High Flow nozzle.json | 17 - ...ma PLA Matte @U1 0.4 High Flow nozzle.json | 17 - ...PLA Metallic @U1 0.4 High Flow nozzle.json | 17 - ...oma PLA Neon @U1 0.4 High Flow nozzle.json | 17 - ...ma PLA Satin @U1 0.4 High Flow nozzle.json | 17 - ...oma PLA Silk @U1 0.4 High Flow nozzle.json | 17 - ...LA Starlight @U1 0.4 High Flow nozzle.json | 17 - ...A Temp Shift @U1 0.4 High Flow nozzle.json | 17 - ... Translucent @U1 0.4 High Flow nozzle.json | 17 - ...PLA UV Shift @U1 0.4 High Flow nozzle.json | 17 - ...yLite CosPLA @U1 0.4 High Flow nozzle.json | 17 - ...olyLite PETG @U1 0.4 High Flow nozzle.json | 17 - ... Translucent @U1 0.4 High Flow nozzle.json | 17 - ...PolyLite PLA @U1 0.4 High Flow nozzle.json | 17 - ...e PLA Galaxy @U1 0.4 High Flow nozzle.json | 17 - ...ite PLA Glow @U1 0.4 High Flow nozzle.json | 17 - ...PLA Luminous @U1 0.4 High Flow nozzle.json | 17 - ...ite PLA Neon @U1 0.4 High Flow nozzle.json | 17 - ...Lite PLA Pro @U1 0.4 High Flow nozzle.json | 17 - ...Pro Metallic @U1 0.4 High Flow nozzle.json | 17 - ...LA Starlight @U1 0.4 High Flow nozzle.json | 17 - ... Translucent @U1 0.4 High Flow nozzle.json | 17 - ...olyTerra PLA @U1 0.4 High Flow nozzle.json | 17 - ...a PLA Marble @U1 0.4 High Flow nozzle.json | 17 - ...lyTerra PLA+ @U1 0.4 High Flow nozzle.json | 17 - ...l PLA Family @U1 0.4 High Flow nozzle.json | 17 - .../Polymaker General PLA Family @U1.json | 11 +- ...maker HT-PLA @U1 0.4 High Flow nozzle.json | 17 - ...er HT-PLA-GF @U1 0.4 High Flow nozzle.json | 17 - ...lymaker PETG @U1 0.4 High Flow nozzle.json | 17 - ... PETG Galaxy @U1 0.4 High Flow nozzle.json | 17 - ...olymaker PLA @U1 0.4 High Flow nozzle.json | 17 - ...aker PLA Pro @U1 0.4 High Flow nozzle.json | 17 - ...Pro Metallic @U1 0.4 High Flow nozzle.json | 17 - ...k PLA Family @U1 0.4 High Flow nozzle.json | 17 - .../Polymaker Silk PLA Family @U1.json | 11 +- ...h PLA Family @U1 0.4 High Flow nozzle.json | 17 - .../Polymaker Tough PLA Family @U1.json | 11 +- .../Fiberon ASA-CF08 @Snapmaker U1.json | 11 +- .../Fiberon PA12-CF10 @Snapmaker U1.json | 11 +- .../Fiberon PA6-CF20 @Snapmaker U1.json | 11 +- .../Fiberon PA612-ESD @Snapmaker U1.json | 11 +- .../Fiberon PETG-ESD @Snapmaker U1.json | 11 +- .../Fiberon PPS-GF20 @Snapmaker U1.json | 13 +- .../Panchroma CoPE @Snapmaker U1.json | 12 +- .../Panchroma PLA @Snapmaker U1.json | 12 +- ...Panchroma PLA Celestial @Snapmaker U1.json | 12 +- .../Panchroma PLA Galaxy @Snapmaker U1.json | 12 +- .../Panchroma PLA Glow @Snapmaker U1.json | 12 +- .../Panchroma PLA Luminous @Snapmaker U1.json | 12 +- .../Panchroma PLA Marble @Snapmaker U1.json | 12 +- .../Panchroma PLA Matte @Snapmaker U1.json | 12 +- .../Panchroma PLA Metallic @Snapmaker U1.json | 12 +- .../Panchroma PLA Neon @Snapmaker U1.json | 12 +- .../Panchroma PLA Satin @Snapmaker U1.json | 12 +- .../Panchroma PLA Silk @Snapmaker U1.json | 12 +- ...Panchroma PLA Starlight @Snapmaker U1.json | 12 +- ...anchroma PLA Temp Shift @Snapmaker U1.json | 24 +- ...nchroma PLA Translucent @Snapmaker U1.json | 12 +- .../Panchroma PLA UV Shift @Snapmaker U1.json | 12 +- .../PolyLite CosPLA @Snapmaker U1.json | 11 +- .../PolyLite PETG @Snapmaker U1.json | 11 +- ...lyLite PETG Translucent @Snapmaker U1.json | 11 +- .../Polymaker/PolyLite PLA @Snapmaker U1.json | 12 +- .../PolyLite PLA Galaxy @Snapmaker U1.json | 11 +- .../PolyLite PLA Glow @Snapmaker U1.json | 11 +- .../PolyLite PLA Luminous @Snapmaker U1.json | 11 +- .../PolyLite PLA Neon @Snapmaker U1.json | 11 +- .../PolyLite PLA Pro @Snapmaker U1.json | 24 +- ...lyLite PLA Pro Metallic @Snapmaker U1.json | 11 +- .../PolyLite PLA Starlight @Snapmaker U1.json | 11 +- ...olyLite PLA Translucent @Snapmaker U1.json | 11 +- .../PolyTerra PLA @Snapmaker U1.json | 12 +- .../PolyTerra PLA Marble @Snapmaker U1.json | 11 +- .../PolyTerra PLA+ @Snapmaker U1.json | 11 +- .../Polymaker HT-PLA @Snapmaker U1.json | 12 +- .../Polymaker HT-PLA-GF @Snapmaker U1.json | 12 +- .../Polymaker PETG @Snapmaker U1.json | 11 +- .../Polymaker PETG Galaxy @Snapmaker U1.json | 11 +- .../Polymaker PLA @Snapmaker U1.json | 10 +- .../Polymaker PLA Pro @Snapmaker U1.json | 11 +- ...ymaker PLA Pro Metallic @Snapmaker U1.json | 11 +- ...napmaker ABS @U1 0.4 High Flow nozzle.json | 50 -- .../Snapmaker/filament/Snapmaker ABS @U1.json | 58 +- ...r ABS Benchy @U1 0.4 High Flow nozzle.json | 20 - .../filament/Snapmaker ABS Benchy @U1.json | 11 +- ...napmaker ASA @U1 0.4 High Flow nozzle.json | 50 -- .../Snapmaker/filament/Snapmaker ASA @U1.json | 58 +- ...port For PLA @U1 0.2 High Flow nozzle.json | 17 - ...akaway Support For PLA @U1 0.2 nozzle.json | 14 +- ...port For PLA @U1 0.4 High Flow nozzle.json | 17 - ...port For PLA @U1 0.6 High Flow nozzle.json | 17 - ...akaway Support For PLA @U1 0.6 nozzle.json | 14 +- ...port For PLA @U1 0.8 High Flow nozzle.json | 17 - ...akaway Support For PLA @U1 0.8 nozzle.json | 14 +- ...apmaker Breakaway Support For PLA @U1.json | 15 +- ...pmaker PA-CF @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker PA-CF @U1.json | 11 +- ...napmaker PET @U1 0.4 High Flow nozzle.json | 17 - .../Snapmaker/filament/Snapmaker PET @U1.json | 11 +- ...apmaker PETG @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker PETG @U1.json | 11 +- ...aker PETG HF @U1 0.2 High Flow nozzle.json | 29 - .../Snapmaker PETG HF @U1 0.2 nozzle.json | 33 +- ... => Snapmaker PETG HF @U1 0.4 nozzle.json} | 98 ++- ...aker PETG HF @U1 0.6 High Flow nozzle.json | 29 - .../Snapmaker PETG HF @U1 0.6 nozzle.json | 30 +- ...aker PETG HF @U1 0.8 High Flow nozzle.json | 29 - .../Snapmaker PETG HF @U1 0.8 nozzle.json | 30 +- ... Translucent @U1 0.2 High Flow nozzle.json | 23 - ...maker PETG Translucent @U1 0.2 nozzle.json | 20 +- ... Translucent @U1 0.4 High Flow nozzle.json | 23 - ...maker PETG Translucent @U1 0.4 nozzle.json | 17 +- ... Translucent @U1 0.6 High Flow nozzle.json | 23 - ...maker PETG Translucent @U1 0.6 nozzle.json | 20 +- ... Translucent @U1 0.8 High Flow nozzle.json | 23 - ...maker PETG Translucent @U1 0.8 nozzle.json | 20 +- ...aker PETG-CF @U1 0.4 High Flow nozzle.json | 65 -- .../filament/Snapmaker PETG-CF @U1.json | 78 +- ...napmaker PLA @U1 0.4 High Flow nozzle.json | 17 - .../Snapmaker/filament/Snapmaker PLA @U1.json | 11 +- ...er PLA Basic @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker PLA Basic @U1.json | 31 +- ...aker PLA Eco @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker PLA Eco @U1.json | 11 +- ...ull Spectrum @U1 0.4 High Flow nozzle.json | 32 - ...aker PLA Full Spectrum @U1 0.4 nozzle.json | 43 +- ...ker PLA Glow @U1 0.4 High Flow nozzle.json | 26 - .../Snapmaker PLA Glow @U1 0.4 nozzle.json | 24 +- ...ker PLA Lite @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker PLA Lite @U1.json | 31 +- ...er PLA Matte @U1 0.2 High Flow nozzle.json | 32 - .../Snapmaker PLA Matte @U1 0.2 nozzle.json | 37 +- ...er PLA Matte @U1 0.4 High Flow nozzle.json | 44 -- ...er PLA Matte @U1 0.6 High Flow nozzle.json | 32 - .../Snapmaker PLA Matte @U1 0.6 nozzle.json | 34 +- ...er PLA Matte @U1 0.8 High Flow nozzle.json | 32 - .../Snapmaker PLA Matte @U1 0.8 nozzle.json | 34 +- .../filament/Snapmaker PLA Matte @U1.json | 73 +- ...er PLA Metal @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker PLA Metal @U1.json | 11 +- ...ker PLA Silk @U1 0.2 High Flow nozzle.json | 17 - .../Snapmaker PLA Silk @U1 0.2 nozzle.json | 19 +- ...ker PLA Silk @U1 0.4 High Flow nozzle.json | 23 - ...ker PLA Silk @U1 0.6 High Flow nozzle.json | 17 - .../Snapmaker PLA Silk @U1 0.6 nozzle.json | 19 +- ...ker PLA Silk @U1 0.8 High Flow nozzle.json | 17 - .../Snapmaker PLA Silk @U1 0.8 nozzle.json | 19 +- .../filament/Snapmaker PLA Silk @U1.json | 28 +- ...LA SnapSpeed @U1 0.2 High Flow nozzle.json | 23 - ...napmaker PLA SnapSpeed @U1 0.2 nozzle.json | 17 +- ...LA SnapSpeed @U1 0.4 High Flow nozzle.json | 26 - ...LA SnapSpeed @U1 0.6 High Flow nozzle.json | 23 - ...napmaker PLA SnapSpeed @U1 0.6 nozzle.json | 18 +- ...LA SnapSpeed @U1 0.8 High Flow nozzle.json | 23 - ...napmaker PLA SnapSpeed @U1 0.8 nozzle.json | 17 +- .../filament/Snapmaker PLA SnapSpeed @U1.json | 40 +- ... Translucent @U1 0.2 High Flow nozzle.json | 23 - ...pmaker PLA Translucent @U1 0.2 nozzle.json | 46 +- ... Translucent @U1 0.4 High Flow nozzle.json | 20 - ...pmaker PLA Translucent @U1 0.4 nozzle.json | 58 +- ... Translucent @U1 0.6 High Flow nozzle.json | 23 - ...pmaker PLA Translucent @U1 0.6 nozzle.json | 55 +- ... Translucent @U1 0.8 High Flow nozzle.json | 23 - ...pmaker PLA Translucent @U1 0.8 nozzle.json | 52 +- ...ker PLA Wood @U1 0.4 High Flow nozzle.json | 26 - .../Snapmaker PLA Wood @U1 0.4 nozzle.json | 27 +- ...ker PLA Wood @U1 0.6 High Flow nozzle.json | 23 - .../Snapmaker PLA Wood @U1 0.6 nozzle.json | 20 +- ...ker PLA Wood @U1 0.8 High Flow nozzle.json | 23 - .../Snapmaker PLA Wood @U1 0.8 nozzle.json | 17 +- ...maker PLA-CF @U1 0.4 High Flow nozzle.json | 38 - .../Snapmaker PLA-CF @U1 0.4 nozzle.json | 42 +- ...maker PLA-CF @U1 0.6 High Flow nozzle.json | 29 - .../Snapmaker PLA-CF @U1 0.6 nozzle.json | 29 +- ...maker PLA-CF @U1 0.8 High Flow nozzle.json | 29 - .../Snapmaker PLA-CF @U1 0.8 nozzle.json | 29 +- ...napmaker PVA @U1 0.4 High Flow nozzle.json | 26 - ...napmaker PVA @U1 0.6 High Flow nozzle.json | 20 - .../Snapmaker PVA @U1 0.6 nozzle.json | 17 +- ...napmaker PVA @U1 0.8 High Flow nozzle.json | 20 - .../Snapmaker PVA @U1 0.8 nozzle.json | 17 +- .../Snapmaker/filament/Snapmaker PVA @U1.json | 24 +- ...napmaker TPE @U1 0.4 High Flow nozzle.json | 17 - .../Snapmaker/filament/Snapmaker TPE @U1.json | 10 +- ...aker TPU 90A @U1 0.4 High Flow nozzle.json | 17 - ...aker TPU 90A @U1 0.6 High Flow nozzle.json | 17 - .../Snapmaker TPU 90A @U1 0.6 nozzle.json | 14 +- ...aker TPU 90A @U1 0.8 High Flow nozzle.json | 17 - .../Snapmaker TPU 90A @U1 0.8 nozzle.json | 17 +- .../filament/Snapmaker TPU 90A @U1.json | 17 +- ...aker TPU 95A @U1 0.4 High Flow nozzle.json | 17 - .../filament/Snapmaker TPU 95A @U1.json | 43 +- ...r TPU 95A HF @U1 0.4 High Flow nozzle.json | 17 - ...r TPU 95A HF @U1 0.6 High Flow nozzle.json | 17 - .../Snapmaker TPU 95A HF @U1 0.6 nozzle.json | 14 +- ...r TPU 95A HF @U1 0.8 High Flow nozzle.json | 17 - .../Snapmaker TPU 95A HF @U1 0.8 nozzle.json | 14 +- .../filament/Snapmaker TPU 95A HF @U1.json | 17 +- ...napmaker TPU @U1 0.4 High Flow nozzle.json | 17 - ...napmaker TPU @U1 0.6 High Flow nozzle.json | 17 - ...napmaker TPU @U1 0.8 High Flow nozzle.json | 17 - .../Snapmaker/filament/Snapmaker TPU @U1.json | 17 +- ...PU High-Flow @U1 0.4 High Flow nozzle.json | 17 - ...PU High-Flow @U1 0.6 High Flow nozzle.json | 17 - ...PU High-Flow @U1 0.8 High Flow nozzle.json | 17 - .../filament/Snapmaker TPU High-Flow @U1.json | 12 +- .../Snapmaker U1 (0.2 High Flow nozzle).json | 42 +- .../Snapmaker U1 (0.4 High Flow nozzle).json | 42 +- .../Snapmaker U1 (0.6 High Flow nozzle).json | 42 +- .../Snapmaker U1 (0.8 High Flow nozzle).json | 42 +- .../Snapmaker/machine/Snapmaker U1.json | 2 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 79 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...6 Standard @Snapmaker U1 (0.2 nozzle).json | 70 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...Extra Fine @Snapmaker U1 (0.4 nozzle).json | 71 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 79 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 90 ++- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...8 Standard @Snapmaker U1 (0.2 nozzle).json | 70 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 79 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...0 Standard @Snapmaker U1 (0.2 nozzle).json | 68 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - .../0.12 Fine @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 90 ++- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...2 Standard @Snapmaker U1 (0.2 nozzle).json | 70 +- ... @Snapmaker U1 (0.2 High Flow nozzle).json | 23 - ...4 Standard @Snapmaker U1 (0.2 nozzle).json | 70 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 90 ++- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...16 Optimal @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...8 Standard @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...20 Quality @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...0 Standard @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...0 Standard @Snapmaker U1 (0.6 nozzle).json | 70 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...0 Strength @Snapmaker U1 (0.4 nozzle).json | 71 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...20 Support @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ... Support W @Snapmaker U1 (0.4 nozzle).json | 71 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...0.24 Draft @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...4 Standard @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 - ...4 Standard @Snapmaker U1 (0.8 nozzle).json | 74 +- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ....25 Benchy @Snapmaker U1 (0.4 nozzle).json | 144 +++- ... @Snapmaker U1 (0.4 High Flow nozzle).json | 23 - ...xtra Draft @Snapmaker U1 (0.4 nozzle).json | 70 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...0.30 Draft @Snapmaker U1 (0.6 nozzle).json | 72 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...0 Standard @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...0 Strength @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 - ...2 Standard @Snapmaker U1 (0.8 nozzle).json | 74 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...6 Standard @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...xtra Draft @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 - ...0 Standard @Snapmaker U1 (0.8 nozzle).json | 74 +- ... @Snapmaker U1 (0.6 High Flow nozzle).json | 23 - ...2 Standard @Snapmaker U1 (0.6 nozzle).json | 74 +- ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 - ...8 Standard @Snapmaker U1 (0.8 nozzle).json | 74 +- ... @Snapmaker U1 (0.8 High Flow nozzle).json | 23 - ...6 Standard @Snapmaker U1 (0.8 nozzle).json | 70 +- scripts/orca_profile_tool.py | 16 +- scripts/tests/test_profile_tool.py | 26 +- .../OrcaSlicer_profile_validator.cpp | 7 + src/libslic3r/PrintConfig.cpp | 13 + src/slic3r/GUI/ConfigManipulation.cpp | 24 +- src/slic3r/GUI/ConfigManipulation.hpp | 8 +- src/slic3r/GUI/Tab.cpp | 49 +- .../test_config_variant_expansion.cpp | 90 +++ 363 files changed, 5067 insertions(+), 5441 deletions(-) delete mode 100644 resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json create mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json rename resources/profiles/Snapmaker/filament/{Snapmaker PETG HF @U1 0.4 High Flow nozzle.json => Snapmaker PETG HF @U1 0.4 nozzle.json} (54%) delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json delete mode 100644 resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index c8e5b1dcc3..afe302529b 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.20", + "version": "02.04.00.21", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ @@ -194,10 +194,6 @@ "name": "fdm_process_idex", "sub_path": "process/fdm_process_idex.json" }, - { - "name": "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json" - }, { "name": "fdm_process_U1_0.06_nozzle_0.2", "sub_path": "process/fdm_process_U1_0.06_nozzle_0.2.json" @@ -618,146 +614,6 @@ "name": "0.40 Extra Draft @Snapmaker U1 (0.6 nozzle)", "sub_path": "process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json" }, - { - "name": "0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.20 Support @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "sub_path": "process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" - }, - { - "name": "0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "sub_path": "process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" - }, - { - "name": "0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "sub_path": "process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" - }, - { - "name": "0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "sub_path": "process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" - }, - { - "name": "0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "sub_path": "process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json" - }, - { - "name": "0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json" - }, { "name": "0.06 Standard @Snapmaker Artisan (0.2 nozzle)", "sub_path": "process/0.06 Standard @Snapmaker Artisan (0.2 nozzle).json" @@ -900,66 +756,6 @@ "name": "fdm_filament_tpu", "sub_path": "filament/fdm_filament_tpu.json" }, - { - "name": "Snapmaker PLA Basic @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Silk @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Silk @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Silk @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Silk @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Lite @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Matte @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Matte @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Matte @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU 95A @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json" - }, { "name": "Fiberon PPS-GF20 @Snapmaker U1 base", "sub_path": "filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1 base.json" @@ -1653,8 +1449,8 @@ "sub_path": "filament/Snapmaker PETG HF @U1 0.2 nozzle.json" }, { - "name": "Snapmaker PETG HF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json" + "name": "Snapmaker PETG HF @U1 0.4 nozzle", + "sub_path": "filament/Snapmaker PETG HF @U1 0.4 nozzle.json" }, { "name": "Snapmaker PETG HF @U1 0.6 nozzle", @@ -1968,30 +1764,6 @@ "name": "Snapmaker PLA Translucent @U1 0.8 nozzle", "sub_path": "filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json" }, - { - "name": "Snapmaker PLA Wood @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Wood @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Wood @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA-CF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA-CF @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA-CF @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json" - }, { "name": "Snapmaker PLA-CF", "sub_path": "filament/Snapmaker PLA-CF.json" @@ -2072,30 +1844,6 @@ "name": "Snapmaker J1 TPU High-Flow", "sub_path": "filament/Snapmaker J1 TPU High-Flow.json" }, - { - "name": "Snapmaker TPU 90A @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU 90A @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU 90A @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json" - }, { "name": "Snapmaker TPE @U1", "sub_path": "filament/Snapmaker TPE @U1.json" @@ -2116,174 +1864,6 @@ "name": "Snapmaker TPU", "sub_path": "filament/Snapmaker TPU.json" }, - { - "name": "Fiberon PPS-GF20 @U1 0.4 High Flow nozzle", - "sub_path": "filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker ABS @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker ABS Benchy @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Fiberon ASA-CF08 @U1 0.4 High Flow nozzle", - "sub_path": "filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker ASA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Fiberon PA12-CF10 @U1 0.4 High Flow nozzle", - "sub_path": "filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Fiberon PA6-CF20 @U1 0.4 High Flow nozzle", - "sub_path": "filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Fiberon PA612-ESD @U1 0.4 High Flow nozzle", - "sub_path": "filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PA-CF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PET @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PET @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Fiberon PETG-ESD @U1 0.4 High Flow nozzle", - "sub_path": "filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PETG @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PETG @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PETG Translucent @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker PETG @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker PETG @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker PETG Galaxy @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG HF @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG HF @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG HF @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG Translucent @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG Translucent @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG Translucent @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG Translucent @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker PETG-CF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma CoPE @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Celestial @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Galaxy @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Glow @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Luminous @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Marble @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Matte @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Metallic @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Neon @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Satin @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Silk @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Starlight @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA Translucent @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Panchroma PLA UV Shift @U1 0.4 High Flow nozzle", - "sub_path": "filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite CosPLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA @U1 0.4 High Flow nozzle.json" - }, { "name": "PolyLite Dual PLA @0.2 nozzle", "sub_path": "filament/Polymaker/PolyLite Dual PLA @0.2 nozzle.json" @@ -2292,114 +1872,6 @@ "name": "PolyLite J1 PLA @0.2 nozzle", "sub_path": "filament/Polymaker/PolyLite J1 PLA @0.2 nozzle.json" }, - { - "name": "PolyLite PLA Galaxy @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Glow @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Luminous @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Neon @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Pro @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Starlight @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyLite PLA Translucent @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyTerra PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyTerra PLA Marble @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json" - }, - { - "name": "PolyTerra PLA+ @U1 0.4 High Flow nozzle", - "sub_path": "filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker HT-PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker PLA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker General PLA Family @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker Silk PLA Family @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker Tough PLA Family @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker PLA Pro @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle", - "sub_path": "filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Matte @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Glow @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Metal @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Translucent @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Translucent @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Translucent @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Translucent @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json" - }, { "name": "Snapmaker Dual PLA Eco", "sub_path": "filament/Snapmaker Dual PLA Eco.json" @@ -2436,50 +1908,6 @@ "name": "Snapmaker Breakaway Support @base", "sub_path": "filament/Snapmaker Breakaway Support @base.json" }, - { - "name": "Snapmaker PVA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker PVA @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker PVA @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPE @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Snapmaker PLA Eco @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json" - }, { "name": "Snapmaker Breakaway Support For PLA @U1", "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1.json" @@ -2497,136 +1925,120 @@ "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json" }, { - "name": "Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json" + "name": "Generic PETG @U1 0.2 nozzle", + "sub_path": "filament/Generic PETG @U1 0.2 nozzle.json" }, { - "name": "Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle", - "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json" + "name": "Generic PETG @U1 0.4 nozzle", + "sub_path": "filament/Generic PETG @U1 0.4 nozzle.json" }, { - "name": "Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle", - "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json" + "name": "Generic PETG @U1 0.6 nozzle", + "sub_path": "filament/Generic PETG @U1 0.6 nozzle.json" }, { - "name": "Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle", - "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json" + "name": "Generic PETG @U1 0.8 nozzle", + "sub_path": "filament/Generic PETG @U1 0.8 nozzle.json" }, { - "name": "Generic PETG @U1 0.2 High Flow nozzle", - "sub_path": "filament/Generic PETG @U1 0.2 High Flow nozzle.json" + "name": "Generic PETG HF @U1 0.2 nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.2 nozzle.json" }, { - "name": "Generic PETG @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PETG @U1 0.4 High Flow nozzle.json" + "name": "Generic PETG HF @U1 0.4 nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.4 nozzle.json" }, { - "name": "Generic PETG @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PETG @U1 0.6 High Flow nozzle.json" + "name": "Generic PETG HF @U1 0.6 nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.6 nozzle.json" }, { - "name": "Generic PETG @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PETG @U1 0.8 High Flow nozzle.json" + "name": "Generic PETG HF @U1 0.8 nozzle", + "sub_path": "filament/Generic PETG HF @U1 0.8 nozzle.json" }, { - "name": "Generic PETG HF @U1 0.2 High Flow nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.2 High Flow nozzle.json" + "name": "Generic PETG-CF @U1 0.4 nozzle", + "sub_path": "filament/Generic PETG-CF @U1 0.4 nozzle.json" }, { - "name": "Generic PETG HF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.4 High Flow nozzle.json" + "name": "Generic PETG-CF @U1 0.6 nozzle", + "sub_path": "filament/Generic PETG-CF @U1 0.6 nozzle.json" }, { - "name": "Generic PETG HF @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.6 High Flow nozzle.json" + "name": "Generic PETG-CF @U1 0.8 nozzle", + "sub_path": "filament/Generic PETG-CF @U1 0.8 nozzle.json" }, { - "name": "Generic PETG HF @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.8 High Flow nozzle.json" + "name": "Generic PLA @U1 0.2 nozzle", + "sub_path": "filament/Generic PLA @U1 0.2 nozzle.json" }, { - "name": "Generic PETG-CF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json" + "name": "Generic PLA @U1 0.4 nozzle", + "sub_path": "filament/Generic PLA @U1 0.4 nozzle.json" }, { - "name": "Generic PETG-CF @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json" + "name": "Generic PLA @U1 0.6 nozzle", + "sub_path": "filament/Generic PLA @U1 0.6 nozzle.json" }, { - "name": "Generic PETG-CF @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json" + "name": "Generic PLA @U1 0.8 nozzle", + "sub_path": "filament/Generic PLA @U1 0.8 nozzle.json" }, { - "name": "Generic PLA @U1 0.2 High Flow nozzle", - "sub_path": "filament/Generic PLA @U1 0.2 High Flow nozzle.json" + "name": "Generic PLA High Speed @U1 0.2 nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.2 nozzle.json" }, { - "name": "Generic PLA @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PLA @U1 0.4 High Flow nozzle.json" + "name": "Generic PLA High Speed @U1 0.4 nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.4 nozzle.json" }, { - "name": "Generic PLA @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PLA @U1 0.6 High Flow nozzle.json" + "name": "Generic PLA High Speed @U1 0.6 nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.6 nozzle.json" }, { - "name": "Generic PLA @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PLA @U1 0.8 High Flow nozzle.json" + "name": "Generic PLA High Speed @U1 0.8 nozzle", + "sub_path": "filament/Generic PLA High Speed @U1 0.8 nozzle.json" }, { - "name": "Generic PLA High Speed @U1 0.2 High Flow nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json" + "name": "Generic PLA Silk @U1 0.2 nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.2 nozzle.json" }, { - "name": "Generic PLA High Speed @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json" + "name": "Generic PLA Silk @U1 0.4 nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.4 nozzle.json" }, { - "name": "Generic PLA High Speed @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json" + "name": "Generic PLA Silk @U1 0.6 nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.6 nozzle.json" }, { - "name": "Generic PLA High Speed @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json" + "name": "Generic PLA Silk @U1 0.8 nozzle", + "sub_path": "filament/Generic PLA Silk @U1 0.8 nozzle.json" }, { - "name": "Generic PLA Silk @U1 0.2 High Flow nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json" + "name": "Generic PLA-CF @U1 0.4 nozzle", + "sub_path": "filament/Generic PLA-CF @U1 0.4 nozzle.json" }, { - "name": "Generic PLA Silk @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json" + "name": "Generic PLA-CF @U1 0.6 nozzle", + "sub_path": "filament/Generic PLA-CF @U1 0.6 nozzle.json" }, { - "name": "Generic PLA Silk @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json" + "name": "Generic PLA-CF @U1 0.8 nozzle", + "sub_path": "filament/Generic PLA-CF @U1 0.8 nozzle.json" }, { - "name": "Generic PLA Silk @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json" + "name": "Generic TPU @U1 0.4 nozzle", + "sub_path": "filament/Generic TPU @U1 0.4 nozzle.json" }, { - "name": "Generic PLA-CF @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json" + "name": "Generic TPU @U1 0.6 nozzle", + "sub_path": "filament/Generic TPU @U1 0.6 nozzle.json" }, { - "name": "Generic PLA-CF @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Generic PLA-CF @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json" - }, - { - "name": "Generic TPU @U1 0.4 High Flow nozzle", - "sub_path": "filament/Generic TPU @U1 0.4 High Flow nozzle.json" - }, - { - "name": "Generic TPU @U1 0.6 High Flow nozzle", - "sub_path": "filament/Generic TPU @U1 0.6 High Flow nozzle.json" - }, - { - "name": "Generic TPU @U1 0.8 High Flow nozzle", - "sub_path": "filament/Generic TPU @U1 0.8 High Flow nozzle.json" + "name": "Generic TPU @U1 0.8 nozzle", + "sub_path": "filament/Generic TPU @U1 0.8 nozzle.json" }, { "name": "PolyTerra J1 PLA", diff --git a/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json deleted file mode 100644 index dc60a89080..0000000000 --- a/resources/profiles/Snapmaker/filament/Fiberon ASA-CF08 @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Fiberon ASA-CF08 @U1 0.4 High Flow nozzle", - "inherits": "Fiberon ASA-CF08 @Snapmaker U1", - "from": "system", - "setting_id": "b1SjanapSsHClohQ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon ASA-CF08 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json deleted file mode 100644 index ca3a3615ec..0000000000 --- a/resources/profiles/Snapmaker/filament/Fiberon PA12-CF10 @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Fiberon PA12-CF10 @U1 0.4 High Flow nozzle", - "inherits": "Fiberon PA12-CF10 @Snapmaker U1", - "from": "system", - "setting_id": "NSFnZepHnBOgrNpc", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA12-CF10 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json deleted file mode 100644 index fdad376102..0000000000 --- a/resources/profiles/Snapmaker/filament/Fiberon PA6-CF20 @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Fiberon PA6-CF20 @U1 0.4 High Flow nozzle", - "inherits": "Fiberon PA6-CF20 @Snapmaker U1", - "from": "system", - "setting_id": "qBf8pKCPs51wZX8h", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA6-CF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json deleted file mode 100644 index bfb433d9d8..0000000000 --- a/resources/profiles/Snapmaker/filament/Fiberon PA612-ESD @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Fiberon PA612-ESD @U1 0.4 High Flow nozzle", - "inherits": "Fiberon PA612-ESD @Snapmaker U1", - "from": "system", - "setting_id": "IE8AoNlftuKceK82", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA612-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 0712b95763..0000000000 --- a/resources/profiles/Snapmaker/filament/Fiberon PETG-ESD @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Fiberon PETG-ESD @U1 0.4 High Flow nozzle", - "inherits": "Fiberon PETG-ESD @Snapmaker U1", - "from": "system", - "setting_id": "tBqwA6bzMZxINvhc", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Fiberon PETG-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json deleted file mode 100644 index ae976bbde1..0000000000 --- a/resources/profiles/Snapmaker/filament/Fiberon PPS-GF20 @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Fiberon PPS-GF20 @U1 0.4 High Flow nozzle", - "inherits": "Fiberon PPS-GF20 @Snapmaker U1", - "from": "system", - "setting_id": "262BRaOSZLUidxzi", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PPS-GF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json deleted file mode 100644 index b3774299ab..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.2 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "CyVMh4hZ5TrBb9Hl", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "1", - "1", - "1", - "1", - "1", - "1" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json new file mode 100644 index 0000000000..f993f4bbf7 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.2 nozzle", + "renamed_from": "Generic PETG U1 0.2 nozzle;Generic PETG @U1 0.2 High Flow nozzle;Generic PETG U1 0.2 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "Iw7T4qqDf1fbSAa1", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "10", + "1" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 3d2d110ab8..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.4 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "AB6Lv3KHVfZ3F17I", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json new file mode 100644 index 0000000000..4a04d39f7f --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.4 nozzle", + "renamed_from": "Generic PETG U1 0.4 nozzle;Generic PETG @U1 0.4 High Flow nozzle;Generic PETG U1 0.4 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "VHRa4TXU7HPz7Fld", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "10", + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 4c39a6b53f..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.6 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "2wjxry91mFaUY5Kv", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json new file mode 100644 index 0000000000..e1d6d82c6d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.6 nozzle", + "renamed_from": "Generic PETG U1 0.6 nozzle;Generic PETG @U1 0.6 High Flow nozzle;Generic PETG U1 0.6 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "Ubr8v7BTxiQMntVk", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "10", + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 7700ec94a9..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.8 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "M5HDLQjQbdOYUkJc", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json new file mode 100644 index 0000000000..368f9f4beb --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG @U1 0.8 nozzle", + "renamed_from": "Generic PETG U1 0.8 nozzle;Generic PETG @U1 0.8 High Flow nozzle;Generic PETG U1 0.8 High Flow nozzle", + "inherits": "Generic PETG @System", + "from": "system", + "setting_id": "ro0mXCLzC5rY95oX", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "10", + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 7afbd3ad2f..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.2 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "hmfOc244iLZRjSxC", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "1", - "1", - "1", - "1", - "1", - "1" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json new file mode 100644 index 0000000000..7ef983ddb7 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.2 nozzle", + "renamed_from": "Generic PETG HF U1 0.2 nozzle;Generic PETG HF @U1 0.2 High Flow nozzle;Generic PETG HF U1 0.2 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "7yjLWAdcQe4ndo1z", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "20", + "1" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 6e79495a5d..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.4 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "QaYzwWA21nLkbeTd", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "16", - "16", - "16", - "16", - "16", - "16" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json new file mode 100644 index 0000000000..1818f8dc9d --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.4 nozzle", + "renamed_from": "Generic PETG HF U1 0.4 nozzle;Generic PETG HF @U1 0.4 High Flow nozzle;Generic PETG HF U1 0.4 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "XWWLYZWkD9ZNRPeO", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "20", + "16" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json deleted file mode 100644 index e2734fdbe7..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.6 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "4FW7HsZg0GOFDyCr", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "16", - "16", - "16", - "16", - "16", - "16" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json new file mode 100644 index 0000000000..d2f3c0a3af --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.6 nozzle", + "renamed_from": "Generic PETG HF U1 0.6 nozzle;Generic PETG HF @U1 0.6 High Flow nozzle;Generic PETG HF U1 0.6 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "1dj4JdFabSql3cEg", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "20", + "16" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 21c39efbb2..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.8 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "DcwXjRYE21g1s7Gd", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "16", - "16", - "16", - "16", - "16", - "16" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json new file mode 100644 index 0000000000..80c670da92 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG HF @U1 0.8 nozzle", + "renamed_from": "Generic PETG HF U1 0.8 nozzle;Generic PETG HF @U1 0.8 High Flow nozzle;Generic PETG HF U1 0.8 High Flow nozzle", + "inherits": "Generic PETG HF @System", + "from": "system", + "setting_id": "1LYZ0KG5kQtaOOKt", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "20", + "16" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index e1ebd81f1c..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG-CF @U1 0.4 High Flow nozzle", - "inherits": "Generic PETG-CF @System", - "from": "system", - "setting_id": "xiruPdezF8sdAwJC", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "11", - "11", - "11", - "11", - "11", - "11" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11 mm^3/s, from Snorca standard preset Generic PETG-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json new file mode 100644 index 0000000000..16d2301a0a --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PETG-CF @U1 0.4 nozzle", + "renamed_from": "Generic PETG-CF U1 0.4 nozzle;Generic PETG-CF @U1 0.4 High Flow nozzle;Generic PETG-CF U1 0.4 High Flow nozzle", + "inherits": "Generic PETG-CF @System", + "from": "system", + "setting_id": "wIKcBM1qLgzLEUJP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 11 mm^3/s, from Snorca standard preset Generic PETG-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "11.5", + "11" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 0bc1021008..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG-CF @U1 0.6 High Flow nozzle", - "inherits": "Generic PETG-CF @System", - "from": "system", - "setting_id": "edRC83pnDlkg8SYB", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "11.5", - "11.5", - "11.5", - "11.5", - "11.5", - "11.5" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json new file mode 100644 index 0000000000..9cb77c98fd --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic PETG-CF @U1 0.6 nozzle", + "renamed_from": "Generic PETG-CF U1 0.6 nozzle;Generic PETG-CF @U1 0.6 High Flow nozzle;Generic PETG-CF U1 0.6 High Flow nozzle", + "inherits": "Generic PETG-CF @System", + "from": "system", + "setting_id": "FvrnBiPbjSvIF3kN", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json deleted file mode 100644 index acdb9f92c9..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG-CF @U1 0.8 High Flow nozzle", - "inherits": "Generic PETG-CF @System", - "from": "system", - "setting_id": "mwzFh7n5O4sSVbdq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "11.5", - "11.5", - "11.5", - "11.5", - "11.5", - "11.5" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json new file mode 100644 index 0000000000..a18aca19e4 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic PETG-CF @U1 0.8 nozzle", + "renamed_from": "Generic PETG-CF U1 0.8 nozzle;Generic PETG-CF @U1 0.8 High Flow nozzle;Generic PETG-CF U1 0.8 High Flow nozzle", + "inherits": "Generic PETG-CF @System", + "from": "system", + "setting_id": "SCfvzDgUWkFHmbtP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 4c60340703..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.2 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "jOJzxhflMDXUSXP1", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "1.6", - "1.6", - "1.6", - "1.6", - "1.6", - "1.6" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Generic PLA @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json new file mode 100644 index 0000000000..334be2020e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.2 nozzle", + "renamed_from": "Generic PLA U1 0.2 nozzle;Generic PLA @U1 0.2 High Flow nozzle;Generic PLA U1 0.2 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "8pb3tert1D4w0SKP", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Generic PLA @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12", + "1.6" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 04f5b81b65..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.4 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "u6ZVj3bEGhEScBAn", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json new file mode 100644 index 0000000000..e37b2d5a25 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.4 nozzle", + "renamed_from": "Generic PLA U1 0.4 nozzle;Generic PLA @U1 0.4 High Flow nozzle;Generic PLA U1 0.4 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "Uilm8oVmZzyxE3ba", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json deleted file mode 100644 index e9cd941ccb..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.6 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "3rI0eWUn62OqztwD", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json new file mode 100644 index 0000000000..8524f17c21 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.6 nozzle", + "renamed_from": "Generic PLA U1 0.6 nozzle;Generic PLA @U1 0.6 High Flow nozzle;Generic PLA U1 0.6 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "aj4G9OSdgtQPqpkI", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 0df240dcd0..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.8 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "Ca1ysi9BTj3SmRnf", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json new file mode 100644 index 0000000000..f4887a84ca --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA @U1 0.8 nozzle", + "renamed_from": "Generic PLA U1 0.8 nozzle;Generic PLA @U1 0.8 High Flow nozzle;Generic PLA U1 0.8 High Flow nozzle", + "inherits": "Generic PLA @System", + "from": "system", + "setting_id": "zom29gBgIZmvjFmh", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 228901f1cf..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.2 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "H45bzmFIJs2jcRxK", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "2", - "2", - "2", - "2", - "2", - "2" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json new file mode 100644 index 0000000000..fe3a8c5886 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.2 nozzle", + "renamed_from": "Generic PLA High Speed U1 0.2 nozzle;Generic PLA High Speed @U1 0.2 High Flow nozzle;Generic PLA High Speed U1 0.2 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "rT8e5f81vwUpwPuq", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "18", + "2" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json deleted file mode 100644 index e016e53d1a..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.4 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "rNAH84Cth3UXfID4", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18", - "18", - "18", - "18", - "18", - "18" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json new file mode 100644 index 0000000000..31fbc7174c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.4 nozzle", + "renamed_from": "Generic PLA High Speed U1 0.4 nozzle;Generic PLA High Speed @U1 0.4 High Flow nozzle;Generic PLA High Speed U1 0.4 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "ZUetX0WveqW8fy6f", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "18" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 348db5014d..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.6 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "1fXum9hyhX2CXp84", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18", - "18", - "18", - "18", - "18", - "18" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json new file mode 100644 index 0000000000..bb910121f9 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.6 nozzle", + "renamed_from": "Generic PLA High Speed U1 0.6 nozzle;Generic PLA High Speed @U1 0.6 High Flow nozzle;Generic PLA High Speed U1 0.6 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "y2WY6qLA3xKZM1Mm", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "18" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json deleted file mode 100644 index f377e30f7b..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.8 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "DEHdvXmDRZ4klSfG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18", - "18", - "18", - "18", - "18", - "18" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json new file mode 100644 index 0000000000..45c7bbda54 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA High Speed @U1 0.8 nozzle", + "renamed_from": "Generic PLA High Speed U1 0.8 nozzle;Generic PLA High Speed @U1 0.8 High Flow nozzle;Generic PLA High Speed U1 0.8 High Flow nozzle", + "inherits": "Generic PLA High Speed @System", + "from": "system", + "setting_id": "VwXJrdwxvzjlIGal", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "18" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 53fc87bb9b..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.2 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "rgP9xBiJi6jtTGe6", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "2", - "2", - "2", - "2", - "2", - "2" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json new file mode 100644 index 0000000000..4f8a43e43e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json @@ -0,0 +1,24 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.2 nozzle", + "renamed_from": "Generic PLA Silk U1 0.2 nozzle;Generic PLA Silk @U1 0.2 High Flow nozzle;Generic PLA Silk U1 0.2 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "381SJIDBre8HxHy2", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "7.5", + "2" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 980840da51..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.4 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "wKeFBUbXAh371HJc", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.5", - "7.5", - "7.5", - "7.5", - "7.5", - "7.5" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json new file mode 100644 index 0000000000..36bb9d7b4e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.4 nozzle", + "renamed_from": "Generic PLA Silk U1 0.4 nozzle;Generic PLA Silk @U1 0.4 High Flow nozzle;Generic PLA Silk U1 0.4 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "Fd1g9OdeM3pUx8f5", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json deleted file mode 100644 index d3e0aced6d..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.6 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "PoZUUozGNqKXDY6j", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.5", - "7.5", - "7.5", - "7.5", - "7.5", - "7.5" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json new file mode 100644 index 0000000000..8981c44ce1 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.6 nozzle", + "renamed_from": "Generic PLA Silk U1 0.6 nozzle;Generic PLA Silk @U1 0.6 High Flow nozzle;Generic PLA Silk U1 0.6 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "8rhmpBFmMj9vVZwG", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 450b030227..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.8 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "lSxapFINSYnT0jU5", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.5", - "7.5", - "7.5", - "7.5", - "7.5", - "7.5" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json new file mode 100644 index 0000000000..ec2669e3ff --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic PLA Silk @U1 0.8 nozzle", + "renamed_from": "Generic PLA Silk U1 0.8 nozzle;Generic PLA Silk @U1 0.8 High Flow nozzle;Generic PLA Silk U1 0.8 High Flow nozzle", + "inherits": "Generic PLA Silk @System", + "from": "system", + "setting_id": "Fo7U7GYHZh1aZPLs", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 07af2d1a24..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA-CF @U1 0.4 High Flow nozzle", - "inherits": "Generic PLA-CF @System", - "from": "system", - "setting_id": "LumAEUgsSqWvQcnD", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json new file mode 100644 index 0000000000..6fcd474adc --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA-CF @U1 0.4 nozzle", + "renamed_from": "Generic PLA-CF U1 0.4 nozzle;Generic PLA-CF @U1 0.4 High Flow nozzle;Generic PLA-CF U1 0.4 High Flow nozzle", + "inherits": "Generic PLA-CF @System", + "from": "system", + "setting_id": "6AWubtDE4PqrKVNJ", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json deleted file mode 100644 index aa2ae79dc5..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA-CF @U1 0.6 High Flow nozzle", - "inherits": "Generic PLA-CF @System", - "from": "system", - "setting_id": "cbQkQEdl7ZZySWyb", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json new file mode 100644 index 0000000000..13a94cd20c --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA-CF @U1 0.6 nozzle", + "renamed_from": "Generic PLA-CF U1 0.6 nozzle;Generic PLA-CF @U1 0.6 High Flow nozzle;Generic PLA-CF U1 0.6 High Flow nozzle", + "inherits": "Generic PLA-CF @System", + "from": "system", + "setting_id": "AvM44HgMCkOCnmzY", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json deleted file mode 100644 index ddeec0f5bd..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA-CF @U1 0.8 High Flow nozzle", - "inherits": "Generic PLA-CF @System", - "from": "system", - "setting_id": "w2R1GQrrewIaFigA", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12", - "12", - "12", - "12", - "12", - "12" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json new file mode 100644 index 0000000000..f763ce696e --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json @@ -0,0 +1,23 @@ +{ + "type": "filament", + "name": "Generic PLA-CF @U1 0.8 nozzle", + "renamed_from": "Generic PLA-CF U1 0.8 nozzle;Generic PLA-CF @U1 0.8 High Flow nozzle;Generic PLA-CF U1 0.8 High Flow nozzle", + "inherits": "Generic PLA-CF @System", + "from": "system", + "setting_id": "tvYAUvQEbbdBCIg2", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12" + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 83ff889f31..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic TPU @U1 0.4 High Flow nozzle", - "inherits": "Generic TPU @System", - "from": "system", - "setting_id": "a1T45jY9dw7cvXT9", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.2", - "3.2", - "3.2", - "3.2", - "3.2", - "3.2" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json new file mode 100644 index 0000000000..b840b579cd --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic TPU @U1 0.4 nozzle", + "renamed_from": "Generic TPU U1 0.4 nozzle;Generic TPU @U1 0.4 High Flow nozzle;Generic TPU U1 0.4 High Flow nozzle", + "inherits": "Generic TPU @System", + "from": "system", + "setting_id": "9d0jRdMgkPMahYL7", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json deleted file mode 100644 index cd745b81b6..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic TPU @U1 0.6 High Flow nozzle", - "inherits": "Generic TPU @System", - "from": "system", - "setting_id": "bHQSXsU5JQW91pkB", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.2", - "3.2", - "3.2", - "3.2", - "3.2", - "3.2" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json new file mode 100644 index 0000000000..de956528f3 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic TPU @U1 0.6 nozzle", + "renamed_from": "Generic TPU U1 0.6 nozzle;Generic TPU @U1 0.6 High Flow nozzle;Generic TPU U1 0.6 High Flow nozzle", + "inherits": "Generic TPU @System", + "from": "system", + "setting_id": "LLjEPSU4l09N3Zb8", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 589302d895..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "filament", - "name": "Generic TPU @U1 0.8 High Flow nozzle", - "inherits": "Generic TPU @System", - "from": "system", - "setting_id": "FnByDNEkW6W6FR63", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.2", - "3.2", - "3.2", - "3.2", - "3.2", - "3.2" - ], - "filament_notes": [ - "Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json new file mode 100644 index 0000000000..4d1b48e333 --- /dev/null +++ b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "filament", + "name": "Generic TPU @U1 0.8 nozzle", + "renamed_from": "Generic TPU U1 0.8 nozzle;Generic TPU @U1 0.8 High Flow nozzle;Generic TPU U1 0.8 High Flow nozzle", + "inherits": "Generic TPU @System", + "from": "system", + "setting_id": "Wfhd3jLEjSKzCeHE", + "instantiation": "true", + "compatible_printers": [ + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] +} diff --git a/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json deleted file mode 100644 index d0bccc66e1..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma CoPE @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma CoPE @U1 0.4 High Flow nozzle", - "inherits": "Panchroma CoPE @Snapmaker U1", - "from": "system", - "setting_id": "nBvDgnN3J4k3Uytq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma CoPE @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 783dea1078..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA @Snapmaker U1", - "from": "system", - "setting_id": "U5ss8ZIjm3fIz1Nq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 9b0df8dd59..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Celestial @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Celestial @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Celestial @Snapmaker U1", - "from": "system", - "setting_id": "R81OCOp3l7L8B8fJ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Celestial @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 4eb0005363..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Galaxy @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Galaxy @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Galaxy @Snapmaker U1", - "from": "system", - "setting_id": "fS50UtV6RhVtB4uP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 57f7e80c9c..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Glow @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Glow @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Glow @Snapmaker U1", - "from": "system", - "setting_id": "JCgz8jS9LuwdaBl1", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json deleted file mode 100644 index e892c5c2f7..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Luminous @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Luminous @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Luminous @Snapmaker U1", - "from": "system", - "setting_id": "K5a28HOfNIVohTxH", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json deleted file mode 100644 index d9f9f9c419..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Marble @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Marble @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Marble @Snapmaker U1", - "from": "system", - "setting_id": "iXtNnANcE9HjT8nn", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 145f766ce9..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Matte @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Matte @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Matte @Snapmaker U1", - "from": "system", - "setting_id": "c9CmXJsBkThq39c6", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Matte @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json deleted file mode 100644 index b54b291bdd..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Metallic @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Metallic @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Metallic @Snapmaker U1", - "from": "system", - "setting_id": "KQ2nKfjxSAroQBJq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json deleted file mode 100644 index da0389bdc7..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Neon @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Neon @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Neon @Snapmaker U1", - "from": "system", - "setting_id": "9fYw7eMrJBaA00at", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 2f705909e8..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Satin @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Satin @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Satin @Snapmaker U1", - "from": "system", - "setting_id": "PRlG1wqgh0KI7fuT", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Satin @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 9553afb7e3..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Silk @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Silk @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Silk @Snapmaker U1", - "from": "system", - "setting_id": "EXLntLMjWCfwg2Wc", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Panchroma PLA Silk @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 4d8310a6c3..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Starlight @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Starlight @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Starlight @Snapmaker U1", - "from": "system", - "setting_id": "4QxRxMj60hcFxdJL", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 5cefff25f1..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Temp Shift @Snapmaker U1", - "from": "system", - "setting_id": "HDp1DlQLCfSKhaNx", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Temp Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 45102a7a19..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA Translucent @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA Translucent @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA Translucent @Snapmaker U1", - "from": "system", - "setting_id": "4ZTw1XHqwI7QjEfR", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json deleted file mode 100644 index d252f70921..0000000000 --- a/resources/profiles/Snapmaker/filament/Panchroma PLA UV Shift @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Panchroma PLA UV Shift @U1 0.4 High Flow nozzle", - "inherits": "Panchroma PLA UV Shift @Snapmaker U1", - "from": "system", - "setting_id": "0pFMgeH7aowERZoU", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA UV Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 7af0ba9b3a..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite CosPLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite CosPLA @U1 0.4 High Flow nozzle", - "inherits": "PolyLite CosPLA @Snapmaker U1", - "from": "system", - "setting_id": "iZqJvDu4xfgIBrpm", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite CosPLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json deleted file mode 100644 index cc4623a7cd..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PETG @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PETG @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PETG @Snapmaker U1", - "from": "system", - "setting_id": "fCNkJQO5skF4ZJcJ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f0b3f91f49..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PETG Translucent @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PETG Translucent @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PETG Translucent @Snapmaker U1", - "from": "system", - "setting_id": "polhiSJb82UYZObE", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 3d3a9bef72..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA @Snapmaker U1", - "from": "system", - "setting_id": "AUKAVbKch9zjmuKG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 3836e62d5e..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Galaxy @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Galaxy @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Galaxy @Snapmaker U1", - "from": "system", - "setting_id": "hncpZTWfBmfoUaCq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f73ede7b25..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Glow @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Glow @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Glow @Snapmaker U1", - "from": "system", - "setting_id": "DUuZDzjm3bmNBKSA", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 2ab15e9fd2..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Luminous @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Luminous @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Luminous @Snapmaker U1", - "from": "system", - "setting_id": "Mv5E1GpGrdHVF2Vs", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 51d1010112..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Neon @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Neon @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Neon @Snapmaker U1", - "from": "system", - "setting_id": "3HWUcpQIJsnxWDp7", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 9b250dfb98..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Pro @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Pro @Snapmaker U1", - "from": "system", - "setting_id": "mOax5GeAHVzqs4zL", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json deleted file mode 100644 index fbfb23ac4b..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Pro Metallic @Snapmaker U1", - "from": "system", - "setting_id": "Vm5ydb09RSgfd1a8", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json deleted file mode 100644 index e1502b10de..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Starlight @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Starlight @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Starlight @Snapmaker U1", - "from": "system", - "setting_id": "urJVmseOC2Px1qIW", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json deleted file mode 100644 index c44e1bb633..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyLite PLA Translucent @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyLite PLA Translucent @U1 0.4 High Flow nozzle", - "inherits": "PolyLite PLA Translucent @Snapmaker U1", - "from": "system", - "setting_id": "jCgyYlatUD3jfQXS", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 454b86d711..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyTerra PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyTerra PLA @U1 0.4 High Flow nozzle", - "inherits": "PolyTerra PLA @Snapmaker U1", - "from": "system", - "setting_id": "MTZ6N0HHdSwkoysG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 4bfbd99b94..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyTerra PLA Marble @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyTerra PLA Marble @U1 0.4 High Flow nozzle", - "inherits": "PolyTerra PLA Marble @Snapmaker U1", - "from": "system", - "setting_id": "uQFqbcqaSTrlM52u", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 20cb320ec8..0000000000 --- a/resources/profiles/Snapmaker/filament/PolyTerra PLA+ @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "PolyTerra PLA+ @U1 0.4 High Flow nozzle", - "inherits": "PolyTerra PLA+ @Snapmaker U1", - "from": "system", - "setting_id": "ckmc2jbEBO6kAfXn", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA+ @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 22401d9ca5..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker General PLA Family @U1 0.4 High Flow nozzle", - "inherits": "Polymaker General PLA Family @U1", - "from": "system", - "setting_id": "lTLCaycZyEv7ATPu", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Polymaker General PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json index 0e743d7aee..171523998a 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Polymaker General PLA Family @U1", + "renamed_from": "Polymaker General PLA Family U1;Polymaker General PLA Family @U1 0.4 High Flow nozzle;Polymaker General PLA Family U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @U1 base", "from": "system", "setting_id": "thXfSaUkOAKJ50ey", "filament_id": "OF44ERDr", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "enable_pressure_advance": [ "0" @@ -29,5 +31,12 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Polymaker General PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index d6d336c3e0..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker HT-PLA @U1 0.4 High Flow nozzle", - "inherits": "Polymaker HT-PLA @Snapmaker U1", - "from": "system", - "setting_id": "YB5Tzz4hghtQ6Iha", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 0d76705426..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle", - "inherits": "Polymaker HT-PLA-GF @Snapmaker U1", - "from": "system", - "setting_id": "XTfLaJ4pTpmfYOHK", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA-GF @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f2b8f17ca2..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker PETG @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker PETG @U1 0.4 High Flow nozzle", - "inherits": "Polymaker PETG @Snapmaker U1", - "from": "system", - "setting_id": "7RmkAZ1MMkUK4UFe", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json deleted file mode 100644 index afc5f6a361..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker PETG Galaxy @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker PETG Galaxy @U1 0.4 High Flow nozzle", - "inherits": "Polymaker PETG Galaxy @Snapmaker U1", - "from": "system", - "setting_id": "cBNSP4FfKq94BuWn", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index b411e8960a..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker PLA @U1 0.4 High Flow nozzle", - "inherits": "Polymaker PLA @Snapmaker U1", - "from": "system", - "setting_id": "N99CwtkZbVeE9ZoP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Polymaker PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 0c98d9a7ee..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker PLA Pro @U1 0.4 High Flow nozzle", - "inherits": "Polymaker PLA Pro @Snapmaker U1", - "from": "system", - "setting_id": "Npw7PdfMi3qIDuIY", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json deleted file mode 100644 index b027a554b2..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle", - "inherits": "Polymaker PLA Pro Metallic @Snapmaker U1", - "from": "system", - "setting_id": "YBy6gtPvOWIYchuS", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 0c9dfe4271..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker Silk PLA Family @U1 0.4 High Flow nozzle", - "inherits": "Polymaker Silk PLA Family @U1", - "from": "system", - "setting_id": "diMNPKgSPiNmv0EG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Silk PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json index 990cc483f3..aa8d9de628 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Polymaker Silk PLA Family @U1", + "renamed_from": "Polymaker Silk PLA Family U1;Polymaker Silk PLA Family @U1 0.4 High Flow nozzle;Polymaker Silk PLA Family U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @U1 base", "from": "system", "setting_id": "h8vIYpXbxFtHPV6e", "filament_id": "OFH57ibH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_density": [ "1.34" @@ -29,5 +31,12 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Silk PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 2eac1cc0c2..0000000000 --- a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Polymaker Tough PLA Family @U1 0.4 High Flow nozzle", - "inherits": "Polymaker Tough PLA Family @U1", - "from": "system", - "setting_id": "tFHtV4suxKdRifWW", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Tough PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json index 1dad000fb2..b3ce7b53ea 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Polymaker Tough PLA Family @U1", + "renamed_from": "Polymaker Tough PLA Family U1;Polymaker Tough PLA Family @U1 0.4 High Flow nozzle;Polymaker Tough PLA Family U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @U1 base", "from": "system", "setting_id": "d6CC81Es2hp46bto", "filament_id": "OFgdaBOt", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_density": [ "1.23" @@ -29,5 +31,12 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Tough PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json index b38925e01b..7117c25419 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json @@ -1,17 +1,26 @@ { "type": "filament", "name": "Fiberon ASA-CF08 @Snapmaker U1", + "renamed_from": "Fiberon ASA-CF08 Snapmaker U1;Fiberon ASA-CF08 @U1 0.4 High Flow nozzle;Fiberon ASA-CF08 U1 0.4 High Flow nozzle", "inherits": "Fiberon ASA-CF08 @Snapmaker U1 base", "from": "system", "setting_id": "CdgDze2g0J4T7FEP", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "pressure_advance": [ "0.05" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon ASA-CF08 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json index 1bb569c291..326c972bcb 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Fiberon PA12-CF10 @Snapmaker U1", + "renamed_from": "Fiberon PA12-CF10 Snapmaker U1;Fiberon PA12-CF10 @U1 0.4 High Flow nozzle;Fiberon PA12-CF10 U1 0.4 High Flow nozzle", "inherits": "Fiberon PA12-CF10 @Snapmaker U1 base", "from": "system", "setting_id": "x3irDyMqwMgklHKg", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -22,5 +24,12 @@ ], "pressure_advance": [ "0.05" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA12-CF10 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json index d6106faa50..3a825179b0 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Fiberon PA6-CF20 @Snapmaker U1", + "renamed_from": "Fiberon PA6-CF20 Snapmaker U1;Fiberon PA6-CF20 @U1 0.4 High Flow nozzle;Fiberon PA6-CF20 U1 0.4 High Flow nozzle", "inherits": "Fiberon PA6-CF20 @Snapmaker U1 base", "from": "system", "setting_id": "AMQyOXYNdWpVXmvK", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -22,5 +24,12 @@ ], "pressure_advance": [ "0.08" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA6-CF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json index 660f4e9ef2..444a3ca35e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Fiberon PA612-ESD @Snapmaker U1", + "renamed_from": "Fiberon PA612-ESD Snapmaker U1;Fiberon PA612-ESD @U1 0.4 High Flow nozzle;Fiberon PA612-ESD U1 0.4 High Flow nozzle", "inherits": "Fiberon PA612-ESD @Snapmaker U1 base", "from": "system", "setting_id": "hUvnA3XmuC3bGLsi", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -20,5 +22,12 @@ "description": " ", "enable_pressure_advance": [ "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA612-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json index 6e1797904c..91a8ccb00f 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Fiberon PETG-ESD @Snapmaker U1", + "renamed_from": "Fiberon PETG-ESD Snapmaker U1;Fiberon PETG-ESD @U1 0.4 High Flow nozzle;Fiberon PETG-ESD U1 0.4 High Flow nozzle", "inherits": "Fiberon PETG-ESD @Snapmaker U1 base", "from": "system", "setting_id": "AUr4Bm6UMW8S9TBh", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -19,5 +21,12 @@ ], "pressure_advance": [ "0.04" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Fiberon PETG-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json index 4f1a9fdc10..f67bf3a3eb 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json @@ -1,15 +1,24 @@ { "type": "filament", "name": "Fiberon PPS-GF20 @Snapmaker U1", + "renamed_from": "Fiberon PPS-GF20 Snapmaker U1;Fiberon PPS-GF20 @U1 0.4 High Flow nozzle;Fiberon PPS-GF20 U1 0.4 High Flow nozzle", "inherits": "Fiberon PPS-GF20 @Snapmaker U1 base", "from": "system", "setting_id": "7IpyPFHEAOmFlINJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], - "description": " " + "description": " ", + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: eSUN ABS+\nSunlu ABS+\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PPS-GF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json index 1ad0f0d820..7c25b29e99 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma CoPE @Snapmaker U1", - "renamed_from": "Panchroma CoPE @U1", + "renamed_from": "Panchroma CoPE @U1;Panchroma CoPE @U1 0.4 High Flow nozzle;Panchroma CoPE U1 0.4 High Flow nozzle", "inherits": "Panchroma CoPE @Snapmaker U1 base", "from": "system", "setting_id": "gI7fRuITm0XhElnG", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma CoPE @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json index dcd84aeba9..acf326d5ca 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json @@ -1,18 +1,26 @@ { "type": "filament", "name": "Panchroma PLA @Snapmaker U1", - "renamed_from": "Panchroma PLA @U1", + "renamed_from": "Panchroma PLA @U1;Panchroma PLA @U1 0.4 High Flow nozzle;Panchroma PLA U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA @Snapmaker U1 base", "from": "system", "setting_id": "ayBhax9ELaiKfGFt", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_wipe": [ "0" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json index 561c748be3..95806c4ce0 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Celestial @Snapmaker U1", - "renamed_from": "Panchroma PLA Celestial @U1", + "renamed_from": "Panchroma PLA Celestial @U1;Panchroma PLA Celestial @U1 0.4 High Flow nozzle;Panchroma PLA Celestial U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Celestial @Snapmaker U1 base", "from": "system", "setting_id": "OVmWNHpQnLZ67Bxd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Celestial @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json index fee27dc997..b52c629427 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Galaxy @Snapmaker U1", - "renamed_from": "Panchroma PLA Galaxy @U1", + "renamed_from": "Panchroma PLA Galaxy @U1;Panchroma PLA Galaxy @U1 0.4 High Flow nozzle;Panchroma PLA Galaxy U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Galaxy @Snapmaker U1 base", "from": "system", "setting_id": "FLECN8LIZ5RLgQGi", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json index 2f1135de61..2b8b9df26f 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Glow @Snapmaker U1", - "renamed_from": "Panchroma PLA Glow @U1", + "renamed_from": "Panchroma PLA Glow @U1;Panchroma PLA Glow @U1 0.4 High Flow nozzle;Panchroma PLA Glow U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Glow @Snapmaker U1 base", "from": "system", "setting_id": "YEf4HFNPNtoFoLJd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json index fd701bf8c3..c508d2e50c 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Luminous @Snapmaker U1", - "renamed_from": "Panchroma PLA Luminous @U1", + "renamed_from": "Panchroma PLA Luminous @U1;Panchroma PLA Luminous @U1 0.4 High Flow nozzle;Panchroma PLA Luminous U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Luminous @Snapmaker U1 base", "from": "system", "setting_id": "bxbXVqsMWWNfWcAn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json index 2a1dbab936..984c8f7a48 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Marble @Snapmaker U1", - "renamed_from": "Panchroma PLA Marble @U1", + "renamed_from": "Panchroma PLA Marble @U1;Panchroma PLA Marble @U1 0.4 High Flow nozzle;Panchroma PLA Marble U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Marble @Snapmaker U1 base", "from": "system", "setting_id": "P9wE2kZSR2Coq1K7", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json index 7652606816..b5a5b1a864 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Matte @Snapmaker U1", - "renamed_from": "Panchroma PLA Matte @U1", + "renamed_from": "Panchroma PLA Matte @U1;Panchroma PLA Matte @U1 0.4 High Flow nozzle;Panchroma PLA Matte U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Matte @Snapmaker U1 base", "from": "system", "setting_id": "0oJUzBq7iYJgJxZ7", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Matte @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json index fe8e68081c..aa0406c856 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Metallic @Snapmaker U1", - "renamed_from": "Panchroma PLA Metallic @U1", + "renamed_from": "Panchroma PLA Metallic @U1;Panchroma PLA Metallic @U1 0.4 High Flow nozzle;Panchroma PLA Metallic U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Metallic @Snapmaker U1 base", "from": "system", "setting_id": "pAQGZzMH04kst11B", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json index cf16cb95c9..a9d9770510 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Neon @Snapmaker U1", - "renamed_from": "Panchroma PLA Neon @U1", + "renamed_from": "Panchroma PLA Neon @U1;Panchroma PLA Neon @U1 0.4 High Flow nozzle;Panchroma PLA Neon U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Neon @Snapmaker U1 base", "from": "system", "setting_id": "qVG9q2ci7Cj4pM5c", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json index eff77a2aa9..549ba39107 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Satin @Snapmaker U1", - "renamed_from": "Panchroma PLA Satin @U1", + "renamed_from": "Panchroma PLA Satin @U1;Panchroma PLA Satin @U1 0.4 High Flow nozzle;Panchroma PLA Satin U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Satin @Snapmaker U1 base", "from": "system", "setting_id": "EHMn3LsbZlfuR7hD", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Satin @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json index 1020a48f0a..c6f550e1da 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Silk @Snapmaker U1", - "renamed_from": "Panchroma PLA Silk @U1", + "renamed_from": "Panchroma PLA Silk @U1;Panchroma PLA Silk @U1 0.4 High Flow nozzle;Panchroma PLA Silk U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Silk @Snapmaker U1 base", "from": "system", "setting_id": "JfIg6ZkVFA0CKLFI", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Panchroma PLA Silk @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json index 114391b517..9882801891 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Starlight @Snapmaker U1", - "renamed_from": "Panchroma PLA Starlight @U1", + "renamed_from": "Panchroma PLA Starlight @U1;Panchroma PLA Starlight @U1 0.4 High Flow nozzle;Panchroma PLA Starlight U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Starlight @Snapmaker U1 base", "from": "system", "setting_id": "pgcdQlQmPFSaduVr", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json index ca50194577..eaf3d61871 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json @@ -1,27 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Temp Shift @Snapmaker U1", - "renamed_from": "Panchroma PLA Temp Shift @U1", + "renamed_from": "Panchroma PLA Temp Shift @U1;Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle;Panchroma PLA Temp Shift U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Temp Shift @Snapmaker U1 base", "from": "system", "setting_id": "SJq26RGx7OoZB4uH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" - ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], - "filament_flow_ratio": [ - "0.98" - ], - "filament_max_volumetric_speed": [ - "15" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "slow_down_layer_time": [ "4" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Temp Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json index cff9ec2011..e5b728ce12 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA Translucent @Snapmaker U1", - "renamed_from": "Panchroma PLA Translucent @U1", + "renamed_from": "Panchroma PLA Translucent @U1;Panchroma PLA Translucent @U1 0.4 High Flow nozzle;Panchroma PLA Translucent U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA Translucent @Snapmaker U1 base", "from": "system", "setting_id": "CBt541HVuOa7GGb9", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json index 1d4a6a0bbe..af9b765442 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Panchroma PLA UV Shift @Snapmaker U1", - "renamed_from": "Panchroma PLA UV Shift @U1", + "renamed_from": "Panchroma PLA UV Shift @U1;Panchroma PLA UV Shift @U1 0.4 High Flow nozzle;Panchroma PLA UV Shift U1 0.4 High Flow nozzle", "inherits": "Panchroma PLA UV Shift @Snapmaker U1 base", "from": "system", "setting_id": "vfVlN8IokcNnixsV", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA UV Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json index 836723cf7a..ac28cfb242 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite CosPLA @Snapmaker U1", + "renamed_from": "PolyLite CosPLA Snapmaker U1;PolyLite CosPLA @U1 0.4 High Flow nozzle;PolyLite CosPLA U1 0.4 High Flow nozzle", "inherits": "PolyLite CosPLA @Snapmaker U1 base", "from": "system", "setting_id": "KwD7WDyJNa4hWfZ8", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite CosPLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json index f51cc5eeec..e52f876805 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "PolyLite PETG @Snapmaker U1", + "renamed_from": "PolyLite PETG Snapmaker U1;PolyLite PETG @U1 0.4 High Flow nozzle;PolyLite PETG U1 0.4 High Flow nozzle", "inherits": "PolyLite PETG @Snapmaker U1 base", "from": "system", "setting_id": "U9U4YmaRxgIdxQOc", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -19,5 +21,12 @@ ], "pressure_advance": [ "0.05" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json index a3efeee371..4702dd7ec9 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "PolyLite PETG Translucent @Snapmaker U1", + "renamed_from": "PolyLite PETG Translucent Snapmaker U1;PolyLite PETG Translucent @U1 0.4 High Flow nozzle;PolyLite PETG Translucent U1 0.4 High Flow nozzle", "inherits": "PolyLite PETG Translucent @Snapmaker U1 base", "from": "system", "setting_id": "NBQOZiQQ8MpcDnLM", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -19,5 +21,12 @@ ], "pressure_advance": [ "0.05" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json index 715d8060e4..00beebd0af 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "PolyLite PLA @Snapmaker U1", - "renamed_from": "PolyLite PLA @U1", + "renamed_from": "PolyLite PLA @U1;PolyLite PLA @U1 0.4 High Flow nozzle;PolyLite PLA U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA @Snapmaker U1 base", "from": "system", "setting_id": "RtK3uYFG0OKtnLkP", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json index e096b1b9b3..ee939ec23d 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Galaxy @Snapmaker U1", + "renamed_from": "PolyLite PLA Galaxy Snapmaker U1;PolyLite PLA Galaxy @U1 0.4 High Flow nozzle;PolyLite PLA Galaxy U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Galaxy @Snapmaker U1 base", "from": "system", "setting_id": "mNMTe346QAgo61NH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json index a36d2b9de5..0a35b4a645 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Glow @Snapmaker U1", + "renamed_from": "PolyLite PLA Glow Snapmaker U1;PolyLite PLA Glow @U1 0.4 High Flow nozzle;PolyLite PLA Glow U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Glow @Snapmaker U1 base", "from": "system", "setting_id": "xYjGzOzWWE2PBfNd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json index 874004e6ea..978da7ad66 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Luminous @Snapmaker U1", + "renamed_from": "PolyLite PLA Luminous Snapmaker U1;PolyLite PLA Luminous @U1 0.4 High Flow nozzle;PolyLite PLA Luminous U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Luminous @Snapmaker U1 base", "from": "system", "setting_id": "pcPG6yUSO8F0e4gM", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json index fbbb2a4585..ade31d33e1 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Neon @Snapmaker U1", + "renamed_from": "PolyLite PLA Neon Snapmaker U1;PolyLite PLA Neon @U1 0.4 High Flow nozzle;PolyLite PLA Neon U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Neon @Snapmaker U1 base", "from": "system", "setting_id": "WMoh0mKVmVsdP5rc", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json index 47163a788c..ac90bd96e1 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json @@ -1,27 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Pro @Snapmaker U1", - "renamed_from": "PolyLite PLA Pro @U1", + "renamed_from": "PolyLite PLA Pro @U1;PolyLite PLA Pro @U1 0.4 High Flow nozzle;PolyLite PLA Pro U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Pro @Snapmaker U1 base", "from": "system", "setting_id": "GxuEzTdHffrvh2q6", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" - ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], - "filament_flow_ratio": [ - "0.98" - ], - "filament_max_volumetric_speed": [ - "15" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "slow_down_layer_time": [ "4" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json index b1611b961d..cabbbe92b2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Pro Metallic @Snapmaker U1", + "renamed_from": "PolyLite PLA Pro Metallic Snapmaker U1;PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle;PolyLite PLA Pro Metallic U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Pro Metallic @Snapmaker U1 base", "from": "system", "setting_id": "21wjCLo6hOb9p38K", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_end_gcode": [ "; filament end gcode \n\n" @@ -26,5 +28,12 @@ ], "enable_pressure_advance": [ "0" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json index 56108cfed2..e6ad43c92e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Starlight @Snapmaker U1", + "renamed_from": "PolyLite PLA Starlight Snapmaker U1;PolyLite PLA Starlight @U1 0.4 High Flow nozzle;PolyLite PLA Starlight U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Starlight @Snapmaker U1 base", "from": "system", "setting_id": "geXEuSvWTJD0rK7F", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json index 799e272fab..b87989cbdf 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyLite PLA Translucent @Snapmaker U1", + "renamed_from": "PolyLite PLA Translucent Snapmaker U1;PolyLite PLA Translucent @U1 0.4 High Flow nozzle;PolyLite PLA Translucent U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Translucent @Snapmaker U1 base", "from": "system", "setting_id": "YpMLpguxd61Bs16O", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json index 244cf91471..d56607a1a3 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "PolyTerra PLA @Snapmaker U1", - "renamed_from": "PolyTerra PLA @U1", + "renamed_from": "PolyTerra PLA @U1;PolyTerra PLA @U1 0.4 High Flow nozzle;PolyTerra PLA U1 0.4 High Flow nozzle", "inherits": "PolyTerra PLA @Snapmaker U1 base", "from": "system", "setting_id": "kyxayLqRGrKKBwqp", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json index 19e419ca36..0799e6c74d 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyTerra PLA Marble @Snapmaker U1", + "renamed_from": "PolyTerra PLA Marble Snapmaker U1;PolyTerra PLA Marble @U1 0.4 High Flow nozzle;PolyTerra PLA Marble U1 0.4 High Flow nozzle", "inherits": "PolyTerra PLA Marble @Snapmaker U1 base", "from": "system", "setting_id": "VjVt04nDFS9l9Rl9", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json index c3dd9d5537..ee62d23187 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "PolyTerra PLA+ @Snapmaker U1", + "renamed_from": "PolyTerra PLA+ Snapmaker U1;PolyTerra PLA+ @U1 0.4 High Flow nozzle;PolyTerra PLA+ U1 0.4 High Flow nozzle", "inherits": "PolyTerra PLA+ @Snapmaker U1 base", "from": "system", "setting_id": "5L213op8PhcYpqiF", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA+ @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json index ef631e550b..3e13b97060 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Polymaker HT-PLA @Snapmaker U1", - "renamed_from": "Polymaker HT-PLA @U1", + "renamed_from": "Polymaker HT-PLA @U1;Polymaker HT-PLA @U1 0.4 High Flow nozzle;Polymaker HT-PLA U1 0.4 High Flow nozzle", "inherits": "Polymaker HT-PLA @Snapmaker U1 base", "from": "system", "setting_id": "0A5ETOrUTF0P3HS4", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json index ac49e4f7ac..8e1bd2f65e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json @@ -1,15 +1,23 @@ { "type": "filament", "name": "Polymaker HT-PLA-GF @Snapmaker U1", - "renamed_from": "Polymaker HT-PLA-GF @U1", + "renamed_from": "Polymaker HT-PLA-GF @U1;Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle;Polymaker HT-PLA-GF U1 0.4 High Flow nozzle", "inherits": "Polymaker HT-PLA-GF @Snapmaker U1 base", "from": "system", "setting_id": "i0LA17aaWvgLG7xd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA-GF @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json index a3e11c2cd2..18a738484f 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Polymaker PETG @Snapmaker U1", + "renamed_from": "Polymaker PETG Snapmaker U1;Polymaker PETG @U1 0.4 High Flow nozzle;Polymaker PETG U1 0.4 High Flow nozzle", "inherits": "Polymaker PETG @Snapmaker U1 base", "from": "system", "setting_id": "kS4t66kFCXtambwx", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -16,5 +18,12 @@ ], "supertack_plate_temp_initial_layer": [ "70" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json index ecac971c5e..b5c48df24e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Polymaker PETG Galaxy @Snapmaker U1", + "renamed_from": "Polymaker PETG Galaxy Snapmaker U1;Polymaker PETG Galaxy @U1 0.4 High Flow nozzle;Polymaker PETG Galaxy U1 0.4 High Flow nozzle", "inherits": "Polymaker PETG Galaxy @Snapmaker U1 base", "from": "system", "setting_id": "a5yZ1QlqIGEAW1sU", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -16,5 +18,12 @@ ], "supertack_plate_temp_initial_layer": [ "70" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json index ff614bc79f..b8fb82ab72 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json @@ -1,20 +1,26 @@ { "type": "filament", "name": "Polymaker PLA @Snapmaker U1", + "renamed_from": "Polymaker PLA Snapmaker U1;Polymaker PLA @U1 0.4 High Flow nozzle;Polymaker PLA U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @Snapmaker U1 base", "from": "system", "setting_id": "NSuD5f5eVr8SHA4n", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_extruder_variant": [ - "Direct Drive Standard" + "Direct Drive Standard", + "Direct Drive High Flow" ], "pressure_advance": [ "0.05" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Polymaker PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json index d5823b53a6..d7b220df0f 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "Polymaker PLA Pro @Snapmaker U1", + "renamed_from": "Polymaker PLA Pro Snapmaker U1;Polymaker PLA Pro @U1 0.4 High Flow nozzle;Polymaker PLA Pro U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA Pro @Snapmaker U1 base", "from": "system", "setting_id": "0iJ5qjVvmrkRzpZD", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json index a26fb437e7..7d9b34cd04 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json @@ -1,14 +1,23 @@ { "type": "filament", "name": "Polymaker PLA Pro Metallic @Snapmaker U1", + "renamed_from": "Polymaker PLA Pro Metallic Snapmaker U1;Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle;Polymaker PLA Pro Metallic U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA Pro Metallic @Snapmaker U1 base", "from": "system", "setting_id": "s4F24JmyDykNvtAV", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ "15" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json deleted file mode 100644 index fb361728c6..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker ABS @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker ABS @U1", - "from": "system", - "setting_id": "23BA0fe1OoCgoHAe", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "0" - ], - "fan_max_speed": [ - "60" - ], - "fan_min_speed": [ - "10" - ], - "filament_max_volumetric_speed": [ - "22" - ], - "filament_minimal_purge_on_wipe_tower": [ - "15" - ], - "filament_multitool_ramming": [ - "1" - ], - "filament_multitool_ramming_flow": [ - "30" - ], - "filament_multitool_ramming_volume": [ - "5" - ], - "filament_retraction_length": [ - "0.2" - ], - "filament_z_hop_types": [ - "Slope Lift" - ], - "nozzle_temperature": [ - "280" - ], - "nozzle_temperature_initial_layer": [ - "280" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ABS @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json index fe6c59ba80..2a4346cc5d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json @@ -1,14 +1,70 @@ { "type": "filament", "name": "Snapmaker ABS @U1", + "renamed_from": "Snapmaker ABS U1;Snapmaker ABS @U1 0.4 High Flow nozzle;Snapmaker ABS U1 0.4 High Flow nozzle", "inherits": "Snapmaker ABS @U1 base", "from": "system", "setting_id": "UFMRgwmOOQhOa3kB", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_retract_length_toolchange": [ "5" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: eSUN ABS+\nSunlu ABS+\n\n\nHigh-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ABS @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_minimal_purge_on_wipe_tower": [ + "0", + "15" + ], + "fan_max_speed": [ + "15", + "60" + ], + "nozzle_temperature_initial_layer": [ + "260", + "280" + ], + "nozzle_temperature": [ + "260", + "280" + ], + "filament_multitool_ramming_volume": [ + "0", + "5" + ], + "additional_cooling_fan_speed": [ + "0" + ], + "filament_retraction_length": [ + "0.6", + "0.2" + ], + "filament_max_volumetric_speed": [ + "8", + "22" + ], + "filament_multitool_ramming_flow": [ + "10", + "30" + ], + "fan_min_speed": [ + "15", + "10" + ], + "filament_z_hop_types": [ + "nil", + "Slope Lift" + ], + "filament_multitool_ramming": [ + "0", + "1" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f8c91285b6..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker ABS Benchy @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker ABS Benchy @U1", - "from": "system", - "setting_id": "TI7PLhJrI5VSiYtg", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "31" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 31 mm^3/s, from local standard fallback preset Snapmaker ABS Benchy @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "compatible_prints": [ - "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json index 1ba6c22c9a..47460e4c92 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker ABS Benchy @U1", + "renamed_from": "Snapmaker ABS Benchy U1;Snapmaker ABS Benchy @U1 0.4 High Flow nozzle;Snapmaker ABS Benchy U1 0.4 High Flow nozzle", "inherits": "Snapmaker ABS @U1 base", "from": "system", "setting_id": "xszx4XRGrp88OdP8", "filament_id": "OFJrvXth", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "compatible_prints": [ "0.25 Benchy @Snapmaker U1 (0.4 nozzle)" @@ -59,5 +61,12 @@ ], "default_filament_colour": [ "" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: eSUN ABS+\nSunlu ABS+\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 31 mm^3/s, from local standard fallback preset Snapmaker ABS Benchy @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index b5609dc056..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker ASA @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker ASA @U1", - "from": "system", - "setting_id": "UlxZs1Ey16Ic8TO8", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "0" - ], - "fan_max_speed": [ - "25" - ], - "fan_min_speed": [ - "10" - ], - "filament_max_volumetric_speed": [ - "22" - ], - "filament_minimal_purge_on_wipe_tower": [ - "15" - ], - "filament_multitool_ramming": [ - "1" - ], - "filament_multitool_ramming_flow": [ - "20" - ], - "filament_multitool_ramming_volume": [ - "10" - ], - "filament_z_hop_types": [ - "Slope Lift" - ], - "nozzle_temperature": [ - "280" - ], - "nozzle_temperature_initial_layer": [ - "280" - ], - "pressure_advance": [ - "0.02" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ASA @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json index 67bde29e8d..2e27630834 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json @@ -1,14 +1,70 @@ { "type": "filament", "name": "Snapmaker ASA @U1", + "renamed_from": "Snapmaker ASA U1;Snapmaker ASA @U1 0.4 High Flow nozzle;Snapmaker ASA U1 0.4 High Flow nozzle", "inherits": "Snapmaker ASA @U1 base", "from": "system", "setting_id": "XlbCMKY8EvV0vM88", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_retract_length_toolchange": [ "5" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ASA @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_minimal_purge_on_wipe_tower": [ + "0", + "15" + ], + "fan_max_speed": [ + "15", + "25" + ], + "nozzle_temperature_initial_layer": [ + "255", + "280" + ], + "nozzle_temperature": [ + "255", + "280" + ], + "filament_multitool_ramming_volume": [ + "0", + "10" + ], + "additional_cooling_fan_speed": [ + "0" + ], + "filament_max_volumetric_speed": [ + "8", + "22" + ], + "pressure_advance": [ + "0.04", + "0.02" + ], + "filament_multitool_ramming_flow": [ + "10", + "20" + ], + "fan_min_speed": [ + "0", + "10" + ], + "filament_z_hop_types": [ + "nil", + "Slope Lift" + ], + "filament_multitool_ramming": [ + "0", + "1" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 9fa11d6101..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker Breakaway Support For PLA @U1 0.2 nozzle", - "from": "system", - "setting_id": "Bzmt5I0LmEGEyWq3", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "0.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 0.5 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.2 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json index 19dc66df4e..716c9926d1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json @@ -1,20 +1,19 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1 0.2 nozzle", + "renamed_from": "Snapmaker Breakaway Support For PLA U1 0.2 nozzle;Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.2 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "mJoaud6wulT4p8SA", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "filament_type": [ "PLA" ], - "enable_pressure_advance": [ - "0" - ], "additional_cooling_fan_speed": [ "70" ], @@ -101,5 +100,12 @@ ], "filament_unloading_speed_start": [ "100" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 0.5 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.2 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 32fd557a65..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker Breakaway Support For PLA @U1", - "from": "system", - "setting_id": "2Wa2yfYIeZ0NfnD0", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker Support For PLA @U1 0.4 nozzle. Its base identifies the material as Snapmaker Breakaway Support. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 63e218c554..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker Breakaway Support For PLA @U1 0.6 nozzle", - "from": "system", - "setting_id": "YRUG55NCp7gQtahJ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.6 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json index 6f800f7b98..0092f4e870 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json @@ -1,20 +1,19 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1 0.6 nozzle", + "renamed_from": "Snapmaker Breakaway Support For PLA U1 0.6 nozzle;Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.6 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "rKUUREJNhstIU0I2", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_type": [ "PLA" ], - "enable_pressure_advance": [ - "0" - ], "filament_cost": [ "69.98" ], @@ -101,5 +100,12 @@ ], "filament_unloading_speed_start": [ "100" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.6 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json deleted file mode 100644 index dce415fefe..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle", - "from": "system", - "setting_id": "vtGhXaMJKZrd1kgP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.8 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json index c7a2b01351..9d296edafb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json @@ -1,20 +1,19 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle", + "renamed_from": "Snapmaker Breakaway Support For PLA U1 0.8 nozzle;Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.8 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "hQ8jQ5GKQKGKuPew", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_type": [ "PLA" ], - "enable_pressure_advance": [ - "0" - ], "additional_cooling_fan_speed": [ "70" ], @@ -95,5 +94,12 @@ ], "textured_plate_temp": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.8 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json index eff74ae5ab..07c100e18d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1", + "renamed_from": "Snapmaker Breakaway Support For PLA U1;Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.4 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "5DqT3CYUgp1oJD1V", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_type": [ "PLA" @@ -89,5 +91,16 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker Support For PLA @U1 0.4 nozzle. Its base identifies the material as Snapmaker Breakaway Support. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "12", + "18" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index c097954185..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PA-CF @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PA-CF @U1", - "from": "system", - "setting_id": "SRbkfUJ8K9LunPYq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Snapmaker PA-CF @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json index b2dc562d3b..f2297d56cf 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json @@ -1,11 +1,20 @@ { "type": "filament", "name": "Snapmaker PA-CF @U1", + "renamed_from": "Snapmaker PA-CF U1;Snapmaker PA-CF @U1 0.4 High Flow nozzle;Snapmaker PA-CF U1 0.4 High Flow nozzle", "inherits": "Snapmaker PA-CF @U1 base", "from": "system", "setting_id": "8aP6ye4aeOS7qKpG", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Snapmaker PA-CF @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json deleted file mode 100644 index a7c30b3000..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PET @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PET @U1", - "from": "system", - "setting_id": "q0JpO9zk404c3d3B", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "6.8" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 6.8 mm^3/s, from local standard fallback preset Snapmaker PET @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json index e4dd0d2035..10723ae4d1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json @@ -1,11 +1,20 @@ { "type": "filament", "name": "Snapmaker PET @U1", + "renamed_from": "Snapmaker PET U1;Snapmaker PET @U1 0.4 High Flow nozzle;Snapmaker PET U1 0.4 High Flow nozzle", "inherits": "Snapmaker PET @U1 base", "from": "system", "setting_id": "sIijFVd50jJB1sK0", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: JiaNong PET 1.63x1.75mm\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 6.8 mm^3/s, from local standard fallback preset Snapmaker PET @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json deleted file mode 100644 index bf18d6fb9d..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PETG @U1", - "from": "system", - "setting_id": "T604QL6Utgbj4kLh", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "10" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10 mm^3/s, from local standard fallback preset Snapmaker PETG @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json index 5df9952064..6a80696224 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json @@ -1,11 +1,20 @@ { "type": "filament", "name": "Snapmaker PETG @U1", + "renamed_from": "Snapmaker PETG U1;Snapmaker PETG @U1 0.4 High Flow nozzle;Snapmaker PETG U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG @U1 base", "from": "system", "setting_id": "r7KJB0EPpsknK5iH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10 mm^3/s, from local standard fallback preset Snapmaker PETG @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json deleted file mode 100644 index c86ce97a3a..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG HF @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker PETG HF @U1 0.2 nozzle", - "from": "system", - "setting_id": "ILQ3IFpogGbjluIN", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "2" - ], - "filament_flow_ratio": [ - "0.955" - ], - "pressure_advance": [ - "0.0525" - ], - "nozzle_temperature_initial_layer": [ - "230" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json index 9d9e3918cf..c015a5d3df 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json @@ -1,27 +1,22 @@ { "type": "filament", "name": "Snapmaker PETG HF @U1 0.2 nozzle", + "renamed_from": "Snapmaker PETG HF U1 0.2 nozzle;Snapmaker PETG HF @U1 0.2 High Flow nozzle;Snapmaker PETG HF U1 0.2 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", "setting_id": "Ujul2YTBwfldFiFd", "instantiation": "true", "filament_extruder_variant": [ - "Direct Drive Standard" + "Direct Drive Standard", + "Direct Drive High Flow" ], "filament_max_volumetric_speed": [ - "1" - ], - "filament_ramming_travel_time": [ - "0" - ], - "long_retractions_when_ec": [ - "0" - ], - "retraction_distances_when_ec": [ - "0" + "1", + "2" ], "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "additional_cooling_fan_speed": [ "70" @@ -75,7 +70,8 @@ "245" ], "nozzle_temperature_initial_layer": [ - "245" + "245", + "230" ], "nozzle_temperature_range_low": [ "230" @@ -103,5 +99,16 @@ ], "filament_z_hop_types": [ "Slope Lift" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "pressure_advance": [ + "0.03", + "0.0525" + ], + "filament_flow_ratio": [ + "0.95", + "0.955" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json similarity index 54% rename from resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json rename to resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json index 232d346113..6489aeaf67 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 High Flow nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json @@ -1,68 +1,92 @@ { "type": "filament", - "name": "Snapmaker PETG HF @U1 0.4 High Flow nozzle", + "name": "Snapmaker PETG HF @U1 0.4 nozzle", + "renamed_from": "Snapmaker PETG HF U1 0.4 nozzle;Snapmaker PETG HF @U1 0.4 High Flow nozzle;Snapmaker PETG HF U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", - "setting_id": "dYZPyzUTqIxCaWMZ", + "setting_id": "9NnYt52azW8zCp4a", "instantiation": "true", "compatible_printers": [ + "Snapmaker U1 (0.4 nozzle)", "Snapmaker U1 (0.4 High Flow nozzle)" ], - "fan_max_speed": [ - "60" + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ], - "fan_min_speed": [ - "30" - ], - "filament_deretraction_speed": [ - "40" - ], - "filament_flow_ratio": [ - "0.955" - ], - "filament_max_volumetric_speed": [ - "30" + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 30 mm^3/s, from Snorca HF preset Snapmaker PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_minimal_purge_on_wipe_tower": [ + "15", "50" ], - "filament_multitool_ramming": [ - "1" + "nozzle_temperature_range_low": [ + "220", + "230" ], - "filament_multitool_ramming_flow": [ - "30" + "fan_max_speed": [ + "40", + "60" + ], + "nozzle_temperature_initial_layer": [ + "220", + "230" + ], + "nozzle_temperature": [ + "220", + "245" ], "filament_multitool_ramming_volume": [ - "8" - ], - "filament_retract_length_toolchange": [ + "0", "8" ], "filament_retraction_length": [ + "nil", "0.8" ], - "filament_retraction_speed": [ - "40" - ], "filament_wipe_distance": [ + "nil", "1" ], - "filament_z_hop_types": [ - "Slope Lift" + "filament_max_volumetric_speed": [ + "16", + "30" ], - "nozzle_temperature": [ - "245" - ], - "nozzle_temperature_initial_layer": [ - "230" - ], - "nozzle_temperature_range_low": [ - "230" + "filament_retract_length_toolchange": [ + "nil", + "8" ], "pressure_advance": [ + "0.03", "0.035" ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 30 mm^3/s, from Snorca HF preset Snapmaker PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "filament_deretraction_speed": [ + "nil", + "40" + ], + "filament_multitool_ramming_flow": [ + "10", + "30" + ], + "fan_min_speed": [ + "20", + "30" + ], + "filament_flow_ratio": [ + "0.95", + "0.955" + ], + "filament_z_hop_types": [ + "nil", + "Slope Lift" + ], + "filament_multitool_ramming": [ + "0", + "1" + ], + "filament_retraction_speed": [ + "nil", + "40" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 80eb645ad2..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG HF @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PETG HF @U1 0.6 nozzle", - "from": "system", - "setting_id": "A4AAbNDIaCgXqiIk", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_flow_ratio": [ - "0.955" - ], - "pressure_advance": [ - "0.0525" - ], - "nozzle_temperature_initial_layer": [ - "230" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json index 985505a027..1103fb9192 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json @@ -1,24 +1,18 @@ { "type": "filament", "name": "Snapmaker PETG HF @U1 0.6 nozzle", + "renamed_from": "Snapmaker PETG HF U1 0.6 nozzle;Snapmaker PETG HF @U1 0.6 High Flow nozzle;Snapmaker PETG HF U1 0.6 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", "setting_id": "2FfQZdp5xunngnF0", "instantiation": "true", "filament_extruder_variant": [ - "Direct Drive Standard" - ], - "filament_ramming_travel_time": [ - "0" - ], - "long_retractions_when_ec": [ - "0" - ], - "retraction_distances_when_ec": [ - "0" + "Direct Drive Standard", + "Direct Drive High Flow" ], "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "additional_cooling_fan_speed": [ "70" @@ -84,7 +78,8 @@ "245" ], "nozzle_temperature_initial_layer": [ - "245" + "245", + "230" ], "nozzle_temperature_range_low": [ "230" @@ -109,5 +104,16 @@ ], "textured_plate_temp_initial_layer": [ "80" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "pressure_advance": [ + "0.03", + "0.0525" + ], + "filament_flow_ratio": [ + "0.95", + "0.955" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 10321dcf95..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG HF @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PETG HF @U1 0.8 nozzle", - "from": "system", - "setting_id": "vDLX4xfsJUq4aWd8", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_flow_ratio": [ - "0.955" - ], - "pressure_advance": [ - "0.0525" - ], - "nozzle_temperature_initial_layer": [ - "230" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json index 83c1b17971..d1fea8441e 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json @@ -1,24 +1,18 @@ { "type": "filament", "name": "Snapmaker PETG HF @U1 0.8 nozzle", + "renamed_from": "Snapmaker PETG HF U1 0.8 nozzle;Snapmaker PETG HF @U1 0.8 High Flow nozzle;Snapmaker PETG HF U1 0.8 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", "setting_id": "ebANa67V4B2nwUaX", "instantiation": "true", "filament_extruder_variant": [ - "Direct Drive Standard" - ], - "filament_ramming_travel_time": [ - "0" - ], - "long_retractions_when_ec": [ - "0" - ], - "retraction_distances_when_ec": [ - "0" + "Direct Drive Standard", + "Direct Drive High Flow" ], "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "additional_cooling_fan_speed": [ "70" @@ -81,7 +75,8 @@ "245" ], "nozzle_temperature_initial_layer": [ - "245" + "245", + "230" ], "nozzle_temperature_range_low": [ "230" @@ -106,5 +101,16 @@ ], "textured_plate_temp_initial_layer": [ "80" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "pressure_advance": [ + "0.03", + "0.0525" + ], + "filament_flow_ratio": [ + "0.95", + "0.955" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 6c67be5752..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG Translucent @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker PETG Translucent @U1 0.2 nozzle", - "from": "system", - "setting_id": "jWQZ4FpmKof6eKP5", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "pressure_advance": [ - "0.2875" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "1" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json index ee6234f406..51725376e7 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.2 nozzle", + "renamed_from": "Snapmaker PETG Translucent U1 0.2 nozzle;Snapmaker PETG Translucent @U1 0.2 High Flow nozzle;Snapmaker PETG Translucent U1 0.2 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "UTpedqYJysaoe3OI", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "additional_cooling_fan_speed": [ "20" @@ -80,9 +82,6 @@ "hot_plate_temp": [ "80" ], - "nozzle_temperature": [ - "245" - ], "nozzle_temperature_initial_layer": [ "250" ], @@ -93,7 +92,8 @@ "10%" ], "pressure_advance": [ - "0.23" + "0.23", + "0.2875" ], "slow_down_min_speed": [ "10" @@ -105,6 +105,14 @@ "80" ], "enable_pressure_advance": [ - "0" + "0", + "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json deleted file mode 100644 index c25ea01897..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG Translucent @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PETG Translucent @U1 0.4 nozzle", - "from": "system", - "setting_id": "SSb1PYKLfJmn0RaW", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "0" - ], - "pressure_advance": [ - "0.05" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 10 mm^3/s, from Snorca HF preset Snapmaker PETG Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "10" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json index e0e60a0e14..0bec924fdd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.4 nozzle", + "renamed_from": "Snapmaker PETG Translucent U1 0.4 nozzle;Snapmaker PETG Translucent @U1 0.4 High Flow nozzle;Snapmaker PETG Translucent U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "k6UQJJASzFSfePqN", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "close_fan_the_first_x_layers": [ "3" @@ -81,7 +83,8 @@ "10%" ], "pressure_advance": [ - "0.04" + "0.04", + "0.05" ], "slow_down_min_speed": [ "10" @@ -94,5 +97,15 @@ ], "filament_density": [ "1.25" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 10 mm^3/s, from Snorca HF preset Snapmaker PETG Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "additional_cooling_fan_speed": [ + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 675e07357b..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG Translucent @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PETG Translucent @U1 0.6 nozzle", - "from": "system", - "setting_id": "8teECJZmuiAx3xzK", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "pressure_advance": [ - "0.025" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "16" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json index 14f59104f8..0f2a565897 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.6 nozzle", + "renamed_from": "Snapmaker PETG Translucent U1 0.6 nozzle;Snapmaker PETG Translucent @U1 0.6 High Flow nozzle;Snapmaker PETG Translucent U1 0.6 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "FlcbnP3kWiJLMDMl", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "close_fan_the_first_x_layers": [ "3" @@ -38,9 +40,6 @@ "hot_plate_temp": [ "80" ], - "nozzle_temperature": [ - "245" - ], "nozzle_temperature_initial_layer": [ "250" ], @@ -51,7 +50,8 @@ "10%" ], "pressure_advance": [ - "0.02" + "0.02", + "0.025" ], "slow_down_min_speed": [ "10" @@ -102,6 +102,14 @@ "Slope Lift" ], "enable_pressure_advance": [ - "0" + "0", + "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json deleted file mode 100644 index d8771ee472..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG Translucent @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PETG Translucent @U1 0.8 nozzle", - "from": "system", - "setting_id": "jFuSDlP3daCftYGd", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "pressure_advance": [ - "0.025" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "16" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json index e746141f06..d8cffc3787 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.8 nozzle", + "renamed_from": "Snapmaker PETG Translucent U1 0.8 nozzle;Snapmaker PETG Translucent @U1 0.8 High Flow nozzle;Snapmaker PETG Translucent U1 0.8 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "aJk3TnFTU3uQGjd3", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "close_fan_the_first_x_layers": [ "3" @@ -38,9 +40,6 @@ "hot_plate_temp": [ "80" ], - "nozzle_temperature": [ - "245" - ], "nozzle_temperature_initial_layer": [ "250" ], @@ -51,7 +50,8 @@ "10%" ], "pressure_advance": [ - "0.02" + "0.02", + "0.025" ], "slow_down_min_speed": [ "10" @@ -99,6 +99,14 @@ "Slope Lift" ], "enable_pressure_advance": [ - "0" + "0", + "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index c5473497d8..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG-CF @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PETG-CF @U1", - "from": "system", - "setting_id": "kTtK6gmRn2LVCgPv", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "20" - ], - "enable_pressure_advance": [ - "1" - ], - "fan_max_speed": [ - "40" - ], - "fan_min_speed": [ - "5" - ], - "filament_flow_ratio": [ - "1.01" - ], - "filament_max_volumetric_speed": [ - "18" - ], - "filament_minimal_purge_on_wipe_tower": [ - "30" - ], - "filament_multitool_ramming": [ - "1" - ], - "filament_multitool_ramming_flow": [ - "13" - ], - "filament_multitool_ramming_volume": [ - "0.5" - ], - "filament_retract_length_toolchange": [ - "nil" - ], - "filament_retraction_length": [ - "0.6" - ], - "nozzle_temperature": [ - "260" - ], - "nozzle_temperature_initial_layer": [ - "260" - ], - "nozzle_temperature_range_high": [ - "270" - ], - "nozzle_temperature_range_low": [ - "240" - ], - "pressure_advance": [ - "0.028" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PETG-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json index f0bae32788..e58dd27968 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json @@ -1,11 +1,87 @@ { "type": "filament", "name": "Snapmaker PETG-CF @U1", + "renamed_from": "Snapmaker PETG-CF U1;Snapmaker PETG-CF @U1 0.4 High Flow nozzle;Snapmaker PETG-CF U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG-CF @U1 base", "from": "system", "setting_id": "i982wlNMSJujh6Vs", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PETG-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_minimal_purge_on_wipe_tower": [ + "0", + "30" + ], + "nozzle_temperature_range_low": [ + "220", + "240" + ], + "fan_max_speed": [ + "20", + "40" + ], + "nozzle_temperature_initial_layer": [ + "250", + "260" + ], + "nozzle_temperature": [ + "245", + "260" + ], + "filament_multitool_ramming_volume": [ + "0", + "0.5" + ], + "additional_cooling_fan_speed": [ + "0", + "20" + ], + "nozzle_temperature_range_high": [ + "255", + "270" + ], + "filament_retraction_length": [ + "nil", + "0.6" + ], + "filament_max_volumetric_speed": [ + "6.4", + "18" + ], + "filament_retract_length_toolchange": [ + "nil" + ], + "pressure_advance": [ + "0.03", + "0.028" + ], + "enable_pressure_advance": [ + "0", + "1" + ], + "filament_multitool_ramming_flow": [ + "10", + "13" + ], + "fan_min_speed": [ + "0", + "5" + ], + "filament_flow_ratio": [ + "0.95", + "1.01" + ], + "filament_multitool_ramming": [ + "0", + "1" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index b0e13a4f79..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA @U1", - "from": "system", - "setting_id": "Iw2vC9RsmNnXlyFC", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "14" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 14 mm^3/s, from local standard fallback preset Snapmaker PLA @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json index 1a2f9e3ff3..f8097b3a27 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json @@ -1,17 +1,26 @@ { "type": "filament", "name": "Snapmaker PLA @U1", + "renamed_from": "Snapmaker PLA U1;Snapmaker PLA @U1 0.4 High Flow nozzle;Snapmaker PLA U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA @U1 base", "from": "system", "setting_id": "upBaAiQ4fqmefjo6", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "hot_plate_temp": [ "55" ], "hot_plate_temp_initial_layer": [ "55" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 14 mm^3/s, from local standard fallback preset Snapmaker PLA @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 86cd96747b..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Basic @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Basic @U1", - "from": "system", - "setting_id": "6pP9yn7QPVxPrKlp", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Snapmaker PLA Basic @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json index 4b68672fa5..cb6d5bd046 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Basic @U1", + "renamed_from": "Snapmaker PLA Basic U1;Snapmaker PLA Basic @U1 0.4 High Flow nozzle;Snapmaker PLA Basic U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "ZtUJN4JpkR0MLpiY", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -26,9 +28,6 @@ "adaptive_pressure_advance_overhangs": [ "0" ], - "additional_cooling_fan_speed": [ - "70" - ], "chamber_temperature": [ "0" ], @@ -68,12 +67,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.5" ], @@ -129,7 +122,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Snapmaker PLA Basic @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -158,9 +151,6 @@ "filament_retraction_distances_when_cut": [ "nil" ], - "filament_retraction_length": [ - "nil" - ], "filament_retraction_minimum_travel": [ "nil" ], @@ -224,18 +214,9 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -280,5 +261,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 100d225a88..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Eco @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Eco @U1", - "from": "system", - "setting_id": "yBXvtWTUvseiCC6p", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker PLA Eco @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json index fadf7e59d2..52d6d8f92a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json @@ -1,11 +1,20 @@ { "type": "filament", "name": "Snapmaker PLA Eco @U1", + "renamed_from": "Snapmaker PLA Eco U1;Snapmaker PLA Eco @U1 0.4 High Flow nozzle;Snapmaker PLA Eco U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Eco @U1 base", "from": "system", "setting_id": "mF73rtMhQQJz1bjt", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker PLA Eco @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f408b23651..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Full Spectrum @U1 0.4 nozzle", - "from": "system", - "setting_id": "Si1HKTz9ZSM8dIaU", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_retract_length_toolchange": [ - "5" - ], - "filament_retraction_length": [ - "0.5" - ], - "filament_z_hop_types": [ - "Slope Lift" - ], - "pressure_advance": [ - "0.024" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 15 mm^3/s, from Snorca HF preset Snapmaker PLA Full Spectrum @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "15" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json index 219e8075e5..e58ce8df61 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA Full Spectrum @U1 0.4 nozzle", + "renamed_from": "Snapmaker PLA Full Spectrum U1 0.4 nozzle;Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle;Snapmaker PLA Full Spectrum U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "iybaFWrXKOsmXgMJ", "filament_id": "OFOPU1xv", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -27,9 +29,6 @@ "adaptive_pressure_advance_overhangs": [ "0" ], - "additional_cooling_fan_speed": [ - "70" - ], "chamber_temperature": [ "0" ], @@ -58,7 +57,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "60" @@ -69,12 +69,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.5" ], @@ -130,7 +124,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 15 mm^3/s, from Snorca HF preset Snapmaker PLA Full Spectrum @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -139,7 +133,8 @@ "nil" ], "filament_retract_length_toolchange": [ - "10" + "10", + "5" ], "filament_retract_lift_above": [ "nil" @@ -160,7 +155,8 @@ "nil" ], "filament_retraction_length": [ - "nil" + "nil", + "0.5" ], "filament_retraction_minimum_travel": [ "nil" @@ -211,7 +207,8 @@ "nil" ], "filament_z_hop_types": [ - "nil" + "nil", + "Slope Lift" ], "full_fan_speed_layer": [ "0" @@ -225,18 +222,9 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -247,7 +235,8 @@ "0.4157" ], "pressure_advance": [ - "0.02" + "0.02", + "0.024" ], "reduce_fan_stop_start_freq": [ "1" @@ -281,5 +270,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 70c4b76b98..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Glow @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Glow @U1 0.4 nozzle", - "from": "system", - "setting_id": "z33Hc87sYPmwPL5J", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "100" - ], - "filament_flow_ratio": [ - "0.98" - ], - "filament_max_volumetric_speed": [ - "18" - ], - "pressure_advance": [ - "0.023" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PLA Glow @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json index 907781cd2c..fa22f4812d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json @@ -1,15 +1,18 @@ { "type": "filament", "name": "Snapmaker PLA Glow @U1 0.4 nozzle", + "renamed_from": "Snapmaker PLA Glow U1 0.4 nozzle;Snapmaker PLA Glow @U1 0.4 High Flow nozzle;Snapmaker PLA Glow U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Glow @U1 base", "from": "system", "setting_id": "ee1YNNr8CwKetuvY", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "additional_cooling_fan_speed": [ - "80" + "80", + "100" ], "complete_print_exhaust_fan_speed": [ "70" @@ -30,7 +33,8 @@ "20" ], "filament_flow_ratio": [ - "0.97" + "0.97", + "0.98" ], "filament_loading_speed": [ "28" @@ -39,7 +43,8 @@ "3" ], "filament_max_volumetric_speed": [ - "16" + "16", + "18" ], "filament_minimal_purge_on_wipe_tower": [ "30" @@ -91,5 +96,16 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PLA Glow @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "pressure_advance": [ + "0.02", + "0.023" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f281f8795b..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Lite @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Lite @U1", - "from": "system", - "setting_id": "siahKp21MtWd5vha", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Snapmaker PLA Lite @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json index d2c9dddefb..8e5c511b1b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Lite @U1", + "renamed_from": "Snapmaker PLA Lite U1;Snapmaker PLA Lite @U1 0.4 High Flow nozzle;Snapmaker PLA Lite U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Lite @U1 base", "from": "system", "setting_id": "ZZyxuKUboYttCqgM", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -26,9 +28,6 @@ "adaptive_pressure_advance_overhangs": [ "0" ], - "additional_cooling_fan_speed": [ - "70" - ], "chamber_temperature": [ "0" ], @@ -71,12 +70,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.5" ], @@ -132,7 +125,7 @@ "10" ], "filament_notes": [ - "" + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Snapmaker PLA Lite @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -158,9 +151,6 @@ "filament_retraction_distances_when_cut": [ "nil" ], - "filament_retraction_length": [ - "nil" - ], "filament_retraction_minimum_travel": [ "nil" ], @@ -224,18 +214,9 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -280,5 +261,9 @@ ], "textured_plate_temp_initial_layer": [ "60" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json deleted file mode 100644 index f9f65c0f64..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Matte @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker PLA Matte @U1 0.2 nozzle", - "from": "system", - "setting_id": "uJJlKJE6cP1VqAhi", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "2" - ], - "filament_flow_ratio": [ - "1.0395" - ], - "pressure_advance": [ - "0.03" - ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json index 2a8c1e40c3..ab7b23a405 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1 0.2 nozzle", + "renamed_from": "Snapmaker PLA Matte U1 0.2 nozzle;Snapmaker PLA Matte @U1 0.2 High Flow nozzle;Snapmaker PLA Matte U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA Matte @U1 base2", "from": "system", "setting_id": "d3c48DeNk7VDeeHf", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -57,7 +59,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "45" @@ -68,12 +71,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "24.99" ], @@ -96,7 +93,7 @@ "1" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -125,9 +122,6 @@ "filament_retraction_distances_when_cut": [ "nil" ], - "filament_retraction_length": [ - "nil" - ], "filament_retraction_minimum_travel": [ "nil" ], @@ -173,9 +167,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -243,7 +234,8 @@ "nil" ], "filament_flow_ratio": [ - "1.05" + "1.05", + "1.0395" ], "filament_loading_speed": [ "10" @@ -276,12 +268,19 @@ "nil" ], "nozzle_temperature": [ - "215" + "215", + "220" ], "nozzle_temperature_initial_layer": [ - "215" + "215", + "220" ], "pressure_advance": [ - "0.025" + "0.025", + "0.03" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 6a39076a6f..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Matte @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Matte @U1", - "from": "system", - "setting_id": "a0eQeSmYLKJpPxo9", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "100" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_flow_ratio": [ - "0.99" - ], - "filament_max_volumetric_speed": [ - "40" - ], - "filament_retraction_length": [ - "1" - ], - "filament_wipe_distance": [ - "1" - ], - "filament_z_hop_types": [ - "Slope Lift" - ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "pressure_advance": [ - "0.024" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA Matte @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 6c471d4f08..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Matte @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PLA Matte @U1 0.6 nozzle", - "from": "system", - "setting_id": "DfrCVTFg0FD7XVkf", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_flow_ratio": [ - "0.9801" - ], - "pressure_advance": [ - "0.018" - ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json index 9de7615ae5..c4cf484227 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1 0.6 nozzle", + "renamed_from": "Snapmaker PLA Matte U1 0.6 nozzle;Snapmaker PLA Matte @U1 0.6 High Flow nozzle;Snapmaker PLA Matte U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA Matte @U1 base2", "from": "system", "setting_id": "l3E1WaljDn2uoTqW", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -57,7 +59,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "45" @@ -68,12 +71,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "24.99" ], @@ -105,7 +102,7 @@ "1" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -185,9 +182,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -243,7 +237,8 @@ "2" ], "filament_flow_ratio": [ - "0.99" + "0.99", + "0.9801" ], "filament_loading_speed": [ "10" @@ -276,12 +271,19 @@ "65" ], "nozzle_temperature": [ - "215" + "215", + "220" ], "nozzle_temperature_initial_layer": [ - "215" + "215", + "220" ], "pressure_advance": [ - "0.015" + "0.015", + "0.018" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json deleted file mode 100644 index d076c056a5..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Matte @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PLA Matte @U1 0.8 nozzle", - "from": "system", - "setting_id": "jMrzguz4b18qMWem", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_flow_ratio": [ - "0.9702" - ], - "pressure_advance": [ - "0.018" - ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json index 251b2a47b6..41548165b6 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1 0.8 nozzle", + "renamed_from": "Snapmaker PLA Matte U1 0.8 nozzle;Snapmaker PLA Matte @U1 0.8 High Flow nozzle;Snapmaker PLA Matte U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA Matte @U1 base2", "from": "system", "setting_id": "NLPd4AA0seW3NXdy", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -57,7 +59,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "45" @@ -68,12 +71,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "24.99" ], @@ -87,7 +84,8 @@ " " ], "filament_flow_ratio": [ - "0.98" + "0.98", + "0.9702" ], "filament_is_support": [ "0" @@ -105,7 +103,7 @@ "1" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -182,9 +180,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -222,7 +217,8 @@ "40" ], "nozzle_temperature": [ - "215" + "215", + "220" ], "textured_plate_temp": [ "65" @@ -279,9 +275,15 @@ "65" ], "nozzle_temperature_initial_layer": [ - "215" + "215", + "220" ], "pressure_advance": [ - "0.015" + "0.015", + "0.018" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json index badf918d97..b97e9dbba4 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1", + "renamed_from": "Snapmaker PLA Matte U1;Snapmaker PLA Matte @U1 0.4 High Flow nozzle;Snapmaker PLA Matte U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA @U1 base", "from": "system", "setting_id": "uAhykL5ap1DAKacf", "filament_id": "OFF9cgY5", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -28,7 +30,8 @@ "0" ], "additional_cooling_fan_speed": [ - "80" + "80", + "100" ], "chamber_temperature": [ "0" @@ -58,7 +61,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "60" @@ -69,12 +73,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.5" ], @@ -90,9 +88,6 @@ "filament_density": [ "1.32" ], - "filament_deretraction_speed": [ - "nil" - ], "filament_diameter": [ "1.75" ], @@ -100,7 +95,8 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "1" + "1", + "0.99" ], "filament_is_support": [ "0" @@ -115,7 +111,8 @@ "nil" ], "filament_max_volumetric_speed": [ - "22" + "22", + "40" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -130,14 +127,11 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA Matte @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], - "filament_retract_before_wipe": [ - "nil" - ], "filament_retract_length_toolchange": [ "5" ], @@ -150,20 +144,12 @@ "filament_retract_lift_enforce": [ "nil" ], - "filament_retract_restart_extra": [ - "nil" - ], - "filament_retract_when_changing_layer": [ - "nil" - ], "filament_retraction_distances_when_cut": [ "nil" ], "filament_retraction_length": [ - "nil" - ], - "filament_retraction_minimum_travel": [ - "nil" + "nil", + "1" ], "filament_retraction_speed": [ "nil" @@ -201,17 +187,13 @@ "filament_vendor": [ "Snapmaker" ], - "filament_wipe": [ - "nil" - ], "filament_wipe_distance": [ - "nil" - ], - "filament_z_hop": [ - "nil" + "nil", + "1" ], "filament_z_hop_types": [ - "nil" + "nil", + "Slope Lift" ], "full_fan_speed_layer": [ "0" @@ -226,16 +208,12 @@ "0" ], "nozzle_temperature": [ - "215" + "215", + "220" ], "nozzle_temperature_initial_layer": [ - "215" - ], - "nozzle_temperature_range_high": [ - "240" - ], - "nozzle_temperature_range_low": [ - "190" + "215", + "220" ], "overhang_fan_speed": [ "100" @@ -247,7 +225,8 @@ "0.4157" ], "pressure_advance": [ - "0.02" + "0.02", + "0.024" ], "reduce_fan_stop_start_freq": [ "1" @@ -281,5 +260,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f26eeb2114..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Metal @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Metal @U1", - "from": "system", - "setting_id": "01UP0AVB1B7TBi78", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "16" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from local standard fallback preset Snapmaker PLA Metal @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json index 538158a155..2c2d57761d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json @@ -1,11 +1,20 @@ { "type": "filament", "name": "Snapmaker PLA Metal @U1", + "renamed_from": "Snapmaker PLA Metal U1;Snapmaker PLA Metal @U1 0.4 High Flow nozzle;Snapmaker PLA Metal U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Metal @U1 base", "from": "system", "setting_id": "lPmCxsx9EpoLoNEk", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from local standard fallback preset Snapmaker PLA Metal @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json deleted file mode 100644 index a3715598dd..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Silk @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker PLA Silk @U1 0.2 nozzle", - "from": "system", - "setting_id": "3YXcTZZ99lTaxy3j", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "2" - ], - "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json index e4fdb8016b..18de9edada 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1 0.2 nozzle", + "renamed_from": "Snapmaker PLA Silk U1 0.2 nozzle;Snapmaker PLA Silk @U1 0.2 High Flow nozzle;Snapmaker PLA Silk U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "Q5xH6oGGlbXr14Pa", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -66,12 +68,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "25.4" ], @@ -109,7 +105,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -189,9 +185,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -281,5 +274,9 @@ ], "pressure_advance": [ "0.015" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 23626faf6a..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Silk @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Silk @U1", - "from": "system", - "setting_id": "rpTq4sJFpmbdl0dy", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "80" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_z_hop_types": [ - "Slope Lift" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Silk @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json deleted file mode 100644 index b4f510bfec..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Silk @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PLA Silk @U1 0.6 nozzle", - "from": "system", - "setting_id": "SYJSv3f1LWPnWTo1", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "10" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json index b144ce86dd..3526a1c871 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1 0.6 nozzle", + "renamed_from": "Snapmaker PLA Silk U1 0.6 nozzle;Snapmaker PLA Silk @U1 0.6 High Flow nozzle;Snapmaker PLA Silk U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "2pieDQoz9PiDCnU1", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -66,12 +68,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "25.4" ], @@ -109,7 +105,7 @@ "10" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -189,9 +185,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -281,5 +274,9 @@ ], "pressure_advance": [ "0.015" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 4919383540..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Silk @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PLA Silk @U1 0.8 nozzle", - "from": "system", - "setting_id": "GpTeZsC8ST8UvkKe", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "10" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json index aa92e97d18..7e11c05252 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1 0.8 nozzle", + "renamed_from": "Snapmaker PLA Silk U1 0.8 nozzle;Snapmaker PLA Silk @U1 0.8 High Flow nozzle;Snapmaker PLA Silk U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "f3H0JFUnds0mkKCN", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -66,12 +68,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "25.4" ], @@ -109,7 +105,7 @@ "10" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -189,9 +185,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -281,5 +274,9 @@ ], "pressure_advance": [ "0.015" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json index 6b6030b919..acd1c82116 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1", + "renamed_from": "Snapmaker PLA Silk U1;Snapmaker PLA Silk @U1 0.4 High Flow nozzle;Snapmaker PLA Silk U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "9PkHSwtXFM0zPWth", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -28,7 +30,8 @@ "0" ], "additional_cooling_fan_speed": [ - "70" + "70", + "80" ], "chamber_temperature": [ "0" @@ -66,12 +69,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cost": [ "25.4" ], @@ -109,7 +106,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Silk @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -178,7 +175,8 @@ "nil" ], "filament_z_hop_types": [ - "nil" + "nil", + "Slope Lift" ], "full_fan_speed_layer": [ "0" @@ -189,9 +187,6 @@ "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -253,7 +248,8 @@ "3" ], "filament_max_volumetric_speed": [ - "10" + "10", + "12" ], "filament_retraction_length": [ "0.2" @@ -281,5 +277,9 @@ ], "pressure_advance": [ "0.015" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 4b3e1405bb..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "from": "system", - "setting_id": "75HoJsG7nurT9m8o", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "2" - ], - "pressure_advance": [ - "0.24" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json index 7fcb20f31b..842d5fee73 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json @@ -1,6 +1,7 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "renamed_from": "Snapmaker PLA SnapSpeed U1 0.2 nozzle;Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base2", "from": "system", "setting_id": "jsAgNNiUC6DoO7rz", @@ -33,7 +34,8 @@ "65" ], "pressure_advance": [ - "0.2" + "0.2", + "0.24" ], "textured_plate_temp": [ "65" @@ -42,9 +44,18 @@ "65" ], "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_pressure_advance": [ - "0" + "0", + "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 48a4044180..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA SnapSpeed @U1", - "from": "system", - "setting_id": "2S7b7VwKFkZuXTHB", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_max_volumetric_speed": [ - "40" - ], - "filament_multitool_ramming_flow": [ - "40" - ], - "pressure_advance": [ - "0.024" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA SnapSpeed @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 92c7b1388d..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "from": "system", - "setting_id": "e3hivxxVP4MEauB0", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "pressure_advance": [ - "0.024" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json index 18fca8b4fd..81b67fee51 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "renamed_from": "Snapmaker PLA SnapSpeed U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base2", "from": "system", "setting_id": "aKudBpQBj3MZZZav", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_deretraction_speed": [ "30" @@ -36,6 +38,18 @@ "65" ], "enable_pressure_advance": [ - "0" + "0", + "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "pressure_advance": [ + "0.02", + "0.024" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 20db16271a..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "from": "system", - "setting_id": "0ZOycCmSyLf9Zr3f", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "pressure_advance": [ - "0.0216" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json index 2ebadbd2b2..1654724651 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "renamed_from": "Snapmaker PLA SnapSpeed U1 0.8 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base2", "from": "system", "setting_id": "sWoInox4hzxzIHfe", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_deretraction_speed": [ "30" @@ -27,7 +29,8 @@ "215" ], "pressure_advance": [ - "0.018" + "0.018", + "0.0216" ], "textured_plate_temp": [ "65" @@ -36,6 +39,14 @@ "65" ], "enable_pressure_advance": [ - "0" + "0", + "1" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json index 2525f5b2a9..98e2cb5877 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1", + "renamed_from": "Snapmaker PLA SnapSpeed U1;Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base", "from": "system", "setting_id": "en6Pvvbc1P9TKm8I", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -26,9 +28,6 @@ "adaptive_pressure_advance_overhangs": [ "0" ], - "additional_cooling_fan_speed": [ - "70" - ], "chamber_temperature": [ "0" ], @@ -57,7 +56,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "60" @@ -68,12 +68,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.5" ], @@ -114,7 +108,8 @@ "nil" ], "filament_max_volumetric_speed": [ - "20" + "20", + "40" ], "filament_minimal_purge_on_wipe_tower": [ "15" @@ -123,13 +118,14 @@ "1" ], "filament_multitool_ramming_flow": [ - "30" + "30", + "40" ], "filament_multitool_ramming_volume": [ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA SnapSpeed @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -224,18 +220,9 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], "nozzle_temperature_range_high": [ "240" ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -246,7 +233,8 @@ "0.4157" ], "pressure_advance": [ - "0.02" + "0.02", + "0.024" ], "reduce_fan_stop_start_freq": [ "1" @@ -280,5 +268,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json deleted file mode 100644 index 30c289604f..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Translucent @U1 0.2 High Flow nozzle", - "inherits": "Snapmaker PLA Translucent @U1 0.2 nozzle", - "from": "system", - "setting_id": "atEZp48A9Ajefn5P", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "pressure_advance": [ - "0.18" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "1.6" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json index 6271b7af42..2e2732dbd5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.2 nozzle", + "renamed_from": "Snapmaker PLA Translucent U1 0.2 nozzle;Snapmaker PLA Translucent @U1 0.2 High Flow nozzle;Snapmaker PLA Translucent U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "zIg9XLrieYNBduyn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -57,7 +59,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "60" @@ -68,12 +71,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.4" ], @@ -89,9 +86,6 @@ "filament_density": [ "1.22" ], - "filament_deretraction_speed": [ - "nil" - ], "filament_diameter": [ "1.75" ], @@ -129,7 +123,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -149,21 +143,12 @@ "filament_retract_lift_enforce": [ "nil" ], - "filament_retract_restart_extra": [ - "nil" - ], - "filament_retract_when_changing_layer": [ - "nil" - ], "filament_retraction_distances_when_cut": [ "nil" ], "filament_retraction_length": [ "0.2" ], - "filament_retraction_minimum_travel": [ - "nil" - ], "filament_retraction_speed": [ "nil" ], @@ -206,9 +191,6 @@ "filament_wipe_distance": [ "1" ], - "filament_z_hop": [ - "nil" - ], "filament_z_hop_types": [ "Slope Lift" ], @@ -224,15 +206,6 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "nozzle_temperature_range_high": [ - "240" - ], "nozzle_temperature_range_low": [ "200" ], @@ -246,7 +219,8 @@ "0.4157" ], "pressure_advance": [ - "0.15" + "0.15", + "0.18" ], "reduce_fan_stop_start_freq": [ "1" @@ -280,5 +254,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json deleted file mode 100644 index d8ab413ff1..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Translucent @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Translucent @U1 0.4 nozzle", - "from": "system", - "setting_id": "OhPRQfEeCCBos1Xk", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "pressure_advance": [ - "0.024" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json index 7d4270ef31..87cbfc8ed8 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.4 nozzle", + "renamed_from": "Snapmaker PLA Translucent U1 0.4 nozzle;Snapmaker PLA Translucent @U1 0.4 High Flow nozzle;Snapmaker PLA Translucent U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "RqtrcLcy124ICGEc", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -56,9 +58,6 @@ "enable_overhang_bridge_fan": [ "1" ], - "enable_pressure_advance": [ - "1" - ], "eng_plate_temp": [ "60" ], @@ -68,12 +67,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.4" ], @@ -89,9 +82,6 @@ "filament_density": [ "1.32" ], - "filament_deretraction_speed": [ - "nil" - ], "filament_diameter": [ "1.75" ], @@ -129,14 +119,11 @@ "10" ], "filament_notes": [ - "" + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], - "filament_retract_before_wipe": [ - "nil" - ], "filament_retract_length_toolchange": [ "5" ], @@ -149,21 +136,12 @@ "filament_retract_lift_enforce": [ "nil" ], - "filament_retract_restart_extra": [ - "nil" - ], - "filament_retract_when_changing_layer": [ - "nil" - ], "filament_retraction_distances_when_cut": [ "nil" ], "filament_retraction_length": [ "0.2" ], - "filament_retraction_minimum_travel": [ - "nil" - ], "filament_retraction_speed": [ "nil" ], @@ -200,15 +178,6 @@ "filament_vendor": [ "Snapmaker" ], - "filament_wipe": [ - "nil" - ], - "filament_wipe_distance": [ - "nil" - ], - "filament_z_hop": [ - "nil" - ], "filament_z_hop_types": [ "Slope Lift" ], @@ -224,18 +193,6 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "nozzle_temperature_range_high": [ - "240" - ], - "nozzle_temperature_range_low": [ - "190" - ], "overhang_fan_speed": [ "100" ], @@ -246,7 +203,8 @@ "0.4157" ], "pressure_advance": [ - "0.02" + "0.02", + "0.024" ], "reduce_fan_stop_start_freq": [ "1" @@ -280,5 +238,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 08ef19a9cb..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Translucent @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PLA Translucent @U1 0.6 nozzle", - "from": "system", - "setting_id": "UkjutA0LDsne1Kr4", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "pressure_advance": [ - "0.024" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json index af3f95c757..b9320cf13b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.6 nozzle", + "renamed_from": "Snapmaker PLA Translucent U1 0.6 nozzle;Snapmaker PLA Translucent @U1 0.6 High Flow nozzle;Snapmaker PLA Translucent U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "OZHemzEcvzFMN8fL", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -57,7 +59,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "60" @@ -68,12 +71,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.4" ], @@ -89,18 +86,12 @@ "filament_density": [ "1.22" ], - "filament_deretraction_speed": [ - "nil" - ], "filament_diameter": [ "1.75" ], "filament_end_gcode": [ "; filament end gcode \n" ], - "filament_flow_ratio": [ - "0.98" - ], "filament_is_support": [ "0" ], @@ -129,7 +120,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -149,21 +140,12 @@ "filament_retract_lift_enforce": [ "nil" ], - "filament_retract_restart_extra": [ - "nil" - ], - "filament_retract_when_changing_layer": [ - "nil" - ], "filament_retraction_distances_when_cut": [ "nil" ], "filament_retraction_length": [ "0.5" ], - "filament_retraction_minimum_travel": [ - "nil" - ], "filament_retraction_speed": [ "50" ], @@ -200,15 +182,6 @@ "filament_vendor": [ "Snapmaker" ], - "filament_wipe": [ - "nil" - ], - "filament_wipe_distance": [ - "nil" - ], - "filament_z_hop": [ - "nil" - ], "filament_z_hop_types": [ "Slope Lift" ], @@ -224,15 +197,6 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "nozzle_temperature_range_high": [ - "240" - ], "nozzle_temperature_range_low": [ "200" ], @@ -246,7 +210,8 @@ "0.4157" ], "pressure_advance": [ - "0.02" + "0.02", + "0.024" ], "reduce_fan_stop_start_freq": [ "1" @@ -280,5 +245,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json deleted file mode 100644 index a2e1b4dc16..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Translucent @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PLA Translucent @U1 0.8 nozzle", - "from": "system", - "setting_id": "IEcagoFA1np00DZq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "pressure_advance": [ - "0.024" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json index a10a4b2fb1..c9f55207ca 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.8 nozzle", + "renamed_from": "Snapmaker PLA Translucent U1 0.8 nozzle;Snapmaker PLA Translucent @U1 0.8 High Flow nozzle;Snapmaker PLA Translucent U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "IPehCg9fKawLorB5", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -57,7 +59,8 @@ "1" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "eng_plate_temp": [ "60" @@ -68,12 +71,6 @@ "fan_cooling_layer_time": [ "100" ], - "fan_max_speed": [ - "100" - ], - "fan_min_speed": [ - "100" - ], "filament_cooling_final_speed": [ "3.4" ], @@ -89,9 +86,6 @@ "filament_density": [ "1.22" ], - "filament_deretraction_speed": [ - "nil" - ], "filament_diameter": [ "1.75" ], @@ -129,7 +123,7 @@ "5" ], "filament_notes": [ - "" + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" @@ -149,21 +143,12 @@ "filament_retract_lift_enforce": [ "nil" ], - "filament_retract_restart_extra": [ - "nil" - ], - "filament_retract_when_changing_layer": [ - "nil" - ], "filament_retraction_distances_when_cut": [ "nil" ], "filament_retraction_length": [ "1" ], - "filament_retraction_minimum_travel": [ - "nil" - ], "filament_retraction_speed": [ "nil" ], @@ -200,15 +185,6 @@ "filament_vendor": [ "Snapmaker" ], - "filament_wipe": [ - "nil" - ], - "filament_wipe_distance": [ - "nil" - ], - "filament_z_hop": [ - "nil" - ], "filament_z_hop_types": [ "Slope Lift" ], @@ -224,15 +200,6 @@ "idle_temperature": [ "0" ], - "nozzle_temperature": [ - "220" - ], - "nozzle_temperature_initial_layer": [ - "220" - ], - "nozzle_temperature_range_high": [ - "240" - ], "nozzle_temperature_range_low": [ "200" ], @@ -246,7 +213,8 @@ "0.4157" ], "pressure_advance": [ - "0.02" + "0.02", + "0.024" ], "reduce_fan_stop_start_freq": [ "1" @@ -280,5 +248,9 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 47fa100cd1..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Wood @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA Wood @U1 0.4 nozzle", - "from": "system", - "setting_id": "7IGP2hVMK7ajiLuk", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_retract_length_toolchange": [ - "6" - ], - "filament_z_hop_types": [ - "Auto Lift" - ], - "pressure_advance": [ - "0.024" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA Wood @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json index 283d5876b7..6ba340e303 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json @@ -1,20 +1,19 @@ { "type": "filament", "name": "Snapmaker PLA Wood @U1 0.4 nozzle", + "renamed_from": "Snapmaker PLA Wood U1 0.4 nozzle;Snapmaker PLA Wood @U1 0.4 High Flow nozzle;Snapmaker PLA Wood U1 0.4 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "XvizDDHbds3bVrzi", "filament_id": "OFdd8ZqJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_start_gcode": [ "" ], - "enable_pressure_advance": [ - "1" - ], "filament_end_gcode": [ "\n" ], @@ -43,7 +42,8 @@ "0.97" ], "filament_max_volumetric_speed": [ - "18" + "18", + "20" ], "filament_multitool_ramming_flow": [ "40" @@ -52,13 +52,15 @@ "10" ], "filament_retract_length_toolchange": [ - "5" + "5", + "6" ], "filament_retraction_length": [ "0.6" ], "pressure_advance": [ - "0.025" + "0.025", + "0.024" ], "slow_down_layer_time": [ "4" @@ -104,5 +106,16 @@ ], "temperature_vitrification": [ "45" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA Wood @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_z_hop_types": [ + "nil", + "Auto Lift" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 9fc8c36369..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Wood @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PLA Wood @U1 0.6 nozzle", - "from": "system", - "setting_id": "8tH46AUyW4uua5QB", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18" - ], - "pressure_advance": [ - "0.0134" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json index 09555457d5..8401fb7635 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json @@ -1,19 +1,22 @@ { "type": "filament", "name": "Snapmaker PLA Wood @U1 0.6 nozzle", + "renamed_from": "Snapmaker PLA Wood U1 0.6 nozzle;Snapmaker PLA Wood @U1 0.6 High Flow nozzle;Snapmaker PLA Wood U1 0.6 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "Kp77DCcitdmoyjqm", "filament_id": "OFdd8ZqJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_start_gcode": [ "" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "filament_end_gcode": [ "\n" @@ -39,9 +42,6 @@ "hot_plate_temp_initial_layer": [ "65" ], - "filament_flow_ratio": [ - "0.98" - ], "filament_max_volumetric_speed": [ "18" ], @@ -58,7 +58,8 @@ "0.4" ], "pressure_advance": [ - "0.014" + "0.014", + "0.0134" ], "slow_down_layer_time": [ "4" @@ -110,5 +111,12 @@ ], "temperature_vitrification": [ "45" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 5300cdbb96..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA Wood @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PLA Wood @U1 0.8 nozzle", - "from": "system", - "setting_id": "LW5RkXBTM4eE3eRK", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "pressure_advance": [ - "0.0067" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "20" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json index 4a5a04a5ee..b645dd168a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json @@ -1,19 +1,22 @@ { "type": "filament", "name": "Snapmaker PLA Wood @U1 0.8 nozzle", + "renamed_from": "Snapmaker PLA Wood U1 0.8 nozzle;Snapmaker PLA Wood @U1 0.8 High Flow nozzle;Snapmaker PLA Wood U1 0.8 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "nBYnNiqwLo6nAsqi", "filament_id": "OFdd8ZqJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_start_gcode": [ "" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "filament_end_gcode": [ "\n" @@ -58,7 +61,8 @@ "0.4" ], "pressure_advance": [ - "0.007" + "0.007", + "0.0067" ], "slow_down_layer_time": [ "4" @@ -110,5 +114,12 @@ ], "temperature_vitrification": [ "45" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index b156c65012..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA-CF @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PLA-CF @U1 0.4 nozzle", - "from": "system", - "setting_id": "ork4mVEyJBzXs2Xj", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "additional_cooling_fan_speed": [ - "100" - ], - "filament_max_volumetric_speed": [ - "20" - ], - "filament_multitool_ramming_flow": [ - "30" - ], - "filament_retract_length_toolchange": [ - "15" - ], - "filament_retraction_length": [ - "0.8" - ], - "nozzle_temperature": [ - "230" - ], - "nozzle_temperature_initial_layer": [ - "230" - ], - "pressure_advance": [ - "0.02" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json index 357c5de50a..98db5dbfbb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json @@ -1,17 +1,15 @@ { "type": "filament", "name": "Snapmaker PLA-CF @U1 0.4 nozzle", - "renamed_from": "Snapmaker PLA-CF @U1", + "renamed_from": "Snapmaker PLA-CF @U1;Snapmaker PLA-CF @U1 0.4 High Flow nozzle;Snapmaker PLA-CF U1 0.4 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "2INwruC3ZVkBTdUT", "filament_id": "OFhQf8ou", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" - ], - "enable_pressure_advance": [ - "1" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_end_gcode": [ "" @@ -20,7 +18,8 @@ "1" ], "filament_multitool_ramming_flow": [ - "20" + "20", + "30" ], "filament_start_gcode": [ "" @@ -40,11 +39,9 @@ "hot_plate_temp_initial_layer": [ "65" ], - "filament_flow_ratio": [ - "0.98" - ], "filament_max_volumetric_speed": [ - "15" + "15", + "20" ], "filament_minimal_purge_on_wipe_tower": [ "50" @@ -53,10 +50,12 @@ "5" ], "filament_retract_length_toolchange": [ - "5" + "5", + "15" ], "filament_retraction_length": [ - "1" + "1", + "0.8" ], "filament_type": [ "PLA-CF" @@ -68,10 +67,12 @@ "Slope Lift" ], "nozzle_temperature": [ - "240" + "240", + "230" ], "nozzle_temperature_initial_layer": [ - "240" + "240", + "230" ], "nozzle_temperature_range_high": [ "250" @@ -80,7 +81,8 @@ "200" ], "pressure_advance": [ - "0.01" + "0.01", + "0.02" ], "activate_air_filtration": [ "0" @@ -128,6 +130,14 @@ "45" ], "additional_cooling_fan_speed": [ - "0" + "0", + "100" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 28e15cb5eb..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA-CF @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PLA-CF @U1 0.6 nozzle", - "from": "system", - "setting_id": "c982QxKnlzVRADnm", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18" - ], - "pressure_advance": [ - "0.03" - ], - "nozzle_temperature": [ - "210" - ], - "nozzle_temperature_initial_layer": [ - "210" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json index 6f09ea5d6a..41f7688d01 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json @@ -1,16 +1,19 @@ { "type": "filament", "name": "Snapmaker PLA-CF @U1 0.6 nozzle", + "renamed_from": "Snapmaker PLA-CF U1 0.6 nozzle;Snapmaker PLA-CF @U1 0.6 High Flow nozzle;Snapmaker PLA-CF U1 0.6 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "CKfdL85SvbOLzMuP", "filament_id": "OFhQf8ou", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "filament_end_gcode": [ "" @@ -39,9 +42,6 @@ "hot_plate_temp_initial_layer": [ "65" ], - "filament_flow_ratio": [ - "0.98" - ], "filament_max_volumetric_speed": [ "18" ], @@ -60,17 +60,16 @@ "filament_type": [ "PLA-CF" ], - "filament_z_hop": [ - "nil" - ], "filament_z_hop_types": [ "Slope Lift" ], "nozzle_temperature": [ - "220" + "220", + "210" ], "nozzle_temperature_initial_layer": [ - "220" + "220", + "210" ], "nozzle_temperature_range_high": [ "250" @@ -79,7 +78,8 @@ "210" ], "pressure_advance": [ - "0.015" + "0.015", + "0.03" ], "filament_cost": [ "35" @@ -134,5 +134,12 @@ ], "additional_cooling_fan_speed": [ "0" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 7c62ffd8ed..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PLA-CF @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PLA-CF @U1 0.8 nozzle", - "from": "system", - "setting_id": "4qXeMrTt9pZNCwEr", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "18" - ], - "pressure_advance": [ - "0.03" - ], - "nozzle_temperature": [ - "210" - ], - "nozzle_temperature_initial_layer": [ - "210" - ], - "enable_pressure_advance": [ - "1" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json index 564e59e1a9..a589a22a83 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json @@ -1,16 +1,19 @@ { "type": "filament", "name": "Snapmaker PLA-CF @U1 0.8 nozzle", + "renamed_from": "Snapmaker PLA-CF U1 0.8 nozzle;Snapmaker PLA-CF @U1 0.8 High Flow nozzle;Snapmaker PLA-CF U1 0.8 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "fhhnfJm4dN9FXpWQ", "filament_id": "OFhQf8ou", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "enable_pressure_advance": [ - "0" + "0", + "1" ], "filament_end_gcode": [ "" @@ -39,9 +42,6 @@ "hot_plate_temp_initial_layer": [ "65" ], - "filament_flow_ratio": [ - "0.98" - ], "filament_max_volumetric_speed": [ "18" ], @@ -60,17 +60,16 @@ "filament_type": [ "PLA-CF" ], - "filament_z_hop": [ - "nil" - ], "filament_z_hop_types": [ "Slope Lift" ], "nozzle_temperature": [ - "220" + "220", + "210" ], "nozzle_temperature_initial_layer": [ - "220" + "220", + "210" ], "nozzle_temperature_range_high": [ "250" @@ -79,7 +78,8 @@ "210" ], "pressure_advance": [ - "0.015" + "0.015", + "0.03" ], "filament_cost": [ "35" @@ -131,5 +131,12 @@ ], "additional_cooling_fan_speed": [ "0" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json deleted file mode 100644 index ab3a76e43d..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PVA @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PVA @U1", - "from": "system", - "setting_id": "NZwDYFscSMlZJusK", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_flow_ratio": [ - "0.97" - ], - "filament_max_volumetric_speed": [ - "6" - ], - "filament_multitool_ramming_flow": [ - "10" - ], - "filament_retraction_length": [ - "0.6" - ], - "filament_notes": [ - "Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 6 mm^3/s, from Snorca HF preset Snapmaker PVA @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json deleted file mode 100644 index b3f8c6db87..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PVA @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker PVA @U1 0.6 nozzle", - "from": "system", - "setting_id": "AO1DsQ9uGg1ivN8b", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_flow_ratio": [ - "0.9506" - ], - "filament_notes": [ - "Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "6" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json index b28e0627ad..c8899e0fcc 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json @@ -1,15 +1,14 @@ { "type": "filament", "name": "Snapmaker PVA @U1 0.6 nozzle", + "renamed_from": "Snapmaker PVA U1 0.6 nozzle;Snapmaker PVA @U1 0.6 High Flow nozzle;Snapmaker PVA U1 0.6 High Flow nozzle", "inherits": "Snapmaker PVA @U1 base", "from": "system", "setting_id": "lIqgnfeZdAm8G0TH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" - ], - "enable_pressure_advance": [ - "0" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "additional_cooling_fan_speed": [ "70" @@ -30,7 +29,8 @@ "nil" ], "filament_flow_ratio": [ - "0.98" + "0.98", + "0.9506" ], "filament_retract_length_toolchange": [ "10" @@ -109,5 +109,12 @@ ], "fan_min_speed": [ "100" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json deleted file mode 100644 index b18972c81f..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PVA @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker PVA @U1 0.8 nozzle", - "from": "system", - "setting_id": "xy6MMfFlgvgrP0Xj", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_flow_ratio": [ - "0.9312" - ], - "filament_notes": [ - "Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "6" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json index 6572a60078..0be4a98af7 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json @@ -1,15 +1,14 @@ { "type": "filament", "name": "Snapmaker PVA @U1 0.8 nozzle", + "renamed_from": "Snapmaker PVA U1 0.8 nozzle;Snapmaker PVA @U1 0.8 High Flow nozzle;Snapmaker PVA U1 0.8 High Flow nozzle", "inherits": "Snapmaker PVA @U1 base", "from": "system", "setting_id": "WxtuvwWxy73Gi0IK", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" - ], - "enable_pressure_advance": [ - "0" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "additional_cooling_fan_speed": [ "70" @@ -33,7 +32,8 @@ "nil" ], "filament_flow_ratio": [ - "0.96" + "0.96", + "0.9312" ], "filament_retract_length_toolchange": [ "10" @@ -103,5 +103,12 @@ ], "filament_unloading_speed_start": [ "100" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json index 5ed5d243ac..a84579c6aa 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker PVA @U1", + "renamed_from": "Snapmaker PVA U1;Snapmaker PVA @U1 0.4 High Flow nozzle;Snapmaker PVA U1 0.4 High Flow nozzle", "inherits": "Snapmaker PVA @U1 base", "from": "system", "setting_id": "A9gyeySGTtUrTPeG", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "enable_pressure_advance": [ "1" @@ -51,7 +53,8 @@ "3" ], "filament_max_volumetric_speed": [ - "5" + "5", + "6" ], "filament_minimal_purge_on_wipe_tower": [ "20" @@ -66,7 +69,8 @@ "4" ], "filament_retraction_length": [ - "0.4" + "0.4", + "0.6" ], "filament_retraction_speed": [ "nil" @@ -97,5 +101,19 @@ ], "textured_plate_temp_initial_layer": [ "65" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 6 mm^3/s, from Snorca HF preset Snapmaker PVA @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_multitool_ramming_flow": [ + "10" + ], + "filament_flow_ratio": [ + "1", + "0.97" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 441562c8d7..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPE @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker TPE @U1", - "from": "system", - "setting_id": "wLR0Jg5KEPd18zik", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.2" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.2 mm^3/s, from local standard fallback preset Snapmaker TPE @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json index a4214b6cd0..7860c778f9 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker TPE @U1", + "renamed_from": "Snapmaker TPE U1;Snapmaker TPE @U1 0.4 High Flow nozzle;Snapmaker TPE U1 0.4 High Flow nozzle", "inherits": "Snapmaker TPU @U1 base", "from": "system", "setting_id": "aqE9IR6dFd2MQLXR", "filament_id": "OFWTAEjH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "hot_plate_temp": [ "45" @@ -37,6 +39,10 @@ "" ], "filament_notes": [ - "eSUN eLastic TPE-83A\n" + "Standard nozzle: eSUN eLastic TPE-83A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.2 mm^3/s, from local standard fallback preset Snapmaker TPE @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 61b7ca0026..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 90A @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker TPU 90A @U1", - "from": "system", - "setting_id": "xAMUl2DDEuMrrzAs", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.2" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 0e85a008d6..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 90A @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker TPU 90A @U1 0.6 nozzle", - "from": "system", - "setting_id": "URpA1Br6ZxbbV0IF", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.2" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json index afb529e983..4f6e935751 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json @@ -1,6 +1,7 @@ { "type": "filament", "name": "Snapmaker TPU 90A @U1 0.6 nozzle", + "renamed_from": "Snapmaker TPU 90A U1 0.6 nozzle;Snapmaker TPU 90A @U1 0.6 High Flow nozzle;Snapmaker TPU 90A U1 0.6 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "uQCplebxmpBpzsnk", @@ -10,7 +11,8 @@ "3.2" ], "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -36,9 +38,6 @@ "during_print_exhaust_fan_speed": [ "70" ], - "enable_pressure_advance": [ - "0" - ], "filament_deretraction_speed": [ "20" ], @@ -146,5 +145,12 @@ ], "nozzle_temperature_range_low": [ "200" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json deleted file mode 100644 index bd3a88d1cd..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 90A @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker TPU 90A @U1 0.8 nozzle", - "from": "system", - "setting_id": "nECpLUQc8O9JE4em", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.5 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json index 8cf3452469..005cf1bf08 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json @@ -1,6 +1,7 @@ { "type": "filament", "name": "Snapmaker TPU 90A @U1 0.8 nozzle", + "renamed_from": "Snapmaker TPU 90A U1 0.8 nozzle;Snapmaker TPU 90A @U1 0.8 High Flow nozzle;Snapmaker TPU 90A U1 0.8 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "w4Hpl2qNPmgNvaeM", @@ -10,7 +11,8 @@ "3.5" ], "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -27,18 +29,12 @@ "pressure_advance": [ "0.02" ], - "additional_cooling_fan_speed": [ - "70" - ], "complete_print_exhaust_fan_speed": [ "70" ], "during_print_exhaust_fan_speed": [ "70" ], - "enable_pressure_advance": [ - "0" - ], "filament_deretraction_speed": [ "20" ], @@ -140,5 +136,12 @@ ], "nozzle_temperature_range_low": [ "200" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.5 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json index 8f6ba9663f..6579347154 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json @@ -1,6 +1,7 @@ { "type": "filament", "name": "Snapmaker TPU 90A @U1", + "renamed_from": "Snapmaker TPU 90A U1;Snapmaker TPU 90A @U1 0.4 High Flow nozzle;Snapmaker TPU 90A U1 0.4 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "T4MmgICRrSzvmCZ5", @@ -10,7 +11,8 @@ "3.2" ], "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -36,9 +38,6 @@ "during_print_exhaust_fan_speed": [ "100" ], - "enable_pressure_advance": [ - "0" - ], "filament_deretraction_speed": [ "15" ], @@ -63,9 +62,6 @@ "nozzle_temperature": [ "230" ], - "nozzle_temperature_initial_layer": [ - "230" - ], "slow_down_layer_time": [ "14" ], @@ -137,5 +133,12 @@ ], "nozzle_temperature_range_low": [ "200" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 391cec907f..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 95A @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker TPU 95A @U1", - "from": "system", - "setting_id": "Ywz3Y08u16nlPSuD", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "3.2" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from local standard fallback preset Snapmaker TPU 95A @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json index 59b125ff72..af1137ccca 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json @@ -1,12 +1,14 @@ { "type": "filament", "name": "Snapmaker TPU 95A @U1", + "renamed_from": "Snapmaker TPU 95A U1;Snapmaker TPU 95A @U1 0.4 High Flow nozzle;Snapmaker TPU 95A U1 0.4 High Flow nozzle", "inherits": "Snapmaker TPU 95A @U1 base", "from": "system", "setting_id": "XdvEzjOGtQKamDRC", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ "0" @@ -26,9 +28,6 @@ "adaptive_pressure_advance_overhangs": [ "0" ], - "additional_cooling_fan_speed": [ - "70" - ], "chamber_temperature": [ "0" ], @@ -98,9 +97,6 @@ "filament_end_gcode": [ "; filament end gcode \n" ], - "filament_flow_ratio": [ - "1" - ], "filament_is_support": [ "0" ], @@ -116,9 +112,6 @@ "filament_max_volumetric_speed": [ "3.2" ], - "filament_minimal_purge_on_wipe_tower": [ - "15" - ], "filament_multitool_ramming": [ "0" ], @@ -129,14 +122,11 @@ "10" ], "filament_notes": [ - "" + "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from local standard fallback preset Snapmaker TPU 95A @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], - "filament_retract_before_wipe": [ - "nil" - ], "filament_retract_lift_above": [ "nil" ], @@ -146,21 +136,12 @@ "filament_retract_lift_enforce": [ "nil" ], - "filament_retract_restart_extra": [ - "nil" - ], - "filament_retract_when_changing_layer": [ - "nil" - ], "filament_retraction_distances_when_cut": [ "nil" ], "filament_retraction_length": [ "1.4" ], - "filament_retraction_minimum_travel": [ - "nil" - ], "filament_retraction_speed": [ "30" ], @@ -197,12 +178,6 @@ "filament_vendor": [ "Snapmaker" ], - "filament_wipe": [ - "nil" - ], - "filament_wipe_distance": [ - "nil" - ], "filament_z_hop": [ "0.4" ], @@ -227,12 +202,6 @@ "nozzle_temperature_initial_layer": [ "225" ], - "nozzle_temperature_range_high": [ - "250" - ], - "nozzle_temperature_range_low": [ - "200" - ], "overhang_fan_speed": [ "100" ], @@ -280,5 +249,9 @@ ], "default_filament_colour": [ "" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json deleted file mode 100644 index f0b49ea018..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker TPU 95A HF @U1", - "from": "system", - "setting_id": "hVOeZYtFdD5f9n8N", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "9" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 9 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 198b8de558..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker TPU 95A HF @U1 0.6 nozzle", - "from": "system", - "setting_id": "gkRhqrvJIZh1o3wJ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "12" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json index b9619dcca5..b3d48fd8dd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker TPU 95A HF @U1 0.6 nozzle", + "renamed_from": "Snapmaker TPU 95A HF U1 0.6 nozzle;Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle;Snapmaker TPU 95A HF U1 0.6 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "Q2svXpkEqg0dCdq3", "filament_id": "OFXSvqOX", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -21,9 +23,6 @@ "filament_multitool_ramming_volume": [ "0.1" ], - "enable_pressure_advance": [ - "0" - ], "fan_cooling_layer_time": [ "100" ], @@ -143,5 +142,12 @@ ], "nozzle_temperature_range_low": [ "200" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 28374dc75a..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker TPU 95A HF @U1 0.8 nozzle", - "from": "system", - "setting_id": "ILlLnES7BQLAKxnH", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "16" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json index 4283ac2651..f72a73a7dc 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker TPU 95A HF @U1 0.8 nozzle", + "renamed_from": "Snapmaker TPU 95A HF U1 0.8 nozzle;Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle;Snapmaker TPU 95A HF U1 0.8 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "RjsE08h3l37cAkAs", "filament_id": "OFXSvqOX", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -21,9 +23,6 @@ "filament_multitool_ramming_volume": [ "0.1" ], - "enable_pressure_advance": [ - "0" - ], "fan_cooling_layer_time": [ "100" ], @@ -143,5 +142,12 @@ ], "nozzle_temperature_range_low": [ "200" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json index 816b78ceb8..f91214894c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json @@ -1,13 +1,15 @@ { "type": "filament", "name": "Snapmaker TPU 95A HF @U1", + "renamed_from": "Snapmaker TPU 95A HF U1;Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle;Snapmaker TPU 95A HF U1 0.4 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "KwcwmV2UmZUgCBoz", "filament_id": "OFXSvqOX", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -21,9 +23,6 @@ "filament_multitool_ramming_volume": [ "0.1" ], - "enable_pressure_advance": [ - "0" - ], "fan_cooling_layer_time": [ "40" ], @@ -60,9 +59,6 @@ "nozzle_temperature": [ "230" ], - "nozzle_temperature_initial_layer": [ - "230" - ], "overhang_fan_threshold": [ "25%" ], @@ -140,5 +136,12 @@ ], "nozzle_temperature_range_low": [ "200" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 9 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json deleted file mode 100644 index c976fd3bc9..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker TPU @U1", - "from": "system", - "setting_id": "fzFdqg3bAFNCOB7S", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "10.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 98b3b6a564..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker TPU @U1", - "from": "system", - "setting_id": "qABR1pUxRSY6jSAk", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "10.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json deleted file mode 100644 index 8f49d839c3..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker TPU @U1", - "from": "system", - "setting_id": "fgWdX41gAzkDoAW5", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "10.5" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json index ba393bed17..7a62ae80ec 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json @@ -1,6 +1,7 @@ { "type": "filament", "name": "Snapmaker TPU @U1", + "renamed_from": "Snapmaker TPU U1;Snapmaker TPU @U1 0.4 High Flow nozzle;Snapmaker TPU U1 0.4 High Flow nozzle;Snapmaker TPU @U1 0.6 High Flow nozzle;Snapmaker TPU U1 0.6 High Flow nozzle;Snapmaker TPU @U1 0.8 High Flow nozzle;Snapmaker TPU U1 0.8 High Flow nozzle", "inherits": "Snapmaker TPU @U1 base", "from": "system", "setting_id": "QLO4hkw6Agd3gSyR", @@ -8,6 +9,20 @@ "compatible_printers": [ "Snapmaker U1 (0.4 nozzle)", "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "filament_notes": [ + "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_max_volumetric_speed": [ + "2.8", + "10.5" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json deleted file mode 100644 index 3118864459..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle", - "inherits": "Snapmaker TPU High-Flow @U1", - "from": "system", - "setting_id": "UFqQnkKICuivJWiW", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.6" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json deleted file mode 100644 index 0c0a988c82..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle", - "inherits": "Snapmaker TPU High-Flow @U1", - "from": "system", - "setting_id": "E5Y9PFB8elCm2yGh", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.6" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json deleted file mode 100644 index ed24acdd57..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle", - "inherits": "Snapmaker TPU High-Flow @U1", - "from": "system", - "setting_id": "FXqeu1WlfeqwdbrM", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_max_volumetric_speed": [ - "7.6" - ], - "filament_notes": [ - "Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json index 0aca011924..949a230b9f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json @@ -1,6 +1,7 @@ { "type": "filament", "name": "Snapmaker TPU High-Flow @U1", + "renamed_from": "Snapmaker TPU High-Flow U1;Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle;Snapmaker TPU High-Flow U1 0.4 High Flow nozzle;Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle;Snapmaker TPU High-Flow U1 0.6 High Flow nozzle;Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle;Snapmaker TPU High-Flow U1 0.8 High Flow nozzle", "inherits": "Snapmaker TPU @U1 base", "from": "system", "setting_id": "5AXIg8WCdcd74S5v", @@ -9,7 +10,10 @@ "compatible_printers": [ "Snapmaker U1 (0.4 nozzle)", "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_flow_ratio": [ "0.983" @@ -27,6 +31,10 @@ "nil" ], "filament_notes": [ - "!! It needs to be dried before use.\nSnapmaker TPU 95A High-Flow\n" + "Standard nozzle: !! It needs to be dried before use.\nSnapmaker TPU 95A High-Flow\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + ], + "filament_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json index 692b06c904..042df8f861 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json @@ -9,11 +9,41 @@ "printer_variant": "0.2 High Flow", "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.2 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle" + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.2 nozzle" ], - "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" + "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", + "extruder_type": [ + "Direct Drive", + "Direct Drive", + "Direct Drive", + "Direct Drive" + ], + "extruder_variant_list": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "printer_extruder_id": [ + "1", + "2", + "3", + "4" + ], + "printer_extruder_variant": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "default_nozzle_volume_type": [ + "High Flow", + "High Flow", + "High Flow", + "High Flow" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json index 92bb62362b..1a8c1a0448 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json @@ -9,11 +9,41 @@ "printer_variant": "0.4 High Flow", "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.4 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle" + "Snapmaker PLA SnapSpeed @U1", + "Snapmaker PLA SnapSpeed @U1", + "Snapmaker PLA SnapSpeed @U1", + "Snapmaker PLA SnapSpeed @U1" ], - "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" + "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", + "extruder_type": [ + "Direct Drive", + "Direct Drive", + "Direct Drive", + "Direct Drive" + ], + "extruder_variant_list": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "printer_extruder_id": [ + "1", + "2", + "3", + "4" + ], + "printer_extruder_variant": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "default_nozzle_volume_type": [ + "High Flow", + "High Flow", + "High Flow", + "High Flow" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json index 81844e4a64..07d651e5c4 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json @@ -9,11 +9,41 @@ "printer_variant": "0.6 High Flow", "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.6 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle" + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.6 nozzle" ], - "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" + "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", + "extruder_type": [ + "Direct Drive", + "Direct Drive", + "Direct Drive", + "Direct Drive" + ], + "extruder_variant_list": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "printer_extruder_id": [ + "1", + "2", + "3", + "4" + ], + "printer_extruder_variant": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "default_nozzle_volume_type": [ + "High Flow", + "High Flow", + "High Flow", + "High Flow" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json index 2b357df990..6730524bc1 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json @@ -9,11 +9,41 @@ "printer_variant": "0.8 High Flow", "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.8 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle" + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", + "Snapmaker PLA SnapSpeed @U1 0.8 nozzle" ], - "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n" + "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", + "extruder_type": [ + "Direct Drive", + "Direct Drive", + "Direct Drive", + "Direct Drive" + ], + "extruder_variant_list": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "printer_extruder_id": [ + "1", + "2", + "3", + "4" + ], + "printer_extruder_variant": [ + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow", + "Direct Drive High Flow" + ], + "default_nozzle_volume_type": [ + "High Flow", + "High Flow", + "High Flow", + "High Flow" + ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1.json b/resources/profiles/Snapmaker/machine/Snapmaker U1.json index 532e3a22bf..3fa02cde23 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1.json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1.json @@ -5,7 +5,7 @@ "family": "Snapmaker", "url": "", "model_id": "797581801", - "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle;Snapmaker PLA SnapSpeed @U1;Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle;Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle;Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle;Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle", + "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle;Snapmaker PLA SnapSpeed @U1", "bed_model": "Snapmaker U1_bed.stl", "bed_texture": "Snapmaker U1_texture.svg", "nozzle_diameter": "0.2;0.4;0.4+0.6;0.6;0.8;0.2 High Flow;0.4 High Flow;0.6 High Flow;0.8 High Flow" diff --git a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index 3deab05cdd..0000000000 --- a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.06 High Quality @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "LFjXyYMCx6Oy12PQ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json index 15546ce4bb..47dc63e06d 100644 --- a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -1,18 +1,33 @@ { "type": "process", "name": "0.06 High Quality @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.06 High Quality Snapmaker U1 (0.2 nozzle);0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle);0.06 High Quality Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.06_nozzle_0.2", "from": "system", "setting_id": "3SCofR6VrVyo3vJp", "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in minimal layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": "4000", + "default_acceleration": [ + "4000" + ], "elefant_foot_compensation": "0.15", - "outer_wall_acceleration": "2000", - "outer_wall_speed": "60", + "outer_wall_acceleration": [ + "2000" + ], + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_pattern": "gyroid", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -20,10 +35,62 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "13.5", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index f48dfa5c3b..0000000000 --- a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.06 Standard @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "nMJ1UXr4uyWr62Nx", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json index 3881460bda..710f8d2547 100644 --- a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.06 Standard @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.06 Standard Snapmaker U1 (0.2 nozzle);0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.06 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.06_nozzle_0.2", "from": "system", "setting_id": "997RaiNHd2YhpaRB", @@ -8,7 +9,8 @@ "description": "Compared with the default profile of 0.2 mm nozzle, it has a smaller layer height, and results in minimal layer lines and higher printing quality, but shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -16,10 +18,72 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "13.5", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index 8fd4c1cbe7..0000000000 --- a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.08 Extra Fine @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "iyzOhKe7WcJvxmQu", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json index 0a3ba43c86..821225907e 100644 --- a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json @@ -1,28 +1,91 @@ { "type": "process", "name": "0.08 Extra Fine @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.08 Extra Fine Snapmaker U1 (0.4 nozzle);0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle);0.08 Extra Fine Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.08", "from": "system", "setting_id": "Tayg0K1WRyJMLoke", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, and results in almost negligible layer lines and longer printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", "wipe_tower_cone_angle": "15", "wipe_tower_extra_spacing": "120%", "enable_arc_fitting": "0", - "outer_wall_speed": "100", + "outer_wall_speed": [ + "100", + "500", + "100", + "500", + "100", + "500", + "100", + "500" + ], "precise_outer_wall": "1", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "450", + "600", + "450", + "600", + "450", + "600", + "450", + "600" + ], + "inner_wall_speed": [ + "350", + "600", + "350", + "600", + "350", + "600", + "350", + "600" + ], + "internal_solid_infill_speed": [ + "350", + "600", + "350", + "600", + "350", + "600", + "350", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index b9e42b5d79..0000000000 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.08 High Quality @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "CaOHxJpx23e32eZm", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json index 2aeb309d58..bff8ea47e6 100644 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -1,18 +1,33 @@ { "type": "process", "name": "0.08 High Quality @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.08 High Quality Snapmaker U1 (0.2 nozzle);0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle);0.08 High Quality Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.08_nozzle_0.2", "from": "system", "setting_id": "6ExpMU3Wq4J1R7wy", "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost invisible layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": "4000", + "default_acceleration": [ + "4000" + ], "elefant_foot_compensation": "0.15", - "outer_wall_acceleration": "2000", - "outer_wall_speed": "60", + "outer_wall_acceleration": [ + "2000" + ], + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_pattern": "gyroid", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -20,10 +35,62 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index a7141e48f1..0000000000 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.08 High Quality @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "3Jsj1rKIzF9dhszS", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json index 107731b2cb..8b12c1409d 100644 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,27 +1,75 @@ { "type": "process", "name": "0.08 High Quality @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.08 High Quality Snapmaker U1 (0.4 nozzle);0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle);0.08 High Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.08", "from": "system", "setting_id": "Bld9ulv0wFza7OCb", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost negligible layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": "4000", - "gap_infill_speed": "210", - "inner_wall_speed": "120", - "internal_solid_infill_speed": "150", - "outer_wall_acceleration": "2000", - "outer_wall_speed": "60", + "default_acceleration": [ + "4000" + ], + "gap_infill_speed": [ + "210" + ], + "inner_wall_speed": [ + "120", + "600", + "120", + "600", + "120", + "600", + "120", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "outer_wall_acceleration": [ + "2000" + ], + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_pattern": "gyroid", - "sparse_infill_speed": "150", - "top_surface_speed": "150", + "sparse_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "top_surface_speed": [ + "150" + ], "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -32,5 +80,25 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ] } diff --git a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index c81c177341..0000000000 --- a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.08 Standard @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "Dk5x3oI8wrO1SYDF", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json index ea8c719085..fc2378404f 100644 --- a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.08 Standard @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.08 Standard Snapmaker U1 (0.2 nozzle);0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.08 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.08_nozzle_0.2", "from": "system", "setting_id": "loFrCYH9ux2L5JpZ", @@ -8,7 +9,8 @@ "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer height, and results in almost invisible layer lines and higher printing quality, but shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -16,10 +18,72 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index ab2f3430c6..0000000000 --- a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.10 High Quality @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "AEdUWWIQuuIvPX48", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json index 6fc26cd55f..80a6b49330 100644 --- a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -1,18 +1,33 @@ { "type": "process", "name": "0.10 High Quality @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.10 High Quality Snapmaker U1 (0.2 nozzle);0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle);0.10 High Quality Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.10_nozzle_0.2", "from": "system", "setting_id": "BfLpmnJnSBYBFInR", "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in much higher printing quality, but a much longer printing time.", - "default_acceleration": "4000", + "default_acceleration": [ + "4000" + ], "elefant_foot_compensation": "0.15", - "outer_wall_acceleration": "2000", - "outer_wall_speed": "60", + "outer_wall_acceleration": [ + "2000" + ], + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_pattern": "gyroid", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -20,10 +35,62 @@ "prime_tower_brim_width": "5", "prime_tower_width": "28", "prime_volume": "25", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index 9dbcbb12da..0000000000 --- a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "k5ltb2fjXfHavuJl", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json index d33ce1c083..8bd076ec2c 100644 --- a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.10 Standard Snapmaker U1 (0.2 nozzle);0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.10 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.10_nozzle_0.2", "from": "system", "setting_id": "SOq962UWv1ml1Mk0", @@ -8,7 +9,8 @@ "description": "It has a small layer height, and results in almost negligible layer lines and high printing quality. It is suitable for most general printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -23,5 +25,67 @@ "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", "wipe_tower_wall_type": "rib", - "slowdown_for_curled_perimeters": "0" + "slowdown_for_curled_perimeters": [ + "0" + ], + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index 77bb9d98a9..0000000000 --- a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.12 Fine @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "68ArVT2y9MR8cLZ1", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json index 6de6d483da..5b82a0021a 100644 --- a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json @@ -1,16 +1,20 @@ { "type": "process", "name": "0.12 Fine @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.12 Fine Snapmaker U1 (0.4 nozzle);0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle);0.12 Fine Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.12", "from": "system", "setting_id": "xBU1j8ldOefYfosp", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, and results in almost negligible layer lines and higher printing quality, but longer printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -24,5 +28,65 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "430", + "600", + "430", + "600", + "430", + "600", + "430", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "350", + "600", + "350", + "600", + "350", + "600", + "350", + "600" + ], + "internal_solid_infill_speed": [ + "350", + "600", + "350", + "600", + "350", + "600", + "350", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index e052a9e517..0000000000 --- a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.12 High Quality @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "m9GIExQJEdmIA0t2", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json index a5f215d7f3..7de2bca333 100644 --- a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,26 +1,74 @@ { "type": "process", "name": "0.12 High Quality @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.12 High Quality Snapmaker U1 (0.4 nozzle);0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle);0.12 High Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.12", "from": "system", "setting_id": "WNWFsx5DJtCRAlFl", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost negligible layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": "4000", - "gap_infill_speed": "230", - "inner_wall_speed": "150", - "internal_solid_infill_speed": "180", - "outer_wall_acceleration": "2000", - "outer_wall_speed": "60", + "default_acceleration": [ + "4000" + ], + "gap_infill_speed": [ + "230" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "180", + "600", + "180", + "600", + "180", + "600", + "180", + "600" + ], + "outer_wall_acceleration": [ + "2000" + ], + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_pattern": "gyroid", - "sparse_infill_speed": "180", - "top_surface_speed": "150", + "sparse_infill_speed": [ + "180", + "600", + "180", + "600", + "180", + "600", + "180", + "600" + ], + "top_surface_speed": [ + "150" + ], "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "prime_tower_width": "30", "prime_volume": "27", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "wipe_tower_filament": "0", "prime_tower_brim_width": "5", "wipe_tower_cone_angle": "15", @@ -33,5 +81,25 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ] } diff --git a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index dc46dd6014..0000000000 --- a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.12 Standard @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "VYRDtZXoZWqcHBJP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json index 632d9614f7..20f1c91813 100644 --- a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.12 Standard @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.12 Standard Snapmaker U1 (0.2 nozzle);0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.12 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.12_nozzle_0.2", "from": "system", "setting_id": "KHC1zpxpuvaUEzvf", @@ -8,7 +9,8 @@ "description": "Compared with the default profile of a 0.2 mm nozzle, it has a slightly bigger layer height, and results in almost negligible layer lines, and slightly shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -16,10 +18,72 @@ "prime_tower_brim_width": "5", "prime_tower_width": "28", "prime_volume": "27", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index 08451b8fa7..0000000000 --- a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "0.14 Standard @Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "NPQCD2bScfCwZAGP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json index 0de823db1f..294b1329f3 100644 --- a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.14 Standard @Snapmaker U1 (0.2 nozzle)", + "renamed_from": "0.14 Standard Snapmaker U1 (0.2 nozzle);0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.14 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.14_nozzle_0.2", "from": "system", "setting_id": "NGJoV0n6T510y9wM", @@ -8,7 +9,8 @@ "description": "Compared with the default profile of a 0.2 mm nozzle, it has a bigger layer height, and results in slightly visible layer lines, but shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" + "Snapmaker U1 (0.2 nozzle)", + "Snapmaker U1 (0.2 High Flow nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -16,10 +18,72 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "32", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index cbf3b13b75..0000000000 --- a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.16 High Quality @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "Wkl0CFo4qXv79E1W", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json index 1306485d6f..8ecdd89b6e 100644 --- a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,27 +1,75 @@ { "type": "process", "name": "0.16 High Quality @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.16 High Quality Snapmaker U1 (0.4 nozzle);0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle);0.16 High Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.16", "from": "system", "setting_id": "Ofe17WrGQ3MOrqNe", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in less apparent layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": "4000", - "gap_infill_speed": "250", - "inner_wall_speed": "150", - "internal_solid_infill_speed": "200", - "outer_wall_acceleration": "2000", - "outer_wall_speed": "60", + "default_acceleration": [ + "4000" + ], + "gap_infill_speed": [ + "250" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "outer_wall_acceleration": [ + "2000" + ], + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_pattern": "gyroid", - "sparse_infill_speed": "200", - "top_surface_speed": "150", + "sparse_infill_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "top_surface_speed": [ + "150" + ], "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "36", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -33,5 +81,25 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ] } diff --git a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index 9ed96478f2..0000000000 --- a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.16 Optimal @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "xGaLQMk52ZD5ZqJh", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json index 838f250e8c..bca9e8d7fb 100644 --- a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json @@ -1,18 +1,22 @@ { "type": "process", "name": "0.16 Optimal @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.16 Optimal Snapmaker U1 (0.4 nozzle);0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle);0.16 Optimal Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.16", "from": "system", "setting_id": "ERcgRLp5bQo1hgz8", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and higher printing quality, but longer printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "36", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -23,5 +27,65 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "330", + "600", + "330", + "600", + "330", + "600", + "330", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 9410d42ef5..0000000000 --- a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.18 Standard @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "aW5X8f9lb5haKlWj", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json index 88fbb33812..b2c06523ac 100644 --- a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.18 Standard @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.18 Standard Snapmaker U1 (0.6 nozzle);0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.18 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.18_nozzle_0.6", "from": "system", "setting_id": "XGSrVgnsUFv6uCRs", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and higher printing quality, but longer printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "layer_height": "0.25", "prime_tower_brim_width": "5", "prime_tower_width": "30", @@ -24,7 +28,69 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index 1a680858b1..0000000000 --- a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.20 Quality @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "g8JVHkdQOCWRZZf5", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json index be894aa221..87a70ad4bb 100644 --- a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,21 +1,85 @@ { "type": "process", "name": "0.20 Quality @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.20 Quality Snapmaker U1 (0.4 nozzle);0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle);0.20 Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "BBXxXQCXCme2Mvy0", "instantiation": "true", "description": "It has a general layer height, and results in general layer lines and printing quality. It is suitable for most general printing cases.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", "wipe_tower_cone_angle": "15", "prime_tower_brim_width": "5", - "initial_layer_print_height": "0.25" + "initial_layer_print_height": "0.25", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "270", + "600", + "270", + "600", + "270", + "600", + "270", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "250", + "600", + "250", + "600", + "250", + "600", + "250", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index de45cf42f9..0000000000 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "5EHp34T55kiTtt68", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json index 443afe55dd..532737eb20 100644 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json @@ -1,22 +1,86 @@ { "type": "process", "name": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.20 Standard Snapmaker U1 (0.4 nozzle);0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle);0.20 Standard Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "XR5r9SuWmSag5Qjo", "instantiation": "true", "description": "It has a general layer height, and results in general layer lines and printing quality. It is suitable for most general printing cases.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "45", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", "wipe_tower_cone_angle": "15", "prime_tower_brim_width": "5", - "initial_layer_print_height": "0.25" + "initial_layer_print_height": "0.25", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "270", + "600", + "270", + "600", + "270", + "600", + "270", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "250", + "600", + "250", + "600", + "250", + "600", + "250", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index dbaa4cac78..0000000000 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.20 Standard @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "l3KpKbqxUGKTjO0t", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json index 5c87ff68d0..d605622b96 100644 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.20 Standard @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.20 Standard Snapmaker U1 (0.6 nozzle);0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.20 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.6_common", "from": "system", "setting_id": "6VM4QhgvHUmyf2um", @@ -8,11 +9,14 @@ "description": "Standard 0.2 mm layer height profile for the Snapmaker U1 with 0.6 mm nozzles. Balances print speed and surface quality for everyday prints.", "layer_height": "0.20", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -20,5 +24,65 @@ "prime_tower_brim_width": "5", "initial_layer_print_height": "0.25", "top_shell_layers": "4", - "bottom_shell_layers": "3" + "bottom_shell_layers": "3", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "250", + "600", + "250", + "600", + "250", + "600", + "250", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index cc1c08848c..0000000000 --- a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.20 Strength @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "jTYxPrkfNAXXeElO", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json index 1d051fc801..cc36e8f20f 100644 --- a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json @@ -1,21 +1,34 @@ { "type": "process", "name": "0.20 Strength @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.20 Strength Snapmaker U1 (0.4 nozzle);0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle);0.20 Strength Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "bYnMOZxTq83EwIyd", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has more wall loops and a higher sparse infill density. So, it results in higher strength of the prints, but more filament consumption and longer printing time.", - "outer_wall_speed": "60", + "outer_wall_speed": [ + "60", + "500", + "60", + "500", + "60", + "500", + "60", + "500" + ], "sparse_infill_density": "25%", "wall_loops": "6", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "45", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -26,5 +39,55 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "270", + "600", + "270", + "600", + "270", + "600", + "270", + "600" + ], + "inner_wall_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "250", + "600", + "250", + "600", + "250", + "600", + "250", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index d305323789..0000000000 --- a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.20 Support @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.20 Support @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "o2uNeeRv8JNXAuD0", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json index 577065ca3f..459f3afefa 100644 --- a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json @@ -1,18 +1,22 @@ { "type": "process", "name": "0.20 Support @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.20 Support Snapmaker U1 (0.4 nozzle);0.20 Support @Snapmaker U1 (0.4 High Flow nozzle);0.20 Support Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "4whjHJVCN44w8SPE", "instantiation": "true", "description": "It has a general layer height, and results in general layer lines and printing quality. It is suitable for most general printing cases.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "40", "prime_volume": "15", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -21,5 +25,65 @@ "enable_support": "1", "initial_layer_print_height": "0.25", "support_interface_spacing": "0", - "support_interface_top_layers": "3" + "support_interface_top_layers": "3", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "270", + "600", + "270", + "600", + "270", + "600", + "270", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "250", + "600", + "250", + "600", + "250", + "600", + "250", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index 6bbd70ca8b..0000000000 --- a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.20 Support W @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "rLdRXBKLSDdjsSs9", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json index 3d1887bea9..6cfc2cba6d 100644 --- a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.20 Support W @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.20 Support W Snapmaker U1 (0.4 nozzle);0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle);0.20 Support W Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "M9JhI2roLcP4jYks", @@ -10,16 +11,78 @@ "support_top_z_distance": "0.2", "support_interface_loop_pattern": "1", "support_interface_spacing": "0", - "support_interface_speed": "80", "support_filament": "0", "support_interface_filament": "0", "enable_prime_tower": "1", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "50", "prime_volume": "38", - "slowdown_for_curled_perimeters": "0", - "standby_temperature_delta": "-150" + "slowdown_for_curled_perimeters": [ + "0" + ], + "standby_temperature_delta": "-150", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "270", + "600", + "270", + "600", + "270", + "600", + "270", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "250", + "600", + "250", + "600", + "250", + "600", + "250", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index ea99859bc7..0000000000 --- a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.24 Draft @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "cZfx1x9K6oYUG6nK", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json index 8e09376daf..90c2cb8f5c 100644 --- a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json @@ -1,18 +1,22 @@ { "type": "process", "name": "0.24 Draft @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.24 Draft Snapmaker U1 (0.4 nozzle);0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle);0.24 Draft Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.24", "from": "system", "setting_id": "MYRo7jTenu7F0Zv4", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a bigger layer height, and results in more apparent layer lines and lower printing quality, but slightly shorter printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "54", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -23,5 +27,65 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "230", + "600", + "230", + "600", + "230", + "600", + "230", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "230", + "600", + "230", + "600", + "230", + "600", + "230", + "600" + ], + "internal_solid_infill_speed": [ + "230", + "600", + "230", + "600", + "230", + "600", + "230", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 1072a08efa..0000000000 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.24 Standard @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "PvF43C7HiDhd7BKg", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json index 2fa2eeabdb..883fef3cc2 100644 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.24 Standard @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.24 Standard Snapmaker U1 (0.6 nozzle);0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.24 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.24_nozzle_0.6", "from": "system", "setting_id": "BziJYuA5U5gWm6FM", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and slight higher printing quality, but longer printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "55", @@ -23,8 +27,70 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json deleted file mode 100644 index 7844a38bf1..0000000000 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "inherits": "0.24 Standard @Snapmaker U1 (0.8 nozzle)", - "from": "system", - "setting_id": "FAVs0dNRa86OWdzy", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json index 316bb0dbee..a47d78f04e 100644 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.24 Standard @Snapmaker U1 (0.8 nozzle)", + "renamed_from": "0.24 Standard Snapmaker U1 (0.8 nozzle);0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.24 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.24_nozzle_0.8", "from": "system", "setting_id": "QcJ5p9h0eYUb91ak", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and slight higher printing quality, but longer printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "min_width_top_surface": "90", "prime_tower_brim_width": "5", "prime_tower_width": "30", @@ -24,7 +28,69 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index bc50c410c5..0000000000 --- a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.25 Benchy @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "21Yht5aHJVG0XebG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json index c7d7abf58a..ad0960c02a 100644 --- a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json @@ -1,29 +1,83 @@ { "type": "process", "name": "0.25 Benchy @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.25 Benchy Snapmaker U1 (0.4 nozzle);0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle);0.25 Benchy Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1", "from": "system", "setting_id": "deA4Xg6QxFTKOYZy", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "elefant_foot_compensation": "0.15", "top_shell_layers": "3", "top_shell_thickness": "0.5", - "initial_layer_speed": "100", - "initial_layer_infill_speed": "180", - "outer_wall_speed": "180", - "inner_wall_speed": "200", - "sparse_infill_speed": "200", - "internal_solid_infill_speed": "200", - "top_surface_speed": "180", - "gap_infill_speed": "200", + "initial_layer_speed": [ + "100" + ], + "initial_layer_infill_speed": [ + "180" + ], + "outer_wall_speed": [ + "180", + "500", + "180", + "500", + "180", + "500", + "180", + "500" + ], + "inner_wall_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "sparse_infill_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "internal_solid_infill_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "top_surface_speed": [ + "180" + ], + "gap_infill_speed": [ + "200" + ], "ironing_speed": "30", - "enable_overhang_speed": "0", - "overhang_1_4_speed": "180", - "overhang_2_4_speed": "20", - "overhang_3_4_speed": "10", + "enable_overhang_speed": [ + "0" + ], + "overhang_1_4_speed": [ + "180" + ], + "overhang_2_4_speed": [ + "20" + ], + "overhang_3_4_speed": [ + "10" + ], "layer_height": "0.25", "initial_layer_print_height": "0.25", "detect_overhang_wall": "0", @@ -45,15 +99,57 @@ "sparse_infill_line_width": "0.5", "top_surface_line_width": "0.5", "bottom_shell_layers": "2", - "initial_layer_travel_speed": "100%", - "bridge_acceleration": "3000", - "bridge_speed": "100", - "default_acceleration": "3000", - "inner_wall_acceleration": "3000", - "internal_solid_infill_acceleration": "3000", - "outer_wall_acceleration": "1600", - "small_perimeter_speed": "200", - "sparse_infill_acceleration": "3000", - "top_surface_acceleration": "3000", - "initial_layer_acceleration": "1600" + "initial_layer_travel_speed": [ + "100%" + ], + "bridge_acceleration": [ + "3000" + ], + "bridge_speed": [ + "100" + ], + "default_acceleration": [ + "3000" + ], + "inner_wall_acceleration": [ + "3000" + ], + "internal_solid_infill_acceleration": [ + "3000" + ], + "outer_wall_acceleration": [ + "1600" + ], + "small_perimeter_speed": [ + "200" + ], + "sparse_infill_acceleration": [ + "3000" + ], + "top_surface_acceleration": [ + "3000" + ], + "initial_layer_acceleration": [ + "1600" + ], + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ] } diff --git a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index bd58038ed9..0000000000 --- a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "0.28 Extra Draft @Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "ofB4cQcZJ2Lkze83", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json index 8829efafdb..93a4f26ef5 100644 --- a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json @@ -1,17 +1,21 @@ { "type": "process", "name": "0.28 Extra Draft @Snapmaker U1 (0.4 nozzle)", + "renamed_from": "0.28 Extra Draft Snapmaker U1 (0.4 nozzle);0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle);0.28 Extra Draft Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.28", "from": "system", "setting_id": "0fSeyAIS6z75A1r3", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a bigger layer height, and results in more apparent layer lines and lower printing quality, but shorter printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)" + "Snapmaker U1 (0.4 nozzle)", + "Snapmaker U1 (0.4 High Flow nozzle)" ], "prime_tower_width": "30", "prime_volume": "63", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "wipe_tower_filament": "0", "prime_tower_brim_width": "5", "wipe_tower_cone_angle": "15", @@ -23,5 +27,65 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom" + "gap_fill_target": "topbottom", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "outer_wall_speed": [ + "200", + "500", + "200", + "500", + "200", + "500", + "200", + "500" + ], + "inner_wall_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ], + "internal_solid_infill_speed": [ + "200", + "600", + "200", + "600", + "200", + "600", + "200", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 00494a3516..0000000000 --- a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.30 Draft @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "K3aD6VMSmWStekaW", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json index defbc6740b..1c5fa45ffd 100644 --- a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.30 Draft @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.30 Draft Snapmaker U1 (0.6 nozzle);0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle);0.30 Draft Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.6_common", "from": "system", "setting_id": "Tk3tG3RGJ8dwXFKf", @@ -8,12 +9,15 @@ "description": "Fast draft profile for the Snapmaker U1 with 0.6 mm nozzles. 0.3 mm layer height for quick prototypes and functional parts.", "layer_height": "0.30", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "68", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -22,8 +26,64 @@ "initial_layer_print_height": "0.30", "top_shell_layers": "3", "bottom_shell_layers": "3", - "outer_wall_speed": "220", - "inner_wall_speed": "330", - "sparse_infill_speed": "300", - "internal_solid_infill_speed": "280" + "outer_wall_speed": [ + "220", + "500", + "220", + "500", + "220", + "500", + "220", + "500" + ], + "inner_wall_speed": [ + "330", + "600", + "330", + "600", + "330", + "600", + "330", + "600" + ], + "sparse_infill_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "internal_solid_infill_speed": [ + "280", + "600", + "280", + "600", + "280", + "600", + "280", + "600" + ], + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ] } diff --git a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 46de759eb1..0000000000 --- a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "G8jjRPwut6AkuW7w", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json index 92bebf6219..43833fe9c1 100644 --- a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.30 Standard Snapmaker U1 (0.6 nozzle);0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.30 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.30_nozzle_0.6", "from": "system", "setting_id": "TWxp6qgz1DJa8Ngo", @@ -8,11 +9,14 @@ "description": "It has a big layer height, and results in apparent layer lines and ordinary printing quality and printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "68", @@ -22,8 +26,70 @@ "wipe_tower_wall_type": "rib", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 40e31b320f..0000000000 --- a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.30 Strength @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "4S1DO4sXQX2RXSjC", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json index f268a92446..568e838002 100644 --- a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.30 Strength @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.30 Strength Snapmaker U1 (0.6 nozzle);0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle);0.30 Strength Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.30_nozzle_0.6", "from": "system", "setting_id": "CBaL0vgwtgsd6Snj", @@ -10,11 +11,14 @@ "sparse_infill_density": "25%", "wall_loops": "4", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_width": "30", "prime_volume": "68", "seam_gap": "15%", @@ -24,8 +28,70 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "prime_tower_brim_width": "5", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_type": "tree(auto)", - "wipe_tower_extra_spacing": "120%" + "wipe_tower_extra_spacing": "120%", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json deleted file mode 100644 index c74cf8b309..0000000000 --- a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "inherits": "0.32 Standard @Snapmaker U1 (0.8 nozzle)", - "from": "system", - "setting_id": "6k3IeQoysYKUxTxG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json index de7559f827..010c82bb91 100644 --- a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.32 Standard @Snapmaker U1 (0.8 nozzle)", + "renamed_from": "0.32 Standard Snapmaker U1 (0.8 nozzle);0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.32 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.32_nozzle_0.8", "from": "system", "setting_id": "iVRz2B0VBIBL1xVd", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.8 mm nozzle, it has a slightly smaller layer height, and results in slightly less but still apparent layer lines and slightly higher printing quality, but longer printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "72", @@ -25,8 +29,70 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index aab911513d..0000000000 --- a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.36 Standard @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "4c5vbxTAK6TVyTpg", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json index 6dc9f26329..ee1f213106 100644 --- a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.36 Standard @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.36 Standard Snapmaker U1 (0.6 nozzle);0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.36 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.36_nozzle_0.6", "from": "system", "setting_id": "C0PiCFd3P222knX9", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a bigger layer height, and results in more apparent layer lines and lower printing quality, but shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "81", @@ -23,8 +27,70 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 3d3a341e2b..0000000000 --- a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.40 Extra Draft @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "vWhACedmOjulskG0", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json index b18e30c210..ab4831845f 100644 --- a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.40 Extra Draft @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.40 Extra Draft Snapmaker U1 (0.6 nozzle);0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle);0.40 Extra Draft Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.6_common", "from": "system", "setting_id": "rAUffOThYmj9FVTw", @@ -8,12 +9,15 @@ "description": "Maximum-speed profile for the Snapmaker U1 with 0.6 mm nozzles. 0.4 mm layer height for rapid prototyping where surface finish is not critical.", "layer_height": "0.40", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "90", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -22,9 +26,65 @@ "initial_layer_print_height": "0.35", "top_shell_layers": "3", "bottom_shell_layers": "3", - "outer_wall_speed": "220", - "inner_wall_speed": "330", - "sparse_infill_speed": "320", - "internal_solid_infill_speed": "300", - "wall_loops": "2" + "outer_wall_speed": [ + "220", + "500", + "220", + "500", + "220", + "500", + "220", + "500" + ], + "inner_wall_speed": [ + "330", + "600", + "330", + "600", + "330", + "600", + "330", + "600" + ], + "sparse_infill_speed": [ + "320", + "600", + "320", + "600", + "320", + "600", + "320", + "600" + ], + "internal_solid_infill_speed": [ + "300", + "600", + "300", + "600", + "300", + "600", + "300", + "600" + ], + "wall_loops": "2", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ] } diff --git a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json deleted file mode 100644 index 1981348438..0000000000 --- a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "inherits": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", - "from": "system", - "setting_id": "YEOmDIj6IFBHwvTr", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json index b0cf935465..a935192df9 100644 --- a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", + "renamed_from": "0.40 Standard Snapmaker U1 (0.8 nozzle);0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.40 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.40_nozzle_0.8", "from": "system", "setting_id": "VqkPgUtZi159BKpJ", @@ -8,11 +9,14 @@ "description": "It has a very big layer height, and results in very apparent layer lines, low printing quality and general printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "90", @@ -26,8 +30,70 @@ "min_width_top_surface": "200%", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "40", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index cca40610a9..0000000000 --- a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "0.42 Standard @Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "kXJPwKEtZ6kSTvcm", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json index eaa04faadc..705021d73d 100644 --- a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.42 Standard @Snapmaker U1 (0.6 nozzle)", + "renamed_from": "0.42 Standard Snapmaker U1 (0.6 nozzle);0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.42 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.42_nozzle_0.6", "from": "system", "setting_id": "SVJzhQA3KQetRZmo", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a bigger layer height, and results in much more apparent layer lines and much lower printing quality, but shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" + "Snapmaker U1 (0.6 nozzle)", + "Snapmaker U1 (0.6 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "precise_z_height": "0", "prime_tower_brim_width": "5", "prime_tower_width": "30", @@ -23,8 +27,70 @@ "wipe_tower_wall_type": "rib", "enable_arc_fitting": "0", "ooze_prevention": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json deleted file mode 100644 index b976586099..0000000000 --- a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "inherits": "0.48 Standard @Snapmaker U1 (0.8 nozzle)", - "from": "system", - "setting_id": "BTuUnaHxKcjQlOv9", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json index 599cf24db5..c0585abda9 100644 --- a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.48 Standard @Snapmaker U1 (0.8 nozzle)", + "renamed_from": "0.48 Standard Snapmaker U1 (0.8 nozzle);0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.48 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.48_nozzle_0.8", "from": "system", "setting_id": "5H6JWChFEo3zqoHQ", @@ -8,11 +9,14 @@ "description": "Compared with the default profile of a 0.8 mm nozzle, it has a bigger layer height, and results in very apparent layer lines and much lower printing quality, but shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", + "internal_bridge_speed": [ + "100%" + ], "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "108", @@ -23,8 +27,70 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json deleted file mode 100644 index 091c301acf..0000000000 --- a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle).json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "process", - "name": "0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle)", - "inherits": "0.56 Standard @Snapmaker U1 (0.8 nozzle)", - "from": "system", - "setting_id": "VrtVj7ZWFPegxXaW", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "inner_wall_speed": [ - "600" - ], - "outer_wall_speed": [ - "500" - ], - "internal_solid_infill_speed": [ - "600" - ], - "sparse_infill_speed": [ - "600" - ] -} diff --git a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json index ca728fe66a..22beae020e 100644 --- a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,6 +1,7 @@ { "type": "process", "name": "0.56 Standard @Snapmaker U1 (0.8 nozzle)", + "renamed_from": "0.56 Standard Snapmaker U1 (0.8 nozzle);0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.56 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.56_nozzle_0.8", "from": "system", "setting_id": "JZEIXOUT33c7MrZj", @@ -8,7 +9,8 @@ "description": "Compared with the default profile of a 0.8 mm nozzle, it has a much bigger layer height, and results in extremely apparent layer lines and much lower printing quality, but much shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)" + "Snapmaker U1 (0.8 nozzle)", + "Snapmaker U1 (0.8 High Flow nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", @@ -22,8 +24,70 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": "0", + "slowdown_for_curled_perimeters": [ + "0" + ], "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)" + "support_type": "tree(auto)", + "print_extruder_variant": [ + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow", + "Direct Drive Standard", + "Direct Drive High Flow" + ], + "print_extruder_id": [ + "1", + "1", + "2", + "2", + "3", + "3", + "4", + "4" + ], + "sparse_infill_speed": [ + "100", + "600", + "100", + "600", + "100", + "600", + "100", + "600" + ], + "outer_wall_speed": [ + "120", + "500", + "120", + "500", + "120", + "500", + "120", + "500" + ], + "inner_wall_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ], + "internal_solid_infill_speed": [ + "150", + "600", + "150", + "600", + "150", + "600", + "150", + "600" + ] } diff --git a/scripts/orca_profile_tool.py b/scripts/orca_profile_tool.py index f7f622c8c0..6728c47127 100755 --- a/scripts/orca_profile_tool.py +++ b/scripts/orca_profile_tool.py @@ -1757,18 +1757,20 @@ def check_variant_names(profiles_dir, vendor): def check_variant_arrays(profiles_dir, vendor, strict=False): - """Every variant array an instantiated preset writes is exactly its - variant length x stride wide, and a printer's variant layout keys agree. + """Variant arrays have the declared width or a shared single value, + and a printer's variant layout keys agree. Only instantiated presets are judged, each at the variant length of its composed config (_variant_length's); a base is not, since what it writes counts only where it reaches a preset that does not override it. A full-width array holds one - value per variant; any other width is an error, one value included and whatever - the values. A key the preset does not write takes what reaches it - the default, + value per variant; a single value at stride 1 broadcasts to every variant. + Short, nonempty filament arrays use element zero for missing variants, + matching the C++ loader. Other widths are errors. A key the preset does not write + takes what reaches it - the default, or an array it inherits or includes - which the loader sizes to the preset, and is not checked. With strict, every instantiated preset also holds each key that - reaches it at its own width, so a preset with other variants than the file its - array comes from restates the array. The machine_max_* limits hold + reaches it at its own width or as a shared single value, so a preset with + other variants restates non-shared arrays when necessary. The machine_max_* limits hold a (normal, silent) pair per variant. A process that lists variants pairs each with its extruder id (extruder 1 everywhere when absent); a machine's layout keys are held to _check_printer_layout, which reports a printer_extruder_id that does @@ -1790,7 +1792,7 @@ def check_variant_arrays(profiles_dir, vendor, strict=False): continue value, path = config[key] width, need = len(_as_list(value)), variant_length * stride - if width == need: + if width == need or (stride == 1 and (width == 1 or (ptype == "filament" and 0 < width < need))): continue print_error(f'{rel}: "{key}" has {width} values for variant length {variant_length}' f"{' (' + reason + ')' if reason else ''} at stride {stride}, " diff --git a/scripts/tests/test_profile_tool.py b/scripts/tests/test_profile_tool.py index a4e9a6f81b..bef4651f97 100644 --- a/scripts/tests/test_profile_tool.py +++ b/scripts/tests/test_profile_tool.py @@ -968,21 +968,25 @@ class TestFixVariant(TreeCase): retraction_length=["0.8", "1.2"], z_hop=["0.4"], machine_max_speed_x=["500", "200"]) errors, out = self.width_errors() - self.assertEqual(errors, 2, out) - self.assertIn('M.json: "z_hop" has 1 values for variant length 2 (no ' - 'printer_extruder_variant, so one default variant per extruder) at stride 1, ' - 'which takes 2', out) + self.assertEqual(errors, 1, out) + self.assertNotIn('"z_hop"', out) self.assertIn('M.json: "machine_max_speed_x" has 2 values for variant length 2', out) self.assertNotIn("retraction_length", out) - def test_one_value_is_an_error_where_the_list_has_more_variants(self): + def test_one_value_applies_to_every_variant(self): self.preset("process/P.json", instantiation="true", outer_wall_speed=["30"], print_extruder_id=["1", "1"], print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow"]) errors, out = self.width_errors() - self.assertEqual(errors, 1, out) - self.assertIn('P.json: "outer_wall_speed" has 1 values for variant length 2 at stride 1, ' - 'which takes 2', out) + self.assertEqual(errors, 0, out) + self.assertEqual(self.width_errors("strict")[0], 0) + + def test_a_short_filament_array_uses_element_zero_for_missing_variants(self): + self.preset("filament/F.json", instantiation="true", pressure_advance=["0.02", "0.04"], + filament_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard"]) + errors, out = self.width_errors() + self.assertEqual(errors, 0, out) + self.assertEqual(self.width_errors("strict")[0], 0) def test_one_value_is_padded_to_every_variant(self): self.preset("machine/M.json", instantiation="true", nozzle_diameter=["0.4"] * 3, @@ -1025,13 +1029,11 @@ class TestFixVariant(TreeCase): self.assertNotIn("outer_wall_speed", self.t.read("V", "process/S1.json")) self.assertEqual(self.width_errors("strict")[0], 0) - def test_an_inherited_array_is_checked_and_restated_only_when_strict(self): + def test_an_inherited_single_value_is_valid_and_can_be_explicitly_expanded(self): self.processes(["30"]) self.assertEqual(self.width_errors()[0], 0) errors, out = self.width_errors("strict") - self.assertEqual(errors, 1, out) - self.assertIn('process/D1.json: "outer_wall_speed" has 1 values for variant length 2 at ' - 'stride 1, which takes 2 (it comes from V/process/common.json)', out) + self.assertEqual(errors, 0, out) before = self.t.bytes_map() self.run_command("fix-variant") self.assertEqual(self.t.bytes_map(), before) diff --git a/src/dev-utils/OrcaSlicer_profile_validator.cpp b/src/dev-utils/OrcaSlicer_profile_validator.cpp index 4bb7eab64d..e41ce737ca 100644 --- a/src/dev-utils/OrcaSlicer_profile_validator.cpp +++ b/src/dev-utils/OrcaSlicer_profile_validator.cpp @@ -314,6 +314,13 @@ DynamicPrintConfig slice_config(PresetBundle &bundle) fmap[i] = int(i % nozzles) + 1; } + // A fresh printer selection uses its declared nozzle volumes, just like the + // app. Otherwise a high-flow preset is silently sliced with Standard tuning. + bundle.reset_default_nozzle_volume_type(); + auto &filament_map = bundle.project_config.option("filament_map", true)->values; + bundle.project_config.option("filament_volume_map", true)->values = + bundle.get_default_nozzle_volume_types_for_filaments(filament_map); + DynamicPrintConfig cfg = bundle.full_config(); cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable // The map above drives full_config()'s per-filament variant collapse; fmmManual on the sliced config diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 440f3e761a..0d744b5003 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -9459,6 +9459,19 @@ std::set print_options_with_variant = { std::set filament_options_with_variant = { "filament_flow_ratio", "filament_max_volumetric_speed", + // Variant arrays use element zero when the selected column is absent. + // A singleton therefore retains the same tuning for every nozzle variant. + "enable_pressure_advance", + "pressure_advance", + "fan_min_speed", + "fan_max_speed", + "additional_cooling_fan_speed", + "filament_minimal_purge_on_wipe_tower", + "filament_multitool_ramming", + "filament_multitool_ramming_volume", + "filament_multitool_ramming_flow", + "nozzle_temperature_range_low", + "nozzle_temperature_range_high", // Per-variant ramming / pre-cooling / nozzle-change filament overrides "filament_ramming_volumetric_speed", "filament_pre_cooling_temperature", diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 5a58ec7b6c..3817799b2f 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -78,12 +78,12 @@ void ConfigManipulation::set_option_label(const std::string& opt_key, const wxSt cb_set_option_label(opt_key, label, opt_index); } -void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config) { +void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; // Get the selected filament type std::string filament_type = ""; @@ -123,16 +123,16 @@ void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrint } } -void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config) +void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config, unsigned int variant_index) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; if (config->has("nozzle_temperature")) { - if (config->opt_int("nozzle_temperature", 0) < temperature_range_low || config->opt_int("nozzle_temperature", 0) > temperature_range_high) { + if (config->opt_int("nozzle_temperature", variant_index) < temperature_range_low || config->opt_int("nozzle_temperature", variant_index) > temperature_range_high) { wxString msg_text = _(L("The nozzle may become clogged when the temperature is out of the recommended range.\nPlease make sure whether to use this temperature to print.\n\n")); msg_text += wxString::Format(_L("The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius."), temperature_range_low, temperature_range_high); MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK); @@ -143,17 +143,17 @@ void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *conf } } -void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config) +void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; if (config->has("nozzle_temperature_initial_layer")) { - if (config->opt_int("nozzle_temperature_initial_layer", 0) < temperature_range_low || - config->opt_int("nozzle_temperature_initial_layer", 0) > temperature_range_high) + if (config->opt_int("nozzle_temperature_initial_layer", variant_index) < temperature_range_low || + config->opt_int("nozzle_temperature_initial_layer", variant_index) > temperature_range_high) { wxString msg_text = _(L("The nozzle may become clogged when the temperature is out of the recommended range.\nPlease make sure whether to use this temperature to print.\n\n")); msg_text += wxString::Format(_L("The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius."), temperature_range_low, temperature_range_high); @@ -1293,15 +1293,15 @@ int ConfigManipulation::show_spiral_mode_settings_dialog(bool is_object_config) return answer; } -bool ConfigManipulation::get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high) +bool ConfigManipulation::get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high, unsigned int variant_index) { bool range_low_exist = false, range_high_exist = false; if (config->has("nozzle_temperature_range_low")) { - range_low = config->opt_int("nozzle_temperature_range_low", (unsigned int) 0); + range_low = config->opt_int("nozzle_temperature_range_low", variant_index); range_low_exist = true; } if (config->has("nozzle_temperature_range_high")) { - range_high = config->opt_int("nozzle_temperature_range_high", (unsigned int) 0); + range_high = config->opt_int("nozzle_temperature_range_high", variant_index); range_high_exist = true; } return range_low_exist && range_high_exist; diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index ac53ffb4bb..bff41effad 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -79,9 +79,9 @@ public: void apply_null_fff_config(DynamicPrintConfig *config, std::vector const &keys, std::map const & configs); //BBS: FFF filament nozzle temperature range - void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config); - void check_nozzle_temperature_range(DynamicPrintConfig* config); - void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config); + void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index = 0); + void check_nozzle_temperature_range(DynamicPrintConfig* config, unsigned int variant_index = 0); + void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index = 0); void check_adaptive_pressure_advance_model(DynamicPrintConfig* config); void check_filament_max_volumetric_speed(DynamicPrintConfig *config); void check_chamber_temperature(DynamicPrintConfig* config); @@ -104,7 +104,7 @@ public: int show_spiral_mode_settings_dialog(bool is_object_config = false); private: - bool get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high); + bool get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high, unsigned int variant_index); }; } // GUI diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b5b93fca17..ac50bfc3a8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4396,16 +4396,16 @@ void TabFilament::build() optgroup->append_single_option_line("temperature_vitrification", "material_basic_information#softening-temperature"); optgroup->append_single_option_line("idle_temperature", "material_basic_information#idle-temperature"); Line line = { L("Recommended nozzle temperature"), L("Recommended nozzle temperature range of this filament. 0 means not set") }; - line.append_option(optgroup->get_option("nozzle_temperature_range_low")); - line.append_option(optgroup->get_option("nozzle_temperature_range_high")); + line.append_option(optgroup->get_option("nozzle_temperature_range_low", 0)); + line.append_option(optgroup->get_option("nozzle_temperature_range_high", 0)); optgroup->append_line(line); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { DynamicPrintConfig &filament_config = m_preset_bundle->filaments.get_edited_preset().config; update_dirty(); - if (!m_postpone_update_ui && (opt_key == "nozzle_temperature_range_low" || opt_key == "nozzle_temperature_range_high")) { - m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config); + if (!m_postpone_update_ui && (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_low" || opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_high")) { + m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); } on_value_change(opt_key, value); }; @@ -4415,8 +4415,8 @@ void TabFilament::build() optgroup->append_single_option_line("pellet_flow_coefficient", "printer_basic_information_advanced#pellet-modded-printer"); optgroup->append_single_option_line("filament_flow_ratio", "material_flow_ratio_and_pressure_advance#flow-ratio", 0); - optgroup->append_single_option_line("enable_pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance"); - optgroup->append_single_option_line("pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance"); + optgroup->append_single_option_line("enable_pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance", 0); + optgroup->append_single_option_line("pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance", 0); // Orca: adaptive pressure advance and calibration model optgroup->append_single_option_line("adaptive_pressure_advance", "material_flow_ratio_and_pressure_advance#enable-adaptive-pressure-advance-beta"); @@ -4532,11 +4532,11 @@ void TabFilament::build() else if (opt_key == "textured_plate_temp" || opt_key == "textured_plate_temp_initial_layer") { m_config_manipulation.check_bed_temperature_difference(BedType::btPTE, &filament_config); } - else */if (opt_key == "nozzle_temperature") { - m_config_manipulation.check_nozzle_temperature_range(&filament_config); + else */if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature") { + m_config_manipulation.check_nozzle_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); } - else if (opt_key == "nozzle_temperature_initial_layer") { - m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config); + else if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_initial_layer") { + m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); } on_value_change(opt_key, value); @@ -4571,12 +4571,12 @@ void TabFilament::build() optgroup = page->new_optgroup(L("Part cooling fan"), L"param_cooling_part_fan"); line = { L("Min fan speed threshold"), L("The part cooling fan will run at the minimum fan speed when the estimated layer time is longer than the threshold value. When the layer time is shorter than the threshold, the fan speed will be interpolated between the minimum and maximum fan speed according to layer printing time.") }; line.label_path = "material_cooling#material-part-cooling-fan"; - line.append_option(optgroup->get_option("fan_min_speed")); + line.append_option(optgroup->get_option("fan_min_speed", 0)); line.append_option(optgroup->get_option("fan_cooling_layer_time")); optgroup->append_line(line); line = { L("Max fan speed threshold"), L("The part cooling fan will run at maximum speed when the estimated layer time is shorter than the threshold value.") }; line.label_path = "material_cooling#material-part-cooling-fan"; - line.append_option(optgroup->get_option("fan_max_speed")); + line.append_option(optgroup->get_option("fan_max_speed", 0)); line.append_option(optgroup->get_option("slow_down_layer_time")); optgroup->append_line(line); optgroup->append_single_option_line("reduce_fan_stop_start_freq", "material_cooling#keep-fan-always-on"); @@ -4592,7 +4592,7 @@ void TabFilament::build() optgroup->append_single_option_line("ironing_fan_speed", "material_cooling#ironing-fan-speed"); // ORCA: Add support for ironing fan speed control optgroup = page->new_optgroup(L("Auxiliary part cooling fan"), L"param_cooling_aux_fan"); - optgroup->append_single_option_line("additional_cooling_fan_speed", "material_cooling#auxiliary-part-cooling-fan"); + optgroup->append_single_option_line("additional_cooling_fan_speed", "material_cooling#auxiliary-part-cooling-fan", 0); optgroup = page->new_optgroup(L("Exhaust fan"),L"param_cooling_exhaust"); @@ -4656,7 +4656,7 @@ void TabFilament::build() page = add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); // ORCA: icon only visible on placeholders optgroup = page->new_optgroup(L("Wipe tower parameters"), "param_tower"); - optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters"); + optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters", 0); optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_dist", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_length", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_ironing_area", "material_multimaterial#multimaterial-wipe-tower-parameters"); @@ -4700,9 +4700,9 @@ void TabFilament::build() }); optgroup = page->new_optgroup(L("Tool change parameters with multi extruder MM printers"), "param_toolchange_multi_extruder"); - optgroup->append_single_option_line("filament_multitool_ramming", "material_multimaterial#tool-change-parameters-with-multi-extruder"); - optgroup->append_single_option_line("filament_multitool_ramming_volume", "material_multimaterial#multi-tool-ramming-volume"); - optgroup->append_single_option_line("filament_multitool_ramming_flow", "material_multimaterial#multi-tool-ramming-flow"); + optgroup->append_single_option_line("filament_multitool_ramming", "material_multimaterial#tool-change-parameters-with-multi-extruder", 0); + optgroup->append_single_option_line("filament_multitool_ramming_volume", "material_multimaterial#multi-tool-ramming-volume", 0); + optgroup->append_single_option_line("filament_multitool_ramming_flow", "material_multimaterial#multi-tool-ramming-flow", 0); page = add_options_page(L("Dependencies"), "advanced"); optgroup = page->new_optgroup(L("Compatible printers"), "param_dependencies_printers"); @@ -4772,6 +4772,7 @@ void TabFilament::update_description_lines() void TabFilament::toggle_options() { + const int variant_index = m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; if (!m_active_page) return; bool is_BBL_printer = false; @@ -4813,7 +4814,7 @@ void TabFilament::toggle_options() } } - toggle_line("additional_cooling_fan_speed", printer_cfg.opt_bool("auxiliary_fan")); + toggle_line("additional_cooling_fan_speed", printer_cfg.opt_bool("auxiliary_fan"), 256 + variant_index); bool support_air_filtration = printer_cfg.opt_bool("support_air_filtration"); for (auto el : {"activate_air_filtration", "during_print_exhaust_fan_speed", "complete_print_exhaust_fan_speed"}) @@ -4829,8 +4830,8 @@ void TabFilament::toggle_options() } if (m_active_page->title() == L("Filament")) { - bool pa = m_config->opt_bool("enable_pressure_advance", 0); - toggle_option("pressure_advance", pa); + bool pa = m_config->opt_bool("enable_pressure_advance", variant_index); + toggle_option("pressure_advance", pa, 256 + variant_index); //Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type. DynamicConfig& proj_cfg = m_preset_bundle->project_config; @@ -4886,11 +4887,11 @@ void TabFilament::toggle_options() for (auto el : {"filament_minimal_purge_on_wipe_tower", "filament_loading_speed_start", "filament_loading_speed", "filament_unloading_speed_start", "filament_unloading_speed", "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed"}) - toggle_option(el, !is_BBL_printer); + toggle_option(el, !is_BBL_printer, filament_options_with_variant.count(el) ? 256 + variant_index : -1); - bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", 0); - toggle_option("filament_multitool_ramming_volume", multitool_ramming); - toggle_option("filament_multitool_ramming_flow", multitool_ramming); + bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", variant_index); + toggle_option("filament_multitool_ramming_volume", multitool_ramming, 256 + variant_index); + toggle_option("filament_multitool_ramming_flow", multitool_ramming, 256 + variant_index); bool is_BBL_multi_extruder = is_BBL_printer && printer_cfg.option("nozzle_diameter")->size() > 1; const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0; diff --git a/tests/libslic3r/test_config_variant_expansion.cpp b/tests/libslic3r/test_config_variant_expansion.cpp index e603808b54..fc03afa2c1 100644 --- a/tests/libslic3r/test_config_variant_expansion.cpp +++ b/tests/libslic3r/test_config_variant_expansion.cpp @@ -1,6 +1,7 @@ #include #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/Preset.hpp" using namespace Slic3r; @@ -30,6 +31,95 @@ void add_print_variant_columns(DynamicPrintConfig &config) } // namespace +TEST_CASE("Filament cooling and pressure settings follow the selected nozzle variant", "[Config][FilamentVariants]") +{ + struct Setting { + const char *key; + const char *standard; + const char *high_flow; + }; + const Setting settings[] = { + {"enable_pressure_advance", "0", "1"}, + {"pressure_advance", "0.04", "0.02"}, + {"fan_min_speed", "15", "10"}, + {"fan_max_speed", "15", "60"}, + {"additional_cooling_fan_speed", "70", "0"}, + {"filament_minimal_purge_on_wipe_tower", "0", "15"}, + {"filament_multitool_ramming", "0", "1"}, + {"filament_multitool_ramming_volume", "0", "5"}, + {"filament_multitool_ramming_flow", "10", "30"}, + {"nozzle_temperature_range_low", "190", "210"}, + {"nozzle_temperature_range_high", "255", "280"}, + }; + const int variant_index = GENERATE(0, 1, 2, 3); + const int value_count = GENERATE(1, 2, 4); + const bool load_preset = GENERATE(false, true); + const bool high_flow = variant_index % 2 != 0; + for (const Setting &setting : settings) { + DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " values=" << value_count << " loaded=" << load_preset) { + DynamicPrintConfig printer = make_hybrid_printer_config(); + if (variant_index >= 2) + printer.option("extruder_type")->values[1] = etBowden; + DynamicPrintConfig filament; + filament.option("filament_extruder_variant", true)->values = + {"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"}; + std::string values = setting.standard; + for (int i = 1; i < value_count; ++i) + values += std::string(",") + (i % 2 ? setting.high_flow : setting.standard); + REQUIRE(filament.option(setting.key, true)->deserialize(values)); + + if (load_preset) { + extend_default_config_length(filament, false, DynamicPrintConfig::full_print_config()); + DynamicPrintConfig expanded; + std::string expanded_values = values; + for (int i = value_count; i < 4; ++i) + expanded_values += std::string(",") + setting.standard; + REQUIRE(expanded.option(setting.key, true)->deserialize(expanded_values)); + REQUIRE(*filament.option(setting.key) == *expanded.option(setting.key)); + } + + std::vector> nozzle_types; + const int count = printer.get_extruder_nozzle_volume_count(2, nozzle_types); + filament.update_values_to_printer_extruders(printer, 2, count, nozzle_types, + filament_options_with_variant, "", "filament_extruder_variant", 1, 2, + high_flow ? nvtHighFlow : nvtStandard); + + DynamicPrintConfig expected; + REQUIRE(expected.option(setting.key, true)->deserialize(high_flow && variant_index < value_count ? setting.high_flow : setting.standard)); + REQUIRE(*filament.option(setting.key) == *expected.option(setting.key)); + } + } +} + +TEST_CASE("Filaments on the same hybrid tool keep their own cooling and pressure variants", "[Config][FilamentVariants]") +{ + DynamicPrintConfig config = make_hybrid_printer_config(); + config.option("filament_self_index", true)->values = {1, 1, 2, 2}; + config.option("filament_extruder_variant", true)->values = + {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}; + config.option("filament_map", true)->values = {2, 2}; + config.option("filament_volume_map", true)->values = {nvtHighFlow, nvtStandard}; + config.option("fan_max_speed", true)->values = {15., 60., 20., 80.}; + config.option("pressure_advance", true)->values = {0.04, 0.02, 0.05, 0.03}; + config.option("filament_multitool_ramming", true)->values = {false, true, false, true}; + std::vector> nozzle_types; + const int count = config.get_extruder_nozzle_volume_count(2, nozzle_types); + std::set keys = filament_options_with_variant; + keys.insert("filament_self_index"); + config.update_values_to_printer_extruders_for_multiple_filaments(config, 2, count, keys, + "filament_self_index", "filament_extruder_variant"); + const auto &fan = config.option("fan_max_speed")->values; + REQUIRE(fan.size() == 2); + REQUIRE_THAT(fan[0], Catch::Matchers::WithinAbs(60., 1e-9)); + REQUIRE_THAT(fan[1], Catch::Matchers::WithinAbs(20., 1e-9)); + const auto &pa = config.option("pressure_advance")->values; + REQUIRE(pa.size() == 2); + REQUIRE_THAT(pa[0], Catch::Matchers::WithinAbs(0.02, 1e-9)); + REQUIRE_THAT(pa[1], Catch::Matchers::WithinAbs(0.05, 1e-9)); + REQUIRE(config.option("filament_multitool_ramming")->get_at(0)); + REQUIRE_FALSE(config.option("filament_multitool_ramming")->get_at(1)); +} + TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]") { ConfigOptionFloats machine({10., 20., 30.}); From 0eb6e6814e5288afc285165bd095ea7f90344162 Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Tue, 29 Sep 2026 22:42:30 -0400 Subject: [PATCH 68/89] Preserve nozzle variant tuning and cooling controls Include variant-aware validation, temperature and pressure controls, named nozzle selection, and filament sidebar refresh. Co-authored-by: Codex --- scripts/orca_profile_tool.py | 16 ++-- scripts/tests/test_profile_tool.py | 26 +++--- .../OrcaSlicer_profile_validator.cpp | 7 ++ src/libslic3r/PrintConfig.cpp | 13 +++ src/slic3r/GUI/ConfigManipulation.cpp | 24 ++--- src/slic3r/GUI/ConfigManipulation.hpp | 8 +- src/slic3r/GUI/GUI_App.cpp | 5 ++ src/slic3r/GUI/Plater.cpp | 15 +++- src/slic3r/GUI/Tab.cpp | 48 +++++----- .../test_config_variant_expansion.cpp | 90 +++++++++++++++++++ 10 files changed, 188 insertions(+), 64 deletions(-) diff --git a/scripts/orca_profile_tool.py b/scripts/orca_profile_tool.py index f7f622c8c0..6728c47127 100755 --- a/scripts/orca_profile_tool.py +++ b/scripts/orca_profile_tool.py @@ -1757,18 +1757,20 @@ def check_variant_names(profiles_dir, vendor): def check_variant_arrays(profiles_dir, vendor, strict=False): - """Every variant array an instantiated preset writes is exactly its - variant length x stride wide, and a printer's variant layout keys agree. + """Variant arrays have the declared width or a shared single value, + and a printer's variant layout keys agree. Only instantiated presets are judged, each at the variant length of its composed config (_variant_length's); a base is not, since what it writes counts only where it reaches a preset that does not override it. A full-width array holds one - value per variant; any other width is an error, one value included and whatever - the values. A key the preset does not write takes what reaches it - the default, + value per variant; a single value at stride 1 broadcasts to every variant. + Short, nonempty filament arrays use element zero for missing variants, + matching the C++ loader. Other widths are errors. A key the preset does not write + takes what reaches it - the default, or an array it inherits or includes - which the loader sizes to the preset, and is not checked. With strict, every instantiated preset also holds each key that - reaches it at its own width, so a preset with other variants than the file its - array comes from restates the array. The machine_max_* limits hold + reaches it at its own width or as a shared single value, so a preset with + other variants restates non-shared arrays when necessary. The machine_max_* limits hold a (normal, silent) pair per variant. A process that lists variants pairs each with its extruder id (extruder 1 everywhere when absent); a machine's layout keys are held to _check_printer_layout, which reports a printer_extruder_id that does @@ -1790,7 +1792,7 @@ def check_variant_arrays(profiles_dir, vendor, strict=False): continue value, path = config[key] width, need = len(_as_list(value)), variant_length * stride - if width == need: + if width == need or (stride == 1 and (width == 1 or (ptype == "filament" and 0 < width < need))): continue print_error(f'{rel}: "{key}" has {width} values for variant length {variant_length}' f"{' (' + reason + ')' if reason else ''} at stride {stride}, " diff --git a/scripts/tests/test_profile_tool.py b/scripts/tests/test_profile_tool.py index a4e9a6f81b..bef4651f97 100644 --- a/scripts/tests/test_profile_tool.py +++ b/scripts/tests/test_profile_tool.py @@ -968,21 +968,25 @@ class TestFixVariant(TreeCase): retraction_length=["0.8", "1.2"], z_hop=["0.4"], machine_max_speed_x=["500", "200"]) errors, out = self.width_errors() - self.assertEqual(errors, 2, out) - self.assertIn('M.json: "z_hop" has 1 values for variant length 2 (no ' - 'printer_extruder_variant, so one default variant per extruder) at stride 1, ' - 'which takes 2', out) + self.assertEqual(errors, 1, out) + self.assertNotIn('"z_hop"', out) self.assertIn('M.json: "machine_max_speed_x" has 2 values for variant length 2', out) self.assertNotIn("retraction_length", out) - def test_one_value_is_an_error_where_the_list_has_more_variants(self): + def test_one_value_applies_to_every_variant(self): self.preset("process/P.json", instantiation="true", outer_wall_speed=["30"], print_extruder_id=["1", "1"], print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow"]) errors, out = self.width_errors() - self.assertEqual(errors, 1, out) - self.assertIn('P.json: "outer_wall_speed" has 1 values for variant length 2 at stride 1, ' - 'which takes 2', out) + self.assertEqual(errors, 0, out) + self.assertEqual(self.width_errors("strict")[0], 0) + + def test_a_short_filament_array_uses_element_zero_for_missing_variants(self): + self.preset("filament/F.json", instantiation="true", pressure_advance=["0.02", "0.04"], + filament_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard"]) + errors, out = self.width_errors() + self.assertEqual(errors, 0, out) + self.assertEqual(self.width_errors("strict")[0], 0) def test_one_value_is_padded_to_every_variant(self): self.preset("machine/M.json", instantiation="true", nozzle_diameter=["0.4"] * 3, @@ -1025,13 +1029,11 @@ class TestFixVariant(TreeCase): self.assertNotIn("outer_wall_speed", self.t.read("V", "process/S1.json")) self.assertEqual(self.width_errors("strict")[0], 0) - def test_an_inherited_array_is_checked_and_restated_only_when_strict(self): + def test_an_inherited_single_value_is_valid_and_can_be_explicitly_expanded(self): self.processes(["30"]) self.assertEqual(self.width_errors()[0], 0) errors, out = self.width_errors("strict") - self.assertEqual(errors, 1, out) - self.assertIn('process/D1.json: "outer_wall_speed" has 1 values for variant length 2 at ' - 'stride 1, which takes 2 (it comes from V/process/common.json)', out) + self.assertEqual(errors, 0, out) before = self.t.bytes_map() self.run_command("fix-variant") self.assertEqual(self.t.bytes_map(), before) diff --git a/src/dev-utils/OrcaSlicer_profile_validator.cpp b/src/dev-utils/OrcaSlicer_profile_validator.cpp index 4bb7eab64d..e41ce737ca 100644 --- a/src/dev-utils/OrcaSlicer_profile_validator.cpp +++ b/src/dev-utils/OrcaSlicer_profile_validator.cpp @@ -314,6 +314,13 @@ DynamicPrintConfig slice_config(PresetBundle &bundle) fmap[i] = int(i % nozzles) + 1; } + // A fresh printer selection uses its declared nozzle volumes, just like the + // app. Otherwise a high-flow preset is silently sliced with Standard tuning. + bundle.reset_default_nozzle_volume_type(); + auto &filament_map = bundle.project_config.option("filament_map", true)->values; + bundle.project_config.option("filament_volume_map", true)->values = + bundle.get_default_nozzle_volume_types_for_filaments(filament_map); + DynamicPrintConfig cfg = bundle.full_config(); cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable // The map above drives full_config()'s per-filament variant collapse; fmmManual on the sliced config diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 16a913a85f..5412558841 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -9463,6 +9463,19 @@ std::set print_options_with_variant = { std::set filament_options_with_variant = { "filament_flow_ratio", "filament_max_volumetric_speed", + // Variant arrays use element zero when the selected column is absent. + // A singleton therefore retains the same tuning for every nozzle variant. + "enable_pressure_advance", + "pressure_advance", + "fan_min_speed", + "fan_max_speed", + "additional_cooling_fan_speed", + "filament_minimal_purge_on_wipe_tower", + "filament_multitool_ramming", + "filament_multitool_ramming_volume", + "filament_multitool_ramming_flow", + "nozzle_temperature_range_low", + "nozzle_temperature_range_high", // Per-variant ramming / pre-cooling / nozzle-change filament overrides "filament_ramming_volumetric_speed", "filament_pre_cooling_temperature", diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index cc687ff84a..fa8e2e5f16 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -78,12 +78,12 @@ void ConfigManipulation::set_option_label(const std::string& opt_key, const wxSt cb_set_option_label(opt_key, label, opt_index); } -void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config) { +void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; // Get the selected filament type std::string filament_type = ""; @@ -123,16 +123,16 @@ void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrint } } -void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config) +void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config, unsigned int variant_index) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; if (config->has("nozzle_temperature")) { - if (config->opt_int("nozzle_temperature", 0) < temperature_range_low || config->opt_int("nozzle_temperature", 0) > temperature_range_high) { + if (config->opt_int("nozzle_temperature", variant_index) < temperature_range_low || config->opt_int("nozzle_temperature", variant_index) > temperature_range_high) { wxString msg_text = _(L("The nozzle may become clogged when the temperature is out of the recommended range.\nPlease make sure whether to use this temperature to print.\n\n")); msg_text += wxString::Format(_L("The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius."), temperature_range_low, temperature_range_high); MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK); @@ -143,17 +143,17 @@ void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *conf } } -void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config) +void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; if (config->has("nozzle_temperature_initial_layer")) { - if (config->opt_int("nozzle_temperature_initial_layer", 0) < temperature_range_low || - config->opt_int("nozzle_temperature_initial_layer", 0) > temperature_range_high) + if (config->opt_int("nozzle_temperature_initial_layer", variant_index) < temperature_range_low || + config->opt_int("nozzle_temperature_initial_layer", variant_index) > temperature_range_high) { wxString msg_text = _(L("The nozzle may become clogged when the temperature is out of the recommended range.\nPlease make sure whether to use this temperature to print.\n\n")); msg_text += wxString::Format(_L("The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius."), temperature_range_low, temperature_range_high); @@ -1295,15 +1295,15 @@ int ConfigManipulation::show_spiral_mode_settings_dialog(bool is_object_config) return answer; } -bool ConfigManipulation::get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high) +bool ConfigManipulation::get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high, unsigned int variant_index) { bool range_low_exist = false, range_high_exist = false; if (config->has("nozzle_temperature_range_low")) { - range_low = config->opt_int("nozzle_temperature_range_low", (unsigned int) 0); + range_low = config->opt_int("nozzle_temperature_range_low", variant_index); range_low_exist = true; } if (config->has("nozzle_temperature_range_high")) { - range_high = config->opt_int("nozzle_temperature_range_high", (unsigned int) 0); + range_high = config->opt_int("nozzle_temperature_range_high", variant_index); range_high_exist = true; } return range_low_exist && range_high_exist; diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index ac53ffb4bb..bff41effad 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -79,9 +79,9 @@ public: void apply_null_fff_config(DynamicPrintConfig *config, std::vector const &keys, std::map const & configs); //BBS: FFF filament nozzle temperature range - void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config); - void check_nozzle_temperature_range(DynamicPrintConfig* config); - void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config); + void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index = 0); + void check_nozzle_temperature_range(DynamicPrintConfig* config, unsigned int variant_index = 0); + void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index = 0); void check_adaptive_pressure_advance_model(DynamicPrintConfig* config); void check_filament_max_volumetric_speed(DynamicPrintConfig *config); void check_chamber_temperature(DynamicPrintConfig* config); @@ -104,7 +104,7 @@ public: int show_spiral_mode_settings_dialog(bool is_object_config = false); private: - bool get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high); + bool get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high, unsigned int variant_index); }; } // GUI diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 72849dd78e..5feebdb777 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -8895,6 +8895,11 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch if (active_preset_combox) tab->reactive_preset_combo_box(); } + // Preset loading can resize the filament list without an extruder-count change event. + // Refresh the controls even when the list already matches the printer's nozzle count. + if (printer_technology == ptFFF) + this->plater()->on_filament_count_change(preset_bundle->filament_presets.size()); + // BBS: model config for (Tab *tab : model_tabs_list) if (tab->supports_printer_technology(printer_technology)) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 51fdba9bf8..22bfe789d1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1803,8 +1803,10 @@ bool Sidebar::priv::switch_diameter_to(const wxString &diameter) auto* nozzle_diameter = dynamic_cast(printer_preset.config.option("nozzle_diameter")); if (nozzle_diameter && nozzle_diameter->size() > 0) { auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]); - // If the selected diameter is the same as current nozzle, don't switch profiles - if (current_nozzle_dia == diameter.ToStdString()) { + // A named variant can share this diameter; selecting the plain diameter + // must still switch back to the standard profile. + if (current_nozzle_dia == diameter.ToStdString() && + printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) { return true; } } @@ -3926,13 +3928,18 @@ void Sidebar::update_presets(Preset::Type preset_type) combo_flow->Show(combo_flow->GetCount() > 0); }; - auto update_extruder_diameter = [&diameters, &nozzle_diameter](int extruder_index,ExtruderGroup & extruder) { + auto update_extruder_diameter = [&diameters, &nozzle_diameter, &diameter](int extruder_index,ExtruderGroup & extruder) { extruder.combo_diameter->Clear(); if (extruder_index >= int(nozzle_diameter->values.size())) return; int select = -1; // ORCA get the actual nozzle diameter from printer config auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]); + // Named variants such as "0.4HS" and "0.4 High Flow" share a physical diameter. + // Retain the variant selection unless the diameter was customized. + const auto selected_variant = + diameter.substr(0, diameter.find_first_not_of("0123456789.")) == nozzle_dia && + std::find(diameters.begin(), diameters.end(), diameter) != diameters.end() ? diameter : nozzle_dia; // ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias if(!diameters.empty() && diameters[0].empty() && !nozzle_dia.empty()){ diameters[0] = nozzle_dia; @@ -3942,7 +3949,7 @@ void Sidebar::update_presets(Preset::Type preset_type) diameters.push_back(nozzle_dia); } for (size_t i = 0; i < diameters.size(); ++i) { - if (diameters[i] == nozzle_dia) + if (diameters[i] == selected_variant) select = extruder.combo_diameter->GetCount(); extruder.combo_diameter->Append(diameters[i], {}); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c824c16d5d..4698a70561 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4396,16 +4396,16 @@ void TabFilament::build() optgroup->append_single_option_line("temperature_vitrification", "material_basic_information#softening-temperature"); optgroup->append_single_option_line("idle_temperature", "material_basic_information#idle-temperature"); Line line = { L("Recommended nozzle temperature"), L("Recommended nozzle temperature range of this filament. 0 means not set") }; - line.append_option(optgroup->get_option("nozzle_temperature_range_low")); - line.append_option(optgroup->get_option("nozzle_temperature_range_high")); + line.append_option(optgroup->get_option("nozzle_temperature_range_low", 0)); + line.append_option(optgroup->get_option("nozzle_temperature_range_high", 0)); optgroup->append_line(line); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { DynamicPrintConfig &filament_config = m_preset_bundle->filaments.get_edited_preset().config; update_dirty(); - if (!m_postpone_update_ui && (opt_key == "nozzle_temperature_range_low" || opt_key == "nozzle_temperature_range_high")) { - m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config); + if (!m_postpone_update_ui && (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_low" || opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_high")) { + m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); } on_value_change(opt_key, value); }; @@ -4532,11 +4532,11 @@ void TabFilament::build() else if (opt_key == "textured_plate_temp" || opt_key == "textured_plate_temp_initial_layer") { m_config_manipulation.check_bed_temperature_difference(BedType::btPTE, &filament_config); } - else */if (opt_key == "nozzle_temperature") { - m_config_manipulation.check_nozzle_temperature_range(&filament_config); + else */if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature") { + m_config_manipulation.check_nozzle_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); } - else if (opt_key == "nozzle_temperature_initial_layer") { - m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config); + else if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_initial_layer") { + m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); } on_value_change(opt_key, value); @@ -4571,12 +4571,12 @@ void TabFilament::build() optgroup = page->new_optgroup(L("Part cooling fan"), L"param_cooling_part_fan"); line = { L("Min fan speed threshold"), L("The part cooling fan will run at the minimum fan speed when the estimated layer time is longer than the threshold value. When the layer time is shorter than the threshold, the fan speed will be interpolated between the minimum and maximum fan speed according to layer printing time.") }; line.label_path = "material_cooling#material-part-cooling-fan"; - line.append_option(optgroup->get_option("fan_min_speed")); + line.append_option(optgroup->get_option("fan_min_speed", 0)); line.append_option(optgroup->get_option("fan_cooling_layer_time")); optgroup->append_line(line); line = { L("Max fan speed threshold"), L("The part cooling fan will run at maximum speed when the estimated layer time is shorter than the threshold value.") }; line.label_path = "material_cooling#material-part-cooling-fan"; - line.append_option(optgroup->get_option("fan_max_speed")); + line.append_option(optgroup->get_option("fan_max_speed", 0)); line.append_option(optgroup->get_option("slow_down_layer_time")); optgroup->append_line(line); optgroup->append_single_option_line("reduce_fan_stop_start_freq", "material_cooling#keep-fan-always-on"); @@ -4592,7 +4592,7 @@ void TabFilament::build() optgroup->append_single_option_line("ironing_fan_speed", "material_cooling#ironing-fan-speed"); // ORCA: Add support for ironing fan speed control optgroup = page->new_optgroup(L("Auxiliary part cooling fan"), L"param_cooling_aux_fan"); - optgroup->append_single_option_line("additional_cooling_fan_speed", "material_cooling#auxiliary-part-cooling-fan"); + optgroup->append_single_option_line("additional_cooling_fan_speed", "material_cooling#auxiliary-part-cooling-fan", 0); optgroup = page->new_optgroup(L("Exhaust fan"),L"param_cooling_exhaust"); @@ -4656,7 +4656,7 @@ void TabFilament::build() page = add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); // ORCA: icon only visible on placeholders optgroup = page->new_optgroup(L("Wipe tower parameters"), "param_tower"); - optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters"); + optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters", 0); optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_dist", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_length", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_ironing_area", "material_multimaterial#multimaterial-wipe-tower-parameters"); @@ -4700,9 +4700,9 @@ void TabFilament::build() }); optgroup = page->new_optgroup(L("Tool change parameters with multi extruder MM printers"), "param_toolchange_multi_extruder"); - optgroup->append_single_option_line("filament_multitool_ramming", "material_multimaterial#tool-change-parameters-with-multi-extruder"); - optgroup->append_single_option_line("filament_multitool_ramming_volume", "material_multimaterial#multi-tool-ramming-volume"); - optgroup->append_single_option_line("filament_multitool_ramming_flow", "material_multimaterial#multi-tool-ramming-flow"); + optgroup->append_single_option_line("filament_multitool_ramming", "material_multimaterial#tool-change-parameters-with-multi-extruder", 0); + optgroup->append_single_option_line("filament_multitool_ramming_volume", "material_multimaterial#multi-tool-ramming-volume", 0); + optgroup->append_single_option_line("filament_multitool_ramming_flow", "material_multimaterial#multi-tool-ramming-flow", 0); page = add_options_page(L("Dependencies"), "advanced"); optgroup = page->new_optgroup(L("Compatible printers"), "param_dependencies_printers"); @@ -4772,6 +4772,7 @@ void TabFilament::update_description_lines() void TabFilament::toggle_options() { + const int variant_index = m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; if (!m_active_page) return; bool is_BBL_printer = false; @@ -4813,7 +4814,7 @@ void TabFilament::toggle_options() } } - toggle_line("additional_cooling_fan_speed", printer_cfg.opt_bool("auxiliary_fan")); + toggle_line("additional_cooling_fan_speed", printer_cfg.opt_bool("auxiliary_fan"), 256 + variant_index); bool support_air_filtration = printer_cfg.opt_bool("support_air_filtration"); for (auto el : {"activate_air_filtration", "during_print_exhaust_fan_speed", "complete_print_exhaust_fan_speed"}) @@ -4829,11 +4830,8 @@ void TabFilament::toggle_options() } if (m_active_page->title() == L("Filament")) { - const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0; - const unsigned int variant_idx = (unsigned int) std::max(selection, 0); - - bool pa = m_config->opt_bool("enable_pressure_advance", variant_idx); - toggle_option("pressure_advance", pa, 0); + bool pa = m_config->opt_bool("enable_pressure_advance", variant_index); + toggle_option("pressure_advance", pa, 256 + variant_index); //Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type. DynamicConfig& proj_cfg = m_preset_bundle->project_config; @@ -4887,11 +4885,11 @@ void TabFilament::toggle_options() for (auto el : {"filament_minimal_purge_on_wipe_tower", "filament_loading_speed_start", "filament_loading_speed", "filament_unloading_speed_start", "filament_unloading_speed", "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed"}) - toggle_option(el, !is_BBL_printer); + toggle_option(el, !is_BBL_printer, filament_options_with_variant.count(el) ? 256 + variant_index : -1); - bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", 0); - toggle_option("filament_multitool_ramming_volume", multitool_ramming); - toggle_option("filament_multitool_ramming_flow", multitool_ramming); + bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", variant_index); + toggle_option("filament_multitool_ramming_volume", multitool_ramming, 256 + variant_index); + toggle_option("filament_multitool_ramming_flow", multitool_ramming, 256 + variant_index); bool is_BBL_multi_extruder = is_BBL_printer && printer_cfg.option("nozzle_diameter")->size() > 1; const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0; diff --git a/tests/libslic3r/test_config_variant_expansion.cpp b/tests/libslic3r/test_config_variant_expansion.cpp index de19e8219c..67a16d2f47 100644 --- a/tests/libslic3r/test_config_variant_expansion.cpp +++ b/tests/libslic3r/test_config_variant_expansion.cpp @@ -1,6 +1,7 @@ #include #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/Preset.hpp" using namespace Slic3r; @@ -30,6 +31,95 @@ void add_print_variant_columns(DynamicPrintConfig &config) } // namespace +TEST_CASE("Filament cooling and pressure settings follow the selected nozzle variant", "[Config][FilamentVariants]") +{ + struct Setting { + const char *key; + const char *standard; + const char *high_flow; + }; + const Setting settings[] = { + {"enable_pressure_advance", "0", "1"}, + {"pressure_advance", "0.04", "0.02"}, + {"fan_min_speed", "15", "10"}, + {"fan_max_speed", "15", "60"}, + {"additional_cooling_fan_speed", "70", "0"}, + {"filament_minimal_purge_on_wipe_tower", "0", "15"}, + {"filament_multitool_ramming", "0", "1"}, + {"filament_multitool_ramming_volume", "0", "5"}, + {"filament_multitool_ramming_flow", "10", "30"}, + {"nozzle_temperature_range_low", "190", "210"}, + {"nozzle_temperature_range_high", "255", "280"}, + }; + const int variant_index = GENERATE(0, 1, 2, 3); + const int value_count = GENERATE(1, 2, 4); + const bool load_preset = GENERATE(false, true); + const bool high_flow = variant_index % 2 != 0; + for (const Setting &setting : settings) { + DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " values=" << value_count << " loaded=" << load_preset) { + DynamicPrintConfig printer = make_hybrid_printer_config(); + if (variant_index >= 2) + printer.option("extruder_type")->values[1] = etBowden; + DynamicPrintConfig filament; + filament.option("filament_extruder_variant", true)->values = + {"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"}; + std::string values = setting.standard; + for (int i = 1; i < value_count; ++i) + values += std::string(",") + (i % 2 ? setting.high_flow : setting.standard); + REQUIRE(filament.option(setting.key, true)->deserialize(values)); + + if (load_preset) { + extend_default_config_length(filament, false, DynamicPrintConfig::full_print_config()); + DynamicPrintConfig expanded; + std::string expanded_values = values; + for (int i = value_count; i < 4; ++i) + expanded_values += std::string(",") + setting.standard; + REQUIRE(expanded.option(setting.key, true)->deserialize(expanded_values)); + REQUIRE(*filament.option(setting.key) == *expanded.option(setting.key)); + } + + std::vector> nozzle_types; + const int count = printer.get_extruder_nozzle_volume_count(2, nozzle_types); + filament.update_values_to_printer_extruders(printer, 2, count, nozzle_types, + filament_options_with_variant, "", "filament_extruder_variant", 1, 2, + high_flow ? nvtHighFlow : nvtStandard); + + DynamicPrintConfig expected; + REQUIRE(expected.option(setting.key, true)->deserialize(high_flow && variant_index < value_count ? setting.high_flow : setting.standard)); + REQUIRE(*filament.option(setting.key) == *expected.option(setting.key)); + } + } +} + +TEST_CASE("Filaments on the same hybrid tool keep their own cooling and pressure variants", "[Config][FilamentVariants]") +{ + DynamicPrintConfig config = make_hybrid_printer_config(); + config.option("filament_self_index", true)->values = {1, 1, 2, 2}; + config.option("filament_extruder_variant", true)->values = + {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}; + config.option("filament_map", true)->values = {2, 2}; + config.option("filament_volume_map", true)->values = {nvtHighFlow, nvtStandard}; + config.option("fan_max_speed", true)->values = {15., 60., 20., 80.}; + config.option("pressure_advance", true)->values = {0.04, 0.02, 0.05, 0.03}; + config.option("filament_multitool_ramming", true)->values = {false, true, false, true}; + std::vector> nozzle_types; + const int count = config.get_extruder_nozzle_volume_count(2, nozzle_types); + std::set keys = filament_options_with_variant; + keys.insert("filament_self_index"); + config.update_values_to_printer_extruders_for_multiple_filaments(config, 2, count, keys, + "filament_self_index", "filament_extruder_variant"); + const auto &fan = config.option("fan_max_speed")->values; + REQUIRE(fan.size() == 2); + REQUIRE_THAT(fan[0], Catch::Matchers::WithinAbs(60., 1e-9)); + REQUIRE_THAT(fan[1], Catch::Matchers::WithinAbs(20., 1e-9)); + const auto &pa = config.option("pressure_advance")->values; + REQUIRE(pa.size() == 2); + REQUIRE_THAT(pa[0], Catch::Matchers::WithinAbs(0.02, 1e-9)); + REQUIRE_THAT(pa[1], Catch::Matchers::WithinAbs(0.05, 1e-9)); + REQUIRE(config.option("filament_multitool_ramming")->get_at(0)); + REQUIRE_FALSE(config.option("filament_multitool_ramming")->get_at(1)); +} + TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]") { ConfigOptionFloats machine({10., 20., 30.}); From d58c3b0d895df36fcce06d6a48d8c622a8d9daee Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Tue, 29 Sep 2026 22:42:50 -0400 Subject: [PATCH 69/89] Keep downstream customization limited to printer profiles Extract non-profile changes into a separate development line while preserving all profile content. Co-authored-by: Codex --- scripts/orca_profile_tool.py | 16 ++-- scripts/tests/test_profile_tool.py | 26 +++--- .../OrcaSlicer_profile_validator.cpp | 7 -- src/libslic3r/PrintConfig.cpp | 13 --- src/slic3r/GUI/ConfigManipulation.cpp | 24 ++--- src/slic3r/GUI/ConfigManipulation.hpp | 8 +- src/slic3r/GUI/GUI_App.cpp | 5 -- src/slic3r/GUI/Plater.cpp | 15 +--- src/slic3r/GUI/Tab.cpp | 48 +++++----- .../test_config_variant_expansion.cpp | 90 ------------------- 10 files changed, 64 insertions(+), 188 deletions(-) diff --git a/scripts/orca_profile_tool.py b/scripts/orca_profile_tool.py index 6728c47127..f7f622c8c0 100755 --- a/scripts/orca_profile_tool.py +++ b/scripts/orca_profile_tool.py @@ -1757,20 +1757,18 @@ def check_variant_names(profiles_dir, vendor): def check_variant_arrays(profiles_dir, vendor, strict=False): - """Variant arrays have the declared width or a shared single value, - and a printer's variant layout keys agree. + """Every variant array an instantiated preset writes is exactly its + variant length x stride wide, and a printer's variant layout keys agree. Only instantiated presets are judged, each at the variant length of its composed config (_variant_length's); a base is not, since what it writes counts only where it reaches a preset that does not override it. A full-width array holds one - value per variant; a single value at stride 1 broadcasts to every variant. - Short, nonempty filament arrays use element zero for missing variants, - matching the C++ loader. Other widths are errors. A key the preset does not write - takes what reaches it - the default, + value per variant; any other width is an error, one value included and whatever + the values. A key the preset does not write takes what reaches it - the default, or an array it inherits or includes - which the loader sizes to the preset, and is not checked. With strict, every instantiated preset also holds each key that - reaches it at its own width or as a shared single value, so a preset with - other variants restates non-shared arrays when necessary. The machine_max_* limits hold + reaches it at its own width, so a preset with other variants than the file its + array comes from restates the array. The machine_max_* limits hold a (normal, silent) pair per variant. A process that lists variants pairs each with its extruder id (extruder 1 everywhere when absent); a machine's layout keys are held to _check_printer_layout, which reports a printer_extruder_id that does @@ -1792,7 +1790,7 @@ def check_variant_arrays(profiles_dir, vendor, strict=False): continue value, path = config[key] width, need = len(_as_list(value)), variant_length * stride - if width == need or (stride == 1 and (width == 1 or (ptype == "filament" and 0 < width < need))): + if width == need: continue print_error(f'{rel}: "{key}" has {width} values for variant length {variant_length}' f"{' (' + reason + ')' if reason else ''} at stride {stride}, " diff --git a/scripts/tests/test_profile_tool.py b/scripts/tests/test_profile_tool.py index bef4651f97..a4e9a6f81b 100644 --- a/scripts/tests/test_profile_tool.py +++ b/scripts/tests/test_profile_tool.py @@ -968,25 +968,21 @@ class TestFixVariant(TreeCase): retraction_length=["0.8", "1.2"], z_hop=["0.4"], machine_max_speed_x=["500", "200"]) errors, out = self.width_errors() - self.assertEqual(errors, 1, out) - self.assertNotIn('"z_hop"', out) + self.assertEqual(errors, 2, out) + self.assertIn('M.json: "z_hop" has 1 values for variant length 2 (no ' + 'printer_extruder_variant, so one default variant per extruder) at stride 1, ' + 'which takes 2', out) self.assertIn('M.json: "machine_max_speed_x" has 2 values for variant length 2', out) self.assertNotIn("retraction_length", out) - def test_one_value_applies_to_every_variant(self): + def test_one_value_is_an_error_where_the_list_has_more_variants(self): self.preset("process/P.json", instantiation="true", outer_wall_speed=["30"], print_extruder_id=["1", "1"], print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow"]) errors, out = self.width_errors() - self.assertEqual(errors, 0, out) - self.assertEqual(self.width_errors("strict")[0], 0) - - def test_a_short_filament_array_uses_element_zero_for_missing_variants(self): - self.preset("filament/F.json", instantiation="true", pressure_advance=["0.02", "0.04"], - filament_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard"]) - errors, out = self.width_errors() - self.assertEqual(errors, 0, out) - self.assertEqual(self.width_errors("strict")[0], 0) + self.assertEqual(errors, 1, out) + self.assertIn('P.json: "outer_wall_speed" has 1 values for variant length 2 at stride 1, ' + 'which takes 2', out) def test_one_value_is_padded_to_every_variant(self): self.preset("machine/M.json", instantiation="true", nozzle_diameter=["0.4"] * 3, @@ -1029,11 +1025,13 @@ class TestFixVariant(TreeCase): self.assertNotIn("outer_wall_speed", self.t.read("V", "process/S1.json")) self.assertEqual(self.width_errors("strict")[0], 0) - def test_an_inherited_single_value_is_valid_and_can_be_explicitly_expanded(self): + def test_an_inherited_array_is_checked_and_restated_only_when_strict(self): self.processes(["30"]) self.assertEqual(self.width_errors()[0], 0) errors, out = self.width_errors("strict") - self.assertEqual(errors, 0, out) + self.assertEqual(errors, 1, out) + self.assertIn('process/D1.json: "outer_wall_speed" has 1 values for variant length 2 at ' + 'stride 1, which takes 2 (it comes from V/process/common.json)', out) before = self.t.bytes_map() self.run_command("fix-variant") self.assertEqual(self.t.bytes_map(), before) diff --git a/src/dev-utils/OrcaSlicer_profile_validator.cpp b/src/dev-utils/OrcaSlicer_profile_validator.cpp index e41ce737ca..4bb7eab64d 100644 --- a/src/dev-utils/OrcaSlicer_profile_validator.cpp +++ b/src/dev-utils/OrcaSlicer_profile_validator.cpp @@ -314,13 +314,6 @@ DynamicPrintConfig slice_config(PresetBundle &bundle) fmap[i] = int(i % nozzles) + 1; } - // A fresh printer selection uses its declared nozzle volumes, just like the - // app. Otherwise a high-flow preset is silently sliced with Standard tuning. - bundle.reset_default_nozzle_volume_type(); - auto &filament_map = bundle.project_config.option("filament_map", true)->values; - bundle.project_config.option("filament_volume_map", true)->values = - bundle.get_default_nozzle_volume_types_for_filaments(filament_map); - DynamicPrintConfig cfg = bundle.full_config(); cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable // The map above drives full_config()'s per-filament variant collapse; fmmManual on the sliced config diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 5412558841..16a913a85f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -9463,19 +9463,6 @@ std::set print_options_with_variant = { std::set filament_options_with_variant = { "filament_flow_ratio", "filament_max_volumetric_speed", - // Variant arrays use element zero when the selected column is absent. - // A singleton therefore retains the same tuning for every nozzle variant. - "enable_pressure_advance", - "pressure_advance", - "fan_min_speed", - "fan_max_speed", - "additional_cooling_fan_speed", - "filament_minimal_purge_on_wipe_tower", - "filament_multitool_ramming", - "filament_multitool_ramming_volume", - "filament_multitool_ramming_flow", - "nozzle_temperature_range_low", - "nozzle_temperature_range_high", // Per-variant ramming / pre-cooling / nozzle-change filament overrides "filament_ramming_volumetric_speed", "filament_pre_cooling_temperature", diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index fa8e2e5f16..cc687ff84a 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -78,12 +78,12 @@ void ConfigManipulation::set_option_label(const std::string& opt_key, const wxSt cb_set_option_label(opt_key, label, opt_index); } -void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index) { +void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; // Get the selected filament type std::string filament_type = ""; @@ -123,16 +123,16 @@ void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrint } } -void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config, unsigned int variant_index) +void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *config) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; if (config->has("nozzle_temperature")) { - if (config->opt_int("nozzle_temperature", variant_index) < temperature_range_low || config->opt_int("nozzle_temperature", variant_index) > temperature_range_high) { + if (config->opt_int("nozzle_temperature", 0) < temperature_range_low || config->opt_int("nozzle_temperature", 0) > temperature_range_high) { wxString msg_text = _(L("The nozzle may become clogged when the temperature is out of the recommended range.\nPlease make sure whether to use this temperature to print.\n\n")); msg_text += wxString::Format(_L("The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius."), temperature_range_low, temperature_range_high); MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK); @@ -143,17 +143,17 @@ void ConfigManipulation::check_nozzle_temperature_range(DynamicPrintConfig *conf } } -void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index) +void ConfigManipulation::check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config) { if (is_msg_dlg_already_exist) return; int temperature_range_low, temperature_range_high; - if (!get_temperature_range(config, temperature_range_low, temperature_range_high, variant_index)) return; + if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return; if (config->has("nozzle_temperature_initial_layer")) { - if (config->opt_int("nozzle_temperature_initial_layer", variant_index) < temperature_range_low || - config->opt_int("nozzle_temperature_initial_layer", variant_index) > temperature_range_high) + if (config->opt_int("nozzle_temperature_initial_layer", 0) < temperature_range_low || + config->opt_int("nozzle_temperature_initial_layer", 0) > temperature_range_high) { wxString msg_text = _(L("The nozzle may become clogged when the temperature is out of the recommended range.\nPlease make sure whether to use this temperature to print.\n\n")); msg_text += wxString::Format(_L("The recommended nozzle temperature for this filament type is [%d, %d] degrees Celsius."), temperature_range_low, temperature_range_high); @@ -1295,15 +1295,15 @@ int ConfigManipulation::show_spiral_mode_settings_dialog(bool is_object_config) return answer; } -bool ConfigManipulation::get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high, unsigned int variant_index) +bool ConfigManipulation::get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high) { bool range_low_exist = false, range_high_exist = false; if (config->has("nozzle_temperature_range_low")) { - range_low = config->opt_int("nozzle_temperature_range_low", variant_index); + range_low = config->opt_int("nozzle_temperature_range_low", (unsigned int) 0); range_low_exist = true; } if (config->has("nozzle_temperature_range_high")) { - range_high = config->opt_int("nozzle_temperature_range_high", variant_index); + range_high = config->opt_int("nozzle_temperature_range_high", (unsigned int) 0); range_high_exist = true; } return range_low_exist && range_high_exist; diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index bff41effad..ac53ffb4bb 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -79,9 +79,9 @@ public: void apply_null_fff_config(DynamicPrintConfig *config, std::vector const &keys, std::map const & configs); //BBS: FFF filament nozzle temperature range - void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index = 0); - void check_nozzle_temperature_range(DynamicPrintConfig* config, unsigned int variant_index = 0); - void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index = 0); + void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config); + void check_nozzle_temperature_range(DynamicPrintConfig* config); + void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config); void check_adaptive_pressure_advance_model(DynamicPrintConfig* config); void check_filament_max_volumetric_speed(DynamicPrintConfig *config); void check_chamber_temperature(DynamicPrintConfig* config); @@ -104,7 +104,7 @@ public: int show_spiral_mode_settings_dialog(bool is_object_config = false); private: - bool get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high, unsigned int variant_index); + bool get_temperature_range(DynamicPrintConfig *config, int &range_low, int &range_high); }; } // GUI diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 5feebdb777..72849dd78e 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -8895,11 +8895,6 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch if (active_preset_combox) tab->reactive_preset_combo_box(); } - // Preset loading can resize the filament list without an extruder-count change event. - // Refresh the controls even when the list already matches the printer's nozzle count. - if (printer_technology == ptFFF) - this->plater()->on_filament_count_change(preset_bundle->filament_presets.size()); - // BBS: model config for (Tab *tab : model_tabs_list) if (tab->supports_printer_technology(printer_technology)) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22bfe789d1..51fdba9bf8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1803,10 +1803,8 @@ bool Sidebar::priv::switch_diameter_to(const wxString &diameter) auto* nozzle_diameter = dynamic_cast(printer_preset.config.option("nozzle_diameter")); if (nozzle_diameter && nozzle_diameter->size() > 0) { auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]); - // A named variant can share this diameter; selecting the plain diameter - // must still switch back to the standard profile. - if (current_nozzle_dia == diameter.ToStdString() && - printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) { + // If the selected diameter is the same as current nozzle, don't switch profiles + if (current_nozzle_dia == diameter.ToStdString()) { return true; } } @@ -3928,18 +3926,13 @@ void Sidebar::update_presets(Preset::Type preset_type) combo_flow->Show(combo_flow->GetCount() > 0); }; - auto update_extruder_diameter = [&diameters, &nozzle_diameter, &diameter](int extruder_index,ExtruderGroup & extruder) { + auto update_extruder_diameter = [&diameters, &nozzle_diameter](int extruder_index,ExtruderGroup & extruder) { extruder.combo_diameter->Clear(); if (extruder_index >= int(nozzle_diameter->values.size())) return; int select = -1; // ORCA get the actual nozzle diameter from printer config auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]); - // Named variants such as "0.4HS" and "0.4 High Flow" share a physical diameter. - // Retain the variant selection unless the diameter was customized. - const auto selected_variant = - diameter.substr(0, diameter.find_first_not_of("0123456789.")) == nozzle_dia && - std::find(diameters.begin(), diameters.end(), diameter) != diameters.end() ? diameter : nozzle_dia; // ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias if(!diameters.empty() && diameters[0].empty() && !nozzle_dia.empty()){ diameters[0] = nozzle_dia; @@ -3949,7 +3942,7 @@ void Sidebar::update_presets(Preset::Type preset_type) diameters.push_back(nozzle_dia); } for (size_t i = 0; i < diameters.size(); ++i) { - if (diameters[i] == selected_variant) + if (diameters[i] == nozzle_dia) select = extruder.combo_diameter->GetCount(); extruder.combo_diameter->Append(diameters[i], {}); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4698a70561..c824c16d5d 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4396,16 +4396,16 @@ void TabFilament::build() optgroup->append_single_option_line("temperature_vitrification", "material_basic_information#softening-temperature"); optgroup->append_single_option_line("idle_temperature", "material_basic_information#idle-temperature"); Line line = { L("Recommended nozzle temperature"), L("Recommended nozzle temperature range of this filament. 0 means not set") }; - line.append_option(optgroup->get_option("nozzle_temperature_range_low", 0)); - line.append_option(optgroup->get_option("nozzle_temperature_range_high", 0)); + line.append_option(optgroup->get_option("nozzle_temperature_range_low")); + line.append_option(optgroup->get_option("nozzle_temperature_range_high")); optgroup->append_line(line); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { DynamicPrintConfig &filament_config = m_preset_bundle->filaments.get_edited_preset().config; update_dirty(); - if (!m_postpone_update_ui && (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_low" || opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_high")) { - m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); + if (!m_postpone_update_ui && (opt_key == "nozzle_temperature_range_low" || opt_key == "nozzle_temperature_range_high")) { + m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config); } on_value_change(opt_key, value); }; @@ -4532,11 +4532,11 @@ void TabFilament::build() else if (opt_key == "textured_plate_temp" || opt_key == "textured_plate_temp_initial_layer") { m_config_manipulation.check_bed_temperature_difference(BedType::btPTE, &filament_config); } - else */if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature") { - m_config_manipulation.check_nozzle_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); + else */if (opt_key == "nozzle_temperature") { + m_config_manipulation.check_nozzle_temperature_range(&filament_config); } - else if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_initial_layer") { - m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); + else if (opt_key == "nozzle_temperature_initial_layer") { + m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config); } on_value_change(opt_key, value); @@ -4571,12 +4571,12 @@ void TabFilament::build() optgroup = page->new_optgroup(L("Part cooling fan"), L"param_cooling_part_fan"); line = { L("Min fan speed threshold"), L("The part cooling fan will run at the minimum fan speed when the estimated layer time is longer than the threshold value. When the layer time is shorter than the threshold, the fan speed will be interpolated between the minimum and maximum fan speed according to layer printing time.") }; line.label_path = "material_cooling#material-part-cooling-fan"; - line.append_option(optgroup->get_option("fan_min_speed", 0)); + line.append_option(optgroup->get_option("fan_min_speed")); line.append_option(optgroup->get_option("fan_cooling_layer_time")); optgroup->append_line(line); line = { L("Max fan speed threshold"), L("The part cooling fan will run at maximum speed when the estimated layer time is shorter than the threshold value.") }; line.label_path = "material_cooling#material-part-cooling-fan"; - line.append_option(optgroup->get_option("fan_max_speed", 0)); + line.append_option(optgroup->get_option("fan_max_speed")); line.append_option(optgroup->get_option("slow_down_layer_time")); optgroup->append_line(line); optgroup->append_single_option_line("reduce_fan_stop_start_freq", "material_cooling#keep-fan-always-on"); @@ -4592,7 +4592,7 @@ void TabFilament::build() optgroup->append_single_option_line("ironing_fan_speed", "material_cooling#ironing-fan-speed"); // ORCA: Add support for ironing fan speed control optgroup = page->new_optgroup(L("Auxiliary part cooling fan"), L"param_cooling_aux_fan"); - optgroup->append_single_option_line("additional_cooling_fan_speed", "material_cooling#auxiliary-part-cooling-fan", 0); + optgroup->append_single_option_line("additional_cooling_fan_speed", "material_cooling#auxiliary-part-cooling-fan"); optgroup = page->new_optgroup(L("Exhaust fan"),L"param_cooling_exhaust"); @@ -4656,7 +4656,7 @@ void TabFilament::build() page = add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); // ORCA: icon only visible on placeholders optgroup = page->new_optgroup(L("Wipe tower parameters"), "param_tower"); - optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters", 0); + optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_dist", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_length", "material_multimaterial#multimaterial-wipe-tower-parameters"); optgroup->append_single_option_line("filament_tower_ironing_area", "material_multimaterial#multimaterial-wipe-tower-parameters"); @@ -4700,9 +4700,9 @@ void TabFilament::build() }); optgroup = page->new_optgroup(L("Tool change parameters with multi extruder MM printers"), "param_toolchange_multi_extruder"); - optgroup->append_single_option_line("filament_multitool_ramming", "material_multimaterial#tool-change-parameters-with-multi-extruder", 0); - optgroup->append_single_option_line("filament_multitool_ramming_volume", "material_multimaterial#multi-tool-ramming-volume", 0); - optgroup->append_single_option_line("filament_multitool_ramming_flow", "material_multimaterial#multi-tool-ramming-flow", 0); + optgroup->append_single_option_line("filament_multitool_ramming", "material_multimaterial#tool-change-parameters-with-multi-extruder"); + optgroup->append_single_option_line("filament_multitool_ramming_volume", "material_multimaterial#multi-tool-ramming-volume"); + optgroup->append_single_option_line("filament_multitool_ramming_flow", "material_multimaterial#multi-tool-ramming-flow"); page = add_options_page(L("Dependencies"), "advanced"); optgroup = page->new_optgroup(L("Compatible printers"), "param_dependencies_printers"); @@ -4772,7 +4772,6 @@ void TabFilament::update_description_lines() void TabFilament::toggle_options() { - const int variant_index = m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; if (!m_active_page) return; bool is_BBL_printer = false; @@ -4814,7 +4813,7 @@ void TabFilament::toggle_options() } } - toggle_line("additional_cooling_fan_speed", printer_cfg.opt_bool("auxiliary_fan"), 256 + variant_index); + toggle_line("additional_cooling_fan_speed", printer_cfg.opt_bool("auxiliary_fan")); bool support_air_filtration = printer_cfg.opt_bool("support_air_filtration"); for (auto el : {"activate_air_filtration", "during_print_exhaust_fan_speed", "complete_print_exhaust_fan_speed"}) @@ -4830,8 +4829,11 @@ void TabFilament::toggle_options() } if (m_active_page->title() == L("Filament")) { - bool pa = m_config->opt_bool("enable_pressure_advance", variant_index); - toggle_option("pressure_advance", pa, 256 + variant_index); + const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0; + const unsigned int variant_idx = (unsigned int) std::max(selection, 0); + + bool pa = m_config->opt_bool("enable_pressure_advance", variant_idx); + toggle_option("pressure_advance", pa, 0); //Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type. DynamicConfig& proj_cfg = m_preset_bundle->project_config; @@ -4885,11 +4887,11 @@ void TabFilament::toggle_options() for (auto el : {"filament_minimal_purge_on_wipe_tower", "filament_loading_speed_start", "filament_loading_speed", "filament_unloading_speed_start", "filament_unloading_speed", "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed"}) - toggle_option(el, !is_BBL_printer, filament_options_with_variant.count(el) ? 256 + variant_index : -1); + toggle_option(el, !is_BBL_printer); - bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", variant_index); - toggle_option("filament_multitool_ramming_volume", multitool_ramming, 256 + variant_index); - toggle_option("filament_multitool_ramming_flow", multitool_ramming, 256 + variant_index); + bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", 0); + toggle_option("filament_multitool_ramming_volume", multitool_ramming); + toggle_option("filament_multitool_ramming_flow", multitool_ramming); bool is_BBL_multi_extruder = is_BBL_printer && printer_cfg.option("nozzle_diameter")->size() > 1; const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0; diff --git a/tests/libslic3r/test_config_variant_expansion.cpp b/tests/libslic3r/test_config_variant_expansion.cpp index 67a16d2f47..de19e8219c 100644 --- a/tests/libslic3r/test_config_variant_expansion.cpp +++ b/tests/libslic3r/test_config_variant_expansion.cpp @@ -1,7 +1,6 @@ #include #include "libslic3r/PrintConfig.hpp" -#include "libslic3r/Preset.hpp" using namespace Slic3r; @@ -31,95 +30,6 @@ void add_print_variant_columns(DynamicPrintConfig &config) } // namespace -TEST_CASE("Filament cooling and pressure settings follow the selected nozzle variant", "[Config][FilamentVariants]") -{ - struct Setting { - const char *key; - const char *standard; - const char *high_flow; - }; - const Setting settings[] = { - {"enable_pressure_advance", "0", "1"}, - {"pressure_advance", "0.04", "0.02"}, - {"fan_min_speed", "15", "10"}, - {"fan_max_speed", "15", "60"}, - {"additional_cooling_fan_speed", "70", "0"}, - {"filament_minimal_purge_on_wipe_tower", "0", "15"}, - {"filament_multitool_ramming", "0", "1"}, - {"filament_multitool_ramming_volume", "0", "5"}, - {"filament_multitool_ramming_flow", "10", "30"}, - {"nozzle_temperature_range_low", "190", "210"}, - {"nozzle_temperature_range_high", "255", "280"}, - }; - const int variant_index = GENERATE(0, 1, 2, 3); - const int value_count = GENERATE(1, 2, 4); - const bool load_preset = GENERATE(false, true); - const bool high_flow = variant_index % 2 != 0; - for (const Setting &setting : settings) { - DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " values=" << value_count << " loaded=" << load_preset) { - DynamicPrintConfig printer = make_hybrid_printer_config(); - if (variant_index >= 2) - printer.option("extruder_type")->values[1] = etBowden; - DynamicPrintConfig filament; - filament.option("filament_extruder_variant", true)->values = - {"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"}; - std::string values = setting.standard; - for (int i = 1; i < value_count; ++i) - values += std::string(",") + (i % 2 ? setting.high_flow : setting.standard); - REQUIRE(filament.option(setting.key, true)->deserialize(values)); - - if (load_preset) { - extend_default_config_length(filament, false, DynamicPrintConfig::full_print_config()); - DynamicPrintConfig expanded; - std::string expanded_values = values; - for (int i = value_count; i < 4; ++i) - expanded_values += std::string(",") + setting.standard; - REQUIRE(expanded.option(setting.key, true)->deserialize(expanded_values)); - REQUIRE(*filament.option(setting.key) == *expanded.option(setting.key)); - } - - std::vector> nozzle_types; - const int count = printer.get_extruder_nozzle_volume_count(2, nozzle_types); - filament.update_values_to_printer_extruders(printer, 2, count, nozzle_types, - filament_options_with_variant, "", "filament_extruder_variant", 1, 2, - high_flow ? nvtHighFlow : nvtStandard); - - DynamicPrintConfig expected; - REQUIRE(expected.option(setting.key, true)->deserialize(high_flow && variant_index < value_count ? setting.high_flow : setting.standard)); - REQUIRE(*filament.option(setting.key) == *expected.option(setting.key)); - } - } -} - -TEST_CASE("Filaments on the same hybrid tool keep their own cooling and pressure variants", "[Config][FilamentVariants]") -{ - DynamicPrintConfig config = make_hybrid_printer_config(); - config.option("filament_self_index", true)->values = {1, 1, 2, 2}; - config.option("filament_extruder_variant", true)->values = - {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}; - config.option("filament_map", true)->values = {2, 2}; - config.option("filament_volume_map", true)->values = {nvtHighFlow, nvtStandard}; - config.option("fan_max_speed", true)->values = {15., 60., 20., 80.}; - config.option("pressure_advance", true)->values = {0.04, 0.02, 0.05, 0.03}; - config.option("filament_multitool_ramming", true)->values = {false, true, false, true}; - std::vector> nozzle_types; - const int count = config.get_extruder_nozzle_volume_count(2, nozzle_types); - std::set keys = filament_options_with_variant; - keys.insert("filament_self_index"); - config.update_values_to_printer_extruders_for_multiple_filaments(config, 2, count, keys, - "filament_self_index", "filament_extruder_variant"); - const auto &fan = config.option("fan_max_speed")->values; - REQUIRE(fan.size() == 2); - REQUIRE_THAT(fan[0], Catch::Matchers::WithinAbs(60., 1e-9)); - REQUIRE_THAT(fan[1], Catch::Matchers::WithinAbs(20., 1e-9)); - const auto &pa = config.option("pressure_advance")->values; - REQUIRE(pa.size() == 2); - REQUIRE_THAT(pa[0], Catch::Matchers::WithinAbs(0.02, 1e-9)); - REQUIRE_THAT(pa[1], Catch::Matchers::WithinAbs(0.05, 1e-9)); - REQUIRE(config.option("filament_multitool_ramming")->get_at(0)); - REQUIRE_FALSE(config.option("filament_multitool_ramming")->get_at(1)); -} - TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]") { ConfigOptionFloats machine({10., 20., 30.}); From 4da478c7ad5371da6f4f49f7643587c5288597cd Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 30 Sep 2026 08:55:52 +0200 Subject: [PATCH 70/89] Write the documentation in plain ASCII Only the degree sign is left, as a unit, which the gizmos already use. --- TEXTURE_DISPLACEMENT.md | 62 +++++++++++------------ TEXTURE_DISPLACEMENT_GUIDE.md | 92 +++++++++++++++++------------------ 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/TEXTURE_DISPLACEMENT.md b/TEXTURE_DISPLACEMENT.md index 93d668b68f..b5c69a060a 100644 --- a/TEXTURE_DISPLACEMENT.md +++ b/TEXTURE_DISPLACEMENT.md @@ -93,7 +93,7 @@ same order - only the positions of displaced vertices differ. coverage per original triangle comes straight off `TriangleSplittingData::triangles_to_split`. 3. For each layer in slot order: deserialize its stored paint mask into a `TriangleSelector` against the **base mesh** (never against a previous layer's output), then - `selector.get_facets_strict(ENFORCER)` → the painted patch. Two facts are exploited: + `selector.get_facets_strict(ENFORCER)` -> the painted patch. Two facts are exploited: - `get_facets_strict()` returns the mesh's **entire** referenced vertex array regardless of which state was asked for - only `.indices` is filtered by state. So `get_facets_strict(ENFORCER)` and `get_facets_strict(NONE)` share identical vertex indexing, which is what lets boundary @@ -162,7 +162,7 @@ sampling a white (1.0) texel multiplies by exactly 1, i.e. leaves the layers bel Divide floors its divisor's magnitude at 0.05: a black texel samples to *exactly* zero, so the divisor really does hit zero in ordinary use, and an unbounded `1/0` would fling vertices thousands of mm away and poison the mesh's bounding box (and every plate/print-volume check downstream). The floor doubles as -a cap on how far Divide can amplify the relief beneath it: at most 20×. +a cap on how far Divide can amplify the relief beneath it: at most 20x. The **lowest painted layer ignores its blend mode**: it has nothing beneath it, and Multiply/Divide against an implicit zero base would annihilate (or blow up) it. Enforced in @@ -179,7 +179,7 @@ texture samples per vertex and so has no single UV that represents it. - **Triplanar** (default) - samples the texture on all three world planes (`(y,z)`, `(x,z)`, `(x,y)`) and blends the three by the vertex's own normal raised to `TRIPLANAR_BLEND_SHARPNESS` (4). Hard-picking the single axis most aligned with the normal instead is discontinuous wherever that dominant axis - flips: on a +X face the planar coordinate is `(y, z)`, on a −Y face it is `(x, z)`, so at the shared + flips: on a +X face the planar coordinate is `(y, z)`, on a -Y face it is `(x, z)`, so at the shared edge `u` jumps. A weighted blend is continuous across the transition by construction, since the weight of the axis being left behind falls smoothly to zero. This removes the hard *seam*; some cross-fade blurring in the band right at a 90° edge is inherent to triplanar mapping. A genuinely seam-free wrap @@ -228,7 +228,7 @@ texture samples per vertex and so has no single UV that represents it. `TextureDisplacementLayer::lscm_seam_edges` - undirected mesh-vertex-index edge pairs the unwrap is forced to cut along, on top of the dihedral-angle seams. `segment_into_charts()` takes a set of these -(translated from mesh → compacted-patch numbering inside `compute_patch_unwrap()`) and refuses to +(translated from mesh -> compacted-patch numbering inside `compute_patch_unwrap()`) and refuses to union two triangles across a marked edge whatever their angle. Both the unwrap cache key and the gizmo's `UVEditorState` include the seam list, so marking a seam (which leaves the paint mask untouched) still forces a re-solve. Like the paint masks, seams are mesh-index-space and so dropped on @@ -251,7 +251,7 @@ Two ways to write to it: drawn over the painted patch (`rebuild_uvcheck_mesh()`/`render_uvcheck_mesh()`, P3N3T2: `normal.x` = distortion, `tex_coord` = uv), pulled forward with a polygon offset. **Checker** samples a procedural checkerboard at the layer's uv (per-vertex for LSCM/ViewProjected, in-shader triplanar otherwise) - -squares that stay square mean low distortion. **Distortion** colours each triangle blue→green→red by +squares that stay square mean low distortion. **Distortion** colours each triangle blue->green->red by `log2(uv_area / surface_area)` centred on the patch's *median* stretch (so a globally-scaled unwrap reads as uniformly ideal and only relative stretch shows), averaged to vertices. A separate **Show mesh wireframe** toggle draws the whole volume's triangle edges, rebuilt only when the vertex count changes @@ -264,16 +264,16 @@ wireframe** toggle draws the whole volume's triangle edges, rebuilt only when th directly** rather than clamping the *coordinate* into range, which would smear the border row/column of pixels outward to infinity in every direction (streaky lines radiating out from the painted patch). -### Subdivision — two modes +### Subdivision - two modes -**Uniform (`subdivide_mesh_uniform()`)** — whole-mesh, 1-to-4 split. Recursive edge-midpoint split with +**Uniform (`subdivide_mesh_uniform()`)** - whole-mesh, 1-to-4 split. Recursive edge-midpoint split with a shared per-pass midpoint cache (keyed by sorted vertex-index pair) so triangles sharing an edge get the *same* new vertex - capped at `max_iterations` (default 6). Whole-mesh so it never leaves a -T-junction, at the cost of densifying everywhere. Wired as a "Subdivide steps" slider (**0–5**, 0 = +T-junction, at the cost of densifying everywhere. Wired as a "Subdivide steps" slider (**0-5**, 0 = no subdivision), Apply snaps back to 0. Drops texture-displacement paint (no remap) via the standard `save_painting()`/`set_mesh()`/`restore_painting()` dance; the other four channels are remapped. -**Adaptive (`subdivide_mesh_adaptive()`)** — refine **only the painted area**, by **Rivara longest-edge +**Adaptive (`subdivide_mesh_adaptive()`)** - refine **only the painted area**, by **Rivara longest-edge bisection**, which is *conformal by construction*. Only **terminal** edges are ever bisected - an edge that is the longest edge of *every* triangle sharing it - which splits both those triangles along one shared midpoint at once, so a hanging node is never created. The edge to split for a triangle that wants @@ -325,8 +325,8 @@ a linear **ramp** needs no extra vertices (linear interpolation is exact for a r test: sample the combined displacement at the triangle's three edge midpoints *and its centroid* (sampling the interior is what catches a hill sitting inside a triangle, the blind spot of an edge-only test) and take the largest departure from the flat triangle's barycentric interpolation. Refine while -that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp ⇒ untouched; high on a -hill/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The +that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp => untouched; high on a +hill/ridge/noise => refined until captured. Same conformal machinery, so still crack-free. The per-triangle error is cached and recomputed only for the children of a split. Four knobs bracket it, and all four matter: @@ -371,10 +371,10 @@ array is longer. `rebuild_shaded_preview_mesh()` and `rebuild_uvcheck_mesh()` th preview (stroke-end/slider-release) but from the **live** `TriangleSelector` state, not the flushed model facets, so it does not lag by a full model round-trip. -The perturbed normal is the analytic one for a height field `H = ±depth_mm · h(uv)` displaced along +The perturbed normal is the analytic one for a height field `H = +/-depth_mm - h(uv)` displaced along `N` over any orthonormal surface tangent pair `T`/`B`: - N' = normalize(N − (dH/da)·T − (dH/db)·B), a = dot(p,T), b = dot(p,B) + N' = normalize(N - (dH/da)-T - (dH/db)-B), a = dot(p,T), b = dot(p,B) The two slopes have to be genuine **mm-per-mm** derivatives for the preview's apparent depth to match the bake's. @@ -389,28 +389,28 @@ the bake's. the CPU (`compute_layer_vertex_uvs()`, so island placement + tiling/rotation/offset are already folded in), and the perturbed normal is built with **Mikkelsen's method** ("Bump Mapping Unparametrized Surfaces on the GPU"): the surface gradient taken directly from the screen-space derivatives of the - *sampled height* and position. **This makes no uv→mm scale assumption**, which is essential, because an + *sampled height* and position. **This makes no uv->mm scale assumption**, which is essential, because an LSCM map is **conformal, not isometric**: it is globally area-scaled but the *local* mm-per-uv varies across the chart, so a single global `1/tiling_scale` factor gets the apparent depth wrong. `dFdx(h)` captures the true on-screen rate of change however the chart is stretched. This path is also what makes the fast preview follow the UV editor: move an island and its uv - hence its shading - moves with it - (the mesh rebuilds on drag-end, `on_island_edited(finished)` → `rebuild_preview()` → + (the mesh rebuilds on drag-end, `on_island_edited(finished)` -> `rebuild_preview()` -> `rebuild_shaded_preview_mesh()`). The branch is uniform and the paint weight gates by multiply, so the - texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv → the - `det≈0` guard skips it (a localised preview-only artifact, never in the bake). + texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv -> the + `det~0` guard skips it (a localised preview-only artifact, never in the bake). **Parallax (triplanar path).** Perturbing the shading normal alone welds the pattern to the base surface: it does not slide as the camera orbits, and does not get deeper as `depth_mm` grows. The triplanar path therefore shades at the point the *displaced* surface would show at this pixel, found by **ray marching** -(parallax occlusion mapping). A point at ray parameter `s`, i.e. `P + V·s` (`P` the base point, `V` the -unit direction to the eye), sits at height `s·dot(V,n)` above the undisplaced surface. The displaced -surface lives in a shell between the extreme values of `amp·(h − midlevel)` - taken from both ends of -`h ∈ [0,1]`, so it holds for an inverted layer and a raised midlevel too, where the surface sits *below* +(parallax occlusion mapping). A point at ray parameter `s`, i.e. `P + V-s` (`P` the base point, `V` the +unit direction to the eye), sits at height `s-dot(V,n)` above the undisplaced surface. The displaced +surface lives in a shell between the extreme values of `amp-(h - midlevel)` - taken from both ends of +`h in [0,1]`, so it holds for an inverted layer and a raised midlevel too, where the surface sits *below* the undisplaced one. The march starts at the top of that shell, where the ray is outside the surface by construction, and steps inward until the ray height drops below the sampled height. That crossing *is* the visible point. -Solving `Q = P + V·(H(Q)/dot(V,n))` by fixed-point iteration instead is geometrically exact but the +Solving `Q = P + V-(H(Q)/dot(V,n))` by fixed-point iteration instead is geometrically exact but the divisor goes to zero edge-on; the sample then lands a large fraction of a tile away and the iteration oscillates, which reads as a second, flat copy of the pattern ghosted over the real one. Clamping the step to one tile does not help - a tile-sized shift lands on the neighbouring tile, the same pattern @@ -420,7 +420,7 @@ enough that the relief still flattens as soon as the camera tilts. Marching has The hit is interpolated between the last two samples, which keeps `PARALLAX_STEPS` (24) affordable, and the whole march is skipped when sweeping the shell would move the sample point less than half a texel - the head-on case, so the common view pays almost nothing. The 140 variant samples with -`textureLod(…, 0.0)` inside the loop, since implicit derivatives are undefined in non-uniform control +`textureLod(..., 0.0)` inside the loop, since implicit derivatives are undefined in non-uniform control flow. Two uniforms exist for this: `midlevel` (parallax needs the real height, not just its derivative) and `eye_model_pos` (the camera in the volume's local frame). @@ -457,17 +457,17 @@ but the bake samples from a **local-space** position, so the two have to be reco `view_project_right/up` can only express an *affine* projection - exact under an orthographic camera, but wrong under perspective, where the near end of a part projects larger than the far end and no pair of axes reproduces that. So the layer instead stores a full projective map (`view_project_matrix`, row-major -3×4, `uv = (row0·p̃/row2·p̃, row1·p̃/row2·p̃)`), built like this: +3x4, `uv = (row0-p/row2-p, row1-p/row2-p)`), built like this: -- `K = projection · view · (instance · volume)`, i.e. local → clip, the same product the renderer uses. +- `K = projection - view - (instance - volume)`, i.e. local -> clip, the same product the renderer uses. Note `Camera::get_projection_matrix()` is typed `Transform3d` (nominally affine) but its perspective - form explicitly writes a `(0, 0, −1, 0)` bottom row into the underlying 4×4, so `clip.w = −z_eye` is + form explicitly writes a `(0, 0, -1, 0)` bottom row into the underlying 4x4, so `clip.w = -z_eye` is genuinely carried. The build therefore multiplies **`.matrix()` products** (plain `Matrix4d`), never `Transform3d` products, which would not compose that row correctly. - Window coordinates follow `igl::project`'s convention (as `CameraUtils::project` does), with y - measured downward. Writing `uv = (win − rect_origin) / rect_size` makes u and v affine in + measured downward. Writing `uv = (win - rect_origin) / rect_size` makes u and v affine in `ndc = clip.xyz / clip.w`; multiplying through by `clip.w` leaves a plain linear combination of `K`'s - rows, which is exactly the 3×4 matrix - the perspective divide survives intact. + rows, which is exactly the 3x4 matrix - the perspective divide survives intact. - `w > 0` is checked rather than divided blindly. A point behind the projector has `w < 0` and divides to a plausible-looking but **mirrored** uv - the classic way a projected decal reappears on the back of a model. `project_uv_projective()` returns false there and the caller treats it as no height. @@ -512,7 +512,7 @@ changes (`unwrap_changed` in `update_uv_editor()`). **Gestures** (canvas-owned, reported to the gizmo as incremental deltas via `IslandEditFn`): left-drag = move, right-drag or **R** = rotate (hold **Shift** to snap to 15° steps - quantised on the *cumulative* rotation, not each delta, so it doesn't judder, and accumulated incrementally so it -survives crossing ±180°), **S** = scale (R/S modal, click/Enter to confirm, Esc to cancel), wheel = +survives crossing +/-180°), **S** = scale (R/S modal, click/Enter to confirm, Esc to cancel), wheel = zoom about the cursor, middle-drag = pan, **Home**/**F** = frame all. Scale writes `TextureIsland::scale`; "Avg scale" (`average_island_scales()`) sets every island to the mean, so one island scaled by hand can be matched back to its neighbours' texel density. **Snap** (canvas-owned @@ -542,11 +542,11 @@ gizmo via `CommandFn`; view-only ones (Frame, Snap) it handles directly. - `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` - the gizmo and its whole panel. - `src/slic3r/GUI/TextureLibrary.hpp/.cpp` - scans the shipped + user texture folders, imports an arbitrary image into the user folder (converting it to the 8-bit grayscale PNG libslic3r decodes), - and loads a library file's bytes for a layer. The image→grayscale-PNG conversion lives here, on the + and loads a library file's bytes for a layer. The image->grayscale-PNG conversion lives here, on the GUI side, because libslic3r has no image toolkit; both the import path and the "pick a shipped texture" path go through the same one function. - `resources/textures/displacement/*.png` - the 10 shipped height maps (Bricks, Grid, Hexagons, - Knurl, Noise, Quilt, Studs, Waves, Weave, Wood Grain). All 512×512 8-bit grayscale and **seamless** + Knurl, Noise, Quilt, Studs, Waves, Weave, Wood Grain). All 512x512 8-bit grayscale and **seamless** (each is periodic over the full image in both axes, so tiling shows no seam). Generated procedurally; the whole `resources/` tree is installed recursively by CMake, so a new folder under it ships with no build-system change. diff --git a/TEXTURE_DISPLACEMENT_GUIDE.md b/TEXTURE_DISPLACEMENT_GUIDE.md index 3400449736..5abc9b94e2 100644 --- a/TEXTURE_DISPLACEMENT_GUIDE.md +++ b/TEXTURE_DISPLACEMENT_GUIDE.md @@ -1,7 +1,7 @@ -# Texture Displacement — Feature & Controls Guide +# Texture Displacement - Feature & Controls Guide Texture Displacement is a paint-style gizmo that stamps height-map textures onto a model's surface -and turns them into real relief — engraved or embossed detail — either as a live preview or baked +and turns them into real relief - engraved or embossed detail - either as a live preview or baked into actual mesh geometry. You paint where the texture applies, stack multiple textures as blended layers, choose how each is projected onto the surface, and (for the unwrap projection) lay the result out by hand in a dedicated 2D **UV Editor** pane. @@ -66,7 +66,7 @@ Choose *how* you paint. All three write into the **active layer's** mask. | **Face** | Click a single triangle to paint it. | | **Connected area** | Click to flood-fill a region; the **Angle threshold** slider limits how far the fill spreads across changes in surface angle. | -- **Select whole model** — marks the entire model as painted for the active layer, instead of +- **Select whole model** - marks the entire model as painted for the active layer, instead of brushing it by hand. --- @@ -78,17 +78,17 @@ radio group; **Wireframe** is an independent toggle. Hover any icon for its tool | View | Meaning | |------|---------| -| **Normal** | The true displaced geometry — exactly what **Bake** produces. Rebuilt in the background. | -| **Fast** | A GPU shaded approximation of the *active layer only*. No real geometry movement — quick to update, not exact. Best while tuning or dragging islands. | +| **Normal** | The true displaced geometry - exactly what **Bake** produces. Rebuilt in the background. | +| **Fast** | A GPU shaded approximation of the *active layer only*. No real geometry movement - quick to update, not exact. Best while tuning or dragging islands. | | **Checker** | A test grid painted over the unwrap so you can see stretching (squares stay square where the map isn't distorted). | -| **Distortion** | A blue→green→red heatmap of how much each area is compressed or stretched in UV space. Needs the **Unwrap (LSCM)** projection. | +| **Distortion** | A blue->green->red heatmap of how much each area is compressed or stretched in UV space. Needs the **Unwrap (LSCM)** projection. | | **Wireframe** | Overlays the mesh edges (white). Independent of the view above; in **Normal** view it sits on the displaced surface. | --- ## Auto update -**Auto update** (on by default) rebuilds the true displaced geometry as soon as *anything* changes — +**Auto update** (on by default) rebuilds the true displaced geometry as soon as *anything* changes - painting, swapping textures, moving sliders. Turn it off on very heavy models to only rebuild when you release a slider (painting still updates on stroke end). @@ -99,12 +99,12 @@ release a slider (painting still updates on stroke end). You can stack up to **8** texture layers. Each has its own independent paint mask, its own texture, and its own parameters, and they combine in slot order like layers in an image editor. -- **Add a layer** — the **+ icon** to the right of the *Texture layers* heading (reuses the tool icon +- **Add a layer** - the **+ icon** to the right of the *Texture layers* heading (reuses the tool icon for now). -- **Remove** — the button on each layer's header row. -- **Active layer** — click a layer's header (or anywhere in its block) to make it active. The active +- **Remove** - the button on each layer's header row. +- **Active layer** - click a layer's header (or anywhere in its block) to make it active. The active layer is the one you paint into and the one whose block is tinted. Only one layer is active at a time. -- **Erase all** — clears the active layer's paint. +- **Erase all** - clears the active layer's paint. Each layer shows a texture **picker** (large preview + name). Open it to choose from the shipped library or import your own image (any png/jpg/bmp; it's converted to an 8-bit grayscale height map and @@ -116,12 +116,12 @@ copied into your user texture folder so app updates can't overwrite it). | Control | Range / options | What it does | |---------|-----------------|--------------| -| **Depth (mm)** | 0.01–10 (log) | Maximum displacement along the surface normal. | -| **Tile size (mm)** | 0.2–200 (log) | Physical size of one texture tile on the surface. | -| **Rotation** | 0–360° | Rotates the texture on the surface. | -| **Midlevel** | 0–10 | The grey level that means "don't move". At 0 the texture only pushes outward; raise it and darker texels cut *inward* (one map both embosses and engraves). 0.5 makes mid-grey neutral. | -| **Smoothing** | 0–1 | Blurs the height texture before it displaces — rounds hard edges and removes speckle without needing a softer source image. | -| **Edge smoothing** | checkbox + **Edge amount** 0–1 | Fades the relief to flat toward the *edge of the painted area*, so it blends into the surrounding surface. A small amount softens only a thin band at the very edge; the maximum flattens the whole painted face. | +| **Depth (mm)** | 0.01-10 (log) | Maximum displacement along the surface normal. | +| **Tile size (mm)** | 0.2-200 (log) | Physical size of one texture tile on the surface. | +| **Rotation** | 0-360° | Rotates the texture on the surface. | +| **Midlevel** | 0-10 | The grey level that means "don't move". At 0 the texture only pushes outward; raise it and darker texels cut *inward* (one map both embosses and engraves). 0.5 makes mid-grey neutral. | +| **Smoothing** | 0-1 | Blurs the height texture before it displaces - rounds hard edges and removes speckle without needing a softer source image. | +| **Edge smoothing** | checkbox + **Edge amount** 0-1 | Fades the relief to flat toward the *edge of the painted area*, so it blends into the surrounding surface. A small amount softens only a thin band at the very edge; the maximum flattens the whole painted face. | | **Invert** | checkbox | Flips the height map (peaks become valleys). | | **Blend** | Add / Subtract / Multiply / Divide | How this layer combines with the layers **below** it where they overlap. Add/Subtract pile relief on or carve it away; Multiply/Divide scale the relief underneath (a mask). The lowest painted layer is the **Base** and always behaves additively. | | **Tile** | checkbox + **Repeat / Mirrored repeat** | When off, the texture is placed once (a decal) instead of repeating. Mirrored repeat flips every other tile to hide seams. | @@ -149,16 +149,16 @@ How the 2D texture is wrapped onto the 3D painted area. These appear when a layer uses **Unwrap (LSCM)**: -- **Seam angle** (5–90°) — edges sharper than this are cut so each piece lies flat. Lower cuts more +- **Seam angle** (5-90°) - edges sharper than this are cut so each piece lies flat. Lower cuts more (less stretching, more seams); raise to keep more in one piece. A box's 90° corners are cut by default. *Ignored once you've marked any seam by hand* (your seams then define the pieces). -- **Connect islands** (on by default) — lays the unwrap out as a **connected net**: pieces that share +- **Connect islands** (on by default) - lays the unwrap out as a **connected net**: pieces that share an edge are unfolded next to each other (a cube becomes a joined net instead of six loose squares). They stay separate islands, so you can still move any of them by hand. Turn off for the classic packed-grid layout. -- **Open UV editor** — shows the flattened unwrap in a side pane (see below). Opens *only* when you - turn this on — it never pops up on its own. -- **Mark seams** / **Path** / **Clear seams** — see [Seams](#seams). +- **Open UV editor** - shows the flattened unwrap in a side pane (see below). Opens *only* when you + turn this on - it never pops up on its own. +- **Mark seams** / **Path** / **Clear seams** - see [Seams](#seams). - An **Unwrap: N islands, F faces, V verts** read-out tells you what the unwrap actually produced. --- @@ -166,9 +166,9 @@ These appear when a layer uses **Unwrap (LSCM)**: ## The UV Editor A dockable 2D pane (enable **Open UV editor** on an LSCM layer) showing the flattened unwrap over the -height texture. Islands are the flattened pieces; you can rearrange them freely — nothing re-packs them +height texture. Islands are the flattened pieces; you can rearrange them freely - nothing re-packs them behind your back. Moving an island updates the model **live** (in Fast view it tracks the cursor -smoothly, via a shader uniform — no rebuild until you release). +smoothly, via a shader uniform - no rebuild until you release). ### Navigation @@ -185,7 +185,7 @@ smoothly, via a shader uniform — no rebuild until you release). | Select | Left-click an island | | Move | Left-drag | | Rotate | Right-drag, or press **R** then move the mouse (click/Enter to confirm, Esc to cancel) | -| Rotate snapped | Hold **Shift** while rotating — snaps to **global** 15° marks (0/15/30…). A protractor dial with tick marks and the current angle is shown. | +| Rotate snapped | Hold **Shift** while rotating - snaps to **global** 15° marks (0/15/30...). A protractor dial with tick marks and the current angle is shown. | | Scale | Press **S** then move the mouse (click/Enter to confirm, Esc to cancel) | | Undo / Redo | **Ctrl+Z** / **Ctrl+Shift+Z** or **Ctrl+Y** | @@ -198,35 +198,35 @@ A **status line** along the bottom always names the current gesture and the shor | Button | Action | |--------|--------| | **Frame** | Frame all islands (same as Home). | -| **Snap** | Toggle magnetic snapping — a dragged island sticks its boundary to a neighbour's when they come close. | +| **Snap** | Toggle magnetic snapping - a dragged island sticks its boundary to a neighbour's when they come close. | | **Avg scale** | Give every island the same texel density (Blender's "Average Islands Scale"). | | **Cut** | Split the selected island across its long axis (useful for very long islands). | -| **Join** | Unfold the selected island onto its nearest neighbour along their shared edge — keeps both as separate islands with their own borders. | +| **Join** | Unfold the selected island onto its nearest neighbour along their shared edge - keeps both as separate islands with their own borders. | | **Unjoin** | Send the selected island back to its own packed position. | -> **Checker / Distortion in the UV editor:** selecting those View modes also colours the UV pane — a -> checker background, or a per-island distortion heatmap — so you can judge stretch in 2D as well as +> **Checker / Distortion in the UV editor:** selecting those View modes also colours the UV pane - a +> checker background, or a per-island distortion heatmap - so you can judge stretch in 2D as well as > on the model. --- ## Seams -Seams are edges the unwrap is forced to cut along, on top of whatever the Seam angle cuts — the +Seams are edges the unwrap is forced to cut along, on top of whatever the Seam angle cuts - the Blender "mark seam" workflow. They let you control exactly where the unwrap splits. Enable **Mark seams** on an active LSCM layer, then: - **Click an edge** on the model to mark it (it turns **red**); click a red edge again to unmark it. The edge under the cursor is highlighted **yellow** so you can see what a click will toggle. -- **Path mode** (the **Path** checkbox) — for dense meshes where clicking each edge is tedious: click a +- **Path mode** (the **Path** checkbox) - for dense meshes where clicking each edge is tedious: click a start point, then an end point, and the whole **shortest path** between them is seamed at once. It chains (each click extends from the last point); the start vertex is shown in **green**. - **Ctrl+drag** rotates/pans the camera while in seam mode. - **Clear seams** removes them all. Once any seam is marked, the automatic Seam-angle cutting is disabled so *your* seams define the -islands — pieces you leave un-seamed merge together. +islands - pieces you leave un-seamed merge together. --- @@ -245,20 +245,20 @@ Displacement can only move vertices that exist, so a coarse model needs more of ### Subdivide -Splits every triangle into four, **1–5 times** (each step roughly quadruples the triangle count). +Splits every triangle into four, **1-5 times** (each step roughly quadruples the triangle count). -- **Subdivide steps** (1–5) — how many times to split. -- **Preview subdivision** — shows the result as a **cyan wireframe** without changing the model. -- **Apply** — commits the subdivision to the geometry. -- **Done** — ends the preview and leaves the model as it is. +- **Subdivide steps** (1-5) - how many times to split. +- **Preview subdivision** - shows the result as a **cyan wireframe** without changing the model. +- **Apply** - commits the subdivision to the geometry. +- **Done** - ends the preview and leaves the model as it is. ### Remesh -Rebuilds the whole model with triangles close to a target edge length — evens out a mesh with wildly +Rebuilds the whole model with triangles close to a target edge length - evens out a mesh with wildly varying triangle sizes (CGAL isotropic remeshing). -- **Target edge (mm)** — desired triangle edge length (seeded to the model's current average). -- **Remesh** — splits the big triangles and merges the small ones to that size. +- **Target edge (mm)** - desired triangle edge length (seeded to the model's current average). +- **Remesh** - splits the big triangles and merges the small ones to that size. > Both Subdivide-Apply and Remesh **replace the geometry** and clear any *not-yet-baked* paint on it > (already-baked relief is kept). If you had the mesh **Wireframe** on before, it stays on afterward. @@ -267,9 +267,9 @@ varying triangle sizes (CGAL isotropic remeshing). ## Baking & resetting -- **Bake** — converts the current preview into real, permanent mesh geometry, restricted to the - painted area. Runs in the background; the button shows *Baking…* while it works. -- **Erase all** — clears the active layer's paint. +- **Bake** - converts the current preview into real, permanent mesh geometry, restricted to the + painted area. Runs in the background; the button shows *Baking...* while it works. +- **Erase all** - clears the active layer's paint. Baking is the exact same algorithm as the **Normal** preview, so what you see is what you get. @@ -277,7 +277,7 @@ Baking is the exact same algorithm as the **Normal** preview, so what you see is ## Controls reference -### Mouse — 3D view (while painting) +### Mouse - 3D view (while painting) | Input | Action | |-------|--------| @@ -285,7 +285,7 @@ Baking is the exact same algorithm as the **Normal** preview, so what you see is | Ctrl + drag | Rotate / pan the camera (works in seam mode too) | | Wheel | Zoom | -### Mouse & keys — UV Editor +### Mouse & keys - UV Editor | Input | Action | |-------|--------| @@ -314,7 +314,7 @@ Baking is the exact same algorithm as the **Normal** preview, so what you see is - **Paint first, then bake.** The preview is free to explore; only Bake changes the real mesh. - **Not enough detail?** Subdivide or Remesh before painting fine textures. - **Inward cuts** (high Midlevel + big Depth) can self-intersect on thin walls or sharp concave - corners — keep Depth modest there. + corners - keep Depth modest there. - **Fast vs Normal:** Fast preview only shades the relief and shows only the active layer; use it for quick tuning and smooth UV dragging, but trust **Normal**/**Bake** for the exact result. - **Topology changes drop unbaked paint.** Subdivide-Apply, Remesh, and Simplify replace the mesh, and From 5d49423faab5b70954e027be9691423dbe978128 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Wed, 30 Sep 2026 09:02:33 +0200 Subject: [PATCH 71/89] Use plain ASCII in the panel labels The degree sign stays, as a unit, the way the other gizmos write it. --- src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 4 ++-- src/slic3r/GUI/UVEditorCanvas.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index f24a3125e7..5c322a1f81 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -6919,9 +6919,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (!slot_painted(l.slot)) skipped.push_back(layer_name(l)); if (skipped.size() > 2) - note += " · " + Slic3r::format(_u8L("%1% layers not painted, skipped"), skipped.size()); + note += ", " + Slic3r::format(_u8L("%1% layers not painted, skipped"), skipped.size()); else if (!skipped.empty()) - note += " · " + Slic3r::format(_u8L("%1% not painted, skipped"), + note += ", " + Slic3r::format(_u8L("%1% not painted, skipped"), skipped.size() == 2 ? skipped[0] + ", " + skipped[1] : skipped[0]); } ImGui::PushTextWrapPos(x0 + panel_w); diff --git a/src/slic3r/GUI/UVEditorCanvas.cpp b/src/slic3r/GUI/UVEditorCanvas.cpp index 855e8630d7..dd0432bc95 100644 --- a/src/slic3r/GUI/UVEditorCanvas.cpp +++ b/src/slic3r/GUI/UVEditorCanvas.cpp @@ -2086,7 +2086,7 @@ void UVEditorPanel::apply_state(const UVEditorCanvas::PaneState &s) m_clear_seams->Enable(s.has_layer && s.has_seams); m_clear_edits->Enable(s.has_layer && s.has_uv_edits); - m_stats->SetLabel(s.unwrapped ? wxString::Format(_L("%d islands · %s faces"), s.island_count, + m_stats->SetLabel(s.unwrapped ? wxString::Format(_L("%d islands, %s faces"), s.island_count, wxString(std::to_string(s.face_count))) : wxString()); refresh_selection_tools(); From 31e7dc0845e60e87b97911b50409c7ffac89a052 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 19:31:43 +0800 Subject: [PATCH 72/89] Fix texture bake stalling at 99% while simplifying A vertex pinned on flat ground could collect a fan of thousands of slivers, and validating each collapse next to it walked the whole fan, turning a second of simplification into minutes. Collapses that would leave more than 64 faces around a vertex are now skipped. --- .../TextureBake/TextureBakeDecimate.cpp | 17 ++++++++++++----- .../TextureBake/TextureBakeDecimate.hpp | 5 +++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index 13d18c5314..afb9976bb2 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -236,10 +236,10 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } } - // Vertex-face incidence as intrusive linked lists of slots over flat arrays. + // Vertex-face incidence as intrusive linked lists of slots over flat arrays, with each list's length. const size_t S = face_count * 3; - std::vector vf_head(vert_count, -1), slot_face(S), slot_vert(S), slot_next(S, -1), - slot_prev(S, -1), face_slot(S, -1); + std::vector vf_head(vert_count, -1), vf_count(vert_count, 0), slot_face(S), slot_vert(S), + slot_next(S, -1), slot_prev(S, -1), face_slot(S, -1); for (size_t f = 0; f < face_count; ++f) for (int k = 0; k < 3; ++k) { const int s = int(f) * 3 + k; @@ -252,12 +252,14 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h slot_prev[size_t(vf_head[size_t(v)])] = s; vf_head[size_t(v)] = s; face_slot[size_t(s)] = s; + ++vf_count[size_t(v)]; } const auto unlink_slot = [&](int s) { - const int p = slot_prev[size_t(s)], nx = slot_next[size_t(s)]; + const int p = slot_prev[size_t(s)], nx = slot_next[size_t(s)], v = slot_vert[size_t(s)]; if (p >= 0) slot_next[size_t(p)] = nx; - else vf_head[size_t(slot_vert[size_t(s)])] = nx; + else vf_head[size_t(v)] = nx; if (nx >= 0) slot_prev[size_t(nx)] = p; + --vf_count[size_t(v)]; }; const auto move_slot = [&](int s, int nv) { unlink_slot(s); @@ -267,6 +269,7 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h slot_prev[size_t(vf_head[size_t(nv)])] = s; vf_head[size_t(nv)] = s; slot_vert[size_t(s)] = nv; + ++vf_count[size_t(nv)]; }; std::vector active(vert_count, 1); @@ -463,6 +466,10 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h ++stale_pops; continue; } + // Ahead of the checks below, which walk the fans (see DECIMATE_MAX_VALENCE). The two shared + // faces, counted in both fans, go. + if (vf_count[size_t(v1)] + vf_count[size_t(v2)] - 4 > DECIMATE_MAX_VALENCE) + continue; if (shared_face_count(v1, v2) < 2) continue; lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp index c40d9de266..7a9e1873e2 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -25,6 +25,11 @@ static constexpr double DECIMATE_FLIP_DOT = 0.2; static constexpr double DECIMATE_CREASE_COS = 0.5; // Quadric penalty weight for a crease plane. static constexpr double DECIMATE_CREASE_WEIGHT = 1e4; +// Most faces a collapse may leave around its surviving vertex. Uncapped, a pinned vertex on flat +// ground grows a fan of thousands of slivers, and validating each of its edges walks the whole fan, +// which turns a decimation of a second or two into minutes. At 64 the triangle count moves by about +// 1% at most, and by far less on large bakes. +static constexpr int DECIMATE_MAX_VALENCE = 64; // Upper bound in mm on the deviation a harvested collapse may introduce; the real one is smaller, // since the cost sums squared distances over all incident faces. From 7c0a3ab916c9355166543166ec70fa396b641842 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 19:31:48 +0800 Subject: [PATCH 73/89] Warn about the texture bake budget only when it cost detail Meeting the triangle budget by merging flat faces alone no longer raises the warning, and the warning now quotes the budget instead of the triangle count left after the flat faces were merged. --- .../TextureBake/TextureBakeDecimate.cpp | 2 ++ .../TextureBake/TextureBakeDecimate.hpp | 3 +++ .../TextureBake/TextureBakePipeline.cpp | 3 +-- .../TextureBake/TextureBakePipeline.hpp | 5 +++-- src/libslic3r/TextureDisplacement.cpp | 1 - src/libslic3r/TextureDisplacement.hpp | 7 +++---- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 2 +- tests/libslic3r/test_texture_displacement.cpp | 19 +++++++++++++++++++ 8 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index afb9976bb2..ccb6db811c 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -478,6 +478,8 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h const Vec3d target = collapse_target(v1, v2).cast().cast(); if (check_flipped(v1, v2, target) || check_flipped(v2, v1, target)) continue; + if (!reached_target && top.cost > harvest_ceil) + result.target_cost_detail = true; // v1 survives at the new position, v2 goes. pos[size_t(v1)] = target; diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp index 7a9e1873e2..5579778050 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -48,6 +48,9 @@ struct DecimateResult // The locked faces alone met the target, so it was unreachable without touching preserved // geometry. bool locked_over_budget = false; + // Reaching the target took a collapse costing more than harvest_tol, so the target removed detail + // and not only flat faces a harvest would have taken anyway. + bool target_cost_detail = false; }; // `locked_faces`: one entry per input triangle; a vertex touching one may neither move nor be diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 45867c10bb..26f44671c7 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -341,6 +341,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, settings.harvest_tol, locked, [&](double f) { return report("decimate", f); }, face_color); result.locked_over_budget = dec.locked_over_budget; + result.budget_limited = result.simplified = dec.target_cost_detail; displaced = std::move(dec.geometry); lap("decimate", displaced, over_budget ? "over budget, simplified" : "flat faces harvested"); BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: " << before << " -> " << displaced.triangle_count() @@ -349,8 +350,6 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } result.triangles_refined = displaced_before_decimate; result.triangles_budget = target; - result.budget_limited = over_budget; - result.simplified = over_budget; if (!report("decimate", 1.0)) { result.canceled = true; return result; diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index 54b40f46dd..482cd36311 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -110,8 +110,9 @@ struct PipelineResult size_t collapse_count = 0; bool canceled = false; // What the refinement produced, before decimation, and the count it had to fit into (the budget - // plus the preserved geometry). budget_limited says the refined mesh did not fit: the result - // carries less of the texture than the resolution asked for, which is what a caller warns about. + // plus the preserved geometry). budget_limited says the refined mesh did not fit without collapsing + // more than flat faces: the result carries less of the texture than the resolution asked for, + // which is what a caller warns about. size_t triangles_refined = 0; size_t triangles_budget = 0; bool budget_limited = false; diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index f3f902e821..ea52ac64ee 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2291,7 +2291,6 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set if (stats != nullptr) { stats->triangles_refined = result.triangles_refined; - stats->triangles_out = result.geometry.triangle_count(); stats->triangles_budget = result.triangles_budget; stats->budget_limited = result.budget_limited; } diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index bb39bc3266..e97178abca 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -824,14 +824,13 @@ void merge_small_color_regions(const indexed_triangle_set &mesh, std::vector #include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TextureBake/TextureBakeDecimate.hpp" #include "libslic3r/TextureBake/TextureBakeFlip.hpp" #include "libslic3r/TextureBake/TextureBakeMesh.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -2013,3 +2014,21 @@ TEST_CASE("Each layer's texture is sampled only on its own painted area", "[Text CHECK_THAT(both(on_side, -Vec3f::UnitX()), WithinAbs(side_only(on_side, -Vec3f::UnitX()), 1e-5f)); CHECK_THAT(both(unpainted, Vec3f::UnitY()), WithinAbs(0.f, 1e-6f)); } + +TEST_CASE("A budget met by collapsing flat faces alone removes no detail", "[TextureDisplacement]") +{ + // 12 * 4^5 = 12288 triangles on six flat faces, which can go down to 12 without moving the surface. + const TextureBake::TriSoup cube = TextureBake::to_soup(subdivide_mesh_uniform(its_make_cube(20., 20., 20.), 0.f, 5)); + const TextureBake::DecimateResult res = TextureBake::decimate(cube, 1000, true, 0.005); + CHECK(res.geometry.triangle_count() <= 1000); + CHECK_FALSE(res.target_cost_detail); +} + +TEST_CASE("A budget met only by flattening curvature removes detail", "[TextureDisplacement]") +{ + // A 10 degree sphere of radius 10 bulges about 0.04 mm out of each facet, far past a 0.005 mm tolerance. + const TextureBake::TriSoup sphere = TextureBake::to_soup(its_make_sphere(10., PI / 18.)); + const TextureBake::DecimateResult res = TextureBake::decimate(sphere, 100, true, 0.005); + CHECK(res.geometry.triangle_count() <= 100); + CHECK(res.target_cost_detail); +} From 36ebe0cde5cb05ed3f44f23346be993c0ff74e5b Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 19:31:54 +0800 Subject: [PATCH 74/89] Let Cancel stop a texture bake while it simplifies Once the budget is met the progress fraction stops moving, and a cancel was only checked when it moved, so Cancel did nothing until the flat-face merging finished. It is now also checked every 16k steps. --- .../TextureBake/TextureBakeDecimate.cpp | 19 ++++++++-------- tests/libslic3r/test_texture_displacement.cpp | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index ccb6db811c..ee335cff3c 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -457,6 +457,16 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h const HeapEntry top = heap_pop(); ++pops; + if (on_progress) { + // Every 16 k pops as well as whenever the fraction moves: this is the only place a cancel + // is seen, and past the target the fraction stops moving. + const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); + if (p - last_progress > 0.005 || (pops & 0x3fff) == 0) { + last_progress = p; + if (!on_progress(p)) + break; + } + } // The popped entry is the cheapest left, so exceeding the tolerance ends the run. if (reached_target && top.cost > harvest_ceil) break; @@ -527,15 +537,6 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } } maybe_compact(); - - if (on_progress) { - const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); - if (p - last_progress > 0.005) { - last_progress = p; - if (!on_progress(p)) - break; - } - } } BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: pops=" << pops << " stale=" << stale_pops diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index b49c9ccfa4..95384dae46 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -2032,3 +2032,25 @@ TEST_CASE("A budget met only by flattening curvature removes detail", "[TextureD CHECK(res.geometry.triangle_count() <= 100); CHECK(res.target_cost_detail); } + +TEST_CASE("Cancelling while flat faces are harvested past the budget stops the decimation", "[TextureDisplacement]") +{ + // 12 * 4^6 = 49152 triangles: the budget is met almost at once, and the harvest after it runs to + // a dozen triangles over several times the two polling intervals a cancel takes to be seen. + const TextureBake::TriSoup cube = TextureBake::to_soup(subdivide_mesh_uniform(its_make_cube(20., 20., 20.), 0.f, 6)); + const size_t target = cube.triangle_count() - 1000; + const size_t full = TextureBake::decimate(cube, target, true, 0.005).geometry.triangle_count(); + + // The Cancel button pressed once the bar is full: every call after the first to report 1.0 cancels. + bool full_seen = false; + const auto cancel_when_full = [&full_seen](double f) { + if (full_seen) + return false; + full_seen = f >= 1.0; + return true; + }; + const size_t canceled = TextureBake::decimate(cube, target, true, 0.005, {}, cancel_when_full).geometry.triangle_count(); + CHECK(full_seen); + CHECK(canceled <= target); + CHECK(canceled > full); +} From 1e39a36a25ab5f662490687a888f1f4d1e8d0b6e Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 19:32:01 +0800 Subject: [PATCH 75/89] Bake a second painted region as finely as the first Refinement now tapers off away from the paint, and the unpainted surface is kept out of simplification, so what one bake leaves unpainted is still the model's own triangles when a later bake paints there. A second bake used to refine the slivers and long triangles the first one left around its stroke, and came out many times denser, with walls off the texture's lines. --- .../TextureBake/TextureBakePipeline.cpp | 26 ++--- .../TextureBake/TextureBakePipeline.hpp | 11 ++- .../TextureBake/TextureBakeSubdivide.cpp | 63 +++++++++--- .../TextureBake/TextureBakeSubdivide.hpp | 19 +++- src/libslic3r/TextureDisplacement.cpp | 12 ++- tests/libslic3r/test_texture_displacement.cpp | 98 +++++++++++++++---- 6 files changed, 174 insertions(+), 55 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 26f44671c7..2b678905b1 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -149,7 +149,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, // 1. Refine to the target edge length. SubdivideResult sub = subdivide( input, settings.refine_length, face_excluded, /* fast */ false, settings.safety_cap, - [&](double f, size_t, double) { return report("subdivide", f); }); + [&](double f, size_t, double) { return report("subdivide", f); }, settings.paint_within); result.safety_cap_hit = sub.safety_cap_hit; lap("subdivide", sub.geometry); if (!report("subdivide", 1.0)) { @@ -180,7 +180,8 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } sub = subdivide(reg.geometry, settings.refine_length * settings.regularize_second_pass_mul, excl, false, settings.safety_cap, - [&](double f, size_t, double) { return report("re-subdivide", f); }); + [&](double f, size_t, double) { return report("re-subdivide", f); }, + settings.paint_within); result.safety_cap_hit = result.safety_cap_hit || sub.safety_cap_hit; // The second pass renumbers faces, so the parent map has to be composed through it. std::vector composed(sub.face_parent_id.size()); @@ -199,12 +200,8 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, // 2b. Paint finer than the input triangles. The caller includes a source triangle when any part of // it is painted; now that the faces are small, ask once more per face and switch the unpainted - // ones off. They are pinned like the excluded region from here on (their own corners at weight - // 1, and the displacement's boundary sealing pins the stroke's rim on the painted side), but they - // are refined pieces of painted triangles, not original geometry, so `soft_excluded` keeps them - // out of the decimation lock below. Every stage between here and the decimation rewrites faces in - // place, so the per-face flag stays valid by index. - std::vector soft_excluded; + // ones off. They are pinned like the excluded region from here on: their own corners at weight 1, + // and the displacement's boundary sealing pins the stroke's rim on the painted side. if (settings.painted) { const size_t nf = sub.geometry.triangle_count(); const bool have_w = !sub.geometry.exclude_weight.empty(); @@ -228,7 +225,6 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, if (unpainted[t]) sub.geometry.exclude_weight[t * 3] = sub.geometry.exclude_weight[t * 3 + 1] = sub.geometry.exclude_weight[t * 3 + 2] = 1.f; - soft_excluded = std::move(unpainted); } lap("paint", sub.geometry, std::to_string(switched) + " faces switched off"); if (!report("paint", 1.0)) { @@ -294,18 +290,14 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, for (size_t t = 0; t < locked.size(); ++t) locked[t] = (displaced.exclude_weight[t * 3] + displaced.exclude_weight[t * 3 + 1] + displaced.exclude_weight[t * 3 + 2]) / 3.f > 0.99f ? 1 : 0; - // Faces the paint test switched off carry weight 1 too, but are refined pieces of - // painted triangles rather than original geometry: locking them would keep a partly - // painted source triangle at full refinement. Face indices survived relocate, flip - // and displace unchanged, so the flag still lines up. - for (size_t t = 0; t < locked.size() && t < soft_excluded.size(); ++t) - if (soft_excluded[t]) - locked[t] = 0; + // That includes the faces the paint test switched off: the harvest would re-triangulate + // them into long slivers, which a later bake painted there would refine instead of the + // grid the graded refinement left. preserved = size_t(std::count(locked.begin(), locked.end(), uint8_t(1))); } } // The budget is what this bake may spend on what it refines. Geometry it only preserves - the - // relief of an earlier bake, which this one does not paint - is counted on top of it: charged + // unpainted surface, and on it the relief of an earlier bake - is counted on top of it: charged // against the same budget, a second bake over a fresh area had to evict the first one's // triangles to fit, so every bake after the first came out coarser than the one before. const size_t target = settings.max_triangles + preserved; diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index 482cd36311..e230cf0d49 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -73,9 +73,16 @@ struct PipelineSettings // once, so it must be safe to call concurrently. std::function painted; + // Optional. Whether the paint comes within a radius of a point, which grades the refinement (see + // subdivide()): only the paint and its surroundings reach refine_length, and the unpainted surface + // further out stays in whole pieces of its own grid. That is what a later bake painted there + // refines, so it meets the same grid a first bake does. Called from several threads at once. + SubdivideWithinFn paint_within; + // Export mode only. - // What this bake may spend on what it refines. Geometry it only preserves (see preserve_untextured) - // is counted on top of it, so an earlier bake's relief does not have to be evicted to fit this one. + // What this bake may spend on what it refines. Geometry it only preserves (see preserve_untextured), + // the unpainted surface and an earlier bake's relief on it, is counted on top of it, so that relief + // does not have to be evicted to fit this one. size_t max_triangles = 750'000; // Keep removing zero-cost flat faces past the target. Only applies when decimation runs, i.e. when // the displaced mesh is over the budget - an under-budget mesh is never decimated. diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp index 8e2a8c82d8..6b882b1fc8 100644 --- a/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.cpp @@ -68,7 +68,8 @@ struct PassResult // 2. Rebuild, allocating once at the now-known size. PassResult subdivide_pass(VertStore &verts, const std::vector &indices, double max_edge_length, int safety_cap, const std::vector &face_excluded, - QuantizedPointMap *pos_canon_map, const std::vector &face_parent_id) + QuantizedPointMap *pos_canon_map, const std::vector &face_parent_id, + const SubdivideWithinFn &within) { PassResult out; const double max_sq = max_edge_length * max_edge_length; @@ -94,15 +95,55 @@ PassResult subdivide_pass(VertStore &verts, const std::vector &indices, dou return (u < v ? split_edges.get_key(u, v, 0) : split_edges.get_key(v, u, 0)) != -1; }; - // Step 1. An excluded triangle marks none of its own edges, so its interior never refines; its - // boundary edges are still marked by an included neighbour, and it follows that split. - for (size_t t = 0; t < tri_count; ++t) { - if (!face_excluded.empty() && face_excluded[t]) - continue; + // Graded, an edge splits while it is longer than max_edge_length + SUBDIVIDE_GRADE * (d - len/2), + // d its midpoint's distance from the region; less half the length bounds the edge's own distance + // from below, so the test errs toward splitting. Solved for d, that is the region coming within the + // radius below. It reads the positions alone, so two triangles sharing the edge agree. + const auto too_long = [&](int a, int b) { + const double len_sq = edge_len_sq(verts, a, b); + if (len_sq <= max_sq) + return false; + if (!within) + return true; + const double len = std::sqrt(len_sq); + return within((verts.pos[size_t(a)] + verts.pos[size_t(b)]) * 0.5, + 0.5 * len + (len - max_edge_length) / SUBDIVIDE_GRADE); + }; + + // Step 1. An excluded triangle marks none of its own edges while left alone, so its interior never + // refines; its boundary edges are still marked by an included neighbour, and ungraded it follows + // that split. Graded, one the refinement reaches marks its own too-long edges too, and so on + // outward. An earlier bake's relief meets the paint along edges already at the target length, so + // it is never reached and stays as it was. + std::vector want(tri_count, 0); + tbb::parallel_for(tbb::blocked_range(0, tri_count), [&](const tbb::blocked_range &range) { + for (size_t t = range.begin(); t < range.end(); ++t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + want[t] = uint8_t(too_long(a, b)) | uint8_t(too_long(b, c) << 1) | uint8_t(too_long(c, a) << 2); + } + }); + const auto mark_wanted = [&](size_t t) { const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; - if (edge_len_sq(verts, a, b) > max_sq) mark_edge(a, b); - if (edge_len_sq(verts, b, c) > max_sq) mark_edge(b, c); - if (edge_len_sq(verts, c, a) > max_sq) mark_edge(c, a); + if (want[t] & 1) mark_edge(a, b); + if (want[t] & 2) mark_edge(b, c); + if (want[t] & 4) mark_edge(c, a); + }; + std::vector unreached; + for (size_t t = 0; t < tri_count; ++t) + if (face_excluded.empty() || !face_excluded[t]) + mark_wanted(t); + else if (within && want[t] != 0) + unreached.push_back(t); + // Marks only accumulate, so the order the reached triangles are taken in does not matter. + for (size_t n = 0; n != unreached.size();) { + n = unreached.size(); + unreached.erase(std::remove_if(unreached.begin(), unreached.end(), [&](size_t t) { + const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2]; + const bool reached = is_marked(a, b) || is_marked(b, c) || is_marked(c, a); + if (reached) + mark_wanted(t); + return reached; + }), unreached.end()); } if (split_edges.size() == 0) { out.indices = indices; @@ -376,7 +417,7 @@ TriSoup to_non_indexed(const VertStore &verts, const std::vector &indices, SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, const std::vector &face_excluded, bool fast, int safety_cap, - const SubdivideProgressFn &on_progress) + const SubdivideProgressFn &on_progress, const SubdivideWithinFn &within) { SubdivideResult result; if (geometry.empty() || max_edge_length <= 0.0) { @@ -401,7 +442,7 @@ SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, } PassResult pass = subdivide_pass(indexed.verts, current_indices, max_edge_length, safety_cap, - current_excluded, canon_map, current_parent); + current_excluded, canon_map, current_parent, within); current_indices = std::move(pass.indices); if (!pass.face_excluded.empty()) current_excluded = std::move(pass.face_excluded); diff --git a/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp b/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp index 30ad56bf00..743c74eb49 100644 --- a/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp +++ b/src/libslic3r/TextureBake/TextureBakeSubdivide.hpp @@ -26,6 +26,9 @@ static constexpr double SUBDIVIDE_SHARP_ANGLE_DEG = 30.0; // A depth bound, not a work bound: the loop stops as soon as a pass changes nothing. static constexpr int SUBDIVIDE_MAX_ITERATIONS = 12; +// Graded refinement: millimetres of edge length allowed per millimetre of distance from the region. +static constexpr double SUBDIVIDE_GRADE = 1.0; + // Built by the indexers, appended to by the passes. Double precision so repeated midpointing does // not drift. struct VertStore @@ -56,6 +59,9 @@ struct IndexedMesh // Fraction, triangle count, longest remaining edge. Returning false cancels; what comes back is // still watertight, because passes apply whole or not at all. using SubdivideProgressFn = std::function; +// Whether the region being refined comes within `radius` mm of `point`. Called from several threads +// at once. +using SubdivideWithinFn = std::function; struct SubdivideResult { @@ -65,13 +71,20 @@ struct SubdivideResult bool safety_cap_hit = false; }; -// `face_excluded`: one entry per input triangle; non-zero means its interior is never refined. Its -// edges still split when an included neighbour marks them, so no T-junction appears at the boundary. +// `face_excluded`: one entry per input triangle; non-zero means its interior is not refined unless the +// graded refinement reaches it (see `within`). Its edges still split when an included neighbour marks +// them, so no T-junction appears at the boundary. // `fast` selects the cheap position-only indexer for previews. +// `within`, when given, grades the refinement: an edge splits only while it is longer than +// max_edge_length plus SUBDIVIDE_GRADE times its distance from the region. Away from the region the +// triangles are then left as whole pieces of the input's own 1->4 grid rather than refined throughout. +// An excluded triangle the refinement reaches - one of its edges splits - splits the same graded way, +// instead of fanning from its far corner to follow the edge it shares with an included one. SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length, const std::vector &face_excluded = {}, bool fast = false, int safety_cap = SUBDIVIDE_SAFETY_CAP, - const SubdivideProgressFn &on_progress = {}); + const SubdivideProgressFn &on_progress = {}, + const SubdivideWithinFn &within = {}); // Displacement needs the same welding and sharp-edge clustering. IndexedMesh to_indexed(const TriSoup &geometry); diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index ea52ac64ee..862c6c9044 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2227,6 +2227,15 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set if (pieces_area < included_area * (1.0 - 1e-4)) settings.painted = painted_at; } + // Graded wherever some of the surface is unpainted. A yes/no to a radius, not a distance: most + // queries are far from the paint, and those the tree turns down at its root. + const bool all_painted = std::none_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; }); + if (settings.painted || !all_painted) + settings.paint_within = [&painted_pieces, &painted_tree](const Vec3d &p, double radius) { + float r2 = float(radius * radius); + return AABBTreeIndirect::is_any_triangle_in_radius(painted_pieces.vertices, painted_pieces.indices, + painted_tree, Vec3f(p.cast()), r2); + }; TextureBake::DisplaceBounds bounds; bounds.min = bounds.max = mesh.vertices.empty() ? Vec3f::Zero() : mesh.vertices.front(); @@ -2310,8 +2319,7 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set std::vector out_color(out.indices.size(), 0); const ColorFieldSampler &sampler = color_sampler; if (sampler) { - const bool all_painted = std::none_of(excluded.begin(), excluded.end(), [](uint8_t e) { return e != 0; }); - float max_depth = 0.f; + float max_depth = 0.f; for (const TextureDisplacementLayer &layer : layers) max_depth = std::max(max_depth, std::abs(layer.depth_mm)); const float relief_tol = max_depth + paint_tol; diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index 95384dae46..04713d3fd9 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -4,12 +4,14 @@ #include #include +#include #include #include #include #include #include +#include "libslic3r/AABBMesh.hpp" #include "libslic3r/TextureDisplacement.hpp" #include "libslic3r/TextureBake/TextureBakeDecimate.hpp" #include "libslic3r/TextureBake/TextureBakeFlip.hpp" @@ -156,6 +158,25 @@ static TriangleSelector::TriangleSplittingData paint_whole_mesh(const indexed_tr return selector.serialize(); } +// Paints a disc of the top face of `mesh` (the plane z = `top`) the way the gizmo's brush does: the +// selector splits the triangles under it, so the stroke need not follow the mesh. +static TriangleSelector::TriangleSplittingData paint_top_disc(const indexed_triangle_set &mesh, float top, + const Vec2f ¢re, float radius) +{ + const TriangleMesh tm(mesh); + const Vec3f at(centre.x(), centre.y(), top); + int start = -1; + Vec3d foot; + AABBMesh(tm).squared_distance(at.cast(), start, foot); // the brush starts on the face under it + TriangleSelector selector(tm); + selector.select_patch(start, + TriangleSelector::SinglePointCursor::cursor_factory( + at, at + Vec3f(0.f, 0.f, 80.f), radius, TriangleSelector::CursorType::SPHERE, + Transform3d::Identity(), TriangleSelector::ClippingPlane()), + EnforcerBlockerType::ENFORCER, Transform3d::Identity(), /* triangle_splitting */ true); + return selector.serialize(); +} + // Regression test for the bug this feature shipped with: with two layers painted over the same // area, the second one was silently dropped (its paint mask was remapped onto the mesh the first // layer had already displaced, which routinely produced an empty bitstream). Every layer is now @@ -1889,27 +1910,9 @@ TEST_CASE("TextureDisplacement: a brush stroke smaller than a triangle displaces // A plain 12-triangle cube. The selector splits the top triangle under a 3 mm spherical brush, // so the painted pieces are far smaller than the triangle. The one-run pipeline used to include // the whole source triangle: the entire top face rose. - const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); - const TriangleMesh mesh(cube); - int top = -1; - for (size_t t = 0; t < cube.indices.size() && top < 0; ++t) { - const auto &f = cube.indices[t]; - if (cube.vertices[size_t(f[0])].z() > 19.9f && cube.vertices[size_t(f[1])].z() > 19.9f && - cube.vertices[size_t(f[2])].z() > 19.9f) - // The triangle that contains the face centre: the brush starts there. - for (int k = 0; k < 3; ++k) - if ((cube.vertices[size_t(f[k])] - Vec3f(10.f, 10.f, 20.f)).norm() < 15.f) - top = int(t); - } - REQUIRE(top >= 0); - TriangleSelector selector(mesh); - selector.select_patch(top, - TriangleSelector::SinglePointCursor::cursor_factory( - Vec3f(10.f, 10.f, 20.f), Vec3f(10.f, 10.f, 100.f), 3.f, TriangleSelector::CursorType::SPHERE, - Transform3d::Identity(), TriangleSelector::ClippingPlane()), - EnforcerBlockerType::ENFORCER, Transform3d::Identity(), /* triangle_splitting */ true); + const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); TextureDisplacementFacetsData facets; - facets[0] = selector.serialize(); + facets[0] = paint_top_disc(cube, 20.f, Vec2f(10.f, 10.f), 3.f); REQUIRE(TriangleSelector::has_facets(facets[0], EnforcerBlockerType::ENFORCER)); TextureDisplacementLayer layer; @@ -2054,3 +2057,58 @@ TEST_CASE("Cancelling while flat faces are harvested past the budget stops the d CHECK(canceled <= target); CHECK(canceled > full); } + +TEST_CASE("A second bake beside a first comes out as fine as a single bake", "[TextureDisplacement]") +{ + // A stroke in each of the cube's two top triangles, baked one after the other. The first bake used + // to leave the unpainted ground fanned into slivers from a far corner, or re-triangulated by the + // flat harvest into long triangles, and the second bake refined those shapes rather than the + // cube's own grid: its stroke came out many times as dense, with walls off the texture's lines. + const indexed_triangle_set cube = its_make_cube(20.f, 20.f, 20.f); + const Vec2f first_stroke(5.f, 15.f), second_stroke(15.f, 5.f); + constexpr float radius = 3.f; + + TextureDisplacementLayer layer; + layer.slot = 0; + layer.image_data = make_checkerboard_png(16, 16); + layer.tiling_scale = 5.f; + layer.depth_mm = 0.5f; + TextureDisplacementOptions options; + options.pipeline_v2 = true; + options.v2_refine_mm = 0.5f; + options.v2_max_triangles_k = 750; // export: the flat harvest runs + const auto bake = [&](const indexed_triangle_set &mesh, const Vec2f &stroke) { + TextureDisplacementFacetsData facets; + facets[0] = paint_top_disc(mesh, 20.f, stroke, radius); + return build_texture_displacement(mesh, { layer }, facets, options); + }; + // Aspect ratios of the top-face triangles whose centroid's distance from `centre` passes `keep`. + const auto top_aspects = [](const indexed_triangle_set &mesh, const Vec2f ¢re, auto keep) { + std::vector out; + for (const stl_triangle_vertex_indices &t : mesh.indices) { + const Vec3f &a = mesh.vertices[size_t(t[0])], &b = mesh.vertices[size_t(t[1])], &c = mesh.vertices[size_t(t[2])]; + const Vec3f g = (a + b + c) / 3.f; + if (g.z() > 19.9f && keep((Vec2f(g.x(), g.y()) - centre).norm())) + out.push_back(triangle_aspect(a, b, c)); + } + return out; + }; + const auto in_stroke = [&](float d) { return d < radius; }; + + const indexed_triangle_set first = bake(cube, first_stroke); + REQUIRE(first.indices.size() > cube.indices.size()); + // What the first bake leaves around its stroke is ground the second one refines: whole pieces of + // the cube's grid, not slivers. + const std::vector ground = top_aspects(first, first_stroke, [&](float d) { return d > radius + 1.f; }); + REQUIRE(!ground.empty()); + const float worst = *std::max_element(ground.begin(), ground.end()); + INFO("worst aspect ratio on the ground the first bake left: " << worst); + CHECK(worst < 6.f); + + const size_t single = top_aspects(bake(cube, second_stroke), second_stroke, in_stroke).size(); + const size_t second = top_aspects(bake(first, second_stroke), second_stroke, in_stroke).size(); + INFO("second stroke baked alone: " << single << " triangles, after the first: " << second); + REQUIRE(single > 0); + CHECK(second <= single * 5 / 4); +} + From 315df5750fae5956fc12e1df1f85054d4ee2b9ba Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Wed, 30 Sep 2026 07:36:25 -0400 Subject: [PATCH 76/89] Fix filament variant index references in GUI controls Use the shared unsigned variant index for adaptive pressure advance and volumetric speed controls, resolving missing identifiers and the ambiguous string accessor. Co-authored-by: Codex --- src/slic3r/GUI/Tab.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4698a70561..9d849cde4b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4772,7 +4772,7 @@ void TabFilament::update_description_lines() void TabFilament::toggle_options() { - const int variant_index = m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; + const unsigned int variant_index = m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; if (!m_active_page) return; bool is_BBL_printer = false; @@ -4861,7 +4861,7 @@ void TabFilament::toggle_options() // If adaptive PA is not enabled, hide the adaptive PA model section toggle_option("adaptive_pressure_advance", pa, 0); toggle_option("adaptive_pressure_advance_overhangs", pa, 0); - bool has_adaptive_pa = m_config->opt_bool("adaptive_pressure_advance", variant_idx); + bool has_adaptive_pa = m_config->opt_bool("adaptive_pressure_advance", variant_index); toggle_line("adaptive_pressure_advance_overhangs", has_adaptive_pa && pa, 0); toggle_line("adaptive_pressure_advance_model", has_adaptive_pa && pa, 0); toggle_line("adaptive_pressure_advance_bridges", has_adaptive_pa && pa, 0); @@ -4872,9 +4872,9 @@ void TabFilament::toggle_options() toggle_line("activate_chamber_temp_control", printer_cfg.opt_bool("support_chamber_temp_control")); - std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", variant_idx); + std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", variant_index); bool enable_fit = volumetric_speed_cos != "0 0 0 0 0 0"; - toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + variant_idx); + toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + variant_index); } if (m_active_page->title() == L("Setting Overrides")) From 09184ac5a46deb37f446f352985415a3e6c81e1b Mon Sep 17 00:00:00 2001 From: Eric McCann Date: Wed, 30 Sep 2026 07:41:17 -0400 Subject: [PATCH 77/89] Fix U1 profile variant array validation Expand shared filament and process values to their declared variant widths while preserving existing tuning. Bump the Snapmaker bundle version to 02.04.00.22. Validation: Snapmaker profile check passed with zero errors and warnings; verified all expanded entries repeat their original values. Co-authored-by: Codex --- resources/profiles/Snapmaker.json | 2 +- .../filament/Generic PLA @U1 0.4 nozzle.json | 1 + .../filament/Generic PLA @U1 0.6 nozzle.json | 1 + .../filament/Generic PLA @U1 0.8 nozzle.json | 1 + ...Generic PLA High Speed @U1 0.4 nozzle.json | 1 + ...Generic PLA High Speed @U1 0.6 nozzle.json | 1 + ...Generic PLA High Speed @U1 0.8 nozzle.json | 1 + .../Generic PLA-CF @U1 0.4 nozzle.json | 1 + .../Generic PLA-CF @U1 0.6 nozzle.json | 1 + .../Generic PLA-CF @U1 0.8 nozzle.json | 1 + .../Polymaker General PLA Family @U1.json | 2 + .../Polymaker Silk PLA Family @U1.json | 2 + .../Polymaker Tough PLA Family @U1.json | 1 + .../Fiberon ASA-CF08 @Snapmaker U1.json | 1 + .../Fiberon PA12-CF10 @Snapmaker U1.json | 4 + .../Fiberon PA6-CF20 @Snapmaker U1.json | 4 + .../Fiberon PA612-ESD @Snapmaker U1.json | 3 + .../Fiberon PETG-ESD @Snapmaker U1.json | 1 + .../Panchroma PLA @Snapmaker U1.json | 1 + .../PolyLite PETG @Snapmaker U1.json | 1 + ...lyLite PETG Translucent @Snapmaker U1.json | 1 + ...lyLite PLA Pro Metallic @Snapmaker U1.json | 1 + .../Polymaker PLA @Snapmaker U1.json | 1 + .../Snapmaker/filament/Snapmaker ABS @U1.json | 1 + .../filament/Snapmaker ABS Benchy @U1.json | 8 ++ .../Snapmaker/filament/Snapmaker ASA @U1.json | 1 + ...akaway Support For PLA @U1 0.2 nozzle.json | 11 ++ ...akaway Support For PLA @U1 0.6 nozzle.json | 12 ++ ...akaway Support For PLA @U1 0.8 nozzle.json | 10 ++ ...apmaker Breakaway Support For PLA @U1.json | 11 ++ .../Snapmaker PETG HF @U1 0.2 nozzle.json | 4 + .../Snapmaker PETG HF @U1 0.6 nozzle.json | 7 + .../Snapmaker PETG HF @U1 0.8 nozzle.json | 7 + ...maker PETG Translucent @U1 0.2 nozzle.json | 10 ++ ...maker PETG Translucent @U1 0.4 nozzle.json | 9 ++ ...maker PETG Translucent @U1 0.6 nozzle.json | 10 ++ ...maker PETG Translucent @U1 0.8 nozzle.json | 9 ++ .../filament/Snapmaker PETG-CF @U1.json | 1 + .../filament/Snapmaker PLA Basic @U1.json | 27 ++++ ...aker PLA Full Spectrum @U1 0.4 nozzle.json | 23 +++ .../Snapmaker PLA Glow @U1 0.4 nozzle.json | 7 + .../filament/Snapmaker PLA Lite @U1.json | 26 ++++ .../Snapmaker PLA Matte @U1 0.2 nozzle.json | 24 ++++ .../Snapmaker PLA Matte @U1 0.6 nozzle.json | 25 ++++ .../Snapmaker PLA Matte @U1 0.8 nozzle.json | 25 ++++ .../filament/Snapmaker PLA Matte @U1.json | 14 ++ .../Snapmaker PLA Silk @U1 0.2 nozzle.json | 30 ++++ .../Snapmaker PLA Silk @U1 0.6 nozzle.json | 30 ++++ .../Snapmaker PLA Silk @U1 0.8 nozzle.json | 30 ++++ .../filament/Snapmaker PLA Silk @U1.json | 28 ++++ ...napmaker PLA SnapSpeed @U1 0.2 nozzle.json | 6 + ...napmaker PLA SnapSpeed @U1 0.6 nozzle.json | 6 + ...napmaker PLA SnapSpeed @U1 0.8 nozzle.json | 5 + .../filament/Snapmaker PLA SnapSpeed @U1.json | 25 ++++ ...pmaker PLA Translucent @U1 0.2 nozzle.json | 21 +++ ...pmaker PLA Translucent @U1 0.4 nozzle.json | 18 +++ ...pmaker PLA Translucent @U1 0.6 nozzle.json | 18 +++ ...pmaker PLA Translucent @U1 0.8 nozzle.json | 19 +++ .../Snapmaker PLA Wood @U1 0.4 nozzle.json | 9 ++ .../Snapmaker PLA Wood @U1 0.6 nozzle.json | 11 ++ .../Snapmaker PLA Wood @U1 0.8 nozzle.json | 12 ++ .../Snapmaker PLA-CF @U1 0.4 nozzle.json | 8 ++ .../Snapmaker PLA-CF @U1 0.6 nozzle.json | 11 ++ .../Snapmaker PLA-CF @U1 0.8 nozzle.json | 10 ++ .../Snapmaker PVA @U1 0.6 nozzle.json | 11 ++ .../Snapmaker PVA @U1 0.8 nozzle.json | 9 ++ .../Snapmaker/filament/Snapmaker PVA @U1.json | 10 ++ .../Snapmaker/filament/Snapmaker TPE @U1.json | 2 + .../Snapmaker TPU 90A @U1 0.6 nozzle.json | 19 +++ .../Snapmaker TPU 90A @U1 0.8 nozzle.json | 17 +++ .../filament/Snapmaker TPU 90A @U1.json | 16 +++ .../filament/Snapmaker TPU 95A @U1.json | 22 +++ .../Snapmaker TPU 95A HF @U1 0.6 nozzle.json | 17 +++ .../Snapmaker TPU 95A HF @U1 0.8 nozzle.json | 17 +++ .../filament/Snapmaker TPU 95A HF @U1.json | 16 +++ .../filament/Snapmaker TPU High-Flow @U1.json | 5 + ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 21 +++ ...6 Standard @Snapmaker U1 (0.2 nozzle).json | 7 + ...Extra Fine @Snapmaker U1 (0.4 nozzle).json | 7 + ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 21 +++ ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 35 +++++ ...8 Standard @Snapmaker U1 (0.2 nozzle).json | 7 + ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 21 +++ ...0 Standard @Snapmaker U1 (0.2 nozzle).json | 7 + .../0.12 Fine @Snapmaker U1 (0.4 nozzle).json | 7 + ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 35 +++++ ...2 Standard @Snapmaker U1 (0.2 nozzle).json | 7 + ...4 Standard @Snapmaker U1 (0.2 nozzle).json | 7 + ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 35 +++++ ...16 Optimal @Snapmaker U1 (0.4 nozzle).json | 7 + ...8 Standard @Snapmaker U1 (0.6 nozzle).json | 14 ++ ...20 Quality @Snapmaker U1 (0.4 nozzle).json | 7 + ...0 Standard @Snapmaker U1 (0.4 nozzle).json | 7 + ...0 Standard @Snapmaker U1 (0.6 nozzle).json | 7 + ...0 Strength @Snapmaker U1 (0.4 nozzle).json | 7 + ...20 Support @Snapmaker U1 (0.4 nozzle).json | 7 + ... Support W @Snapmaker U1 (0.4 nozzle).json | 7 + ...0.24 Draft @Snapmaker U1 (0.4 nozzle).json | 7 + ...4 Standard @Snapmaker U1 (0.6 nozzle).json | 14 ++ ...4 Standard @Snapmaker U1 (0.8 nozzle).json | 14 ++ ....25 Benchy @Snapmaker U1 (0.4 nozzle).json | 133 ++++++++++++++++++ ...xtra Draft @Snapmaker U1 (0.4 nozzle).json | 7 + ...0.30 Draft @Snapmaker U1 (0.6 nozzle).json | 7 + ...0 Standard @Snapmaker U1 (0.6 nozzle).json | 14 ++ ...0 Strength @Snapmaker U1 (0.6 nozzle).json | 14 ++ ...2 Standard @Snapmaker U1 (0.8 nozzle).json | 14 ++ ...6 Standard @Snapmaker U1 (0.6 nozzle).json | 14 ++ ...xtra Draft @Snapmaker U1 (0.6 nozzle).json | 7 + ...0 Standard @Snapmaker U1 (0.8 nozzle).json | 14 ++ ...2 Standard @Snapmaker U1 (0.6 nozzle).json | 14 ++ ...8 Standard @Snapmaker U1 (0.8 nozzle).json | 14 ++ ...6 Standard @Snapmaker U1 (0.8 nozzle).json | 7 + 112 files changed, 1347 insertions(+), 1 deletion(-) diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index afe302529b..14adc77dfc 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.21", + "version": "02.04.00.22", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json index e37b2d5a25..743800b360 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "12", "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json index 8524f17c21..ba65c7f999 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "12", "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json index f4887a84ca..73c997dd6b 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "12", "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json index 31fbc7174c..125a7a8f77 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "18", "18" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json index bb910121f9..5a0c7f73cd 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "18", "18" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json index 45c7bbda54..8826241929 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "18", "18" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json index 6fcd474adc..2be327d2c2 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "12", "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json index 13a94cd20c..24dd167796 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "12", "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json index f763ce696e..f354ff4078 100644 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json @@ -18,6 +18,7 @@ "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ], "filament_max_volumetric_speed": [ + "12", "12" ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json index 171523998a..0086bd4128 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json @@ -12,9 +12,11 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "enable_pressure_advance": [ + "0", "0" ], "filament_max_volumetric_speed": [ + "15", "15" ], "filament_vendor": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json index aa8d9de628..f022a2f98e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json @@ -18,9 +18,11 @@ "Polymaker" ], "nozzle_temperature": [ + "230", "230" ], "nozzle_temperature_initial_layer": [ + "230", "230" ], "nozzle_temperature_range_high": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json index b3ce7b53ea..a7f2a911f7 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json @@ -15,6 +15,7 @@ "1.23" ], "filament_flow_ratio": [ + "0.96", "0.96" ], "filament_vendor": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json index 7117c25419..bd0df7f754 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json @@ -14,6 +14,7 @@ "15" ], "pressure_advance": [ + "0.05", "0.05" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json index 326c972bcb..0b0213973e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json @@ -14,15 +14,19 @@ "15" ], "filament_retraction_length": [ + "1.0", "1.0" ], "filament_z_hop": [ + "0.0", "0.0" ], "enable_pressure_advance": [ + "1", "1" ], "pressure_advance": [ + "0.05", "0.05" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json index 3a825179b0..b4f18c6bb3 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json @@ -14,15 +14,19 @@ "15" ], "filament_retraction_length": [ + "1.0", "1.0" ], "filament_z_hop": [ + "0.0", "0.0" ], "enable_pressure_advance": [ + "1", "1" ], "pressure_advance": [ + "0.08", "0.08" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json index 444a3ca35e..a8b5296826 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json @@ -14,13 +14,16 @@ "15" ], "filament_retraction_length": [ + "1.0", "1.0" ], "filament_z_hop": [ + "0.0", "0.0" ], "description": " ", "enable_pressure_advance": [ + "1", "1" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json index 91a8ccb00f..432d7c36b6 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json @@ -20,6 +20,7 @@ "70" ], "pressure_advance": [ + "0.04", "0.04" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json index acf326d5ca..1c1e32dacb 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json @@ -14,6 +14,7 @@ "15" ], "filament_wipe": [ + "0", "0" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json index e52f876805..467555862c 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json @@ -20,6 +20,7 @@ "70" ], "pressure_advance": [ + "0.05", "0.05" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json index 4702dd7ec9..307572f778 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json @@ -20,6 +20,7 @@ "70" ], "pressure_advance": [ + "0.05", "0.05" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json index cabbbe92b2..104d1abb2f 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json @@ -27,6 +27,7 @@ "0" ], "enable_pressure_advance": [ + "0", "0" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json index b8fb82ab72..529ca24bf0 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json @@ -18,6 +18,7 @@ "Direct Drive High Flow" ], "pressure_advance": [ + "0.05", "0.05" ], "filament_notes": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json index 2a4346cc5d..19402cd821 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json @@ -11,6 +11,7 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json index 47460e4c92..113f526b11 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json @@ -24,27 +24,35 @@ "2" ], "filament_deretraction_speed": [ + "55", "55" ], "filament_max_volumetric_speed": [ + "31", "31" ], "filament_retract_when_changing_layer": [ + "0", "0" ], "filament_retraction_length": [ + "0.5", "0.5" ], "filament_z_hop": [ + "0", "0" ], "filament_retraction_speed": [ + "55", "55" ], "filament_wipe": [ + "0", "0" ], "nozzle_temperature": [ + "250", "250" ], "fan_max_speed": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json index 2e27630834..132701bafd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json @@ -11,6 +11,7 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json index 716c9926d1..835cb46246 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json @@ -21,9 +21,11 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_max_speed": [ @@ -39,36 +41,45 @@ "1.3" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ + "1", "1" ], "filament_max_volumetric_speed": [ + "0.5", "0.5" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ + "240", "240" ], "nozzle_temperature_initial_layer": [ + "240", "240" ], "nozzle_temperature_range_low": [ "190" ], "pressure_advance": [ + "0.2", "0.2" ], "textured_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json index 0092f4e870..36b7d88e06 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json @@ -21,21 +21,25 @@ "1.3" ], "filament_flow_ratio": [ + "0.95", "0.95" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ + "210", "210" ], "nozzle_temperature_initial_layer": [ + "210", "210" ], "nozzle_temperature_range_low": [ "190" ], "pressure_advance": [ + "0.02", "0.02" ], "textured_plate_temp": [ @@ -51,30 +55,38 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_min_speed": [ "100" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "1", "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_wipe_distance": [ + "2", "2" ], "filament_cooling_moves": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json index 9d296edafb..a720a9e27c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json @@ -21,9 +21,11 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cost": [ @@ -33,33 +35,41 @@ "1.3" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ + "0.93", "0.93" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "3", "3" ], "filament_retraction_speed": [ + "nil", "nil" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ + "210", "210" ], "nozzle_temperature_initial_layer": [ + "210", "210" ], "nozzle_temperature_range_low": [ "190" ], "pressure_advance": [ + "0.015", "0.015" ], "textured_plate_temp_initial_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json index 07c100e18d..b93c632c55 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json @@ -15,6 +15,7 @@ "PLA" ], "enable_pressure_advance": [ + "1", "1" ], "additional_cooling_fan_speed": [ @@ -24,9 +25,11 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_min_speed": [ @@ -36,9 +39,11 @@ "4" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ + "1", "1" ], "filament_loading_speed": [ @@ -57,12 +62,15 @@ "5" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_unloading_speed": [ @@ -75,15 +83,18 @@ "65" ], "nozzle_temperature": [ + "210", "210" ], "nozzle_temperature_initial_layer": [ + "210", "210" ], "nozzle_temperature_range_low": [ "190" ], "pressure_advance": [ + "0.03", "0.03" ], "textured_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json index c015a5d3df..549fb49ffb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json @@ -49,9 +49,11 @@ "5" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "1.5", "1.5" ], "filament_start_gcode": [ @@ -67,6 +69,7 @@ "80" ], "nozzle_temperature": [ + "245", "245" ], "nozzle_temperature_initial_layer": [ @@ -98,6 +101,7 @@ "80" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "filament_notes": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json index 1103fb9192..89c53be8aa 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json @@ -36,6 +36,7 @@ "\n" ], "filament_max_volumetric_speed": [ + "20", "20" ], "filament_multitool_ramming": [ @@ -48,9 +49,11 @@ "5" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "2.5", "2.5" ], "filament_start_gcode": [ @@ -60,12 +63,15 @@ "Snapmaker" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "1", "1" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ @@ -75,6 +81,7 @@ "80" ], "nozzle_temperature": [ + "245", "245" ], "nozzle_temperature_initial_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json index d1fea8441e..2ff43ed5f6 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json @@ -33,6 +33,7 @@ "30" ], "filament_max_volumetric_speed": [ + "20", "20" ], "filament_multitool_ramming": [ @@ -45,9 +46,11 @@ "5" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "1", "1" ], "filament_start_gcode": [ @@ -57,12 +60,15 @@ "Snapmaker" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "2", "2" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ @@ -72,6 +78,7 @@ "80" ], "nozzle_temperature": [ + "245", "245" ], "nozzle_temperature_initial_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json index 51725376e7..22b2f18955 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json @@ -17,9 +17,11 @@ "3" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_max_speed": [ @@ -38,12 +40,14 @@ "1.25" ], "filament_flow_ratio": [ + "0.98", "0.98" ], "filament_loading_speed": [ "28" ], "filament_max_volumetric_speed": [ + "1", "1" ], "filament_minimal_purge_on_wipe_tower": [ @@ -59,15 +63,19 @@ "5" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "50", "50" ], "filament_unloading_speed": [ @@ -77,12 +85,14 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ "80" ], "nozzle_temperature_initial_layer": [ + "250", "250" ], "nozzle_temperature_range_low": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json index 0bec924fdd..db05ae3bc1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json @@ -14,9 +14,11 @@ "3" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_max_speed": [ @@ -32,6 +34,7 @@ "24.99" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_loading_speed": [ @@ -50,12 +53,15 @@ "5" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_unloading_speed": [ @@ -65,15 +71,18 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ "80" ], "nozzle_temperature": [ + "250", "250" ], "nozzle_temperature_initial_layer": [ + "250", "250" ], "nozzle_temperature_range_low": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json index 0f2a565897..69702eb4a1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json @@ -14,9 +14,11 @@ "3" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_max_speed": [ @@ -32,15 +34,18 @@ "1.25" ], "filament_flow_ratio": [ + "0.98", "0.98" ], "filament_max_volumetric_speed": [ + "16", "16" ], "hot_plate_temp": [ "80" ], "nozzle_temperature_initial_layer": [ + "250", "250" ], "nozzle_temperature_range_low": [ @@ -81,12 +86,15 @@ "5" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "1.2", "1.2" ], "filament_retraction_minimum_travel": [ + "1", "1" ], "filament_unloading_speed": [ @@ -96,9 +104,11 @@ "100" ], "filament_wipe_distance": [ + "2", "2" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "enable_pressure_advance": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json index d8cffc3787..39989ccc7d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json @@ -14,9 +14,11 @@ "3" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_max_speed": [ @@ -32,15 +34,18 @@ "1.25" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_max_volumetric_speed": [ + "16", "16" ], "hot_plate_temp": [ "80" ], "nozzle_temperature_initial_layer": [ + "250", "250" ], "nozzle_temperature_range_low": [ @@ -81,12 +86,15 @@ "10" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_unloading_speed": [ @@ -96,6 +104,7 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "enable_pressure_advance": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json index e58dd27968..174e0f9d29 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json @@ -58,6 +58,7 @@ "18" ], "filament_retract_length_toolchange": [ + "nil", "nil" ], "pressure_advance": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json index cb6d5bd046..8c970ef1c4 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -50,12 +56,14 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "0", "0" ], "eng_plate_temp": [ @@ -83,6 +91,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -92,6 +101,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.98", "0.98" ], "filament_is_support": [ @@ -104,9 +114,11 @@ "50" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "15", "15" ], "filament_minimal_purge_on_wipe_tower": [ @@ -128,33 +140,43 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -191,15 +213,19 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "nil", "nil" ], "full_fan_speed_layer": [ @@ -227,6 +253,7 @@ "0.4157" ], "pressure_advance": [ + "0.02", "0.02" ], "reduce_fan_stop_start_freq": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json index e58ce8df61..a03c3e6b9d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json @@ -12,21 +12,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -39,6 +44,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -51,6 +57,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -85,6 +92,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -94,6 +102,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.98", "0.98" ], "filament_is_support": [ @@ -106,9 +115,11 @@ "50" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "15", "15" ], "filament_minimal_purge_on_wipe_tower": [ @@ -130,6 +141,7 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ @@ -137,21 +149,27 @@ "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ @@ -159,9 +177,11 @@ "0.5" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -198,12 +218,15 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json index fa22f4812d..fe10c56d81 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json @@ -15,9 +15,11 @@ "100" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cooling_final_speed": [ @@ -59,12 +61,15 @@ "10" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "0.4", "0.4" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_unloading_speed": [ @@ -74,9 +79,11 @@ "100" ], "filament_z_hop": [ + "0.2", "0.2" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp_initial_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json index 8e5c511b1b..fb56f3c5bd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,12 +59,14 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "1", "1" ], "eng_plate_temp": [ @@ -86,6 +94,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -95,6 +104,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.98", "0.98" ], "filament_is_support": [ @@ -107,9 +117,11 @@ "50" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "15", "15" ], "filament_minimal_purge_on_wipe_tower": [ @@ -131,30 +143,39 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -191,15 +212,19 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "nil", "nil" ], "full_fan_speed_layer": [ @@ -227,6 +252,7 @@ "0.4157" ], "pressure_advance": [ + "0.02", "0.02" ], "reduce_fan_stop_start_freq": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json index ab7b23a405..b02152df2f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -87,6 +94,7 @@ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_multitool_ramming": [ @@ -99,30 +107,39 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "2", "2" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_shrink": [ @@ -150,12 +167,15 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "full_fan_speed_layer": [ @@ -204,6 +224,7 @@ "40" ], "filament_max_volumetric_speed": [ + "2", "2" ], "hot_plate_temp": [ @@ -231,6 +252,7 @@ "2" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ @@ -253,6 +275,7 @@ "4" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_stamping_distance": [ @@ -265,6 +288,7 @@ "100" ], "filament_z_hop_types": [ + "nil", "nil" ], "nozzle_temperature": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json index c4cf484227..abc1877f4d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -78,6 +85,7 @@ "1.26" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -90,9 +98,11 @@ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "20", "20" ], "filament_minimal_purge_on_wipe_tower": [ @@ -108,33 +118,43 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -162,15 +182,19 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "nil", "nil" ], "full_fan_speed_layer": [ @@ -253,6 +277,7 @@ "10" ], "filament_retraction_length": [ + "0.6", "0.6" ], "filament_stamping_distance": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json index 41548165b6..e9ab4bade2 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -91,9 +98,11 @@ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "20", "20" ], "filament_minimal_purge_on_wipe_tower": [ @@ -109,30 +118,39 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_shrink": [ @@ -160,15 +178,19 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "nil", "nil" ], "full_fan_speed_layer": [ @@ -239,6 +261,7 @@ "2" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_loading_speed": [ @@ -254,9 +277,11 @@ "10" ], "filament_retraction_length": [ + "0.6", "0.6" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_stamping_distance": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json index b97e9dbba4..1ef9e20fa2 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json @@ -12,21 +12,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -43,6 +48,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -55,6 +61,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -108,6 +115,7 @@ "50" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ @@ -133,18 +141,23 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ @@ -152,6 +165,7 @@ "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json index 18de9edada..abb41b2196 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json @@ -12,21 +12,26 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -42,6 +47,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -51,12 +57,14 @@ "60" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "0", "0" ], "eng_plate_temp": [ @@ -75,6 +83,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -84,12 +93,14 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "1.02", "1.02" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ @@ -111,33 +122,43 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -165,15 +186,19 @@ "Snapmaker" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -246,9 +271,11 @@ "3" ], "filament_max_volumetric_speed": [ + "2", "2" ], "filament_retraction_length": [ + "0.4", "0.4" ], "filament_stamping_distance": [ @@ -267,12 +294,15 @@ "65" ], "nozzle_temperature": [ + "230", "230" ], "nozzle_temperature_initial_layer": [ + "230", "230" ], "pressure_advance": [ + "0.015", "0.015" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json index 3526a1c871..deb6ce4833 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json @@ -12,21 +12,26 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -42,6 +47,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -51,12 +57,14 @@ "60" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "0", "0" ], "eng_plate_temp": [ @@ -75,6 +83,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -84,12 +93,14 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ @@ -111,33 +122,43 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -165,15 +186,19 @@ "Snapmaker" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "1", "1" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -246,9 +271,11 @@ "3" ], "filament_max_volumetric_speed": [ + "10", "10" ], "filament_retraction_length": [ + "0.4", "0.4" ], "filament_stamping_distance": [ @@ -267,12 +294,15 @@ "65" ], "nozzle_temperature": [ + "230", "230" ], "nozzle_temperature_initial_layer": [ + "230", "230" ], "pressure_advance": [ + "0.015", "0.015" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json index 7e11c05252..188fe678c9 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json @@ -12,21 +12,26 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -42,6 +47,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -51,12 +57,14 @@ "60" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "0", "0" ], "eng_plate_temp": [ @@ -75,6 +83,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -84,12 +93,14 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ @@ -111,33 +122,43 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -165,15 +186,19 @@ "Snapmaker" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "1", "1" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -246,9 +271,11 @@ "3" ], "filament_max_volumetric_speed": [ + "10", "10" ], "filament_retraction_length": [ + "0.4", "0.4" ], "filament_stamping_distance": [ @@ -267,12 +294,15 @@ "65" ], "nozzle_temperature": [ + "230", "230" ], "nozzle_temperature_initial_layer": [ + "230", "230" ], "pressure_advance": [ + "0.015", "0.015" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json index acd1c82116..6dfb87b84b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json @@ -12,21 +12,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -43,6 +48,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -52,12 +58,14 @@ "60" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "0", "0" ], "eng_plate_temp": [ @@ -76,6 +84,7 @@ "1.32" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -85,12 +94,14 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.98", "0.98" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ @@ -112,33 +123,43 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -166,12 +187,15 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ @@ -252,6 +276,7 @@ "12" ], "filament_retraction_length": [ + "0.2", "0.2" ], "filament_stamping_distance": [ @@ -270,12 +295,15 @@ "65" ], "nozzle_temperature": [ + "230", "230" ], "nozzle_temperature_initial_layer": [ + "230", "230" ], "pressure_advance": [ + "0.015", "0.015" ], "filament_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json index 842d5fee73..4138a54e34 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json @@ -7,24 +7,30 @@ "setting_id": "jsAgNNiUC6DoO7rz", "instantiation": "true", "filament_deretraction_speed": [ + "30", "30" ], "filament_flow_ratio": [ + "1.01", "1.01" ], "filament_max_volumetric_speed": [ + "2", "2" ], "filament_multitool_ramming_flow": [ "30" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_speed": [ + "30", "30" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json index 81b67fee51..18aefb2ecd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json @@ -11,24 +11,30 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "filament_deretraction_speed": [ + "30", "30" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_multitool_ramming_flow": [ "30" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "1.6", "1.6" ], "filament_retraction_speed": [ + "30", "30" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "textured_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json index 1654724651..1dacec6c8e 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_deretraction_speed": [ + "30", "30" ], "filament_multitool_ramming_flow": [ "30" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "1.6", "1.6" ], "filament_retraction_speed": [ + "30", "30" ], "nozzle_temperature": [ + "215", "215" ], "pressure_advance": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json index 98e2cb5877..1d4bb8ae38 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -50,6 +56,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -84,6 +91,7 @@ "1.24" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_diameter": [ @@ -93,6 +101,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.966", "0.966" ], "filament_is_support": [ @@ -105,6 +114,7 @@ "50" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ @@ -131,36 +141,47 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "nil", "nil" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retract_restart_extra": [ + "nil", "nil" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ + "1.2", "1.2" ], "filament_retraction_minimum_travel": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -197,15 +218,19 @@ "Snapmaker" ], "filament_wipe": [ + "nil", "nil" ], "filament_wipe_distance": [ + "nil", "nil" ], "filament_z_hop": [ + "0.4", "0.4" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json index 2e2732dbd5..3505f346af 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.2 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -41,6 +46,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -93,6 +100,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "1.02", "1.02" ], "filament_is_support": [ @@ -105,9 +113,11 @@ "3" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "1.6", "1.6" ], "filament_minimal_purge_on_wipe_tower": [ @@ -129,27 +139,35 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "0%", "0%" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ + "0.2", "0.2" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -186,12 +204,15 @@ "Snapmaker" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "1", "1" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json index 87cbfc8ed8..80fd2f2dfb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -41,6 +46,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -89,6 +96,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_is_support": [ @@ -101,9 +109,11 @@ "3" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "12", "12" ], "filament_minimal_purge_on_wipe_tower": [ @@ -125,24 +135,31 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ + "0.2", "0.2" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -179,6 +196,7 @@ "Snapmaker" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json index b9320cf13b..38c7df86ac 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.6 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -41,6 +46,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -102,9 +109,11 @@ "3" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "12", "12" ], "filament_minimal_purge_on_wipe_tower": [ @@ -126,27 +135,35 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "0%", "0%" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ + "0.5", "0.5" ], "filament_retraction_speed": [ + "50", "50" ], "filament_shrink": [ @@ -183,6 +200,7 @@ "Snapmaker" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json index c9f55207ca..05425cc8ea 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "additional_cooling_fan_speed": [ @@ -41,6 +46,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -53,6 +59,7 @@ "0" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "enable_overhang_bridge_fan": [ @@ -93,6 +100,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ + "0.95", "0.95" ], "filament_is_support": [ @@ -105,9 +113,11 @@ "3" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "12", "12" ], "filament_minimal_purge_on_wipe_tower": [ @@ -129,27 +139,35 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ + "0%", "0%" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_shrink": [ @@ -186,6 +204,7 @@ "Snapmaker" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json index 6ba340e303..528f6a8075 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json @@ -39,6 +39,7 @@ "65" ], "filament_flow_ratio": [ + "0.97", "0.97" ], "filament_max_volumetric_speed": [ @@ -56,6 +57,7 @@ "6" ], "filament_retraction_length": [ + "0.6", "0.6" ], "pressure_advance": [ @@ -66,12 +68,14 @@ "4" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -81,21 +85,26 @@ "35" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "nozzle_temperature": [ + "220", "220" ], "overhang_fan_threshold": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json index 8401fb7635..fa527b2520 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json @@ -43,6 +43,7 @@ "65" ], "filament_max_volumetric_speed": [ + "18", "18" ], "filament_multitool_ramming_flow": [ @@ -52,9 +53,11 @@ "10" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "0.4", "0.4" ], "pressure_advance": [ @@ -68,15 +71,18 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -86,21 +92,26 @@ "35" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "nozzle_temperature": [ + "220", "220" ], "overhang_fan_threshold": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json index b645dd168a..808ab849b3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json @@ -43,9 +43,11 @@ "65" ], "filament_flow_ratio": [ + "0.965", "0.965" ], "filament_max_volumetric_speed": [ + "20", "20" ], "filament_multitool_ramming_flow": [ @@ -55,9 +57,11 @@ "10" ], "filament_retract_length_toolchange": [ + "5", "5" ], "filament_retraction_length": [ + "0.4", "0.4" ], "pressure_advance": [ @@ -71,15 +75,18 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -89,21 +96,26 @@ "35" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "nozzle_temperature": [ + "220", "220" ], "overhang_fan_threshold": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json index 98db5dbfbb..1f0a2d2fa3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json @@ -61,9 +61,11 @@ "PLA-CF" ], "filament_z_hop": [ + "0.4", "0.4" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "nozzle_temperature": [ @@ -85,12 +87,14 @@ "0.02" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -100,18 +104,22 @@ "45" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "overhang_fan_threshold": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json index 41f7688d01..9b720b69b0 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json @@ -43,6 +43,7 @@ "65" ], "filament_max_volumetric_speed": [ + "18", "18" ], "filament_minimal_purge_on_wipe_tower": [ @@ -52,15 +53,18 @@ "5" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "2", "2" ], "filament_type": [ "PLA-CF" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "nozzle_temperature": [ @@ -91,15 +95,18 @@ "8" ], "filament_wipe_distance": [ + "2", "2" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -109,15 +116,19 @@ "45" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "overhang_fan_threshold": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json index a589a22a83..641f0ac199 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json @@ -43,6 +43,7 @@ "65" ], "filament_max_volumetric_speed": [ + "18", "18" ], "filament_minimal_purge_on_wipe_tower": [ @@ -52,15 +53,18 @@ "5" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "3", "3" ], "filament_type": [ "PLA-CF" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "nozzle_temperature": [ @@ -91,12 +95,14 @@ "8" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -106,15 +112,19 @@ "45" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "overhang_fan_threshold": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json index c8899e0fcc..a1be5a1146 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json @@ -17,15 +17,18 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_cost": [ "79.98" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ @@ -33,36 +36,44 @@ "0.9506" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "nil", "nil" ], "filament_retraction_minimum_travel": [ + "1", "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_soluble": [ "1" ], "filament_wipe_distance": [ + "2", "2" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ + "230", "230" ], "nozzle_temperature_initial_layer": [ + "230", "230" ], "nozzle_temperature_range_low": [ "210" ], "pressure_advance": [ + "0.03", "0.03" ], "slow_down_layer_time": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json index 0be4a98af7..2bbe8d07e4 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json @@ -17,9 +17,11 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_min_speed": [ @@ -29,6 +31,7 @@ "79.98" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ @@ -36,12 +39,15 @@ "0.9312" ], "filament_retract_length_toolchange": [ + "10", "10" ], "filament_retraction_length": [ + "nil", "nil" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_soluble": [ @@ -51,15 +57,18 @@ "65" ], "nozzle_temperature": [ + "225", "225" ], "nozzle_temperature_initial_layer": [ + "225", "225" ], "nozzle_temperature_range_low": [ "205" ], "pressure_advance": [ + "0.03", "0.03" ], "slow_down_layer_time": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json index a84579c6aa..c903cfc491 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json @@ -11,12 +11,14 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "enable_pressure_advance": [ + "1", "1" ], "nozzle_temperature_range_high": [ "240" ], "pressure_advance": [ + "0.03", "0.03" ], "additional_cooling_fan_speed": [ @@ -26,9 +28,11 @@ "1" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "fan_min_speed": [ @@ -44,6 +48,7 @@ "4" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_loading_speed": [ @@ -66,6 +71,7 @@ "0.1" ], "filament_retract_length_toolchange": [ + "4", "4" ], "filament_retraction_length": [ @@ -73,6 +79,7 @@ "0.6" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_unloading_speed": [ @@ -82,15 +89,18 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ + "240", "240" ], "nozzle_temperature_initial_layer": [ + "240", "240" ], "slow_down_layer_time": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json index 7860c778f9..9eed42efe7 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json @@ -21,12 +21,14 @@ "65" ], "filament_flow_ratio": [ + "1.1", "1.1" ], "filament_density": [ "1.22" ], "filament_max_volumetric_speed": [ + "7.2", "7.2" ], "fan_max_speed": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json index 4f6e935751..51aec20d5c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json @@ -8,6 +8,7 @@ "filament_id": "OFY7jxcS", "instantiation": "true", "filament_max_volumetric_speed": [ + "3.2", "3.2" ], "compatible_printers": [ @@ -27,42 +28,52 @@ "0.1" ], "pressure_advance": [ + "0.02", "0.02" ], "additional_cooling_fan_speed": [ "100" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_deretraction_speed": [ + "20", "20" ], "filament_flow_ratio": [ + "1.093", "1.093" ], "filament_multitool_ramming_flow": [ "5" ], "filament_retract_length_toolchange": [ + "4", "4" ], "filament_retraction_length": [ + "0.6", "0.6" ], "filament_retraction_speed": [ + "20", "20" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ + "220", "220" ], "nozzle_temperature_initial_layer": [ + "220", "220" ], "slow_down_layer_time": [ @@ -72,15 +83,19 @@ "240" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "filament_wipe": [ + "1", "1" ], "filament_wipe_distance": [ + "0", "0" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ @@ -108,18 +123,22 @@ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json index 005cf1bf08..8def2c08da 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json @@ -8,6 +8,7 @@ "filament_id": "OFY7jxcS", "instantiation": "true", "filament_max_volumetric_speed": [ + "3.5", "3.5" ], "compatible_printers": [ @@ -27,39 +28,49 @@ "0.1" ], "pressure_advance": [ + "0.02", "0.02" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "filament_deretraction_speed": [ + "20", "20" ], "filament_flow_ratio": [ + "1.095", "1.095" ], "filament_multitool_ramming_flow": [ "5" ], "filament_retract_length_toolchange": [ + "4", "4" ], "filament_retraction_length": [ + "1.2", "1.2" ], "filament_retraction_speed": [ + "20", "20" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ + "220", "220" ], "nozzle_temperature_initial_layer": [ + "220", "220" ], "slow_down_layer_time": [ @@ -69,9 +80,11 @@ "240" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ @@ -99,18 +112,22 @@ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json index 6579347154..aeea4cc694 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json @@ -8,6 +8,7 @@ "filament_id": "OFY7jxcS", "instantiation": "true", "filament_max_volumetric_speed": [ + "3.2", "3.2" ], "compatible_printers": [ @@ -27,45 +28,55 @@ "0.1" ], "pressure_advance": [ + "0.4", "0.4" ], "additional_cooling_fan_speed": [ "100" ], "complete_print_exhaust_fan_speed": [ + "100", "100" ], "during_print_exhaust_fan_speed": [ + "100", "100" ], "filament_deretraction_speed": [ + "15", "15" ], "filament_flow_ratio": [ + "1.045", "1.045" ], "filament_multitool_ramming_flow": [ "5" ], "filament_retract_length_toolchange": [ + "2", "2" ], "filament_retraction_length": [ + "1.2", "1.2" ], "filament_retraction_speed": [ + "15", "15" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ + "230", "230" ], "slow_down_layer_time": [ "14" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ @@ -93,21 +104,26 @@ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "nil", "nil" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json index af1137ccca..f7b73ac74f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json @@ -11,21 +11,26 @@ "Snapmaker U1 (0.4 High Flow nozzle)" ], "activate_air_filtration": [ + "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ + "0", "0" ], "adaptive_pressure_advance_bridges": [ + "0", "0" ], "adaptive_pressure_advance_model": [ + "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ + "0", "0" ], "chamber_temperature": [ @@ -38,6 +43,7 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ + "80", "80" ], "cool_plate_temp": [ @@ -50,12 +56,14 @@ "0" ], "during_print_exhaust_fan_speed": [ + "60", "60" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ + "0", "0" ], "eng_plate_temp": [ @@ -89,6 +97,7 @@ "1.24" ], "filament_deretraction_speed": [ + "8", "8" ], "filament_diameter": [ @@ -107,9 +116,11 @@ "3" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "3.2", "3.2" ], "filament_multitool_ramming": [ @@ -128,21 +139,27 @@ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_lift_above": [ + "nil", "nil" ], "filament_retract_lift_below": [ + "nil", "nil" ], "filament_retract_lift_enforce": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_retraction_length": [ + "1.4", "1.4" ], "filament_retraction_speed": [ + "30", "30" ], "filament_shrink": [ @@ -179,9 +196,11 @@ "Snapmaker" ], "filament_z_hop": [ + "0.4", "0.4" ], "filament_z_hop_types": [ + "Auto Lift", "Auto Lift" ], "full_fan_speed_layer": [ @@ -197,9 +216,11 @@ "0" ], "nozzle_temperature": [ + "225", "225" ], "nozzle_temperature_initial_layer": [ + "225", "225" ], "overhang_fan_speed": [ @@ -212,6 +233,7 @@ "0.4157" ], "pressure_advance": [ + "0.02", "0.02" ], "reduce_fan_stop_start_freq": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json index b3d48fd8dd..6d23763c95 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json @@ -33,39 +33,48 @@ "100" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ + "1.067", "1.067" ], "filament_max_volumetric_speed": [ + "12", "12" ], "filament_multitool_ramming_flow": [ "10" ], "filament_retract_length_toolchange": [ + "8", "8" ], "filament_retraction_length": [ + "0.8", "0.8" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ + "215", "215" ], "nozzle_temperature_initial_layer": [ + "215", "215" ], "overhang_fan_threshold": [ "95%" ], "pressure_advance": [ + "0.14", "0.14" ], "slow_down_layer_time": [ @@ -75,15 +84,18 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -93,6 +105,7 @@ "30" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "eng_plate_temp": [ @@ -105,18 +118,22 @@ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json index f72a73a7dc..b834aaeed8 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json @@ -33,39 +33,48 @@ "100" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ + "1.045", "1.045" ], "filament_max_volumetric_speed": [ + "16", "16" ], "filament_multitool_ramming_flow": [ "15" ], "filament_retract_length_toolchange": [ + "8", "8" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ + "215", "215" ], "nozzle_temperature_initial_layer": [ + "215", "215" ], "overhang_fan_threshold": [ "95%" ], "pressure_advance": [ + "0.12", "0.12" ], "slow_down_layer_time": [ @@ -75,15 +84,18 @@ "100" ], "filament_z_hop_types": [ + "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -93,6 +105,7 @@ "30" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "eng_plate_temp": [ @@ -105,18 +118,22 @@ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json index f91214894c..63905a89c3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json @@ -33,48 +33,58 @@ "10" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_flow_ratio": [ + "1.067", "1.067" ], "filament_max_volumetric_speed": [ + "9", "9" ], "filament_multitool_ramming_flow": [ "10" ], "filament_retract_length_toolchange": [ + "6", "6" ], "filament_retraction_length": [ + "1", "1" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ + "230", "230" ], "overhang_fan_threshold": [ "25%" ], "pressure_advance": [ + "0.23", "0.23" ], "slow_down_layer_time": [ "10" ], "activate_air_filtration": [ + "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ + "70", "70" ], "cool_plate_temp": [ @@ -84,6 +94,7 @@ "30" ], "during_print_exhaust_fan_speed": [ + "70", "70" ], "eng_plate_temp": [ @@ -96,21 +107,26 @@ "20" ], "filament_long_retractions_when_cut": [ + "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ "15" ], "filament_retract_when_changing_layer": [ + "nil", "nil" ], "filament_retraction_distances_when_cut": [ + "nil", "nil" ], "filament_z_hop": [ + "nil", "nil" ], "filament_z_hop_types": [ + "nil", "nil" ], "hot_plate_temp": [ diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json index 949a230b9f..6dce65a512 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json @@ -16,18 +16,23 @@ "Snapmaker U1 (0.8 High Flow nozzle)" ], "filament_flow_ratio": [ + "0.983", "0.983" ], "filament_deretraction_speed": [ + "nil", "nil" ], "filament_max_volumetric_speed": [ + "7.6", "7.6" ], "filament_retraction_length": [ + "0.8", "0.8" ], "filament_retraction_speed": [ + "nil", "nil" ], "filament_notes": [ diff --git a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json index 47dc63e06d..73cee4710b 100644 --- a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -8,10 +8,24 @@ "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in minimal layer lines and much higher printing quality, but much longer printing time.", "default_acceleration": [ + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", "4000" ], "elefant_foot_compensation": "0.15", "outer_wall_acceleration": [ + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", "2000" ], "outer_wall_speed": [ @@ -36,6 +50,13 @@ "prime_tower_width": "30", "prime_volume": "13.5", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json index 710f8d2547..98f28fe3d5 100644 --- a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json @@ -19,6 +19,13 @@ "prime_tower_width": "30", "prime_volume": "13.5", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json index 821225907e..8f8d3aa158 100644 --- a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json @@ -15,6 +15,13 @@ "prime_tower_width": "30", "prime_volume": "18", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json index bff8ea47e6..1ae8b8aba2 100644 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -8,10 +8,24 @@ "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost invisible layer lines and much higher printing quality, but much longer printing time.", "default_acceleration": [ + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", "4000" ], "elefant_foot_compensation": "0.15", "outer_wall_acceleration": [ + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", "2000" ], "outer_wall_speed": [ @@ -36,6 +50,13 @@ "prime_tower_width": "30", "prime_volume": "18", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json index 8b12c1409d..6ba52ed409 100644 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -8,9 +8,23 @@ "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost negligible layer lines and much higher printing quality, but much longer printing time.", "default_acceleration": [ + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", "4000" ], "gap_infill_speed": [ + "210", + "210", + "210", + "210", + "210", + "210", + "210", "210" ], "inner_wall_speed": [ @@ -34,6 +48,13 @@ "600" ], "outer_wall_acceleration": [ + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", "2000" ], "outer_wall_speed": [ @@ -58,6 +79,13 @@ "600" ], "top_surface_speed": [ + "150", + "150", + "150", + "150", + "150", + "150", + "150", "150" ], "compatible_printers": [ @@ -68,6 +96,13 @@ "prime_tower_width": "30", "prime_volume": "18", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json index fc2378404f..0d17be8403 100644 --- a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json @@ -19,6 +19,13 @@ "prime_tower_width": "30", "prime_volume": "18", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json index 80a6b49330..d4b6f44745 100644 --- a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -8,10 +8,24 @@ "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in much higher printing quality, but a much longer printing time.", "default_acceleration": [ + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", "4000" ], "elefant_foot_compensation": "0.15", "outer_wall_acceleration": [ + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", "2000" ], "outer_wall_speed": [ @@ -36,6 +50,13 @@ "prime_tower_width": "28", "prime_volume": "25", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json index 8bd076ec2c..e6486cee9b 100644 --- a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json @@ -26,6 +26,13 @@ "wipe_tower_extra_spacing": "120%", "wipe_tower_wall_type": "rib", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "print_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json index 5b82a0021a..f72b90e84b 100644 --- a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json @@ -13,6 +13,13 @@ ], "ooze_prevention": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json index 7de2bca333..d4a347bc1f 100644 --- a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -8,9 +8,23 @@ "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost negligible layer lines and much higher printing quality, but much longer printing time.", "default_acceleration": [ + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", "4000" ], "gap_infill_speed": [ + "230", + "230", + "230", + "230", + "230", + "230", + "230", "230" ], "inner_wall_speed": [ @@ -34,6 +48,13 @@ "600" ], "outer_wall_acceleration": [ + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", "2000" ], "outer_wall_speed": [ @@ -58,6 +79,13 @@ "600" ], "top_surface_speed": [ + "150", + "150", + "150", + "150", + "150", + "150", + "150", "150" ], "compatible_printers": [ @@ -67,6 +95,13 @@ "prime_tower_width": "30", "prime_volume": "27", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "wipe_tower_filament": "0", diff --git a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json index 20f1c91813..c1108faea3 100644 --- a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json @@ -19,6 +19,13 @@ "prime_tower_width": "28", "prime_volume": "27", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json index 294b1329f3..5735194c70 100644 --- a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json @@ -19,6 +19,13 @@ "prime_tower_width": "30", "prime_volume": "32", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json index 8ecdd89b6e..5126a45f91 100644 --- a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -8,9 +8,23 @@ "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in less apparent layer lines and much higher printing quality, but much longer printing time.", "default_acceleration": [ + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", + "4000", "4000" ], "gap_infill_speed": [ + "250", + "250", + "250", + "250", + "250", + "250", + "250", "250" ], "inner_wall_speed": [ @@ -34,6 +48,13 @@ "600" ], "outer_wall_acceleration": [ + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", + "2000", "2000" ], "outer_wall_speed": [ @@ -58,6 +79,13 @@ "600" ], "top_surface_speed": [ + "150", + "150", + "150", + "150", + "150", + "150", + "150", "150" ], "compatible_printers": [ @@ -68,6 +96,13 @@ "prime_tower_width": "30", "prime_volume": "36", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json index bca9e8d7fb..c5f4c492da 100644 --- a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json @@ -15,6 +15,13 @@ "prime_tower_width": "30", "prime_volume": "36", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json index b2c06523ac..c9a5216347 100644 --- a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "layer_height": "0.25", @@ -29,6 +36,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json index 87a70ad4bb..701b719713 100644 --- a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json @@ -14,6 +14,13 @@ "ooze_prevention": "1", "prime_tower_width": "30", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json index 532737eb20..75b4c935d5 100644 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json @@ -15,6 +15,13 @@ "prime_tower_width": "30", "prime_volume": "45", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json index d605622b96..8dcbb68fa5 100644 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json @@ -15,6 +15,13 @@ "ooze_prevention": "1", "prime_tower_width": "30", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json index cc36e8f20f..157333ffad 100644 --- a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json @@ -27,6 +27,13 @@ "prime_tower_width": "30", "prime_volume": "45", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json index 459f3afefa..5e031b3517 100644 --- a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json @@ -15,6 +15,13 @@ "prime_tower_width": "40", "prime_volume": "15", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json index 6cfc2cba6d..57a763a355 100644 --- a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json @@ -22,6 +22,13 @@ "prime_tower_width": "50", "prime_volume": "38", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json index 90c2cb8f5c..b1ad97d537 100644 --- a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json @@ -15,6 +15,13 @@ "prime_tower_width": "30", "prime_volume": "54", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json index 883fef3cc2..8aae7fa25c 100644 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_brim_width": "5", @@ -28,6 +35,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json index a47d78f04e..1c08f23f8e 100644 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "min_width_top_surface": "90", @@ -29,6 +36,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json index ad0960c02a..f5c9737efd 100644 --- a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json @@ -14,9 +14,23 @@ "top_shell_layers": "3", "top_shell_thickness": "0.5", "initial_layer_speed": [ + "100", + "100", + "100", + "100", + "100", + "100", + "100", "100" ], "initial_layer_infill_speed": [ + "180", + "180", + "180", + "180", + "180", + "180", + "180", "180" ], "outer_wall_speed": [ @@ -60,22 +74,64 @@ "600" ], "top_surface_speed": [ + "180", + "180", + "180", + "180", + "180", + "180", + "180", "180" ], "gap_infill_speed": [ + "200", + "200", + "200", + "200", + "200", + "200", + "200", "200" ], "ironing_speed": "30", "enable_overhang_speed": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "overhang_1_4_speed": [ + "180", + "180", + "180", + "180", + "180", + "180", + "180", "180" ], "overhang_2_4_speed": [ + "20", + "20", + "20", + "20", + "20", + "20", + "20", "20" ], "overhang_3_4_speed": [ + "10", + "10", + "10", + "10", + "10", + "10", + "10", "10" ], "layer_height": "0.25", @@ -100,36 +156,113 @@ "top_surface_line_width": "0.5", "bottom_shell_layers": "2", "initial_layer_travel_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "bridge_acceleration": [ + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", "3000" ], "bridge_speed": [ + "100", + "100", + "100", + "100", + "100", + "100", + "100", "100" ], "default_acceleration": [ + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", "3000" ], "inner_wall_acceleration": [ + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", "3000" ], "internal_solid_infill_acceleration": [ + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", "3000" ], "outer_wall_acceleration": [ + "1600", + "1600", + "1600", + "1600", + "1600", + "1600", + "1600", "1600" ], "small_perimeter_speed": [ + "200", + "200", + "200", + "200", + "200", + "200", + "200", "200" ], "sparse_infill_acceleration": [ + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", "3000" ], "top_surface_acceleration": [ + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", + "3000", "3000" ], "initial_layer_acceleration": [ + "1600", + "1600", + "1600", + "1600", + "1600", + "1600", + "1600", "1600" ], "print_extruder_variant": [ diff --git a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json index 93a4f26ef5..3b2e7f8746 100644 --- a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json @@ -14,6 +14,13 @@ "prime_tower_width": "30", "prime_volume": "63", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "wipe_tower_filament": "0", diff --git a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json index 1c5fa45ffd..1ca162e67a 100644 --- a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json @@ -16,6 +16,13 @@ "prime_tower_width": "30", "prime_volume": "68", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json index 43833fe9c1..b84b343dcc 100644 --- a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_brim_width": "5", @@ -27,6 +34,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json index 568e838002..470490fb9f 100644 --- a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json @@ -17,6 +17,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_width": "30", @@ -29,6 +36,13 @@ "precise_outer_wall": "1", "prime_tower_brim_width": "5", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json index 010c82bb91..97ae45ed4e 100644 --- a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_brim_width": "5", @@ -30,6 +37,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json index ee1f213106..e964e08e5d 100644 --- a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_brim_width": "5", @@ -28,6 +35,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json index ab4831845f..51b76f6e33 100644 --- a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json @@ -16,6 +16,13 @@ "prime_tower_width": "30", "prime_volume": "90", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json index a935192df9..bf9f2644d6 100644 --- a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_brim_width": "5", @@ -31,6 +38,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json index 705021d73d..4338dccaa8 100644 --- a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "precise_z_height": "0", @@ -28,6 +35,13 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json index c0585abda9..45b2d9431b 100644 --- a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json @@ -15,6 +15,13 @@ "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", "internal_bridge_speed": [ + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", + "100%", "100%" ], "prime_tower_brim_width": "5", @@ -28,6 +35,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", diff --git a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json index 22beae020e..b2fb877e5f 100644 --- a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json @@ -25,6 +25,13 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "slowdown_for_curled_perimeters": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", "0" ], "standby_temperature_delta": "-150", From 9abad9619d497af92bba8a0e35bc535a5629630e Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 20:54:25 +0800 Subject: [PATCH 78/89] Add weathered bricks displacement texture --- .../displacement/Bricks weathered.png | Bin 0 -> 243374 bytes scripts/generate_displacement_textures.py | 42 +++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 resources/textures/displacement/Bricks weathered.png diff --git a/resources/textures/displacement/Bricks weathered.png b/resources/textures/displacement/Bricks weathered.png new file mode 100644 index 0000000000000000000000000000000000000000..a20d979ec84f4e558033f89ec82542b1f331ae47 GIT binary patch literal 243374 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

    yVf!k2Q7*8Q<>@dEYIh=;{RMHWX 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

    >#T1o(jvOreN3~lcjO8^ok1!@P2 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 edges[row, i] + a = edges[row, k]; b = edges[row, k + 1] + # rounded-rectangle distance, in tile units so the corners stay round, negative inside the brick + g, rad = 0.006, 0.012 + px = np.abs(x - (a + b) / 2) - ((b - a) / 2 - g - rad) + py = np.abs((fy - 0.5) / rows) - (0.5 / rows - g - rad) + d = np.hypot(np.maximum(px, 0), np.maximum(py, 0)) + np.minimum(np.maximum(px, py), 0) - rad + # ragged edges plus the odd bite out of an arris + d += 0.005 * (fbm(u, v, 40, 182, octaves=3) - 0.5) + 0.006 * smoothstep(0.66, 0.78, fbm(u, v, 24, 183, octaves=3)) + t = np.clip(-d / 0.014, 0, 1) + shoulder = np.sqrt(1 - (1 - t) ** 2) # rounded bevel + bid = row * per_row + k + per = rng.random((rows * per_row, 5)) + # each brick sits at its own height and leans a little + face = 0.82 + 0.1 * per[bid, 0] + 0.04 * ((per[bid, 1] - 0.5) * (x - (a + b) / 2) / ((b - a) / 2) + + (per[bid, 2] - 0.5) * (fy - 0.5) * 2) + # mottled face: domain-warped noise shifted per brick so neighbours don't share a pattern + su = (u + per[bid, 3]) % 1; sv = (v + per[bid, 4]) % 1 + wu = su + 0.08 * fbm(u, v, 6, 184, octaves=3); wv = sv + 0.08 * fbm(u, v, 6, 185, octaves=3) + mottle = smoothstep(0.3, 0.7, fbm(wu, wv, 6, 186, octaves=5, gain=0.55)) + # shallow pits that deepen gradually from the rim; the warp keeps their outlines off the noise lattice + pn = fbm(wu + 0.02 * fbm(u, v, 32, 190, octaves=2), wv, 48, 187, octaves=3) + pits = smoothstep(0.62, 0.82, pn) ** 2 * smoothstep(0.8, 0.86, fbm(u, v, 8, 188, octaves=2)) + face += 0.3 * (mottle - 0.5) - 0.16 * pits + mortar = 0.06 + 0.06 * fbm(u, v, 64, 189, octaves=3) + return norm(mortar + (face - mortar) * shoulder) + # ---- colour textures: (rgb in 0..1, height = luminance by construction) def lum(rgb): return 0.299 * rgb[..., 0] + 0.587 * rgb[..., 1] + 0.114 * rgb[..., 2] @@ -439,7 +479,7 @@ GREY = { 'Stone Wall': stone_wall, 'Wood Planks': wood_planks, 'Basket Weave': basket_weave, 'Chainmail': chainmail, 'Pyramids': pyramids, 'Waffle': waffle, 'Bubbles': bubbles, 'Cracked Earth': cracked_earth, 'Sand Ripples': sand_ripples, 'Bark': bark, 'Slate': slate, 'Triangles': triangles, 'Roof Tiles': roof_tiles, - 'Star Tiles': star_tiles, + 'Star Tiles': star_tiles, 'Bricks weathered': weathered_bricks, } COLOUR = {'Colour Bricks': colour_bricks, 'Mosaic Tiles': mosaic, 'Hex Tiles': hex_tiles, 'Terrazzo': terrazzo, 'Camouflage': camouflage, 'Tartan': tartan} From bd4306e8f8cca5e8086b28157c6fc1972ff5f2f9 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 21:01:45 +0800 Subject: [PATCH 79/89] Open the texture displacement tool in the Normal view --- src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 47de461f2b..705408b153 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -648,11 +648,11 @@ private: // mouse button driving the drag hasn't been released yet - see on_render_input_window(). bool m_preview_params_dirty = false; - // See rebuild_shaded_preview_mesh()/render_shaded_preview_mesh(). On by default: it is the cheap, - // instant-updating preview, so it is the better first impression while painting. The true- - // displacement view (a background CPU remesh) is one click away in the View row when the user - // wants an exact look at what Bake will produce. - bool m_use_shaded_preview = true; + // See rebuild_shaded_preview_mesh()/render_shaded_preview_mesh(). Off by default: the Normal + // (true-displacement) view shows the real geometry Bake will produce, across every layer, so it is + // the honest first impression. The cheap, instant-updating Fast view is one click away in the View + // row for painting on a heavy model. + bool m_use_shaded_preview = false; // Set from the UV editor's per-move island edits instead of rebuilding the (potentially large) shaded // mesh synchronously inside that mouse handler - doing the rebuild there stalled both the UV pane // and the 3D view. The rebuild is instead coalesced to once per 3D frame (render_painter_gizmo). From 0028e65763d51d2e7e6a8105b54205d2b6978fec Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 1 Oct 2026 00:51:50 +0800 Subject: [PATCH 80/89] revert profile changes --- resources/profiles/Snapmaker.json | 138 +-------- .../filament/Generic PETG @U1 0.2 nozzle.json | 24 -- .../filament/Generic PETG @U1 0.4 nozzle.json | 24 -- .../filament/Generic PETG @U1 0.6 nozzle.json | 24 -- .../filament/Generic PETG @U1 0.8 nozzle.json | 24 -- .../Generic PETG HF @U1 0.2 nozzle.json | 24 -- .../Generic PETG HF @U1 0.4 nozzle.json | 24 -- .../Generic PETG HF @U1 0.6 nozzle.json | 24 -- .../Generic PETG HF @U1 0.8 nozzle.json | 24 -- .../Generic PETG-CF @U1 0.4 nozzle.json | 24 -- .../Generic PETG-CF @U1 0.6 nozzle.json | 20 -- .../Generic PETG-CF @U1 0.8 nozzle.json | 20 -- .../filament/Generic PLA @U1 0.2 nozzle.json | 24 -- .../filament/Generic PLA @U1 0.4 nozzle.json | 24 -- .../filament/Generic PLA @U1 0.6 nozzle.json | 24 -- .../filament/Generic PLA @U1 0.8 nozzle.json | 24 -- ...Generic PLA High Speed @U1 0.2 nozzle.json | 24 -- ...Generic PLA High Speed @U1 0.4 nozzle.json | 24 -- ...Generic PLA High Speed @U1 0.6 nozzle.json | 24 -- ...Generic PLA High Speed @U1 0.8 nozzle.json | 24 -- .../Generic PLA Silk @U1 0.2 nozzle.json | 24 -- .../Generic PLA Silk @U1 0.4 nozzle.json | 20 -- .../Generic PLA Silk @U1 0.6 nozzle.json | 20 -- .../Generic PLA Silk @U1 0.8 nozzle.json | 20 -- .../Generic PLA-CF @U1 0.4 nozzle.json | 24 -- .../Generic PLA-CF @U1 0.6 nozzle.json | 24 -- .../Generic PLA-CF @U1 0.8 nozzle.json | 24 -- .../filament/Generic TPU @U1 0.4 nozzle.json | 20 -- .../filament/Generic TPU @U1 0.6 nozzle.json | 20 -- .../filament/Generic TPU @U1 0.8 nozzle.json | 20 -- .../Polymaker General PLA Family @U1.json | 14 +- .../Polymaker Silk PLA Family @U1.json | 14 +- .../Polymaker Tough PLA Family @U1.json | 12 +- .../Fiberon ASA-CF08 @Snapmaker U1.json | 13 +- .../Fiberon PA12-CF10 @Snapmaker U1.json | 16 +- .../Fiberon PA6-CF20 @Snapmaker U1.json | 16 +- .../Fiberon PA612-ESD @Snapmaker U1.json | 15 +- .../Fiberon PETG-ESD @Snapmaker U1.json | 13 +- .../Fiberon PPS-GF20 @Snapmaker U1.json | 14 +- .../Panchroma CoPE @Snapmaker U1.json | 13 +- .../Panchroma PLA @Snapmaker U1.json | 14 +- ...Panchroma PLA Celestial @Snapmaker U1.json | 13 +- .../Panchroma PLA Galaxy @Snapmaker U1.json | 13 +- .../Panchroma PLA Glow @Snapmaker U1.json | 13 +- .../Panchroma PLA Luminous @Snapmaker U1.json | 13 +- .../Panchroma PLA Marble @Snapmaker U1.json | 13 +- .../Panchroma PLA Matte @Snapmaker U1.json | 13 +- .../Panchroma PLA Metallic @Snapmaker U1.json | 13 +- .../Panchroma PLA Neon @Snapmaker U1.json | 13 +- .../Panchroma PLA Satin @Snapmaker U1.json | 13 +- .../Panchroma PLA Silk @Snapmaker U1.json | 13 +- ...Panchroma PLA Starlight @Snapmaker U1.json | 13 +- ...anchroma PLA Temp Shift @Snapmaker U1.json | 24 +- ...nchroma PLA Translucent @Snapmaker U1.json | 13 +- .../Panchroma PLA UV Shift @Snapmaker U1.json | 13 +- .../PolyLite CosPLA @Snapmaker U1.json | 12 +- .../PolyLite PETG @Snapmaker U1.json | 13 +- ...lyLite PETG Translucent @Snapmaker U1.json | 13 +- .../Polymaker/PolyLite PLA @Snapmaker U1.json | 13 +- .../PolyLite PLA Galaxy @Snapmaker U1.json | 12 +- .../PolyLite PLA Glow @Snapmaker U1.json | 12 +- .../PolyLite PLA Luminous @Snapmaker U1.json | 12 +- .../PolyLite PLA Neon @Snapmaker U1.json | 12 +- .../PolyLite PLA Pro @Snapmaker U1.json | 24 +- ...lyLite PLA Pro Metallic @Snapmaker U1.json | 13 +- .../PolyLite PLA Starlight @Snapmaker U1.json | 12 +- ...olyLite PLA Translucent @Snapmaker U1.json | 12 +- .../PolyTerra PLA @Snapmaker U1.json | 13 +- .../PolyTerra PLA Marble @Snapmaker U1.json | 12 +- .../PolyTerra PLA+ @Snapmaker U1.json | 12 +- .../Polymaker HT-PLA @Snapmaker U1.json | 13 +- .../Polymaker HT-PLA-GF @Snapmaker U1.json | 13 +- .../Polymaker PETG @Snapmaker U1.json | 12 +- .../Polymaker PETG Galaxy @Snapmaker U1.json | 12 +- .../Polymaker PLA @Snapmaker U1.json | 12 +- .../Polymaker PLA Pro @Snapmaker U1.json | 12 +- ...ymaker PLA Pro Metallic @Snapmaker U1.json | 12 +- .../Snapmaker/filament/Snapmaker ABS @U1.json | 60 +--- .../filament/Snapmaker ABS Benchy @U1.json | 21 +- .../Snapmaker/filament/Snapmaker ASA @U1.json | 60 +--- ...akaway Support For PLA @U1 0.2 nozzle.json | 33 +-- ...akaway Support For PLA @U1 0.6 nozzle.json | 33 +-- ...akaway Support For PLA @U1 0.8 nozzle.json | 31 +- ...apmaker Breakaway Support For PLA @U1.json | 33 +-- .../filament/Snapmaker PA-CF @U1.json | 11 +- .../Snapmaker/filament/Snapmaker PET @U1.json | 11 +- .../filament/Snapmaker PETG @U1.json | 11 +- .../Snapmaker PETG HF @U1 0.2 nozzle.json | 44 +-- .../Snapmaker PETG HF @U1 0.4 nozzle.json | 92 ------ .../Snapmaker PETG HF @U1 0.6 nozzle.json | 44 +-- .../Snapmaker PETG HF @U1 0.8 nozzle.json | 44 +-- ...maker PETG Translucent @U1 0.2 nozzle.json | 38 +-- ...maker PETG Translucent @U1 0.4 nozzle.json | 34 +-- ...maker PETG Translucent @U1 0.6 nozzle.json | 37 +-- ...maker PETG Translucent @U1 0.8 nozzle.json | 36 +-- .../filament/Snapmaker PETG-CF @U1.json | 79 +---- .../Snapmaker/filament/Snapmaker PLA @U1.json | 11 +- .../filament/Snapmaker PLA Basic @U1.json | 63 ++-- .../filament/Snapmaker PLA Eco @U1.json | 11 +- ...aker PLA Full Spectrum @U1 0.4 nozzle.json | 71 ++--- .../Snapmaker PLA Glow @U1 0.4 nozzle.json | 35 +-- .../filament/Snapmaker PLA Lite @U1.json | 62 ++-- .../Snapmaker PLA Matte @U1 0.2 nozzle.json | 67 ++--- .../Snapmaker PLA Matte @U1 0.6 nozzle.json | 65 +--- .../Snapmaker PLA Matte @U1 0.8 nozzle.json | 65 +--- .../filament/Snapmaker PLA Matte @U1.json | 91 +++--- .../filament/Snapmaker PLA Metal @U1.json | 11 +- .../Snapmaker PLA Silk @U1 0.2 nozzle.json | 55 +--- .../Snapmaker PLA Silk @U1 0.6 nozzle.json | 55 +--- .../Snapmaker PLA Silk @U1 0.8 nozzle.json | 55 +--- .../filament/Snapmaker PLA Silk @U1.json | 61 +--- ...napmaker PLA SnapSpeed @U1 0.2 nozzle.json | 24 +- ...napmaker PLA SnapSpeed @U1 0.6 nozzle.json | 25 +- ...napmaker PLA SnapSpeed @U1 0.8 nozzle.json | 23 +- .../filament/Snapmaker PLA SnapSpeed @U1.json | 69 ++--- ...pmaker PLA Translucent @U1 0.2 nozzle.json | 73 +++-- ...pmaker PLA Translucent @U1 0.4 nozzle.json | 81 ++--- ...pmaker PLA Translucent @U1 0.6 nozzle.json | 79 ++--- ...pmaker PLA Translucent @U1 0.8 nozzle.json | 77 ++--- .../Snapmaker PLA Wood @U1 0.4 nozzle.json | 40 +-- .../Snapmaker PLA Wood @U1 0.6 nozzle.json | 36 +-- .../Snapmaker PLA Wood @U1 0.8 nozzle.json | 34 +-- .../Snapmaker PLA-CF @U1 0.4 nozzle.json | 55 +--- .../Snapmaker PLA-CF @U1 0.6 nozzle.json | 47 +-- .../Snapmaker PLA-CF @U1 0.8 nozzle.json | 46 +-- .../Snapmaker PVA @U1 0.6 nozzle.json | 35 +-- .../Snapmaker PVA @U1 0.8 nozzle.json | 33 +-- .../Snapmaker/filament/Snapmaker PVA @U1.json | 41 +-- .../Snapmaker/filament/Snapmaker TPE @U1.json | 14 +- .../Snapmaker TPU 90A @U1 0.6 nozzle.json | 42 +-- .../Snapmaker TPU 90A @U1 0.8 nozzle.json | 42 +-- .../filament/Snapmaker TPU 90A @U1.json | 41 +-- .../filament/Snapmaker TPU 95A @U1.json | 70 ++--- .../Snapmaker TPU 95A HF @U1 0.6 nozzle.json | 39 +-- .../Snapmaker TPU 95A HF @U1 0.8 nozzle.json | 39 +-- .../filament/Snapmaker TPU 95A HF @U1.json | 40 +-- .../Snapmaker/filament/Snapmaker TPU @U1.json | 17 +- .../filament/Snapmaker TPU High-Flow @U1.json | 17 +- .../Snapmaker U1 (0.2 High Flow nozzle).json | 49 --- .../machine/Snapmaker U1 (0.2 nozzle).json | 12 +- .../Snapmaker U1 (0.4 High Flow nozzle).json | 49 --- .../machine/Snapmaker U1 (0.4 nozzle).json | 12 +- .../Snapmaker U1 (0.4+0.6 nozzle).json | 6 - .../Snapmaker U1 (0.6 High Flow nozzle).json | 49 --- .../machine/Snapmaker U1 (0.6 nozzle).json | 12 +- .../Snapmaker U1 (0.8 High Flow nozzle).json | 49 --- .../machine/Snapmaker U1 (0.8 nozzle).json | 12 +- .../Snapmaker/machine/Snapmaker U1.json | 4 +- .../profiles/Snapmaker/machine/fdm_U1.json | 26 +- ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 100 +------ ...6 Standard @Snapmaker U1 (0.2 nozzle).json | 77 +---- ...Extra Fine @Snapmaker U1 (0.4 nozzle).json | 78 +---- ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 100 +------ ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 125 +------- ...8 Standard @Snapmaker U1 (0.2 nozzle).json | 77 +---- ...gh Quality @Snapmaker U1 (0.2 nozzle).json | 100 +------ ...0 Standard @Snapmaker U1 (0.2 nozzle).json | 75 +---- .../0.12 Fine @Snapmaker U1 (0.4 nozzle).json | 77 +---- ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 125 +------- ...2 Standard @Snapmaker U1 (0.2 nozzle).json | 77 +---- ...4 Standard @Snapmaker U1 (0.2 nozzle).json | 77 +---- ...gh Quality @Snapmaker U1 (0.4 nozzle).json | 125 +------- ...16 Optimal @Snapmaker U1 (0.4 nozzle).json | 77 +---- ...8 Standard @Snapmaker U1 (0.6 nozzle).json | 88 +----- ...20 Quality @Snapmaker U1 (0.4 nozzle).json | 77 +---- ...0 Standard @Snapmaker U1 (0.4 nozzle).json | 77 +---- ...0 Standard @Snapmaker U1 (0.6 nozzle).json | 77 +---- ...0 Strength @Snapmaker U1 (0.4 nozzle).json | 78 +---- ...20 Support @Snapmaker U1 (0.4 nozzle).json | 77 +---- ... Support W @Snapmaker U1 (0.4 nozzle).json | 78 +---- ...0.24 Draft @Snapmaker U1 (0.4 nozzle).json | 77 +---- ...4 Standard @Snapmaker U1 (0.6 nozzle).json | 88 +----- ...4 Standard @Snapmaker U1 (0.8 nozzle).json | 88 +----- ....25 Benchy @Snapmaker U1 (0.4 nozzle).json | 278 ++---------------- ...xtra Draft @Snapmaker U1 (0.4 nozzle).json | 77 +---- ...0.30 Draft @Snapmaker U1 (0.6 nozzle).json | 79 +---- ...0 Standard @Snapmaker U1 (0.6 nozzle).json | 88 +----- ...0 Strength @Snapmaker U1 (0.6 nozzle).json | 88 +----- ...2 Standard @Snapmaker U1 (0.8 nozzle).json | 88 +----- ...6 Standard @Snapmaker U1 (0.6 nozzle).json | 88 +----- ...xtra Draft @Snapmaker U1 (0.6 nozzle).json | 81 +---- ...0 Standard @Snapmaker U1 (0.8 nozzle).json | 88 +----- ...2 Standard @Snapmaker U1 (0.6 nozzle).json | 88 +----- ...8 Standard @Snapmaker U1 (0.8 nozzle).json | 88 +----- ...6 Standard @Snapmaker U1 (0.8 nozzle).json | 77 +---- .../Snapmaker/process/fdm_process_U1.json | 2 +- 186 files changed, 952 insertions(+), 6774 deletions(-) delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json delete mode 100644 resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json delete mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json delete mode 100644 resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index 4d63c1b085..d13ed8bf21 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "02.04.00.23", + "version": "02.04.00.16", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ @@ -1448,10 +1448,6 @@ "name": "Snapmaker PETG HF @U1 0.2 nozzle", "sub_path": "filament/Snapmaker PETG HF @U1 0.2 nozzle.json" }, - { - "name": "Snapmaker PETG HF @U1 0.4 nozzle", - "sub_path": "filament/Snapmaker PETG HF @U1 0.4 nozzle.json" - }, { "name": "Snapmaker PETG HF @U1 0.6 nozzle", "sub_path": "filament/Snapmaker PETG HF @U1 0.6 nozzle.json" @@ -1924,122 +1920,6 @@ "name": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle", "sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json" }, - { - "name": "Generic PETG @U1 0.2 nozzle", - "sub_path": "filament/Generic PETG @U1 0.2 nozzle.json" - }, - { - "name": "Generic PETG @U1 0.4 nozzle", - "sub_path": "filament/Generic PETG @U1 0.4 nozzle.json" - }, - { - "name": "Generic PETG @U1 0.6 nozzle", - "sub_path": "filament/Generic PETG @U1 0.6 nozzle.json" - }, - { - "name": "Generic PETG @U1 0.8 nozzle", - "sub_path": "filament/Generic PETG @U1 0.8 nozzle.json" - }, - { - "name": "Generic PETG HF @U1 0.2 nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.2 nozzle.json" - }, - { - "name": "Generic PETG HF @U1 0.4 nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.4 nozzle.json" - }, - { - "name": "Generic PETG HF @U1 0.6 nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.6 nozzle.json" - }, - { - "name": "Generic PETG HF @U1 0.8 nozzle", - "sub_path": "filament/Generic PETG HF @U1 0.8 nozzle.json" - }, - { - "name": "Generic PETG-CF @U1 0.4 nozzle", - "sub_path": "filament/Generic PETG-CF @U1 0.4 nozzle.json" - }, - { - "name": "Generic PETG-CF @U1 0.6 nozzle", - "sub_path": "filament/Generic PETG-CF @U1 0.6 nozzle.json" - }, - { - "name": "Generic PETG-CF @U1 0.8 nozzle", - "sub_path": "filament/Generic PETG-CF @U1 0.8 nozzle.json" - }, - { - "name": "Generic PLA @U1 0.2 nozzle", - "sub_path": "filament/Generic PLA @U1 0.2 nozzle.json" - }, - { - "name": "Generic PLA @U1 0.4 nozzle", - "sub_path": "filament/Generic PLA @U1 0.4 nozzle.json" - }, - { - "name": "Generic PLA @U1 0.6 nozzle", - "sub_path": "filament/Generic PLA @U1 0.6 nozzle.json" - }, - { - "name": "Generic PLA @U1 0.8 nozzle", - "sub_path": "filament/Generic PLA @U1 0.8 nozzle.json" - }, - { - "name": "Generic PLA High Speed @U1 0.2 nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.2 nozzle.json" - }, - { - "name": "Generic PLA High Speed @U1 0.4 nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.4 nozzle.json" - }, - { - "name": "Generic PLA High Speed @U1 0.6 nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.6 nozzle.json" - }, - { - "name": "Generic PLA High Speed @U1 0.8 nozzle", - "sub_path": "filament/Generic PLA High Speed @U1 0.8 nozzle.json" - }, - { - "name": "Generic PLA Silk @U1 0.2 nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.2 nozzle.json" - }, - { - "name": "Generic PLA Silk @U1 0.4 nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.4 nozzle.json" - }, - { - "name": "Generic PLA Silk @U1 0.6 nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.6 nozzle.json" - }, - { - "name": "Generic PLA Silk @U1 0.8 nozzle", - "sub_path": "filament/Generic PLA Silk @U1 0.8 nozzle.json" - }, - { - "name": "Generic PLA-CF @U1 0.4 nozzle", - "sub_path": "filament/Generic PLA-CF @U1 0.4 nozzle.json" - }, - { - "name": "Generic PLA-CF @U1 0.6 nozzle", - "sub_path": "filament/Generic PLA-CF @U1 0.6 nozzle.json" - }, - { - "name": "Generic PLA-CF @U1 0.8 nozzle", - "sub_path": "filament/Generic PLA-CF @U1 0.8 nozzle.json" - }, - { - "name": "Generic TPU @U1 0.4 nozzle", - "sub_path": "filament/Generic TPU @U1 0.4 nozzle.json" - }, - { - "name": "Generic TPU @U1 0.6 nozzle", - "sub_path": "filament/Generic TPU @U1 0.6 nozzle.json" - }, - { - "name": "Generic TPU @U1 0.8 nozzle", - "sub_path": "filament/Generic TPU @U1 0.8 nozzle.json" - }, { "name": "PolyTerra J1 PLA", "sub_path": "filament/Polymaker/PolyTerra J1 PLA.json" @@ -2358,22 +2238,6 @@ "name": "Snapmaker Artisan (0.8 nozzle)", "sub_path": "machine/Snapmaker Artisan (0.8 nozzle).json" }, - { - "name": "Snapmaker U1 (0.2 High Flow nozzle)", - "sub_path": "machine/Snapmaker U1 (0.2 High Flow nozzle).json" - }, - { - "name": "Snapmaker U1 (0.4 High Flow nozzle)", - "sub_path": "machine/Snapmaker U1 (0.4 High Flow nozzle).json" - }, - { - "name": "Snapmaker U1 (0.6 High Flow nozzle)", - "sub_path": "machine/Snapmaker U1 (0.6 High Flow nozzle).json" - }, - { - "name": "Snapmaker U1 (0.8 High Flow nozzle)", - "sub_path": "machine/Snapmaker U1 (0.8 High Flow nozzle).json" - }, { "name": "Snapmaker A250 Dual BKit (0.2 nozzle)", "sub_path": "machine/Snapmaker A250 Dual BKit (0.2 nozzle).json" diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json deleted file mode 100644 index f993f4bbf7..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.2 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.2 nozzle", - "renamed_from": "Generic PETG U1 0.2 nozzle;Generic PETG @U1 0.2 High Flow nozzle;Generic PETG U1 0.2 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "Iw7T4qqDf1fbSAa1", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "10", - "1" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json deleted file mode 100644 index 4a04d39f7f..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.4 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.4 nozzle", - "renamed_from": "Generic PETG U1 0.4 nozzle;Generic PETG @U1 0.4 High Flow nozzle;Generic PETG U1 0.4 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "VHRa4TXU7HPz7Fld", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "10", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json deleted file mode 100644 index e1d6d82c6d..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.6 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.6 nozzle", - "renamed_from": "Generic PETG U1 0.6 nozzle;Generic PETG @U1 0.6 High Flow nozzle;Generic PETG U1 0.6 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "Ubr8v7BTxiQMntVk", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "10", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json deleted file mode 100644 index 368f9f4beb..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG @U1 0.8 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG @U1 0.8 nozzle", - "renamed_from": "Generic PETG U1 0.8 nozzle;Generic PETG @U1 0.8 High Flow nozzle;Generic PETG U1 0.8 High Flow nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "ro0mXCLzC5rY95oX", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PETG @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "10", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json deleted file mode 100644 index 7ef983ddb7..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.2 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.2 nozzle", - "renamed_from": "Generic PETG HF U1 0.2 nozzle;Generic PETG HF @U1 0.2 High Flow nozzle;Generic PETG HF U1 0.2 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "7yjLWAdcQe4ndo1z", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "20", - "1" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json deleted file mode 100644 index 1818f8dc9d..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.4 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.4 nozzle", - "renamed_from": "Generic PETG HF U1 0.4 nozzle;Generic PETG HF @U1 0.4 High Flow nozzle;Generic PETG HF U1 0.4 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "XWWLYZWkD9ZNRPeO", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "20", - "16" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json deleted file mode 100644 index d2f3c0a3af..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.6 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.6 nozzle", - "renamed_from": "Generic PETG HF U1 0.6 nozzle;Generic PETG HF @U1 0.6 High Flow nozzle;Generic PETG HF U1 0.6 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "1dj4JdFabSql3cEg", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "20", - "16" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json deleted file mode 100644 index 80c670da92..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG HF @U1 0.8 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG HF @U1 0.8 nozzle", - "renamed_from": "Generic PETG HF U1 0.8 nozzle;Generic PETG HF @U1 0.8 High Flow nozzle;Generic PETG HF U1 0.8 High Flow nozzle", - "inherits": "Generic PETG HF @System", - "from": "system", - "setting_id": "1LYZ0KG5kQtaOOKt", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Generic PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "20", - "16" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json deleted file mode 100644 index 16d2301a0a..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.4 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG-CF @U1 0.4 nozzle", - "renamed_from": "Generic PETG-CF U1 0.4 nozzle;Generic PETG-CF @U1 0.4 High Flow nozzle;Generic PETG-CF U1 0.4 High Flow nozzle", - "inherits": "Generic PETG-CF @System", - "from": "system", - "setting_id": "wIKcBM1qLgzLEUJP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 11 mm^3/s, from Snorca standard preset Generic PETG-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "11.5", - "11" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json deleted file mode 100644 index 9cb77c98fd..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.6 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG-CF @U1 0.6 nozzle", - "renamed_from": "Generic PETG-CF U1 0.6 nozzle;Generic PETG-CF @U1 0.6 High Flow nozzle;Generic PETG-CF U1 0.6 High Flow nozzle", - "inherits": "Generic PETG-CF @System", - "from": "system", - "setting_id": "FvrnBiPbjSvIF3kN", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json deleted file mode 100644 index a18aca19e4..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PETG-CF @U1 0.8 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic PETG-CF @U1 0.8 nozzle", - "renamed_from": "Generic PETG-CF U1 0.8 nozzle;Generic PETG-CF @U1 0.8 High Flow nozzle;Generic PETG-CF U1 0.8 High Flow nozzle", - "inherits": "Generic PETG-CF @System", - "from": "system", - "setting_id": "SCfvzDgUWkFHmbtP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 11.5 mm^3/s, from Snorca standard preset Generic PETG-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json deleted file mode 100644 index 334be2020e..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.2 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.2 nozzle", - "renamed_from": "Generic PLA U1 0.2 nozzle;Generic PLA @U1 0.2 High Flow nozzle;Generic PLA U1 0.2 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "8pb3tert1D4w0SKP", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Generic PLA @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "1.6" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json deleted file mode 100644 index 743800b360..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.4 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.4 nozzle", - "renamed_from": "Generic PLA U1 0.4 nozzle;Generic PLA @U1 0.4 High Flow nozzle;Generic PLA U1 0.4 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "Uilm8oVmZzyxE3ba", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json deleted file mode 100644 index ba65c7f999..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.6 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.6 nozzle", - "renamed_from": "Generic PLA U1 0.6 nozzle;Generic PLA @U1 0.6 High Flow nozzle;Generic PLA U1 0.6 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "aj4G9OSdgtQPqpkI", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json deleted file mode 100644 index 73c997dd6b..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA @U1 0.8 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA @U1 0.8 nozzle", - "renamed_from": "Generic PLA U1 0.8 nozzle;Generic PLA @U1 0.8 High Flow nozzle;Generic PLA U1 0.8 High Flow nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "zom29gBgIZmvjFmh", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json deleted file mode 100644 index fe3a8c5886..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.2 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.2 nozzle", - "renamed_from": "Generic PLA High Speed U1 0.2 nozzle;Generic PLA High Speed @U1 0.2 High Flow nozzle;Generic PLA High Speed U1 0.2 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "rT8e5f81vwUpwPuq", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "18", - "2" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json deleted file mode 100644 index 125a7a8f77..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.4 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.4 nozzle", - "renamed_from": "Generic PLA High Speed U1 0.4 nozzle;Generic PLA High Speed @U1 0.4 High Flow nozzle;Generic PLA High Speed U1 0.4 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "ZUetX0WveqW8fy6f", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "18", - "18" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json deleted file mode 100644 index 5a0c7f73cd..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.6 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.6 nozzle", - "renamed_from": "Generic PLA High Speed U1 0.6 nozzle;Generic PLA High Speed @U1 0.6 High Flow nozzle;Generic PLA High Speed U1 0.6 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "y2WY6qLA3xKZM1Mm", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "18", - "18" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json deleted file mode 100644 index 8826241929..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA High Speed @U1 0.8 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA High Speed @U1 0.8 nozzle", - "renamed_from": "Generic PLA High Speed U1 0.8 nozzle;Generic PLA High Speed @U1 0.8 High Flow nozzle;Generic PLA High Speed U1 0.8 High Flow nozzle", - "inherits": "Generic PLA High Speed @System", - "from": "system", - "setting_id": "VwXJrdwxvzjlIGal", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Generic PLA High Speed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "18", - "18" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json deleted file mode 100644 index 4f8a43e43e..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.2 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.2 nozzle", - "renamed_from": "Generic PLA Silk U1 0.2 nozzle;Generic PLA Silk @U1 0.2 High Flow nozzle;Generic PLA Silk U1 0.2 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "381SJIDBre8HxHy2", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "7.5", - "2" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json deleted file mode 100644 index 36bb9d7b4e..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.4 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.4 nozzle", - "renamed_from": "Generic PLA Silk U1 0.4 nozzle;Generic PLA Silk @U1 0.4 High Flow nozzle;Generic PLA Silk U1 0.4 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "Fd1g9OdeM3pUx8f5", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json deleted file mode 100644 index 8981c44ce1..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.6 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.6 nozzle", - "renamed_from": "Generic PLA Silk U1 0.6 nozzle;Generic PLA Silk @U1 0.6 High Flow nozzle;Generic PLA Silk U1 0.6 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "8rhmpBFmMj9vVZwG", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json deleted file mode 100644 index ec2669e3ff..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA Silk @U1 0.8 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA Silk @U1 0.8 nozzle", - "renamed_from": "Generic PLA Silk U1 0.8 nozzle;Generic PLA Silk @U1 0.8 High Flow nozzle;Generic PLA Silk U1 0.8 High Flow nozzle", - "inherits": "Generic PLA Silk @System", - "from": "system", - "setting_id": "Fo7U7GYHZh1aZPLs", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 7.5 mm^3/s, from Snorca standard preset Generic PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json deleted file mode 100644 index 2be327d2c2..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.4 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA-CF @U1 0.4 nozzle", - "renamed_from": "Generic PLA-CF U1 0.4 nozzle;Generic PLA-CF @U1 0.4 High Flow nozzle;Generic PLA-CF U1 0.4 High Flow nozzle", - "inherits": "Generic PLA-CF @System", - "from": "system", - "setting_id": "6AWubtDE4PqrKVNJ", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json deleted file mode 100644 index 24dd167796..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.6 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA-CF @U1 0.6 nozzle", - "renamed_from": "Generic PLA-CF U1 0.6 nozzle;Generic PLA-CF @U1 0.6 High Flow nozzle;Generic PLA-CF U1 0.6 High Flow nozzle", - "inherits": "Generic PLA-CF @System", - "from": "system", - "setting_id": "AvM44HgMCkOCnmzY", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json deleted file mode 100644 index f354ff4078..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic PLA-CF @U1 0.8 nozzle.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "filament", - "name": "Generic PLA-CF @U1 0.8 nozzle", - "renamed_from": "Generic PLA-CF U1 0.8 nozzle;Generic PLA-CF @U1 0.8 High Flow nozzle;Generic PLA-CF U1 0.8 High Flow nozzle", - "inherits": "Generic PLA-CF @System", - "from": "system", - "setting_id": "tvYAUvQEbbdBCIg2", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Generic PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "12" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json deleted file mode 100644 index b840b579cd..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.4 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic TPU @U1 0.4 nozzle", - "renamed_from": "Generic TPU U1 0.4 nozzle;Generic TPU @U1 0.4 High Flow nozzle;Generic TPU U1 0.4 High Flow nozzle", - "inherits": "Generic TPU @System", - "from": "system", - "setting_id": "9d0jRdMgkPMahYL7", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json deleted file mode 100644 index de956528f3..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.6 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic TPU @U1 0.6 nozzle", - "renamed_from": "Generic TPU U1 0.6 nozzle;Generic TPU @U1 0.6 High Flow nozzle;Generic TPU U1 0.6 High Flow nozzle", - "inherits": "Generic TPU @System", - "from": "system", - "setting_id": "LLjEPSU4l09N3Zb8", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json deleted file mode 100644 index 4d1b48e333..0000000000 --- a/resources/profiles/Snapmaker/filament/Generic TPU @U1 0.8 nozzle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "filament", - "name": "Generic TPU @U1 0.8 nozzle", - "renamed_from": "Generic TPU U1 0.8 nozzle;Generic TPU @U1 0.8 High Flow nozzle;Generic TPU U1 0.8 High Flow nozzle", - "inherits": "Generic TPU @System", - "from": "system", - "setting_id": "Wfhd3jLEjSKzCeHE", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Uses the active Orca Generic material settings. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Generic TPU @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] -} diff --git a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json index 95d2564b31..0e743d7aee 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker General PLA Family @U1.json @@ -1,29 +1,24 @@ { "type": "filament", "name": "Polymaker General PLA Family @U1", - "renamed_from": "Polymaker General PLA Family U1;Polymaker General PLA Family @U1 0.4 High Flow nozzle;Polymaker General PLA Family U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @U1 base", "from": "system", "setting_id": "thXfSaUkOAKJ50ey", "filament_id": "OF44ERDr", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "enable_pressure_advance": [ - "0", "0" ], "filament_max_volumetric_speed": [ - "15", "15" ], "filament_vendor": [ "Polymaker" ], "nozzle_temperature_range_high": [ - "230", "230" ], "temperature_vitrification": [ @@ -34,12 +29,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Polymaker General PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json index 3f07a84bc2..990cc483f3 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Silk PLA Family @U1.json @@ -1,15 +1,13 @@ { "type": "filament", "name": "Polymaker Silk PLA Family @U1", - "renamed_from": "Polymaker Silk PLA Family U1;Polymaker Silk PLA Family @U1 0.4 High Flow nozzle;Polymaker Silk PLA Family U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @U1 base", "from": "system", "setting_id": "h8vIYpXbxFtHPV6e", "filament_id": "OFH57ibH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_density": [ "1.34" @@ -18,15 +16,12 @@ "Polymaker" ], "nozzle_temperature": [ - "230", "230" ], "nozzle_temperature_initial_layer": [ - "230", "230" ], "nozzle_temperature_range_high": [ - "230", "230" ], "textured_plate_temp": [ @@ -34,12 +29,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Silk PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json index a7f2a911f7..1dad000fb2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker Tough PLA Family @U1.json @@ -1,21 +1,18 @@ { "type": "filament", "name": "Polymaker Tough PLA Family @U1", - "renamed_from": "Polymaker Tough PLA Family U1;Polymaker Tough PLA Family @U1 0.4 High Flow nozzle;Polymaker Tough PLA Family U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @U1 base", "from": "system", "setting_id": "d6CC81Es2hp46bto", "filament_id": "OFgdaBOt", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_density": [ "1.23" ], "filament_flow_ratio": [ - "0.96", "0.96" ], "filament_vendor": [ @@ -32,12 +29,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Polymaker Tough PLA Family @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json index 7ac058b8d4..b38925e01b 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon ASA-CF08 @Snapmaker U1.json @@ -1,28 +1,17 @@ { "type": "filament", "name": "Fiberon ASA-CF08 @Snapmaker U1", - "renamed_from": "Fiberon ASA-CF08 Snapmaker U1;Fiberon ASA-CF08 @U1 0.4 High Flow nozzle;Fiberon ASA-CF08 U1 0.4 High Flow nozzle", "inherits": "Fiberon ASA-CF08 @Snapmaker U1 base", "from": "system", "setting_id": "CdgDze2g0J4T7FEP", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "pressure_advance": [ - "0.05", "0.05" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon ASA-CF08 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json index 4a70c171d1..1bb569c291 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA12-CF10 @Snapmaker U1.json @@ -1,40 +1,26 @@ { "type": "filament", "name": "Fiberon PA12-CF10 @Snapmaker U1", - "renamed_from": "Fiberon PA12-CF10 Snapmaker U1;Fiberon PA12-CF10 @U1 0.4 High Flow nozzle;Fiberon PA12-CF10 U1 0.4 High Flow nozzle", "inherits": "Fiberon PA12-CF10 @Snapmaker U1 base", "from": "system", "setting_id": "x3irDyMqwMgklHKg", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retraction_length": [ - "1.0", "1.0" ], "filament_z_hop": [ - "0.0", "0.0" ], "enable_pressure_advance": [ - "1", "1" ], "pressure_advance": [ - "0.05", "0.05" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA12-CF10 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json index cf1cf91a53..d6106faa50 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA6-CF20 @Snapmaker U1.json @@ -1,40 +1,26 @@ { "type": "filament", "name": "Fiberon PA6-CF20 @Snapmaker U1", - "renamed_from": "Fiberon PA6-CF20 Snapmaker U1;Fiberon PA6-CF20 @U1 0.4 High Flow nozzle;Fiberon PA6-CF20 U1 0.4 High Flow nozzle", "inherits": "Fiberon PA6-CF20 @Snapmaker U1 base", "from": "system", "setting_id": "AMQyOXYNdWpVXmvK", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retraction_length": [ - "1.0", "1.0" ], "filament_z_hop": [ - "0.0", "0.0" ], "enable_pressure_advance": [ - "1", "1" ], "pressure_advance": [ - "0.08", "0.08" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA6-CF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json index 3260929361..660f4e9ef2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PA612-ESD @Snapmaker U1.json @@ -1,37 +1,24 @@ { "type": "filament", "name": "Fiberon PA612-ESD @Snapmaker U1", - "renamed_from": "Fiberon PA612-ESD Snapmaker U1;Fiberon PA612-ESD @U1 0.4 High Flow nozzle;Fiberon PA612-ESD U1 0.4 High Flow nozzle", "inherits": "Fiberon PA612-ESD @Snapmaker U1 base", "from": "system", "setting_id": "hUvnA3XmuC3bGLsi", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retraction_length": [ - "1.0", "1.0" ], "filament_z_hop": [ - "0.0", "0.0" ], "description": " ", "enable_pressure_advance": [ - "1", "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PA612-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json index 2111620245..6e1797904c 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PETG-ESD @Snapmaker U1.json @@ -1,17 +1,14 @@ { "type": "filament", "name": "Fiberon PETG-ESD @Snapmaker U1", - "renamed_from": "Fiberon PETG-ESD Snapmaker U1;Fiberon PETG-ESD @U1 0.4 High Flow nozzle;Fiberon PETG-ESD U1 0.4 High Flow nozzle", "inherits": "Fiberon PETG-ESD @Snapmaker U1 base", "from": "system", "setting_id": "AUr4Bm6UMW8S9TBh", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "supertack_plate_temp": [ @@ -21,14 +18,6 @@ "70" ], "pressure_advance": [ - "0.04", "0.04" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Fiberon PETG-ESD @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json index 2922f5c35e..4f1a9fdc10 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Fiberon PPS-GF20 @Snapmaker U1.json @@ -1,25 +1,15 @@ { "type": "filament", "name": "Fiberon PPS-GF20 @Snapmaker U1", - "renamed_from": "Fiberon PPS-GF20 Snapmaker U1;Fiberon PPS-GF20 @U1 0.4 High Flow nozzle;Fiberon PPS-GF20 U1 0.4 High Flow nozzle", "inherits": "Fiberon PPS-GF20 @Snapmaker U1 base", "from": "system", "setting_id": "7IpyPFHEAOmFlINJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], - "description": " ", - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: eSUN ABS+\nSunlu ABS+\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Fiberon PPS-GF20 @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ] + "description": " " } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json index e7dac937ee..1ad0f0d820 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma CoPE @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma CoPE @Snapmaker U1", - "renamed_from": "Panchroma CoPE @U1;Panchroma CoPE @U1 0.4 High Flow nozzle;Panchroma CoPE U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma CoPE @U1", "inherits": "Panchroma CoPE @Snapmaker U1 base", "from": "system", "setting_id": "gI7fRuITm0XhElnG", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma CoPE @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json index 73c689fc17..dcd84aeba9 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA @Snapmaker U1.json @@ -1,28 +1,18 @@ { "type": "filament", "name": "Panchroma PLA @Snapmaker U1", - "renamed_from": "Panchroma PLA @U1;Panchroma PLA @U1 0.4 High Flow nozzle;Panchroma PLA U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA @U1", "inherits": "Panchroma PLA @Snapmaker U1 base", "from": "system", "setting_id": "ayBhax9ELaiKfGFt", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_wipe": [ - "0", "0" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json index 1a13f95e37..561c748be3 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Celestial @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Celestial @Snapmaker U1", - "renamed_from": "Panchroma PLA Celestial @U1;Panchroma PLA Celestial @U1 0.4 High Flow nozzle;Panchroma PLA Celestial U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Celestial @U1", "inherits": "Panchroma PLA Celestial @Snapmaker U1 base", "from": "system", "setting_id": "OVmWNHpQnLZ67Bxd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Celestial @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json index 7472cb0a0a..fee27dc997 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Galaxy @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Galaxy @Snapmaker U1", - "renamed_from": "Panchroma PLA Galaxy @U1;Panchroma PLA Galaxy @U1 0.4 High Flow nozzle;Panchroma PLA Galaxy U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Galaxy @U1", "inherits": "Panchroma PLA Galaxy @Snapmaker U1 base", "from": "system", "setting_id": "FLECN8LIZ5RLgQGi", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json index f39b67101a..2f1135de61 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Glow @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Glow @Snapmaker U1", - "renamed_from": "Panchroma PLA Glow @U1;Panchroma PLA Glow @U1 0.4 High Flow nozzle;Panchroma PLA Glow U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Glow @U1", "inherits": "Panchroma PLA Glow @Snapmaker U1 base", "from": "system", "setting_id": "YEf4HFNPNtoFoLJd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json index c68daed901..fd701bf8c3 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Luminous @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Luminous @Snapmaker U1", - "renamed_from": "Panchroma PLA Luminous @U1;Panchroma PLA Luminous @U1 0.4 High Flow nozzle;Panchroma PLA Luminous U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Luminous @U1", "inherits": "Panchroma PLA Luminous @Snapmaker U1 base", "from": "system", "setting_id": "bxbXVqsMWWNfWcAn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json index a1e846c551..2a1dbab936 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Marble @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Marble @Snapmaker U1", - "renamed_from": "Panchroma PLA Marble @U1;Panchroma PLA Marble @U1 0.4 High Flow nozzle;Panchroma PLA Marble U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Marble @U1", "inherits": "Panchroma PLA Marble @Snapmaker U1 base", "from": "system", "setting_id": "P9wE2kZSR2Coq1K7", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json index a6df908ab7..7652606816 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Matte @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Matte @Snapmaker U1", - "renamed_from": "Panchroma PLA Matte @U1;Panchroma PLA Matte @U1 0.4 High Flow nozzle;Panchroma PLA Matte U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Matte @U1", "inherits": "Panchroma PLA Matte @Snapmaker U1 base", "from": "system", "setting_id": "0oJUzBq7iYJgJxZ7", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Matte @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json index 5cefb4ed7f..fe8e68081c 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Metallic @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Metallic @Snapmaker U1", - "renamed_from": "Panchroma PLA Metallic @U1;Panchroma PLA Metallic @U1 0.4 High Flow nozzle;Panchroma PLA Metallic U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Metallic @U1", "inherits": "Panchroma PLA Metallic @Snapmaker U1 base", "from": "system", "setting_id": "pAQGZzMH04kst11B", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json index b042855daf..cf16cb95c9 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Neon @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Neon @Snapmaker U1", - "renamed_from": "Panchroma PLA Neon @U1;Panchroma PLA Neon @U1 0.4 High Flow nozzle;Panchroma PLA Neon U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Neon @U1", "inherits": "Panchroma PLA Neon @Snapmaker U1 base", "from": "system", "setting_id": "qVG9q2ci7Cj4pM5c", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json index 8f920e7b9c..eff77a2aa9 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Satin @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Satin @Snapmaker U1", - "renamed_from": "Panchroma PLA Satin @U1;Panchroma PLA Satin @U1 0.4 High Flow nozzle;Panchroma PLA Satin U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Satin @U1", "inherits": "Panchroma PLA Satin @Snapmaker U1 base", "from": "system", "setting_id": "EHMn3LsbZlfuR7hD", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Panchroma PLA Satin @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json index e8ed450506..1020a48f0a 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Silk @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Silk @Snapmaker U1", - "renamed_from": "Panchroma PLA Silk @U1;Panchroma PLA Silk @U1 0.4 High Flow nozzle;Panchroma PLA Silk U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Silk @U1", "inherits": "Panchroma PLA Silk @Snapmaker U1 base", "from": "system", "setting_id": "JfIg6ZkVFA0CKLFI", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Panchroma PLA Silk @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json index 901a6ce47f..114391b517 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Starlight @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Starlight @Snapmaker U1", - "renamed_from": "Panchroma PLA Starlight @U1;Panchroma PLA Starlight @U1 0.4 High Flow nozzle;Panchroma PLA Starlight U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Starlight @U1", "inherits": "Panchroma PLA Starlight @Snapmaker U1 base", "from": "system", "setting_id": "pgcdQlQmPFSaduVr", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json index eaf3d61871..ca50194577 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Temp Shift @Snapmaker U1.json @@ -1,23 +1,27 @@ { "type": "filament", "name": "Panchroma PLA Temp Shift @Snapmaker U1", - "renamed_from": "Panchroma PLA Temp Shift @U1;Panchroma PLA Temp Shift @U1 0.4 High Flow nozzle;Panchroma PLA Temp Shift U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Temp Shift @U1", "inherits": "Panchroma PLA Temp Shift @Snapmaker U1 base", "from": "system", "setting_id": "SJq26RGx7OoZB4uH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], + "filament_flow_ratio": [ + "0.98" + ], + "filament_max_volumetric_speed": [ + "15" ], "slow_down_layer_time": [ "4" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Temp Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json index d361fefb66..cff9ec2011 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA Translucent @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA Translucent @Snapmaker U1", - "renamed_from": "Panchroma PLA Translucent @U1;Panchroma PLA Translucent @U1 0.4 High Flow nozzle;Panchroma PLA Translucent U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA Translucent @U1", "inherits": "Panchroma PLA Translucent @Snapmaker U1 base", "from": "system", "setting_id": "CBt541HVuOa7GGb9", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json index a02175965e..1d4a6a0bbe 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Panchroma PLA UV Shift @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Panchroma PLA UV Shift @Snapmaker U1", - "renamed_from": "Panchroma PLA UV Shift @U1;Panchroma PLA UV Shift @U1 0.4 High Flow nozzle;Panchroma PLA UV Shift U1 0.4 High Flow nozzle", + "renamed_from": "Panchroma PLA UV Shift @U1", "inherits": "Panchroma PLA UV Shift @Snapmaker U1 base", "from": "system", "setting_id": "vfVlN8IokcNnixsV", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Panchroma PLA UV Shift @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json index 99871fd7cc..836723cf7a 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite CosPLA @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite CosPLA @Snapmaker U1", - "renamed_from": "PolyLite CosPLA Snapmaker U1;PolyLite CosPLA @U1 0.4 High Flow nozzle;PolyLite CosPLA U1 0.4 High Flow nozzle", "inherits": "PolyLite CosPLA @Snapmaker U1 base", "from": "system", "setting_id": "KwD7WDyJNa4hWfZ8", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite CosPLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json index e1452a28d6..f51cc5eeec 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG @Snapmaker U1.json @@ -1,17 +1,14 @@ { "type": "filament", "name": "PolyLite PETG @Snapmaker U1", - "renamed_from": "PolyLite PETG Snapmaker U1;PolyLite PETG @U1 0.4 High Flow nozzle;PolyLite PETG U1 0.4 High Flow nozzle", "inherits": "PolyLite PETG @Snapmaker U1 base", "from": "system", "setting_id": "U9U4YmaRxgIdxQOc", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "supertack_plate_temp": [ @@ -21,14 +18,6 @@ "70" ], "pressure_advance": [ - "0.05", "0.05" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json index 135c13d405..a3efeee371 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PETG Translucent @Snapmaker U1.json @@ -1,17 +1,14 @@ { "type": "filament", "name": "PolyLite PETG Translucent @Snapmaker U1", - "renamed_from": "PolyLite PETG Translucent Snapmaker U1;PolyLite PETG Translucent @U1 0.4 High Flow nozzle;PolyLite PETG Translucent U1 0.4 High Flow nozzle", "inherits": "PolyLite PETG Translucent @Snapmaker U1 base", "from": "system", "setting_id": "NBQOZiQQ8MpcDnLM", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "supertack_plate_temp": [ @@ -21,14 +18,6 @@ "70" ], "pressure_advance": [ - "0.05", "0.05" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.5 mm^3/s, from local standard fallback preset PolyLite PETG Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json index 12b39b237b..715d8060e4 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "PolyLite PLA @Snapmaker U1", - "renamed_from": "PolyLite PLA @U1;PolyLite PLA @U1 0.4 High Flow nozzle;PolyLite PLA U1 0.4 High Flow nozzle", + "renamed_from": "PolyLite PLA @U1", "inherits": "PolyLite PLA @Snapmaker U1 base", "from": "system", "setting_id": "RtK3uYFG0OKtnLkP", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json index f869eb42f5..e096b1b9b3 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Galaxy @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Galaxy @Snapmaker U1", - "renamed_from": "PolyLite PLA Galaxy Snapmaker U1;PolyLite PLA Galaxy @U1 0.4 High Flow nozzle;PolyLite PLA Galaxy U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Galaxy @Snapmaker U1 base", "from": "system", "setting_id": "mNMTe346QAgo61NH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json index 8969ef20f9..a36d2b9de5 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Glow @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Glow @Snapmaker U1", - "renamed_from": "PolyLite PLA Glow Snapmaker U1;PolyLite PLA Glow @U1 0.4 High Flow nozzle;PolyLite PLA Glow U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Glow @Snapmaker U1 base", "from": "system", "setting_id": "xYjGzOzWWE2PBfNd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Glow @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json index 838a7e9b0d..874004e6ea 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Luminous @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Luminous @Snapmaker U1", - "renamed_from": "PolyLite PLA Luminous Snapmaker U1;PolyLite PLA Luminous @U1 0.4 High Flow nozzle;PolyLite PLA Luminous U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Luminous @Snapmaker U1 base", "from": "system", "setting_id": "pcPG6yUSO8F0e4gM", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Luminous @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json index e2c90ba3df..fbbb2a4585 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Neon @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Neon @Snapmaker U1", - "renamed_from": "PolyLite PLA Neon Snapmaker U1;PolyLite PLA Neon @U1 0.4 High Flow nozzle;PolyLite PLA Neon U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Neon @Snapmaker U1 base", "from": "system", "setting_id": "WMoh0mKVmVsdP5rc", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Neon @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json index ac90bd96e1..47163a788c 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro @Snapmaker U1.json @@ -1,23 +1,27 @@ { "type": "filament", "name": "PolyLite PLA Pro @Snapmaker U1", - "renamed_from": "PolyLite PLA Pro @U1;PolyLite PLA Pro @U1 0.4 High Flow nozzle;PolyLite PLA Pro U1 0.4 High Flow nozzle", + "renamed_from": "PolyLite PLA Pro @U1", "inherits": "PolyLite PLA Pro @Snapmaker U1 base", "from": "system", "setting_id": "GxuEzTdHffrvh2q6", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], + "filament_flow_ratio": [ + "0.98" + ], + "filament_max_volumetric_speed": [ + "15" ], "slow_down_layer_time": [ "4" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json index e7747546cc..b1611b961d 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Pro Metallic @Snapmaker U1.json @@ -1,20 +1,17 @@ { "type": "filament", "name": "PolyLite PLA Pro Metallic @Snapmaker U1", - "renamed_from": "PolyLite PLA Pro Metallic Snapmaker U1;PolyLite PLA Pro Metallic @U1 0.4 High Flow nozzle;PolyLite PLA Pro Metallic U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Pro Metallic @Snapmaker U1 base", "from": "system", "setting_id": "21wjCLo6hOb9p38K", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_end_gcode": [ "; filament end gcode \n\n" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_start_gcode": [ @@ -28,14 +25,6 @@ "0" ], "enable_pressure_advance": [ - "0", "0" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyLite PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json index e8e917c77d..56108cfed2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Starlight @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Starlight @Snapmaker U1", - "renamed_from": "PolyLite PLA Starlight Snapmaker U1;PolyLite PLA Starlight @U1 0.4 High Flow nozzle;PolyLite PLA Starlight U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Starlight @Snapmaker U1 base", "from": "system", "setting_id": "geXEuSvWTJD0rK7F", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Starlight @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json index c72f88974a..799e272fab 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyLite PLA Translucent @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyLite PLA Translucent @Snapmaker U1", - "renamed_from": "PolyLite PLA Translucent Snapmaker U1;PolyLite PLA Translucent @U1 0.4 High Flow nozzle;PolyLite PLA Translucent U1 0.4 High Flow nozzle", "inherits": "PolyLite PLA Translucent @Snapmaker U1 base", "from": "system", "setting_id": "YpMLpguxd61Bs16O", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset PolyLite PLA Translucent @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json index 860fd28cc4..244cf91471 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "PolyTerra PLA @Snapmaker U1", - "renamed_from": "PolyTerra PLA @U1;PolyTerra PLA @U1 0.4 High Flow nozzle;PolyTerra PLA U1 0.4 High Flow nozzle", + "renamed_from": "PolyTerra PLA @U1", "inherits": "PolyTerra PLA @Snapmaker U1 base", "from": "system", "setting_id": "kyxayLqRGrKKBwqp", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json index 85d38d728b..19e419ca36 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA Marble @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyTerra PLA Marble @Snapmaker U1", - "renamed_from": "PolyTerra PLA Marble Snapmaker U1;PolyTerra PLA Marble @U1 0.4 High Flow nozzle;PolyTerra PLA Marble U1 0.4 High Flow nozzle", "inherits": "PolyTerra PLA Marble @Snapmaker U1 base", "from": "system", "setting_id": "VjVt04nDFS9l9Rl9", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA Marble @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json index 251549352a..c3dd9d5537 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/PolyTerra PLA+ @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "PolyTerra PLA+ @Snapmaker U1", - "renamed_from": "PolyTerra PLA+ Snapmaker U1;PolyTerra PLA+ @U1 0.4 High Flow nozzle;PolyTerra PLA+ U1 0.4 High Flow nozzle", "inherits": "PolyTerra PLA+ @Snapmaker U1 base", "from": "system", "setting_id": "5L213op8PhcYpqiF", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset PolyTerra PLA+ @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json index 085e76dd37..ef631e550b 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Polymaker HT-PLA @Snapmaker U1", - "renamed_from": "Polymaker HT-PLA @U1;Polymaker HT-PLA @U1 0.4 High Flow nozzle;Polymaker HT-PLA U1 0.4 High Flow nozzle", + "renamed_from": "Polymaker HT-PLA @U1", "inherits": "Polymaker HT-PLA @Snapmaker U1 base", "from": "system", "setting_id": "0A5ETOrUTF0P3HS4", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json index 663f56a179..ac49e4f7ac 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker HT-PLA-GF @Snapmaker U1.json @@ -1,24 +1,15 @@ { "type": "filament", "name": "Polymaker HT-PLA-GF @Snapmaker U1", - "renamed_from": "Polymaker HT-PLA-GF @U1;Polymaker HT-PLA-GF @U1 0.4 High Flow nozzle;Polymaker HT-PLA-GF U1 0.4 High Flow nozzle", + "renamed_from": "Polymaker HT-PLA-GF @U1", "inherits": "Polymaker HT-PLA-GF @Snapmaker U1 base", "from": "system", "setting_id": "i0LA17aaWvgLG7xd", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker HT-PLA-GF @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json index bfd9836810..a3e11c2cd2 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG @Snapmaker U1.json @@ -1,17 +1,14 @@ { "type": "filament", "name": "Polymaker PETG @Snapmaker U1", - "renamed_from": "Polymaker PETG Snapmaker U1;Polymaker PETG @U1 0.4 High Flow nozzle;Polymaker PETG U1 0.4 High Flow nozzle", "inherits": "Polymaker PETG @Snapmaker U1 base", "from": "system", "setting_id": "kS4t66kFCXtambwx", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "supertack_plate_temp": [ @@ -19,12 +16,5 @@ ], "supertack_plate_temp_initial_layer": [ "70" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json index fc8917d773..ecac971c5e 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PETG Galaxy @Snapmaker U1.json @@ -1,17 +1,14 @@ { "type": "filament", "name": "Polymaker PETG Galaxy @Snapmaker U1", - "renamed_from": "Polymaker PETG Galaxy Snapmaker U1;Polymaker PETG Galaxy @U1 0.4 High Flow nozzle;Polymaker PETG Galaxy U1 0.4 High Flow nozzle", "inherits": "Polymaker PETG Galaxy @Snapmaker U1 base", "from": "system", "setting_id": "a5yZ1QlqIGEAW1sU", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "supertack_plate_temp": [ @@ -19,12 +16,5 @@ ], "supertack_plate_temp_initial_layer": [ "70" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PETG Galaxy @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json index 077a259687..ff614bc79f 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA @Snapmaker U1.json @@ -1,28 +1,20 @@ { "type": "filament", "name": "Polymaker PLA @Snapmaker U1", - "renamed_from": "Polymaker PLA Snapmaker U1;Polymaker PLA @U1 0.4 High Flow nozzle;Polymaker PLA U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA @Snapmaker U1 base", "from": "system", "setting_id": "NSuD5f5eVr8SHA4n", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "Direct Drive Standard" ], "pressure_advance": [ - "0.05", "0.05" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 20 mm^3/s, from local standard fallback preset Polymaker PLA @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json index e9a11b4989..d5823b53a6 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "Polymaker PLA Pro @Snapmaker U1", - "renamed_from": "Polymaker PLA Pro Snapmaker U1;Polymaker PLA Pro @U1 0.4 High Flow nozzle;Polymaker PLA Pro U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA Pro @Snapmaker U1 base", "from": "system", "setting_id": "0iJ5qjVvmrkRzpZD", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json index a1a75b337a..a26fb437e7 100644 --- a/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json +++ b/resources/profiles/Snapmaker/filament/Polymaker/Polymaker PLA Pro Metallic @Snapmaker U1.json @@ -1,24 +1,14 @@ { "type": "filament", "name": "Polymaker PLA Pro Metallic @Snapmaker U1", - "renamed_from": "Polymaker PLA Pro Metallic Snapmaker U1;Polymaker PLA Pro Metallic @U1 0.4 High Flow nozzle;Polymaker PLA Pro Metallic U1 0.4 High Flow nozzle", "inherits": "Polymaker PLA Pro Metallic @Snapmaker U1 base", "from": "system", "setting_id": "s4F24JmyDykNvtAV", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Polymaker PLA Pro Metallic @Snapmaker U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json index a9137c0b4a..fe6c59ba80 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS @U1.json @@ -1,72 +1,14 @@ { "type": "filament", "name": "Snapmaker ABS @U1", - "renamed_from": "Snapmaker ABS U1;Snapmaker ABS @U1 0.4 High Flow nozzle;Snapmaker ABS U1 0.4 High Flow nozzle", "inherits": "Snapmaker ABS @U1 base", "from": "system", "setting_id": "UFMRgwmOOQhOa3kB", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_retract_length_toolchange": [ - "5", "5" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: eSUN ABS+\nSunlu ABS+\n\n\nHigh-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ABS @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_minimal_purge_on_wipe_tower": [ - "0", - "15" - ], - "fan_max_speed": [ - "15", - "60" - ], - "nozzle_temperature_initial_layer": [ - "260", - "280" - ], - "nozzle_temperature": [ - "260", - "280" - ], - "filament_multitool_ramming_volume": [ - "0", - "5" - ], - "additional_cooling_fan_speed": [ - "0", - "0" - ], - "filament_retraction_length": [ - "0.6", - "0.2" - ], - "filament_max_volumetric_speed": [ - "8", - "22" - ], - "filament_multitool_ramming_flow": [ - "10", - "30" - ], - "fan_min_speed": [ - "15", - "10" - ], - "filament_z_hop_types": [ - "nil", - "Slope Lift" - ], - "filament_multitool_ramming": [ - "0", - "1" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json index 8e2a71a0a7..1ba6c22c9a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ABS Benchy @U1.json @@ -1,15 +1,13 @@ { "type": "filament", "name": "Snapmaker ABS Benchy @U1", - "renamed_from": "Snapmaker ABS Benchy U1;Snapmaker ABS Benchy @U1 0.4 High Flow nozzle;Snapmaker ABS Benchy U1 0.4 High Flow nozzle", "inherits": "Snapmaker ABS @U1 base", "from": "system", "setting_id": "xszx4XRGrp88OdP8", "filament_id": "OFJrvXth", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "compatible_prints": [ "0.25 Benchy @Snapmaker U1 (0.4 nozzle)" @@ -24,43 +22,33 @@ "2" ], "filament_deretraction_speed": [ - "55", "55" ], "filament_max_volumetric_speed": [ - "31", "31" ], "filament_retract_when_changing_layer": [ - "0", "0" ], "filament_retraction_length": [ - "0.5", "0.5" ], "filament_z_hop": [ - "0", "0" ], "filament_retraction_speed": [ - "55", "55" ], "filament_wipe": [ - "0", "0" ], "nozzle_temperature": [ - "250", "250" ], "fan_max_speed": [ - "50", "50" ], "fan_min_speed": [ - "35", "35" ], "slow_down_min_speed": [ @@ -71,12 +59,5 @@ ], "default_filament_colour": [ "" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: eSUN ABS+\nSunlu ABS+\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 31 mm^3/s, from local standard fallback preset Snapmaker ABS Benchy @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json index 2911df67e8..67bde29e8d 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker ASA @U1.json @@ -1,72 +1,14 @@ { "type": "filament", "name": "Snapmaker ASA @U1", - "renamed_from": "Snapmaker ASA U1;Snapmaker ASA @U1 0.4 High Flow nozzle;Snapmaker ASA U1 0.4 High Flow nozzle", "inherits": "Snapmaker ASA @U1 base", "from": "system", "setting_id": "XlbCMKY8EvV0vM88", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_retract_length_toolchange": [ - "5", "5" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 22 mm^3/s, from Snorca HF preset Snapmaker ASA @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_minimal_purge_on_wipe_tower": [ - "0", - "15" - ], - "fan_max_speed": [ - "15", - "25" - ], - "nozzle_temperature_initial_layer": [ - "255", - "280" - ], - "nozzle_temperature": [ - "255", - "280" - ], - "filament_multitool_ramming_volume": [ - "0", - "10" - ], - "additional_cooling_fan_speed": [ - "0", - "0" - ], - "filament_max_volumetric_speed": [ - "8", - "22" - ], - "pressure_advance": [ - "0.04", - "0.02" - ], - "filament_multitool_ramming_flow": [ - "10", - "20" - ], - "fan_min_speed": [ - "0", - "10" - ], - "filament_z_hop_types": [ - "nil", - "Slope Lift" - ], - "filament_multitool_ramming": [ - "0", - "1" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json index 8baf211010..19dc66df4e 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.2 nozzle.json @@ -1,40 +1,36 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1 0.2 nozzle", - "renamed_from": "Snapmaker Breakaway Support For PLA U1 0.2 nozzle;Snapmaker Breakaway Support For PLA @U1 0.2 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.2 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "mJoaud6wulT4p8SA", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "filament_type": [ "PLA" ], + "enable_pressure_advance": [ + "0" + ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_max_speed": [ - "30", "30" ], "fan_min_speed": [ - "20", "20" ], "filament_cost": [ @@ -44,46 +40,36 @@ "1.3" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "1", "1" ], "filament_max_volumetric_speed": [ - "0.5", "0.5" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ - "240", "240" ], "nozzle_temperature_initial_layer": [ - "240", "240" ], "nozzle_temperature_range_low": [ - "190", "190" ], "pressure_advance": [ - "0.2", "0.2" ], "textured_plate_temp": [ @@ -99,19 +85,15 @@ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "5", "5" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_unloading_speed": [ @@ -119,12 +101,5 @@ ], "filament_unloading_speed_start": [ "100" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 0.5 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.2 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json index ad4321da3f..6f800f7b98 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.6 nozzle.json @@ -1,19 +1,20 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1 0.6 nozzle", - "renamed_from": "Snapmaker Breakaway Support For PLA U1 0.6 nozzle;Snapmaker Breakaway Support For PLA @U1 0.6 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.6 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "rKUUREJNhstIU0I2", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filament_type": [ "PLA" ], + "enable_pressure_advance": [ + "0" + ], "filament_cost": [ "69.98" ], @@ -21,26 +22,21 @@ "1.3" ], "filament_flow_ratio": [ - "0.95", "0.95" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ - "210", "210" ], "nozzle_temperature_initial_layer": [ - "210", "210" ], "nozzle_temperature_range_low": [ - "190", "190" ], "pressure_advance": [ - "0.02", "0.02" ], "textured_plate_temp": [ @@ -50,46 +46,36 @@ "65" ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_min_speed": [ - "100", "100" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "1", "1" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_wipe_distance": [ - "2", "2" ], "filament_cooling_moves": [ @@ -99,19 +85,15 @@ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_unloading_speed": [ @@ -119,12 +101,5 @@ ], "filament_unloading_speed_start": [ "100" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.6 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json index 3e2fdc633a..c7a2b01351 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json @@ -1,32 +1,30 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle", - "renamed_from": "Snapmaker Breakaway Support For PLA U1 0.8 nozzle;Snapmaker Breakaway Support For PLA @U1 0.8 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.8 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "hQ8jQ5GKQKGKuPew", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filament_type": [ "PLA" ], + "enable_pressure_advance": [ + "0" + ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cost": [ @@ -36,42 +34,33 @@ "1.3" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "0.93", "0.93" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "3", "3" ], "filament_retraction_speed": [ - "nil", "nil" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ - "210", "210" ], "nozzle_temperature_initial_layer": [ - "210", "210" ], "nozzle_temperature_range_low": [ - "190", "190" ], "pressure_advance": [ - "0.015", "0.015" ], "textured_plate_temp_initial_layer": [ @@ -84,19 +73,15 @@ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_unloading_speed": [ @@ -106,17 +91,9 @@ "100" ], "fan_min_speed": [ - "100", "100" ], "textured_plate_temp": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker Breakaway Support For PLA @U1 0.8 nozzle; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json index 877e005324..eff74ae5ab 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker Breakaway Support For PLA @U1.json @@ -1,82 +1,66 @@ { "type": "filament", "name": "Snapmaker Breakaway Support For PLA @U1", - "renamed_from": "Snapmaker Breakaway Support For PLA U1;Snapmaker Breakaway Support For PLA @U1 0.4 High Flow nozzle;Snapmaker Breakaway Support For PLA U1 0.4 High Flow nozzle", "inherits": "Snapmaker Breakaway Support @base", "from": "system", "setting_id": "5DqT3CYUgp1oJD1V", "filament_id": "OFnmp2pO", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_type": [ "PLA" ], "enable_pressure_advance": [ - "1", "1" ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_min_speed": [ - "100", "100" ], "filament_cooling_moves": [ "4" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "1", "1" ], "filament_loading_speed": [ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "20", "20" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "1", "1" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_unloading_speed": [ @@ -89,19 +73,15 @@ "65" ], "nozzle_temperature": [ - "210", "210" ], "nozzle_temperature_initial_layer": [ - "210", "210" ], "nozzle_temperature_range_low": [ - "190", "190" ], "pressure_advance": [ - "0.03", "0.03" ], "textured_plate_temp": [ @@ -109,16 +89,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker Support For PLA @U1 0.4 nozzle. Its base identifies the material as Snapmaker Breakaway Support. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "12", - "18" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json index f2297d56cf..b2dc562d3b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PA-CF @U1.json @@ -1,20 +1,11 @@ { "type": "filament", "name": "Snapmaker PA-CF @U1", - "renamed_from": "Snapmaker PA-CF U1;Snapmaker PA-CF @U1 0.4 High Flow nozzle;Snapmaker PA-CF U1 0.4 High Flow nozzle", "inherits": "Snapmaker PA-CF @U1 base", "from": "system", "setting_id": "8aP6ye4aeOS7qKpG", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 8 mm^3/s, from local standard fallback preset Snapmaker PA-CF @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Snapmaker U1 (0.4 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json index 10723ae4d1..e4dd0d2035 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PET @U1.json @@ -1,20 +1,11 @@ { "type": "filament", "name": "Snapmaker PET @U1", - "renamed_from": "Snapmaker PET U1;Snapmaker PET @U1 0.4 High Flow nozzle;Snapmaker PET U1 0.4 High Flow nozzle", "inherits": "Snapmaker PET @U1 base", "from": "system", "setting_id": "sIijFVd50jJB1sK0", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: JiaNong PET 1.63x1.75mm\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 6.8 mm^3/s, from local standard fallback preset Snapmaker PET @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Snapmaker U1 (0.4 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json index 6a80696224..5df9952064 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG @U1.json @@ -1,20 +1,11 @@ { "type": "filament", "name": "Snapmaker PETG @U1", - "renamed_from": "Snapmaker PETG U1;Snapmaker PETG @U1 0.4 High Flow nozzle;Snapmaker PETG U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG @U1 base", "from": "system", "setting_id": "r7KJB0EPpsknK5iH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10 mm^3/s, from local standard fallback preset Snapmaker PETG @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Snapmaker U1 (0.4 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json index 6609f2a381..9d9e3918cf 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.2 nozzle.json @@ -1,25 +1,29 @@ { "type": "filament", "name": "Snapmaker PETG HF @U1 0.2 nozzle", - "renamed_from": "Snapmaker PETG HF U1 0.2 nozzle;Snapmaker PETG HF @U1 0.2 High Flow nozzle;Snapmaker PETG HF U1 0.2 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", "setting_id": "Ujul2YTBwfldFiFd", "instantiation": "true", "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "Direct Drive Standard" ], "filament_max_volumetric_speed": [ - "1", - "2" + "1" + ], + "filament_ramming_travel_time": [ + "0" + ], + "long_retractions_when_ec": [ + "0" + ], + "retraction_distances_when_ec": [ + "0" ], "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "additional_cooling_fan_speed": [ - "70", "70" ], "eng_plate_temp": [ @@ -32,34 +36,27 @@ "20" ], "fan_max_speed": [ - "60", "60" ], "fan_min_speed": [ - "30", "30" ], "filament_end_gcode": [ "\n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "3", "3" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "1.5", "1.5" ], "filament_start_gcode": [ @@ -75,15 +72,12 @@ "80" ], "nozzle_temperature": [ - "245", "245" ], "nozzle_temperature_initial_layer": [ - "245", - "230" + "245" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_speed": [ @@ -108,18 +102,6 @@ "80" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "pressure_advance": [ - "0.03", - "0.0525" - ], - "filament_flow_ratio": [ - "0.95", - "0.955" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json deleted file mode 100644 index 6489aeaf67..0000000000 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.4 nozzle.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "type": "filament", - "name": "Snapmaker PETG HF @U1 0.4 nozzle", - "renamed_from": "Snapmaker PETG HF U1 0.4 nozzle;Snapmaker PETG HF @U1 0.4 High Flow nozzle;Snapmaker PETG HF U1 0.4 High Flow nozzle", - "inherits": "Snapmaker PETG HF @U1 base2", - "from": "system", - "setting_id": "9NnYt52azW8zCp4a", - "instantiation": "true", - "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 30 mm^3/s, from Snorca HF preset Snapmaker PETG HF (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_minimal_purge_on_wipe_tower": [ - "15", - "50" - ], - "nozzle_temperature_range_low": [ - "220", - "230" - ], - "fan_max_speed": [ - "40", - "60" - ], - "nozzle_temperature_initial_layer": [ - "220", - "230" - ], - "nozzle_temperature": [ - "220", - "245" - ], - "filament_multitool_ramming_volume": [ - "0", - "8" - ], - "filament_retraction_length": [ - "nil", - "0.8" - ], - "filament_wipe_distance": [ - "nil", - "1" - ], - "filament_max_volumetric_speed": [ - "16", - "30" - ], - "filament_retract_length_toolchange": [ - "nil", - "8" - ], - "pressure_advance": [ - "0.03", - "0.035" - ], - "filament_deretraction_speed": [ - "nil", - "40" - ], - "filament_multitool_ramming_flow": [ - "10", - "30" - ], - "fan_min_speed": [ - "20", - "30" - ], - "filament_flow_ratio": [ - "0.95", - "0.955" - ], - "filament_z_hop_types": [ - "nil", - "Slope Lift" - ], - "filament_multitool_ramming": [ - "0", - "1" - ], - "filament_retraction_speed": [ - "nil", - "40" - ] -} diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json index 9a30b59d07..985505a027 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.6 nozzle.json @@ -1,21 +1,26 @@ { "type": "filament", "name": "Snapmaker PETG HF @U1 0.6 nozzle", - "renamed_from": "Snapmaker PETG HF U1 0.6 nozzle;Snapmaker PETG HF @U1 0.6 High Flow nozzle;Snapmaker PETG HF U1 0.6 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", "setting_id": "2FfQZdp5xunngnF0", "instantiation": "true", "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "Direct Drive Standard" + ], + "filament_ramming_travel_time": [ + "0" + ], + "long_retractions_when_ec": [ + "0" + ], + "retraction_distances_when_ec": [ + "0" ], "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "additional_cooling_fan_speed": [ - "70", "70" ], "eng_plate_temp": [ @@ -28,38 +33,30 @@ "20" ], "fan_max_speed": [ - "60", "60" ], "fan_min_speed": [ - "30", "30" ], "filament_end_gcode": [ "\n" ], "filament_max_volumetric_speed": [ - "20", "20" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "2.5", "2.5" ], "filament_start_gcode": [ @@ -69,15 +66,12 @@ "Snapmaker" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "1", "1" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ @@ -87,15 +81,12 @@ "80" ], "nozzle_temperature": [ - "245", "245" ], "nozzle_temperature_initial_layer": [ - "245", - "230" + "245" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_speed": [ @@ -118,16 +109,5 @@ ], "textured_plate_temp_initial_layer": [ "80" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "pressure_advance": [ - "0.03", - "0.0525" - ], - "filament_flow_ratio": [ - "0.95", - "0.955" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json index fcee4d1f99..83c1b17971 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG HF @U1 0.8 nozzle.json @@ -1,21 +1,26 @@ { "type": "filament", "name": "Snapmaker PETG HF @U1 0.8 nozzle", - "renamed_from": "Snapmaker PETG HF U1 0.8 nozzle;Snapmaker PETG HF @U1 0.8 High Flow nozzle;Snapmaker PETG HF U1 0.8 High Flow nozzle", "inherits": "Snapmaker PETG HF @U1 base2", "from": "system", "setting_id": "ebANa67V4B2nwUaX", "instantiation": "true", "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "Direct Drive Standard" + ], + "filament_ramming_travel_time": [ + "0" + ], + "long_retractions_when_ec": [ + "0" + ], + "retraction_distances_when_ec": [ + "0" ], "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "additional_cooling_fan_speed": [ - "70", "70" ], "eng_plate_temp": [ @@ -28,35 +33,27 @@ "20" ], "fan_max_speed": [ - "60", "60" ], "fan_min_speed": [ - "30", "30" ], "filament_max_volumetric_speed": [ - "20", "20" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "1", "1" ], "filament_start_gcode": [ @@ -66,15 +63,12 @@ "Snapmaker" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "2", "2" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ @@ -84,15 +78,12 @@ "80" ], "nozzle_temperature": [ - "245", "245" ], "nozzle_temperature_initial_layer": [ - "245", - "230" + "245" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_speed": [ @@ -115,16 +106,5 @@ ], "textured_plate_temp_initial_layer": [ "80" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PETG HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "pressure_advance": [ - "0.03", - "0.0525" - ], - "filament_flow_ratio": [ - "0.95", - "0.955" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json index 87612234c4..ee6234f406 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.2 nozzle.json @@ -1,36 +1,29 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.2 nozzle", - "renamed_from": "Snapmaker PETG Translucent U1 0.2 nozzle;Snapmaker PETG Translucent @U1 0.2 High Flow nozzle;Snapmaker PETG Translucent U1 0.2 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "UTpedqYJysaoe3OI", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "additional_cooling_fan_speed": [ - "20", "20" ], "close_fan_the_first_x_layers": [ "3" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_max_speed": [ - "40", "40" ], "fan_min_speed": [ - "20", "20" ], "filament_cooling_moves": [ @@ -43,46 +36,36 @@ "1.25" ], "filament_flow_ratio": [ - "0.98", "0.98" ], "filament_loading_speed": [ "28" ], "filament_max_volumetric_speed": [ - "1", "1" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "5", "5" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "1", "1" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "50", "50" ], "filament_unloading_speed": [ @@ -92,26 +75,25 @@ "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ "80" ], + "nozzle_temperature": [ + "245" + ], "nozzle_temperature_initial_layer": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_threshold": [ "10%" ], "pressure_advance": [ - "0.23", - "0.2875" + "0.23" ], "slow_down_min_speed": [ "10" @@ -123,14 +105,6 @@ "80" ], "enable_pressure_advance": [ - "0", - "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json index 239e2c6c7a..e0e60a0e14 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.4 nozzle.json @@ -1,32 +1,26 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.4 nozzle", - "renamed_from": "Snapmaker PETG Translucent U1 0.4 nozzle;Snapmaker PETG Translucent @U1 0.4 High Flow nozzle;Snapmaker PETG Translucent U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "k6UQJJASzFSfePqN", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "close_fan_the_first_x_layers": [ "3" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_max_speed": [ - "30", "30" ], "fan_min_speed": [ - "10", "10" ], "filament_cooling_moves": [ @@ -36,38 +30,30 @@ "24.99" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_loading_speed": [ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "1", "1" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_unloading_speed": [ @@ -77,30 +63,25 @@ "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ "80" ], "nozzle_temperature": [ - "250", "250" ], "nozzle_temperature_initial_layer": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_threshold": [ "10%" ], "pressure_advance": [ - "0.04", - "0.05" + "0.04" ], "slow_down_min_speed": [ "10" @@ -113,16 +94,5 @@ ], "filament_density": [ "1.25" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 10 mm^3/s, from Snorca HF preset Snapmaker PETG Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "additional_cooling_fan_speed": [ - "0", - "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json index 48d397028c..14f59104f8 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.6 nozzle.json @@ -1,32 +1,26 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.6 nozzle", - "renamed_from": "Snapmaker PETG Translucent U1 0.6 nozzle;Snapmaker PETG Translucent @U1 0.6 High Flow nozzle;Snapmaker PETG Translucent U1 0.6 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "FlcbnP3kWiJLMDMl", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "close_fan_the_first_x_layers": [ "3" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_max_speed": [ - "60", "60" ], "fan_min_speed": [ - "20", "20" ], "filament_cost": [ @@ -36,30 +30,28 @@ "1.25" ], "filament_flow_ratio": [ - "0.98", "0.98" ], "filament_max_volumetric_speed": [ - "16", "16" ], "hot_plate_temp": [ "80" ], + "nozzle_temperature": [ + "245" + ], "nozzle_temperature_initial_layer": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_threshold": [ "10%" ], "pressure_advance": [ - "0.02", - "0.025" + "0.02" ], "slow_down_min_speed": [ "10" @@ -77,31 +69,24 @@ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "1.2", "1.2" ], "filament_retraction_minimum_travel": [ - "1", "1" ], "filament_unloading_speed": [ @@ -111,22 +96,12 @@ "100" ], "filament_wipe_distance": [ - "2", "2" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "enable_pressure_advance": [ - "0", - "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json index d226179805..e746141f06 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG Translucent @U1 0.8 nozzle.json @@ -1,32 +1,26 @@ { "type": "filament", "name": "Snapmaker PETG Translucent @U1 0.8 nozzle", - "renamed_from": "Snapmaker PETG Translucent U1 0.8 nozzle;Snapmaker PETG Translucent @U1 0.8 High Flow nozzle;Snapmaker PETG Translucent U1 0.8 High Flow nozzle", "inherits": "Snapmaker PETG Translucent @U1 base", "from": "system", "setting_id": "aJk3TnFTU3uQGjd3", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "close_fan_the_first_x_layers": [ "3" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_max_speed": [ - "60", "60" ], "fan_min_speed": [ - "20", "20" ], "filament_cost": [ @@ -36,30 +30,28 @@ "1.25" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_max_volumetric_speed": [ - "16", "16" ], "hot_plate_temp": [ "80" ], + "nozzle_temperature": [ + "245" + ], "nozzle_temperature_initial_layer": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "230", "230" ], "overhang_fan_threshold": [ "10%" ], "pressure_advance": [ - "0.02", - "0.025" + "0.02" ], "slow_down_min_speed": [ "10" @@ -77,31 +69,24 @@ "28" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "30", "30" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "1", "1" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_unloading_speed": [ @@ -111,18 +96,9 @@ "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "enable_pressure_advance": [ - "0", - "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker PETG Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json index 174e0f9d29..f0bae32788 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PETG-CF @U1.json @@ -1,88 +1,11 @@ { "type": "filament", "name": "Snapmaker PETG-CF @U1", - "renamed_from": "Snapmaker PETG-CF U1;Snapmaker PETG-CF @U1 0.4 High Flow nozzle;Snapmaker PETG-CF U1 0.4 High Flow nozzle", "inherits": "Snapmaker PETG-CF @U1 base", "from": "system", "setting_id": "i982wlNMSJujh6Vs", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PETG-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_minimal_purge_on_wipe_tower": [ - "0", - "30" - ], - "nozzle_temperature_range_low": [ - "220", - "240" - ], - "fan_max_speed": [ - "20", - "40" - ], - "nozzle_temperature_initial_layer": [ - "250", - "260" - ], - "nozzle_temperature": [ - "245", - "260" - ], - "filament_multitool_ramming_volume": [ - "0", - "0.5" - ], - "additional_cooling_fan_speed": [ - "0", - "20" - ], - "nozzle_temperature_range_high": [ - "255", - "270" - ], - "filament_retraction_length": [ - "nil", - "0.6" - ], - "filament_max_volumetric_speed": [ - "6.4", - "18" - ], - "filament_retract_length_toolchange": [ - "nil", - "nil" - ], - "pressure_advance": [ - "0.03", - "0.028" - ], - "enable_pressure_advance": [ - "0", - "1" - ], - "filament_multitool_ramming_flow": [ - "10", - "13" - ], - "fan_min_speed": [ - "0", - "5" - ], - "filament_flow_ratio": [ - "0.95", - "1.01" - ], - "filament_multitool_ramming": [ - "0", - "1" + "Snapmaker U1 (0.4 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json index f8097b3a27..1a2f9e3ff3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA @U1.json @@ -1,26 +1,17 @@ { "type": "filament", "name": "Snapmaker PLA @U1", - "renamed_from": "Snapmaker PLA U1;Snapmaker PLA @U1 0.4 High Flow nozzle;Snapmaker PLA U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA @U1 base", "from": "system", "setting_id": "upBaAiQ4fqmefjo6", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "hot_plate_temp": [ "55" ], "hot_plate_temp_initial_layer": [ "55" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 14 mm^3/s, from local standard fallback preset Snapmaker PLA @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json index 4189cdd470..4b68672fa5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Basic @U1.json @@ -1,38 +1,34 @@ { "type": "filament", "name": "Snapmaker PLA Basic @U1", - "renamed_from": "Snapmaker PLA Basic U1;Snapmaker PLA Basic @U1 0.4 High Flow nozzle;Snapmaker PLA Basic U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "ZtUJN4JpkR0MLpiY", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], + "additional_cooling_fan_speed": [ + "70" + ], "chamber_temperature": [ "0" ], @@ -43,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -56,14 +51,12 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", "0" ], "eng_plate_temp": [ @@ -75,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.5" ], @@ -91,7 +90,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -101,7 +99,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.98", "0.98" ], "filament_is_support": [ @@ -114,73 +111,60 @@ "50" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "15", "15" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "25", "25" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from Snorca standard preset Snapmaker PLA Basic @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", + "nil" + ], + "filament_retraction_length": [ "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -217,19 +201,15 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", "nil" ], "full_fan_speed_layer": [ @@ -244,10 +224,18 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -258,7 +246,6 @@ "0.4157" ], "pressure_advance": [ - "0.02", "0.02" ], "reduce_fan_stop_start_freq": [ @@ -293,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json index 52d6d8f92a..fadf7e59d2 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Eco @U1.json @@ -1,20 +1,11 @@ { "type": "filament", "name": "Snapmaker PLA Eco @U1", - "renamed_from": "Snapmaker PLA Eco U1;Snapmaker PLA Eco @U1 0.4 High Flow nozzle;Snapmaker PLA Eco U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Eco @U1 base", "from": "system", "setting_id": "mF73rtMhQQJz1bjt", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from local standard fallback preset Snapmaker PLA Eco @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Snapmaker U1 (0.4 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json index 33ff1a785f..219e8075e5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Full Spectrum @U1 0.4 nozzle.json @@ -1,39 +1,35 @@ { "type": "filament", "name": "Snapmaker PLA Full Spectrum @U1 0.4 nozzle", - "renamed_from": "Snapmaker PLA Full Spectrum U1 0.4 nozzle;Snapmaker PLA Full Spectrum @U1 0.4 High Flow nozzle;Snapmaker PLA Full Spectrum U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "iybaFWrXKOsmXgMJ", "filament_id": "OFOPU1xv", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], + "additional_cooling_fan_speed": [ + "70" + ], "chamber_temperature": [ "0" ], @@ -44,7 +40,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -57,15 +52,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "60" @@ -76,6 +69,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.5" ], @@ -92,7 +91,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -102,7 +100,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.98", "0.98" ], "filament_is_support": [ @@ -115,77 +112,60 @@ "50" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "15", "15" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "25", "25" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 15 mm^3/s, from Snorca HF preset Snapmaker PLA Full Spectrum @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", - "5" + "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "nil", - "0.5" + "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -222,20 +202,16 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", - "Slope Lift" + "nil" ], "full_fan_speed_layer": [ "0" @@ -249,10 +225,18 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -263,8 +247,7 @@ "0.4157" ], "pressure_advance": [ - "0.02", - "0.024" + "0.02" ], "reduce_fan_stop_start_freq": [ "1" @@ -298,9 +281,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json index e3c4427434..907781cd2c 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Glow @U1 0.4 nozzle.json @@ -1,25 +1,20 @@ { "type": "filament", "name": "Snapmaker PLA Glow @U1 0.4 nozzle", - "renamed_from": "Snapmaker PLA Glow U1 0.4 nozzle;Snapmaker PLA Glow @U1 0.4 High Flow nozzle;Snapmaker PLA Glow U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Glow @U1 base", "from": "system", "setting_id": "ee1YNNr8CwKetuvY", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "additional_cooling_fan_speed": [ - "80", - "100" + "80" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cooling_final_speed": [ @@ -35,8 +30,7 @@ "20" ], "filament_flow_ratio": [ - "0.97", - "0.98" + "0.97" ], "filament_loading_speed": [ "28" @@ -45,35 +39,27 @@ "3" ], "filament_max_volumetric_speed": [ - "16", - "18" + "16" ], "filament_minimal_purge_on_wipe_tower": [ - "30", "30" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "0.4", "0.4" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_unloading_speed": [ @@ -83,11 +69,9 @@ "100" ], "filament_z_hop": [ - "0.2", "0.2" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp_initial_layer": [ @@ -107,16 +91,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 18 mm^3/s, from Snorca HF preset Snapmaker PLA Glow @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "pressure_advance": [ - "0.02", - "0.023" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json index af90945691..d2c9dddefb 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Lite @U1.json @@ -1,38 +1,34 @@ { "type": "filament", "name": "Snapmaker PLA Lite @U1", - "renamed_from": "Snapmaker PLA Lite U1;Snapmaker PLA Lite @U1 0.4 High Flow nozzle;Snapmaker PLA Lite U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Lite @U1 base", "from": "system", "setting_id": "ZZyxuKUboYttCqgM", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], + "additional_cooling_fan_speed": [ + "70" + ], "chamber_temperature": [ "0" ], @@ -43,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -59,14 +54,12 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "1", "1" ], "eng_plate_temp": [ @@ -78,6 +71,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.5" ], @@ -94,7 +93,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -104,7 +102,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.98", "0.98" ], "filament_is_support": [ @@ -117,69 +114,57 @@ "50" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "15", "15" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 15 mm^3/s, from local standard fallback preset Snapmaker PLA Lite @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", + "nil" + ], + "filament_retraction_length": [ "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -216,19 +201,15 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", "nil" ], "full_fan_speed_layer": [ @@ -243,10 +224,18 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -257,7 +246,6 @@ "0.4157" ], "pressure_advance": [ - "0.02", "0.02" ], "reduce_fan_stop_start_freq": [ @@ -292,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "60" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json index 45190fa614..2a8c1e40c3 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.2 nozzle.json @@ -1,36 +1,29 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1 0.2 nozzle", - "renamed_from": "Snapmaker PLA Matte U1 0.2 nozzle;Snapmaker PLA Matte @U1 0.2 High Flow nozzle;Snapmaker PLA Matte U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA Matte @U1 base2", "from": "system", "setting_id": "d3c48DeNk7VDeeHf", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "chamber_temperature": [ @@ -43,7 +36,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -59,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "45" @@ -78,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "24.99" ], @@ -94,53 +90,45 @@ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "2", "2" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", + "nil" + ], + "filament_retraction_length": [ "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_shrink": [ @@ -168,15 +156,12 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "full_fan_speed_layer": [ @@ -186,9 +171,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -226,7 +213,6 @@ "40" ], "filament_max_volumetric_speed": [ - "2", "2" ], "hot_plate_temp": [ @@ -242,7 +228,6 @@ "65" ], "additional_cooling_fan_speed": [ - "80", "80" ], "filament_cooling_final_speed": [ @@ -255,12 +240,10 @@ "2" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "1.05", - "1.0395" + "1.05" ], "filament_loading_speed": [ "10" @@ -269,19 +252,15 @@ "50" ], "filament_minimal_purge_on_wipe_tower": [ - "5", "5" ], "filament_multitool_ramming_flow": [ - "4", "4" ], "filament_multitool_ramming_volume": [ - "4", "4" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_stamping_distance": [ @@ -294,23 +273,15 @@ "100" ], "filament_z_hop_types": [ - "nil", "nil" ], "nozzle_temperature": [ - "215", - "220" + "215" ], "nozzle_temperature_initial_layer": [ - "215", - "220" + "215" ], "pressure_advance": [ - "0.025", - "0.03" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "0.025" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json index 9252983f4f..9de7615ae5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.6 nozzle.json @@ -1,36 +1,29 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1 0.6 nozzle", - "renamed_from": "Snapmaker PLA Matte U1 0.6 nozzle;Snapmaker PLA Matte @U1 0.6 High Flow nozzle;Snapmaker PLA Matte U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA Matte @U1 base2", "from": "system", "setting_id": "l3E1WaljDn2uoTqW", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "chamber_temperature": [ @@ -43,7 +36,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -59,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "45" @@ -78,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "24.99" ], @@ -85,7 +81,6 @@ "1.26" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -98,65 +93,51 @@ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "20", "20" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -184,19 +165,15 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", "nil" ], "full_fan_speed_layer": [ @@ -206,9 +183,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -252,7 +231,6 @@ "45" ], "additional_cooling_fan_speed": [ - "80", "80" ], "filament_cooling_final_speed": [ @@ -265,8 +243,7 @@ "2" ], "filament_flow_ratio": [ - "0.99", - "0.9801" + "0.99" ], "filament_loading_speed": [ "10" @@ -275,15 +252,12 @@ "50" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retraction_length": [ - "0.6", "0.6" ], "filament_stamping_distance": [ @@ -302,19 +276,12 @@ "65" ], "nozzle_temperature": [ - "215", - "220" + "215" ], "nozzle_temperature_initial_layer": [ - "215", - "220" + "215" ], "pressure_advance": [ - "0.015", - "0.018" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "0.015" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json index 0c7bd7c613..251b2a47b6 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1 0.8 nozzle.json @@ -1,36 +1,29 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1 0.8 nozzle", - "renamed_from": "Snapmaker PLA Matte U1 0.8 nozzle;Snapmaker PLA Matte @U1 0.8 High Flow nozzle;Snapmaker PLA Matte U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA Matte @U1 base2", "from": "system", "setting_id": "NLPd4AA0seW3NXdy", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "chamber_temperature": [ @@ -43,7 +36,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -59,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "45" @@ -78,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "24.99" ], @@ -91,68 +87,54 @@ " " ], "filament_flow_ratio": [ - "0.98", - "0.9702" + "0.98" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "20", "20" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Matte @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_shrink": [ @@ -180,19 +162,15 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", "nil" ], "full_fan_speed_layer": [ @@ -202,9 +180,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -242,8 +222,7 @@ "40" ], "nozzle_temperature": [ - "215", - "220" + "215" ], "textured_plate_temp": [ "65" @@ -252,7 +231,6 @@ "65" ], "additional_cooling_fan_speed": [ - "80", "80" ], "filament_cooling_final_speed": [ @@ -265,7 +243,6 @@ "2" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_loading_speed": [ @@ -275,19 +252,15 @@ "50" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retraction_length": [ - "0.6", "0.6" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_stamping_distance": [ @@ -306,15 +279,9 @@ "65" ], "nozzle_temperature_initial_layer": [ - "215", - "220" + "215" ], "pressure_advance": [ - "0.015", - "0.018" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "0.015" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json index a29fcc6eb2..badf918d97 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Matte @U1.json @@ -1,42 +1,34 @@ { "type": "filament", "name": "Snapmaker PLA Matte @U1", - "renamed_from": "Snapmaker PLA Matte U1;Snapmaker PLA Matte @U1 0.4 High Flow nozzle;Snapmaker PLA Matte U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA @U1 base", "from": "system", "setting_id": "uAhykL5ap1DAKacf", "filament_id": "OFF9cgY5", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "80", - "100" + "80" ], "chamber_temperature": [ "0" @@ -48,7 +40,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -61,15 +52,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "60" @@ -80,6 +69,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.5" ], @@ -95,6 +90,9 @@ "filament_density": [ "1.32" ], + "filament_deretraction_speed": [ + "nil" + ], "filament_diameter": [ "1.75" ], @@ -102,8 +100,7 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "1", - "0.99" + "1" ], "filament_is_support": [ "0" @@ -115,61 +112,60 @@ "50" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "22", - "40" + "22" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA Matte @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], + "filament_retract_before_wipe": [ + "nil" + ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retract_when_changing_layer": [ "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "nil", - "1" + "nil" + ], + "filament_retraction_minimum_travel": [ + "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -205,13 +201,17 @@ "filament_vendor": [ "Snapmaker" ], + "filament_wipe": [ + "nil" + ], "filament_wipe_distance": [ - "nil", - "1" + "nil" + ], + "filament_z_hop": [ + "nil" ], "filament_z_hop_types": [ - "nil", - "Slope Lift" + "nil" ], "full_fan_speed_layer": [ "0" @@ -226,12 +226,16 @@ "0" ], "nozzle_temperature": [ - "215", - "220" + "215" ], "nozzle_temperature_initial_layer": [ - "215", - "220" + "215" + ], + "nozzle_temperature_range_high": [ + "240" + ], + "nozzle_temperature_range_low": [ + "190" ], "overhang_fan_speed": [ "100" @@ -243,8 +247,7 @@ "0.4157" ], "pressure_advance": [ - "0.02", - "0.024" + "0.02" ], "reduce_fan_stop_start_freq": [ "1" @@ -278,9 +281,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json index 2c2d57761d..538158a155 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Metal @U1.json @@ -1,20 +1,11 @@ { "type": "filament", "name": "Snapmaker PLA Metal @U1", - "renamed_from": "Snapmaker PLA Metal U1;Snapmaker PLA Metal @U1 0.4 High Flow nozzle;Snapmaker PLA Metal U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Metal @U1 base", "from": "system", "setting_id": "lPmCxsx9EpoLoNEk", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from local standard fallback preset Snapmaker PLA Metal @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "Snapmaker U1 (0.4 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json index a6d4993ef3..e4fdb8016b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.2 nozzle.json @@ -1,41 +1,33 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1 0.2 nozzle", - "renamed_from": "Snapmaker PLA Silk U1 0.2 nozzle;Snapmaker PLA Silk @U1 0.2 High Flow nozzle;Snapmaker PLA Silk U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "Q5xH6oGGlbXr14Pa", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -48,7 +40,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -58,14 +49,12 @@ "60" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", "0" ], "eng_plate_temp": [ @@ -77,6 +66,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "25.4" ], @@ -84,7 +79,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -94,76 +88,60 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "1.02", "1.02" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "3", "3" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -191,19 +169,15 @@ "Snapmaker" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -213,9 +187,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -277,11 +253,9 @@ "3" ], "filament_max_volumetric_speed": [ - "2", "2" ], "filament_retraction_length": [ - "0.4", "0.4" ], "filament_stamping_distance": [ @@ -300,19 +274,12 @@ "65" ], "nozzle_temperature": [ - "230", "230" ], "nozzle_temperature_initial_layer": [ - "230", "230" ], "pressure_advance": [ - "0.015", "0.015" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json index 32a942090a..b144ce86dd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.6 nozzle.json @@ -1,41 +1,33 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1 0.6 nozzle", - "renamed_from": "Snapmaker PLA Silk U1 0.6 nozzle;Snapmaker PLA Silk @U1 0.6 High Flow nozzle;Snapmaker PLA Silk U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "2pieDQoz9PiDCnU1", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -48,7 +40,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -58,14 +49,12 @@ "60" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", "0" ], "eng_plate_temp": [ @@ -77,6 +66,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "25.4" ], @@ -84,7 +79,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -94,76 +88,60 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -191,19 +169,15 @@ "Snapmaker" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "1", "1" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -213,9 +187,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -277,11 +253,9 @@ "3" ], "filament_max_volumetric_speed": [ - "10", "10" ], "filament_retraction_length": [ - "0.4", "0.4" ], "filament_stamping_distance": [ @@ -300,19 +274,12 @@ "65" ], "nozzle_temperature": [ - "230", "230" ], "nozzle_temperature_initial_layer": [ - "230", "230" ], "pressure_advance": [ - "0.015", "0.015" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json index 45b4239ac3..aa92e97d18 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1 0.8 nozzle.json @@ -1,41 +1,33 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1 0.8 nozzle", - "renamed_from": "Snapmaker PLA Silk U1 0.8 nozzle;Snapmaker PLA Silk @U1 0.8 High Flow nozzle;Snapmaker PLA Silk U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "f3H0JFUnds0mkKCN", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -48,7 +40,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -58,14 +49,12 @@ "60" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", "0" ], "eng_plate_temp": [ @@ -77,6 +66,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "25.4" ], @@ -84,7 +79,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -94,76 +88,60 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 10 mm^3/s, from Snorca standard preset Snapmaker PLA Silk @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -191,19 +169,15 @@ "Snapmaker" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "1", "1" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -213,9 +187,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -277,11 +253,9 @@ "3" ], "filament_max_volumetric_speed": [ - "10", "10" ], "filament_retraction_length": [ - "0.4", "0.4" ], "filament_stamping_distance": [ @@ -300,19 +274,12 @@ "65" ], "nozzle_temperature": [ - "230", "230" ], "nozzle_temperature_initial_layer": [ - "230", "230" ], "pressure_advance": [ - "0.015", "0.015" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json index 1f3a8d469c..6b6030b919 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Silk @U1.json @@ -1,42 +1,34 @@ { "type": "filament", "name": "Snapmaker PLA Silk @U1", - "renamed_from": "Snapmaker PLA Silk U1;Snapmaker PLA Silk @U1 0.4 High Flow nozzle;Snapmaker PLA Silk U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Basic @U1 base", "from": "system", "setting_id": "9PkHSwtXFM0zPWth", "filament_id": "OFWgFOjn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "70", - "80" + "70" ], "chamber_temperature": [ "0" @@ -48,7 +40,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -58,14 +49,12 @@ "60" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", "0" ], "eng_plate_temp": [ @@ -77,6 +66,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cost": [ "25.4" ], @@ -84,7 +79,6 @@ "1.32" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -94,76 +88,60 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.98", "0.98" ], "filament_is_support": [ "0" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "25", "25" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Silk @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -191,20 +169,16 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", - "Slope Lift" + "nil" ], "full_fan_speed_layer": [ "0" @@ -213,9 +187,11 @@ "0" ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -277,11 +253,9 @@ "3" ], "filament_max_volumetric_speed": [ - "10", - "12" + "10" ], "filament_retraction_length": [ - "0.2", "0.2" ], "filament_stamping_distance": [ @@ -300,19 +274,12 @@ "65" ], "nozzle_temperature": [ - "230", "230" ], "nozzle_temperature_initial_layer": [ - "230", "230" ], "pressure_advance": [ - "0.015", "0.015" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json index 81b61550a4..7fcb20f31b 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.2 nozzle.json @@ -1,37 +1,29 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "renamed_from": "Snapmaker PLA SnapSpeed U1 0.2 nozzle;Snapmaker PLA SnapSpeed @U1 0.2 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base2", "from": "system", "setting_id": "jsAgNNiUC6DoO7rz", "instantiation": "true", "filament_deretraction_speed": [ - "30", "30" ], "filament_flow_ratio": [ - "1.01", "1.01" ], "filament_max_volumetric_speed": [ - "2", "2" ], "filament_multitool_ramming_flow": [ - "30", "30" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_speed": [ - "30", "30" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp": [ @@ -41,8 +33,7 @@ "65" ], "pressure_advance": [ - "0.2", - "0.24" + "0.2" ], "textured_plate_temp": [ "65" @@ -51,18 +42,9 @@ "65" ], "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_pressure_advance": [ - "0", - "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 2 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json index df71144582..18fca8b4fd 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.6 nozzle.json @@ -1,41 +1,32 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "renamed_from": "Snapmaker PLA SnapSpeed U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.6 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base2", "from": "system", "setting_id": "aKudBpQBj3MZZZav", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filament_deretraction_speed": [ - "30", "30" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_multitool_ramming_flow": [ - "30", "30" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "1.6", "1.6" ], "filament_retraction_speed": [ - "30", "30" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "textured_plate_temp": [ @@ -45,18 +36,6 @@ "65" ], "enable_pressure_advance": [ - "0", - "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "pressure_advance": [ - "0.02", - "0.024" + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json index b7610458eb..2ebadbd2b2 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1 0.8 nozzle.json @@ -1,42 +1,33 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "renamed_from": "Snapmaker PLA SnapSpeed U1 0.8 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base2", "from": "system", "setting_id": "sWoInox4hzxzIHfe", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filament_deretraction_speed": [ - "30", "30" ], "filament_multitool_ramming_flow": [ - "30", "30" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "1.6", "1.6" ], "filament_retraction_speed": [ - "30", "30" ], "nozzle_temperature": [ - "215", "215" ], "pressure_advance": [ - "0.018", - "0.0216" + "0.018" ], "textured_plate_temp": [ "65" @@ -45,14 +36,6 @@ "65" ], "enable_pressure_advance": [ - "0", - "1" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA SnapSpeed @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json index 0694e43e88..2525f5b2a9 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA SnapSpeed @U1.json @@ -1,38 +1,34 @@ { "type": "filament", "name": "Snapmaker PLA SnapSpeed @U1", - "renamed_from": "Snapmaker PLA SnapSpeed U1;Snapmaker PLA SnapSpeed @U1 0.4 High Flow nozzle;Snapmaker PLA SnapSpeed U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA SnapSpeed @U1 base", "from": "system", "setting_id": "en6Pvvbc1P9TKm8I", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], + "additional_cooling_fan_speed": [ + "70" + ], "chamber_temperature": [ "0" ], @@ -43,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -56,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "60" @@ -75,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.5" ], @@ -91,7 +90,6 @@ "1.24" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_diameter": [ @@ -101,7 +99,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.966", "0.966" ], "filament_is_support": [ @@ -114,77 +111,60 @@ "50" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "20", - "40" + "20" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "30", - "40" + "30" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 40 mm^3/s, from Snorca HF preset Snapmaker PLA SnapSpeed @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "nil", "nil" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", "nil" ], "filament_retract_restart_extra": [ - "nil", "nil" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "1.2", "1.2" ], "filament_retraction_minimum_travel": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -221,19 +201,15 @@ "Snapmaker" ], "filament_wipe": [ - "nil", "nil" ], "filament_wipe_distance": [ - "nil", "nil" ], "filament_z_hop": [ - "0.4", "0.4" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -248,10 +224,18 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], "nozzle_temperature_range_high": [ - "240", "240" ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -262,8 +246,7 @@ "0.4157" ], "pressure_advance": [ - "0.02", - "0.024" + "0.02" ], "reduce_fan_stop_start_freq": [ "1" @@ -297,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json index 22d1c3af57..6271b7af42 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.2 nozzle.json @@ -1,40 +1,32 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.2 nozzle", - "renamed_from": "Snapmaker PLA Translucent U1 0.2 nozzle;Snapmaker PLA Translucent @U1 0.2 High Flow nozzle;Snapmaker PLA Translucent U1 0.2 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "zIg9XLrieYNBduyn", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -47,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -60,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "60" @@ -79,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.4" ], @@ -94,6 +89,9 @@ "filament_density": [ "1.22" ], + "filament_deretraction_speed": [ + "nil" + ], "filament_diameter": [ "1.75" ], @@ -101,7 +99,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "1.02", "1.02" ], "filament_is_support": [ @@ -114,65 +111,60 @@ "3" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "1.6", "1.6" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "5", "5" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.2 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 1.6 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.2 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "0%", "0%" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retract_when_changing_layer": [ "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "0.2", "0.2" ], + "filament_retraction_minimum_travel": [ + "nil" + ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -209,15 +201,15 @@ "Snapmaker" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "1", "1" ], + "filament_z_hop": [ + "nil" + ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -232,8 +224,16 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "nozzle_temperature_range_high": [ + "240" + ], "nozzle_temperature_range_low": [ - "200", "200" ], "overhang_fan_speed": [ @@ -246,8 +246,7 @@ "0.4157" ], "pressure_advance": [ - "0.15", - "0.18" + "0.15" ], "reduce_fan_stop_start_freq": [ "1" @@ -281,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json index 060156df75..7d4270ef31 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.4 nozzle.json @@ -1,40 +1,32 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.4 nozzle", - "renamed_from": "Snapmaker PLA Translucent U1 0.4 nozzle;Snapmaker PLA Translucent @U1 0.4 High Flow nozzle;Snapmaker PLA Translucent U1 0.4 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "RqtrcLcy124ICGEc", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -47,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -60,12 +51,14 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], + "enable_pressure_advance": [ + "1" + ], "eng_plate_temp": [ "60" ], @@ -75,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.4" ], @@ -90,6 +89,9 @@ "filament_density": [ "1.32" ], + "filament_deretraction_speed": [ + "nil" + ], "filament_diameter": [ "1.75" ], @@ -97,7 +99,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_is_support": [ @@ -110,61 +111,60 @@ "3" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "12", "12" ], "filament_minimal_purge_on_wipe_tower": [ - "50", "50" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 12 mm^3/s, from Snorca HF preset Snapmaker PLA Translucent @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], + "filament_retract_before_wipe": [ + "nil" + ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retract_when_changing_layer": [ "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "0.2", "0.2" ], + "filament_retraction_minimum_travel": [ + "nil" + ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -200,8 +200,16 @@ "filament_vendor": [ "Snapmaker" ], + "filament_wipe": [ + "nil" + ], + "filament_wipe_distance": [ + "nil" + ], + "filament_z_hop": [ + "nil" + ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -216,6 +224,18 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "nozzle_temperature_range_high": [ + "240" + ], + "nozzle_temperature_range_low": [ + "190" + ], "overhang_fan_speed": [ "100" ], @@ -226,8 +246,7 @@ "0.4157" ], "pressure_advance": [ - "0.02", - "0.024" + "0.02" ], "reduce_fan_stop_start_freq": [ "1" @@ -261,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json index 3dc4c73ada..af3f95c757 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.6 nozzle.json @@ -1,40 +1,32 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.6 nozzle", - "renamed_from": "Snapmaker PLA Translucent U1 0.6 nozzle;Snapmaker PLA Translucent @U1 0.6 High Flow nozzle;Snapmaker PLA Translucent U1 0.6 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "OZHemzEcvzFMN8fL", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -47,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -60,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "60" @@ -79,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.4" ], @@ -94,12 +89,18 @@ "filament_density": [ "1.22" ], + "filament_deretraction_speed": [ + "nil" + ], "filament_diameter": [ "1.75" ], "filament_end_gcode": [ "; filament end gcode \n" ], + "filament_flow_ratio": [ + "0.98" + ], "filament_is_support": [ "0" ], @@ -110,65 +111,60 @@ "3" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "12", "12" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "0%", "0%" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retract_when_changing_layer": [ "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "0.5", "0.5" ], + "filament_retraction_minimum_travel": [ + "nil" + ], "filament_retraction_speed": [ - "50", "50" ], "filament_shrink": [ @@ -204,8 +200,16 @@ "filament_vendor": [ "Snapmaker" ], + "filament_wipe": [ + "nil" + ], + "filament_wipe_distance": [ + "nil" + ], + "filament_z_hop": [ + "nil" + ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -220,8 +224,16 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "nozzle_temperature_range_high": [ + "240" + ], "nozzle_temperature_range_low": [ - "200", "200" ], "overhang_fan_speed": [ @@ -234,8 +246,7 @@ "0.4157" ], "pressure_advance": [ - "0.02", - "0.024" + "0.02" ], "reduce_fan_stop_start_freq": [ "1" @@ -269,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json index a975052b1b..a10a4b2fb1 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Translucent @U1 0.8 nozzle.json @@ -1,40 +1,32 @@ { "type": "filament", "name": "Snapmaker PLA Translucent @U1 0.8 nozzle", - "renamed_from": "Snapmaker PLA Translucent U1 0.8 nozzle;Snapmaker PLA Translucent @U1 0.8 High Flow nozzle;Snapmaker PLA Translucent U1 0.8 High Flow nozzle", "inherits": "Snapmaker PLA Translucent @U1 base", "from": "system", "setting_id": "IPehCg9fKawLorB5", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], "additional_cooling_fan_speed": [ - "100", "100" ], "chamber_temperature": [ @@ -47,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -60,15 +51,13 @@ "0" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "eng_plate_temp": [ "60" @@ -79,6 +68,12 @@ "fan_cooling_layer_time": [ "100" ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], "filament_cooling_final_speed": [ "3.4" ], @@ -94,6 +89,9 @@ "filament_density": [ "1.22" ], + "filament_deretraction_speed": [ + "nil" + ], "filament_diameter": [ "1.75" ], @@ -101,7 +99,6 @@ "; filament end gcode \n" ], "filament_flow_ratio": [ - "0.95", "0.95" ], "filament_is_support": [ @@ -114,65 +111,60 @@ "3" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "12", "12" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker PLA Translucent @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], "filament_retract_before_wipe": [ - "0%", "0%" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retract_when_changing_layer": [ "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "1", "1" ], + "filament_retraction_minimum_travel": [ + "nil" + ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_shrink": [ @@ -208,8 +200,16 @@ "filament_vendor": [ "Snapmaker" ], + "filament_wipe": [ + "nil" + ], + "filament_wipe_distance": [ + "nil" + ], + "filament_z_hop": [ + "nil" + ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "full_fan_speed_layer": [ @@ -224,8 +224,16 @@ "idle_temperature": [ "0" ], + "nozzle_temperature": [ + "220" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "nozzle_temperature_range_high": [ + "240" + ], "nozzle_temperature_range_low": [ - "200", "200" ], "overhang_fan_speed": [ @@ -238,8 +246,7 @@ "0.4157" ], "pressure_advance": [ - "0.02", - "0.024" + "0.02" ], "reduce_fan_stop_start_freq": [ "1" @@ -273,9 +280,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json index 84febf3f84..283d5876b7 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.4 nozzle.json @@ -1,28 +1,27 @@ { "type": "filament", "name": "Snapmaker PLA Wood @U1 0.4 nozzle", - "renamed_from": "Snapmaker PLA Wood U1 0.4 nozzle;Snapmaker PLA Wood @U1 0.4 High Flow nozzle;Snapmaker PLA Wood U1 0.4 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "XvizDDHbds3bVrzi", "filament_id": "OFdd8ZqJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_start_gcode": [ "" ], + "enable_pressure_advance": [ + "1" + ], "filament_end_gcode": [ "\n" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_vendor": [ @@ -41,45 +40,36 @@ "65" ], "filament_flow_ratio": [ - "0.97", "0.97" ], "filament_max_volumetric_speed": [ - "18", - "20" + "18" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "5", - "6" + "5" ], "filament_retraction_length": [ - "0.6", "0.6" ], "pressure_advance": [ - "0.025", - "0.024" + "0.025" ], "slow_down_layer_time": [ "4" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -89,26 +79,21 @@ "35" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "nozzle_temperature": [ - "220", "220" ], "overhang_fan_threshold": [ @@ -119,16 +104,5 @@ ], "temperature_vitrification": [ "45" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA Wood @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_z_hop_types": [ - "nil", - "Auto Lift" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json index 39dc566a93..09555457d5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.6 nozzle.json @@ -1,32 +1,27 @@ { "type": "filament", "name": "Snapmaker PLA Wood @U1 0.6 nozzle", - "renamed_from": "Snapmaker PLA Wood U1 0.6 nozzle;Snapmaker PLA Wood @U1 0.6 High Flow nozzle;Snapmaker PLA Wood U1 0.6 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "Kp77DCcitdmoyjqm", "filament_id": "OFdd8ZqJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filament_start_gcode": [ "" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "filament_end_gcode": [ "\n" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_vendor": [ @@ -44,50 +39,43 @@ "hot_plate_temp_initial_layer": [ "65" ], + "filament_flow_ratio": [ + "0.98" + ], "filament_max_volumetric_speed": [ - "18", "18" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "0.4", "0.4" ], "pressure_advance": [ - "0.014", - "0.0134" + "0.014" ], "slow_down_layer_time": [ "4" ], "additional_cooling_fan_speed": [ - "100", "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -97,26 +85,21 @@ "35" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "nozzle_temperature": [ - "220", "220" ], "overhang_fan_threshold": [ @@ -127,12 +110,5 @@ ], "temperature_vitrification": [ "45" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json index 5aca28812f..4a5a04a5ee 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA Wood @U1 0.8 nozzle.json @@ -1,32 +1,27 @@ { "type": "filament", "name": "Snapmaker PLA Wood @U1 0.8 nozzle", - "renamed_from": "Snapmaker PLA Wood U1 0.8 nozzle;Snapmaker PLA Wood @U1 0.8 High Flow nozzle;Snapmaker PLA Wood U1 0.8 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "nBYnNiqwLo6nAsqi", "filament_id": "OFdd8ZqJ", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filament_start_gcode": [ "" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "filament_end_gcode": [ "\n" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_vendor": [ @@ -45,53 +40,42 @@ "65" ], "filament_flow_ratio": [ - "0.965", "0.965" ], "filament_max_volumetric_speed": [ - "20", "20" ], "filament_multitool_ramming_flow": [ - "40", "40" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "5", "5" ], "filament_retraction_length": [ - "0.4", "0.4" ], "pressure_advance": [ - "0.007", - "0.0067" + "0.007" ], "slow_down_layer_time": [ "4" ], "additional_cooling_fan_speed": [ - "100", "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -101,26 +85,21 @@ "35" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "nozzle_temperature": [ - "220", "220" ], "overhang_fan_threshold": [ @@ -131,12 +110,5 @@ ], "temperature_vitrification": [ "45" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 20 mm^3/s, from Snorca standard preset Snapmaker PLA Wood @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json index 88e0fdd7db..357c5de50a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.4 nozzle.json @@ -1,26 +1,26 @@ { "type": "filament", "name": "Snapmaker PLA-CF @U1 0.4 nozzle", - "renamed_from": "Snapmaker PLA-CF @U1;Snapmaker PLA-CF @U1 0.4 High Flow nozzle;Snapmaker PLA-CF U1 0.4 High Flow nozzle", + "renamed_from": "Snapmaker PLA-CF @U1", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "2INwruC3ZVkBTdUT", "filament_id": "OFhQf8ou", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" + ], + "enable_pressure_advance": [ + "1" ], "filament_end_gcode": [ "" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", - "30" + "20" ], "filament_start_gcode": [ "" @@ -40,66 +40,55 @@ "hot_plate_temp_initial_layer": [ "65" ], + "filament_flow_ratio": [ + "0.98" + ], "filament_max_volumetric_speed": [ - "15", - "20" + "15" ], "filament_minimal_purge_on_wipe_tower": [ - "50", "50" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "5", - "15" + "5" ], "filament_retraction_length": [ - "1", - "0.8" + "1" ], "filament_type": [ "PLA-CF" ], "filament_z_hop": [ - "0.4", "0.4" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "nozzle_temperature": [ - "240", - "230" + "240" ], "nozzle_temperature_initial_layer": [ - "240", - "230" + "240" ], "nozzle_temperature_range_high": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "200", "200" ], "pressure_advance": [ - "0.01", - "0.02" + "0.01" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -109,22 +98,18 @@ "45" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "overhang_fan_threshold": [ @@ -143,14 +128,6 @@ "45" ], "additional_cooling_fan_speed": [ - "0", - "100" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 20 mm^3/s, from Snorca HF preset Snapmaker PLA-CF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "0" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json index 24ca2d1603..6f09ea5d6a 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.6 nozzle.json @@ -1,29 +1,24 @@ { "type": "filament", "name": "Snapmaker PLA-CF @U1 0.6 nozzle", - "renamed_from": "Snapmaker PLA-CF U1 0.6 nozzle;Snapmaker PLA-CF @U1 0.6 High Flow nozzle;Snapmaker PLA-CF U1 0.6 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "CKfdL85SvbOLzMuP", "filament_id": "OFhQf8ou", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "filament_end_gcode": [ "" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "25", "25" ], "filament_start_gcode": [ @@ -44,52 +39,47 @@ "hot_plate_temp_initial_layer": [ "65" ], + "filament_flow_ratio": [ + "0.98" + ], "filament_max_volumetric_speed": [ - "18", "18" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "2", "2" ], "filament_type": [ "PLA-CF" ], + "filament_z_hop": [ + "nil" + ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "nozzle_temperature": [ - "220", - "210" + "220" ], "nozzle_temperature_initial_layer": [ - "220", - "210" + "220" ], "nozzle_temperature_range_high": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "210", "210" ], "pressure_advance": [ - "0.015", - "0.03" + "0.015" ], "filament_cost": [ "35" @@ -101,18 +91,15 @@ "8" ], "filament_wipe_distance": [ - "2", "2" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -122,19 +109,15 @@ "45" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "overhang_fan_threshold": [ @@ -150,14 +133,6 @@ "45" ], "additional_cooling_fan_speed": [ - "0", "0" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json index e20c31fc91..564e59e1a9 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PLA-CF @U1 0.8 nozzle.json @@ -1,29 +1,24 @@ { "type": "filament", "name": "Snapmaker PLA-CF @U1 0.8 nozzle", - "renamed_from": "Snapmaker PLA-CF U1 0.8 nozzle;Snapmaker PLA-CF @U1 0.8 High Flow nozzle;Snapmaker PLA-CF U1 0.8 High Flow nozzle", "inherits": "fdm_filament_pla", "from": "system", "setting_id": "fhhnfJm4dN9FXpWQ", "filament_id": "OFhQf8ou", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "enable_pressure_advance": [ - "0", - "1" + "0" ], "filament_end_gcode": [ "" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "25", "25" ], "filament_start_gcode": [ @@ -44,52 +39,47 @@ "hot_plate_temp_initial_layer": [ "65" ], + "filament_flow_ratio": [ + "0.98" + ], "filament_max_volumetric_speed": [ - "18", "18" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "3", "3" ], "filament_type": [ "PLA-CF" ], + "filament_z_hop": [ + "nil" + ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "nozzle_temperature": [ - "220", - "210" + "220" ], "nozzle_temperature_initial_layer": [ - "220", - "210" + "220" ], "nozzle_temperature_range_high": [ - "250", "250" ], "nozzle_temperature_range_low": [ - "210", "210" ], "pressure_advance": [ - "0.015", - "0.03" + "0.015" ], "filament_cost": [ "35" @@ -101,14 +91,12 @@ "8" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -118,19 +106,15 @@ "45" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "overhang_fan_threshold": [ @@ -146,14 +130,6 @@ "45" ], "additional_cooling_fan_speed": [ - "0", "0" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 18 mm^3/s, from Snorca standard preset Snapmaker PLA-CF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json index 12796319d8..b28e0627ad 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.6 nozzle.json @@ -1,81 +1,68 @@ { "type": "filament", "name": "Snapmaker PVA @U1 0.6 nozzle", - "renamed_from": "Snapmaker PVA U1 0.6 nozzle;Snapmaker PVA @U1 0.6 High Flow nozzle;Snapmaker PVA U1 0.6 High Flow nozzle", "inherits": "Snapmaker PVA @U1 base", "from": "system", "setting_id": "lIqgnfeZdAm8G0TH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" + ], + "enable_pressure_advance": [ + "0" ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "filament_cost": [ "79.98" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "0.98", - "0.9506" + "0.98" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "nil", "nil" ], "filament_retraction_minimum_travel": [ - "1", "1" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_soluble": [ "1" ], "filament_wipe_distance": [ - "2", "2" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ - "230", "230" ], "nozzle_temperature_initial_layer": [ - "230", "230" ], "nozzle_temperature_range_low": [ - "210", "210" ], "pressure_advance": [ - "0.03", "0.03" ], "slow_down_layer_time": [ @@ -103,19 +90,15 @@ "3" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_unloading_speed": [ @@ -125,14 +108,6 @@ "100" ], "fan_min_speed": [ - "100", "100" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.6 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json index 8197f2b927..6572a60078 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1 0.8 nozzle.json @@ -1,55 +1,47 @@ { "type": "filament", "name": "Snapmaker PVA @U1 0.8 nozzle", - "renamed_from": "Snapmaker PVA U1 0.8 nozzle;Snapmaker PVA @U1 0.8 High Flow nozzle;Snapmaker PVA U1 0.8 High Flow nozzle", "inherits": "Snapmaker PVA @U1 base", "from": "system", "setting_id": "WxtuvwWxy73Gi0IK", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" + ], + "enable_pressure_advance": [ + "0" ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_min_speed": [ - "100", "100" ], "filament_cost": [ "79.98" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "0.96", - "0.9312" + "0.96" ], "filament_retract_length_toolchange": [ - "10", "10" ], "filament_retraction_length": [ - "nil", "nil" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_soluble": [ @@ -59,19 +51,15 @@ "65" ], "nozzle_temperature": [ - "225", "225" ], "nozzle_temperature_initial_layer": [ - "225", "225" ], "nozzle_temperature_range_low": [ - "205", "205" ], "pressure_advance": [ - "0.03", "0.03" ], "slow_down_layer_time": [ @@ -99,19 +87,15 @@ "3" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_flow": [ - "20", "20" ], "filament_multitool_ramming_volume": [ - "5", "5" ], "filament_unloading_speed": [ @@ -119,12 +103,5 @@ ], "filament_unloading_speed_start": [ "100" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Estimated 0.8 mm high-flow starting values, not manufacturer-calibrated. Flow ratio and pressure advance use this diameter's standard values times the material's 0.4 mm HF/standard ratio from Snapmaker OrcaSlicer. Temperature uses the 0.4 mm HF-standard difference. Retraction and cooling remain diameter-specific. Volumetric speed ceiling: 6 mm^3/s, from Snorca standard preset Snapmaker PVA @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json index ff6341e89d..5ed5d243ac 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker PVA @U1.json @@ -1,44 +1,35 @@ { "type": "filament", "name": "Snapmaker PVA @U1", - "renamed_from": "Snapmaker PVA U1;Snapmaker PVA @U1 0.4 High Flow nozzle;Snapmaker PVA U1 0.4 High Flow nozzle", "inherits": "Snapmaker PVA @U1 base", "from": "system", "setting_id": "A9gyeySGTtUrTPeG", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "enable_pressure_advance": [ - "1", "1" ], "nozzle_temperature_range_high": [ - "240", "240" ], "pressure_advance": [ - "0.03", "0.03" ], "additional_cooling_fan_speed": [ - "70", "70" ], "close_fan_the_first_x_layers": [ "1" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "fan_min_speed": [ - "100", "100" ], "filament_cooling_final_speed": [ @@ -51,7 +42,6 @@ "4" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_loading_speed": [ @@ -61,31 +51,24 @@ "3" ], "filament_max_volumetric_speed": [ - "5", - "6" + "5" ], "filament_minimal_purge_on_wipe_tower": [ - "20", "20" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], "filament_retract_length_toolchange": [ - "4", "4" ], "filament_retraction_length": [ - "0.4", - "0.6" + "0.4" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_unloading_speed": [ @@ -95,18 +78,15 @@ "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "hot_plate_temp_initial_layer": [ "65" ], "nozzle_temperature": [ - "240", "240" ], "nozzle_temperature_initial_layer": [ - "240", "240" ], "slow_down_layer_time": [ @@ -117,20 +97,5 @@ ], "textured_plate_temp_initial_layer": [ "65" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "High-flow nozzle: Temperature, flow ratio, pressure advance and other high-flow overrides are based on Snapmaker OrcaSlicer for 0.4 mm nozzles. Volumetric speed ceiling: 6 mm^3/s, from Snorca HF preset Snapmaker PVA @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_multitool_ramming_flow": [ - "10", - "10" - ], - "filament_flow_ratio": [ - "1", - "0.97" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json index 04059f3e60..a4214b6cd0 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPE @U1.json @@ -1,15 +1,13 @@ { "type": "filament", "name": "Snapmaker TPE @U1", - "renamed_from": "Snapmaker TPE U1;Snapmaker TPE @U1 0.4 High Flow nozzle;Snapmaker TPE U1 0.4 High Flow nozzle", "inherits": "Snapmaker TPU @U1 base", "from": "system", "setting_id": "aqE9IR6dFd2MQLXR", "filament_id": "OFWTAEjH", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "hot_plate_temp": [ "45" @@ -21,32 +19,24 @@ "65" ], "filament_flow_ratio": [ - "1.1", "1.1" ], "filament_density": [ "1.22" ], "filament_max_volumetric_speed": [ - "7.2", "7.2" ], "fan_max_speed": [ - "65", "65" ], "fan_min_speed": [ - "65", "65" ], "default_filament_colour": [ "" ], "filament_notes": [ - "Standard nozzle: eSUN eLastic TPE-83A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.2 mm^3/s, from local standard fallback preset Snapmaker TPE @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "eSUN eLastic TPE-83A\n" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json index 00133cde35..afb529e983 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.6 nozzle.json @@ -1,19 +1,16 @@ { "type": "filament", "name": "Snapmaker TPU 90A @U1 0.6 nozzle", - "renamed_from": "Snapmaker TPU 90A U1 0.6 nozzle;Snapmaker TPU 90A @U1 0.6 High Flow nozzle;Snapmaker TPU 90A U1 0.6 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "uQCplebxmpBpzsnk", "filament_id": "OFY7jxcS", "instantiation": "true", "filament_max_volumetric_speed": [ - "3.2", "3.2" ], "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -22,85 +19,69 @@ "; filament end gcode \n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], "pressure_advance": [ - "0.02", "0.02" ], "additional_cooling_fan_speed": [ - "100", "100" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], + "enable_pressure_advance": [ + "0" + ], "filament_deretraction_speed": [ - "20", "20" ], "filament_flow_ratio": [ - "1.093", "1.093" ], "filament_multitool_ramming_flow": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "4", "4" ], "filament_retraction_length": [ - "0.6", "0.6" ], "filament_retraction_speed": [ - "20", "20" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ - "220", "220" ], "nozzle_temperature_initial_layer": [ - "220", "220" ], "slow_down_layer_time": [ "14" ], "nozzle_temperature_range_high": [ - "240", "240" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "filament_wipe": [ - "1", "1" ], "filament_wipe_distance": [ - "0", "0" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ @@ -119,34 +100,27 @@ "30" ], "fan_max_speed": [ - "100", "100" ], "fan_min_speed": [ - "100", "100" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "hot_plate_temp": [ @@ -171,14 +145,6 @@ "35" ], "nozzle_temperature_range_low": [ - "200", "200" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json index 5330e38078..8cf3452469 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1 0.8 nozzle.json @@ -1,19 +1,16 @@ { "type": "filament", "name": "Snapmaker TPU 90A @U1 0.8 nozzle", - "renamed_from": "Snapmaker TPU 90A U1 0.8 nozzle;Snapmaker TPU 90A @U1 0.8 High Flow nozzle;Snapmaker TPU 90A U1 0.8 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "w4Hpl2qNPmgNvaeM", "filament_id": "OFY7jxcS", "instantiation": "true", "filament_max_volumetric_speed": [ - "3.5", "3.5" ], "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -22,73 +19,63 @@ "; filament end gcode \n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], "pressure_advance": [ - "0.02", "0.02" ], + "additional_cooling_fan_speed": [ + "70" + ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], + "enable_pressure_advance": [ + "0" + ], "filament_deretraction_speed": [ - "20", "20" ], "filament_flow_ratio": [ - "1.095", "1.095" ], "filament_multitool_ramming_flow": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "4", "4" ], "filament_retraction_length": [ - "1.2", "1.2" ], "filament_retraction_speed": [ - "20", "20" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ - "220", "220" ], "nozzle_temperature_initial_layer": [ - "220", "220" ], "slow_down_layer_time": [ "14" ], "nozzle_temperature_range_high": [ - "240", "240" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ @@ -107,34 +94,27 @@ "30" ], "fan_max_speed": [ - "100", "100" ], "fan_min_speed": [ - "100", "100" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "hot_plate_temp": [ @@ -159,14 +139,6 @@ "35" ], "nozzle_temperature_range_low": [ - "200", "200" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.5 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json index 77e002298e..8f6ba9663f 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 90A @U1.json @@ -1,19 +1,16 @@ { "type": "filament", "name": "Snapmaker TPU 90A @U1", - "renamed_from": "Snapmaker TPU 90A U1;Snapmaker TPU 90A @U1 0.4 High Flow nozzle;Snapmaker TPU 90A U1 0.4 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "T4MmgICRrSzvmCZ5", "filament_id": "OFY7jxcS", "instantiation": "true", "filament_max_volumetric_speed": [ - "3.2", "3.2" ], "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -22,65 +19,57 @@ "; filament end gcode \n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], "pressure_advance": [ - "0.4", "0.4" ], "additional_cooling_fan_speed": [ - "100", "100" ], "complete_print_exhaust_fan_speed": [ - "100", "100" ], "during_print_exhaust_fan_speed": [ - "100", "100" ], + "enable_pressure_advance": [ + "0" + ], "filament_deretraction_speed": [ - "15", "15" ], "filament_flow_ratio": [ - "1.045", "1.045" ], "filament_multitool_ramming_flow": [ - "5", "5" ], "filament_retract_length_toolchange": [ - "2", "2" ], "filament_retraction_length": [ - "1.2", "1.2" ], "filament_retraction_speed": [ - "15", "15" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ - "230", + "230" + ], + "nozzle_temperature_initial_layer": [ "230" ], "slow_down_layer_time": [ "14" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ @@ -99,38 +88,30 @@ "30" ], "fan_max_speed": [ - "100", "100" ], "fan_min_speed": [ - "100", "100" ], "filament_cost": [ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", "nil" ], "hot_plate_temp": [ @@ -155,14 +136,6 @@ "35" ], "nozzle_temperature_range_low": [ - "200", "200" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from Snorca standard preset Snapmaker TPU 90A @U1 (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json index e5dd59dad2..59b125ff72 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A @U1.json @@ -1,38 +1,34 @@ { "type": "filament", "name": "Snapmaker TPU 95A @U1", - "renamed_from": "Snapmaker TPU 95A U1;Snapmaker TPU 95A @U1 0.4 High Flow nozzle;Snapmaker TPU 95A U1 0.4 High Flow nozzle", "inherits": "Snapmaker TPU 95A @U1 base", "from": "system", "setting_id": "XdvEzjOGtQKamDRC", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "activate_air_filtration": [ - "0", "0" ], "activate_chamber_temp_control": [ "0" ], "adaptive_pressure_advance": [ - "0", "0" ], "adaptive_pressure_advance_bridges": [ - "0", "0" ], "adaptive_pressure_advance_model": [ - "0,0,0\n0,0,0", "0,0,0\n0,0,0" ], "adaptive_pressure_advance_overhangs": [ - "0", "0" ], + "additional_cooling_fan_speed": [ + "70" + ], "chamber_temperature": [ "0" ], @@ -43,7 +39,6 @@ "compatible_prints": [], "compatible_prints_condition": "", "complete_print_exhaust_fan_speed": [ - "80", "80" ], "cool_plate_temp": [ @@ -56,14 +51,12 @@ "0" ], "during_print_exhaust_fan_speed": [ - "60", "60" ], "enable_overhang_bridge_fan": [ "1" ], "enable_pressure_advance": [ - "0", "0" ], "eng_plate_temp": [ @@ -76,11 +69,9 @@ "100" ], "fan_max_speed": [ - "70", "70" ], "fan_min_speed": [ - "70", "70" ], "filament_cooling_final_speed": [ @@ -99,7 +90,6 @@ "1.24" ], "filament_deretraction_speed": [ - "8", "8" ], "filament_diameter": [ @@ -108,6 +98,9 @@ "filament_end_gcode": [ "; filament end gcode \n" ], + "filament_flow_ratio": [ + "1" + ], "filament_is_support": [ "0" ], @@ -118,53 +111,57 @@ "3" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "3.2", "3.2" ], + "filament_minimal_purge_on_wipe_tower": [ + "15" + ], "filament_multitool_ramming": [ - "0", "0" ], "filament_multitool_ramming_flow": [ - "10", "10" ], "filament_multitool_ramming_volume": [ - "10", "10" ], "filament_notes": [ - "High-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 3.2 mm^3/s, from local standard fallback preset Snapmaker TPU 95A @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." + "" ], "filament_ramming_parameters": [ "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" ], + "filament_retract_before_wipe": [ + "nil" + ], "filament_retract_lift_above": [ - "nil", "nil" ], "filament_retract_lift_below": [ - "nil", "nil" ], "filament_retract_lift_enforce": [ - "nil", + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retract_when_changing_layer": [ "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_retraction_length": [ - "1.4", "1.4" ], + "filament_retraction_minimum_travel": [ + "nil" + ], "filament_retraction_speed": [ - "30", "30" ], "filament_shrink": [ @@ -200,12 +197,16 @@ "filament_vendor": [ "Snapmaker" ], + "filament_wipe": [ + "nil" + ], + "filament_wipe_distance": [ + "nil" + ], "filament_z_hop": [ - "0.4", "0.4" ], "filament_z_hop_types": [ - "Auto Lift", "Auto Lift" ], "full_fan_speed_layer": [ @@ -221,13 +222,17 @@ "0" ], "nozzle_temperature": [ - "225", "225" ], "nozzle_temperature_initial_layer": [ - "225", "225" ], + "nozzle_temperature_range_high": [ + "250" + ], + "nozzle_temperature_range_low": [ + "200" + ], "overhang_fan_speed": [ "100" ], @@ -238,7 +243,6 @@ "0.4157" ], "pressure_advance": [ - "0.02", "0.02" ], "reduce_fan_stop_start_freq": [ @@ -276,9 +280,5 @@ ], "default_filament_colour": [ "" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json index 8fdda2dc94..b9619dcca5 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.6 nozzle.json @@ -1,15 +1,13 @@ { "type": "filament", "name": "Snapmaker TPU 95A HF @U1 0.6 nozzle", - "renamed_from": "Snapmaker TPU 95A HF U1 0.6 nozzle;Snapmaker TPU 95A HF @U1 0.6 High Flow nozzle;Snapmaker TPU 95A HF U1 0.6 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "Q2svXpkEqg0dCdq3", "filament_id": "OFXSvqOX", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -18,90 +16,75 @@ "; filament end gcode \n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], + "enable_pressure_advance": [ + "0" + ], "fan_cooling_layer_time": [ "100" ], "fan_max_speed": [ - "100", "100" ], "fan_min_speed": [ - "100", "100" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "1.067", "1.067" ], "filament_max_volumetric_speed": [ - "12", "12" ], "filament_multitool_ramming_flow": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "8", "8" ], "filament_retraction_length": [ - "0.8", "0.8" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ - "215", "215" ], "nozzle_temperature_initial_layer": [ - "215", "215" ], "overhang_fan_threshold": [ "95%" ], "pressure_advance": [ - "0.14", "0.14" ], "slow_down_layer_time": [ "8" ], "additional_cooling_fan_speed": [ - "100", "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -111,7 +94,6 @@ "30" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "eng_plate_temp": [ @@ -124,23 +106,18 @@ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "hot_plate_temp": [ @@ -165,14 +142,6 @@ "35" ], "nozzle_temperature_range_low": [ - "200", "200" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 12 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.6 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json index f677f2111f..4283ac2651 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1 0.8 nozzle.json @@ -1,15 +1,13 @@ { "type": "filament", "name": "Snapmaker TPU 95A HF @U1 0.8 nozzle", - "renamed_from": "Snapmaker TPU 95A HF U1 0.8 nozzle;Snapmaker TPU 95A HF @U1 0.8 High Flow nozzle;Snapmaker TPU 95A HF U1 0.8 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "RjsE08h3l37cAkAs", "filament_id": "OFXSvqOX", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -18,90 +16,75 @@ "; filament end gcode \n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], + "enable_pressure_advance": [ + "0" + ], "fan_cooling_layer_time": [ "100" ], "fan_max_speed": [ - "100", "100" ], "fan_min_speed": [ - "100", "100" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "1.045", "1.045" ], "filament_max_volumetric_speed": [ - "16", "16" ], "filament_multitool_ramming_flow": [ - "15", "15" ], "filament_retract_length_toolchange": [ - "8", "8" ], "filament_retraction_length": [ - "1", "1" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ - "215", "215" ], "nozzle_temperature_initial_layer": [ - "215", "215" ], "overhang_fan_threshold": [ "95%" ], "pressure_advance": [ - "0.12", "0.12" ], "slow_down_layer_time": [ "12" ], "additional_cooling_fan_speed": [ - "100", "100" ], "filament_z_hop_types": [ - "Slope Lift", "Slope Lift" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -111,7 +94,6 @@ "30" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "eng_plate_temp": [ @@ -124,23 +106,18 @@ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "hot_plate_temp": [ @@ -165,14 +142,6 @@ "35" ], "nozzle_temperature_range_low": [ - "200", "200" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 16 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.8 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json index f609cd0b50..816b78ceb8 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU 95A HF @U1.json @@ -1,15 +1,13 @@ { "type": "filament", "name": "Snapmaker TPU 95A HF @U1", - "renamed_from": "Snapmaker TPU 95A HF U1;Snapmaker TPU 95A HF @U1 0.4 High Flow nozzle;Snapmaker TPU 95A HF U1 0.4 High Flow nozzle", "inherits": "fdm_filament_tpu", "from": "system", "setting_id": "KwcwmV2UmZUgCBoz", "filament_id": "OFXSvqOX", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "filament_start_gcode": [ "; filament start gcode\n" @@ -18,78 +16,69 @@ "; filament end gcode \n\n" ], "filament_multitool_ramming": [ - "1", "1" ], "filament_multitool_ramming_volume": [ - "0.1", "0.1" ], + "enable_pressure_advance": [ + "0" + ], "fan_cooling_layer_time": [ "40" ], "fan_max_speed": [ - "50", "50" ], "fan_min_speed": [ - "10", "10" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_flow_ratio": [ - "1.067", "1.067" ], "filament_max_volumetric_speed": [ - "9", "9" ], "filament_multitool_ramming_flow": [ - "10", "10" ], "filament_retract_length_toolchange": [ - "6", "6" ], "filament_retraction_length": [ - "1", "1" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_vendor": [ "Snapmaker" ], "nozzle_temperature": [ - "230", + "230" + ], + "nozzle_temperature_initial_layer": [ "230" ], "overhang_fan_threshold": [ "25%" ], "pressure_advance": [ - "0.23", "0.23" ], "slow_down_layer_time": [ "10" ], "activate_air_filtration": [ - "0", "0" ], "chamber_temperatures": [ "0" ], "complete_print_exhaust_fan_speed": [ - "70", "70" ], "cool_plate_temp": [ @@ -99,7 +88,6 @@ "30" ], "during_print_exhaust_fan_speed": [ - "70", "70" ], "eng_plate_temp": [ @@ -112,27 +100,21 @@ "20" ], "filament_long_retractions_when_cut": [ - "nil", "nil" ], "filament_minimal_purge_on_wipe_tower": [ - "15", "15" ], "filament_retract_when_changing_layer": [ - "nil", "nil" ], "filament_retraction_distances_when_cut": [ - "nil", "nil" ], "filament_z_hop": [ - "nil", "nil" ], "filament_z_hop_types": [ - "nil", "nil" ], "hot_plate_temp": [ @@ -157,14 +139,6 @@ "35" ], "nozzle_temperature_range_low": [ - "200", "200" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 9 mm^3/s, from Snorca standard preset Snapmaker TPU 95A HF @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json index 7a62ae80ec..ba393bed17 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU @U1.json @@ -1,7 +1,6 @@ { "type": "filament", "name": "Snapmaker TPU @U1", - "renamed_from": "Snapmaker TPU U1;Snapmaker TPU @U1 0.4 High Flow nozzle;Snapmaker TPU U1 0.4 High Flow nozzle;Snapmaker TPU @U1 0.6 High Flow nozzle;Snapmaker TPU U1 0.6 High Flow nozzle;Snapmaker TPU @U1 0.8 High Flow nozzle;Snapmaker TPU U1 0.8 High Flow nozzle", "inherits": "Snapmaker TPU @U1 base", "from": "system", "setting_id": "QLO4hkw6Agd3gSyR", @@ -9,20 +8,6 @@ "compatible_printers": [ "Snapmaker U1 (0.4 nozzle)", "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSunlu TPU 95A\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 10.5 mm^3/s, from Snorca standard preset Snapmaker TPU @U1 0.4 nozzle (Snapmaker OrcaSlicer). No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_max_volumetric_speed": [ - "2.8", - "10.5" + "Snapmaker U1 (0.8 nozzle)" ] } diff --git a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json index 6dce65a512..0aca011924 100644 --- a/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json +++ b/resources/profiles/Snapmaker/filament/Snapmaker TPU High-Flow @U1.json @@ -1,7 +1,6 @@ { "type": "filament", "name": "Snapmaker TPU High-Flow @U1", - "renamed_from": "Snapmaker TPU High-Flow U1;Snapmaker TPU High-Flow @U1 0.4 High Flow nozzle;Snapmaker TPU High-Flow U1 0.4 High Flow nozzle;Snapmaker TPU High-Flow @U1 0.6 High Flow nozzle;Snapmaker TPU High-Flow U1 0.6 High Flow nozzle;Snapmaker TPU High-Flow @U1 0.8 High Flow nozzle;Snapmaker TPU High-Flow U1 0.8 High Flow nozzle", "inherits": "Snapmaker TPU @U1 base", "from": "system", "setting_id": "5AXIg8WCdcd74S5v", @@ -10,36 +9,24 @@ "compatible_printers": [ "Snapmaker U1 (0.4 nozzle)", "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filament_flow_ratio": [ - "0.983", "0.983" ], "filament_deretraction_speed": [ - "nil", "nil" ], "filament_max_volumetric_speed": [ - "7.6", "7.6" ], "filament_retraction_length": [ - "0.8", "0.8" ], "filament_retraction_speed": [ - "nil", "nil" ], "filament_notes": [ - "Standard nozzle: !! It needs to be dried before use.\nSnapmaker TPU 95A High-Flow\n\n\nHigh-flow nozzle: Other material settings are inherited from the matching standard-nozzle preset. Volumetric speed ceiling: 7.6 mm^3/s, from local standard fallback preset Snapmaker TPU High-Flow @U1; no matching Snorca U1 material/nozzle profile. No multiplier or cross-material extrapolation is applied. Calibrate for your nozzle and filament." - ], - "filament_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow" + "!! It needs to be dried before use.\nSnapmaker TPU 95A High-Flow\n" ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json deleted file mode 100644 index 042df8f861..0000000000 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 High Flow nozzle).json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "type": "machine", - "name": "Snapmaker U1 (0.2 High Flow nozzle)", - "inherits": "Snapmaker U1 (0.2 nozzle)", - "from": "system", - "setting_id": "rypfKOLmPmXpR3Z9", - "instantiation": "true", - "printer_model": "Snapmaker U1", - "printer_variant": "0.2 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.2 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle" - ], - "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", - "extruder_type": [ - "Direct Drive", - "Direct Drive", - "Direct Drive", - "Direct Drive" - ], - "extruder_variant_list": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "printer_extruder_id": [ - "1", - "2", - "3", - "4" - ], - "printer_extruder_variant": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "default_nozzle_volume_type": [ - "High Flow", - "High Flow", - "High Flow", - "High Flow" - ] -} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json index 6e2939134e..674b82930c 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.2 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", "machine_max_jerk_z": [ "3", "0.4", @@ -65,7 +65,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.14", @@ -215,11 +215,5 @@ "143" ], "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER={layer_num+1}", - "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.2 nozzle" - ] + "default_print_profile": "0.10 Standard @Snapmaker U1 (0.2 nozzle)" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json deleted file mode 100644 index 1a8c1a0448..0000000000 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 High Flow nozzle).json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "type": "machine", - "name": "Snapmaker U1 (0.4 High Flow nozzle)", - "inherits": "Snapmaker U1 (0.4 nozzle)", - "from": "system", - "setting_id": "YVXcn9a2pyGfl8wo", - "instantiation": "true", - "printer_model": "Snapmaker U1", - "printer_variant": "0.4 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.4 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1", - "Snapmaker PLA SnapSpeed @U1", - "Snapmaker PLA SnapSpeed @U1", - "Snapmaker PLA SnapSpeed @U1" - ], - "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", - "extruder_type": [ - "Direct Drive", - "Direct Drive", - "Direct Drive", - "Direct Drive" - ], - "extruder_variant_list": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "printer_extruder_id": [ - "1", - "2", - "3", - "4" - ], - "printer_extruder_variant": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "default_nozzle_volume_type": [ - "High Flow", - "High Flow", - "High Flow", - "High Flow" - ] -} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json index a1dcbf2029..3acd21c2dd 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", "machine_max_jerk_z": [ "3", "0.4", @@ -65,7 +65,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.32", @@ -218,11 +218,5 @@ ], "resonance_avoidance": "1", "min_resonance_avoidance_speed": "40", - "max_resonance_avoidance_speed": "90", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1", - "Snapmaker PLA SnapSpeed @U1", - "Snapmaker PLA SnapSpeed @U1", - "Snapmaker PLA SnapSpeed @U1" - ] + "max_resonance_avoidance_speed": "90" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json index e9d28a5761..cc4e7a0515 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4+0.6 nozzle).json @@ -31,11 +31,5 @@ "stainless_steel", "stainless_steel", "stainless_steel" - ], - "default_filament_profile": [ - "Generic PLA @System", - "Generic PLA @System", - "Generic PLA @System", - "Generic PLA @System" ] } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json deleted file mode 100644 index 07d651e5c4..0000000000 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 High Flow nozzle).json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "type": "machine", - "name": "Snapmaker U1 (0.6 High Flow nozzle)", - "inherits": "Snapmaker U1 (0.6 nozzle)", - "from": "system", - "setting_id": "7Nqiw8MQIWaQqB1R", - "instantiation": "true", - "printer_model": "Snapmaker U1", - "printer_variant": "0.6 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.6 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle" - ], - "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", - "extruder_type": [ - "Direct Drive", - "Direct Drive", - "Direct Drive", - "Direct Drive" - ], - "extruder_variant_list": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "printer_extruder_id": [ - "1", - "2", - "3", - "4" - ], - "printer_extruder_variant": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "default_nozzle_volume_type": [ - "High Flow", - "High Flow", - "High Flow", - "High Flow" - ] -} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json index b26043aa7b..98b6f7e683 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.6 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", "machine_max_jerk_z": [ "3", "0.4", @@ -65,7 +65,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.42", @@ -215,11 +215,5 @@ "143", "143" ], - "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.6 nozzle" - ] + "default_print_profile": "0.30 Standard @Snapmaker U1 (0.6 nozzle)" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json deleted file mode 100644 index 6730524bc1..0000000000 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 High Flow nozzle).json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "type": "machine", - "name": "Snapmaker U1 (0.8 High Flow nozzle)", - "inherits": "Snapmaker U1 (0.8 nozzle)", - "from": "system", - "setting_id": "aTs5dXyBbXItUEeS", - "instantiation": "true", - "printer_model": "Snapmaker U1", - "printer_variant": "0.8 High Flow", - "printer_notes": "High-flow nozzle preset for all four U1 tools. Uses the existing 0.8 mm geometry, motion limits and G-code. Matching High Flow filament presets use the source-specific volumetric speed ceilings documented in their filament notes. Calibrate for your nozzle and filament.", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle" - ], - "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"high_flow,\"), /,$/, \"\")}\n", - "extruder_type": [ - "Direct Drive", - "Direct Drive", - "Direct Drive", - "Direct Drive" - ], - "extruder_variant_list": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "printer_extruder_id": [ - "1", - "2", - "3", - "4" - ], - "printer_extruder_variant": [ - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow", - "Direct Drive High Flow" - ], - "default_nozzle_volume_type": [ - "High Flow", - "High Flow", - "High Flow", - "High Flow" - ] -} diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json index 3934dc73f8..6ab566e0aa 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.8 nozzle).json @@ -34,7 +34,7 @@ "0", "0" ], - "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n\n; U1 nozzle flow metadata, one value per logical filament\n; filament_volume_type = {regex_replace(regex_replace(zdigits(0, size(filament_type)), /0/, \"standard,\"), /,$/, \"\")}\n", + "machine_end_gcode": ";===== date: 20260605 =====================\n; layer [layer_num]\n; max_layer_z [max_layer_z]\n; print_sequence [print_sequence]\n\n{if print_sequence == \"by object\"}\n{\nlocal move_z = max_print_height;\n\nif max_layer_z < ((max_print_height - z_offset) - 2) then\n move_z = z_offset + min(((max_layer_z - z_offset) + 2), max_print_height);\nendif\n}\n\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{move_z} F600\n{endif}\n\nPRINT_END\nTIMELAPSE_STOP\n", "machine_max_jerk_z": [ "3", "0.4", @@ -65,7 +65,7 @@ "20", "12" ], - "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20260128 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count} CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\nG28 X Y\nDEFECT_DETECT_NOODLE_FIRST\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nWAIT_CHAMBER_TEMP TIMEOUT=180\n{if curr_bed_type==\"High Temp Plate\"} \nG28 Z Z_OFFSET -0.07 \n{else} \nG28 Z \n{endif} \n\n\n;===== 热床调平 =================\n{if curr_bed_type==\"High Temp Plate\"} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 Z_OFFSET=-0.07\n{else} \n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n{endif} \n\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X85 Y1 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X185 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.56", @@ -215,11 +215,5 @@ "143", "143" ], - "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", - "default_filament_profile": [ - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle", - "Snapmaker PLA SnapSpeed @U1 0.8 nozzle" - ] + "default_print_profile": "0.40 Standard @Snapmaker U1 (0.8 nozzle)" } diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1.json b/resources/profiles/Snapmaker/machine/Snapmaker U1.json index 3fa02cde23..96eb53f81a 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1.json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1.json @@ -5,8 +5,8 @@ "family": "Snapmaker", "url": "", "model_id": "797581801", - "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle;Snapmaker PLA SnapSpeed @U1", + "default_materials": "Snapmaker PLA SnapSpeed @U1 0.2 nozzle;Panchroma PLA @Snapmaker U1;Generic PLA @System;Snapmaker PLA SnapSpeed @U1 0.6 nozzle;Snapmaker PLA SnapSpeed @U1 0.8 nozzle", "bed_model": "Snapmaker U1_bed.stl", "bed_texture": "Snapmaker U1_texture.svg", - "nozzle_diameter": "0.2;0.4;0.4+0.6;0.6;0.8;0.2 High Flow;0.4 High Flow;0.6 High Flow;0.8 High Flow" + "nozzle_diameter": "0.2;0.4;0.4+0.6;0.6;0.8" } diff --git a/resources/profiles/Snapmaker/machine/fdm_U1.json b/resources/profiles/Snapmaker/machine/fdm_U1.json index bec96fc2c1..0b3d89a303 100644 --- a/resources/profiles/Snapmaker/machine/fdm_U1.json +++ b/resources/profiles/Snapmaker/machine/fdm_U1.json @@ -12,150 +12,174 @@ "0.32", "0.32", "0.32", + "0.32", "0.32" ], "min_layer_height": [ "0.08", "0.08", "0.08", + "0.08", "0.08" ], "deretraction_speed": [ "30", "30", "30", + "30", "30" ], "extruder_colour": [ "#FCE94F", "#FCE94F", "#FCE94F", + "#FCE94F", "#FCE94F" ], "extruder_offset": [ "0x0", "0x0", "0x0", + "0x0", "0x0" ], "long_retractions_when_cut": [ "0", "0", "0", + "0", "0" ], "nozzle_diameter": [ "0.4", "0.4", "0.4", + "0.4", "0.4" ], "retract_before_wipe": [ "70%", "70%", "70%", + "70%", "70%" ], "retract_length_toolchange": [ "2", "2", "2", + "2", "2" ], "retract_lift_above": [ "0", "0", "0", + "0", "0" ], "retract_lift_below": [ "0", "0", "0", + "0", "0" ], "retract_lift_enforce": [ "All Surfaces", "All Surfaces", "All Surfaces", + "All Surfaces", "All Surfaces" ], "retract_restart_extra": [ "0", "0", "0", + "0", "0" ], "retract_restart_extra_toolchange": [ "0", "0", "0", + "0", "0" ], "retract_when_changing_layer": [ "1", "1", "1", + "1", "1" ], "retraction_distances_when_cut": [ "18", "18", "18", + "18", "18" ], "retraction_length": [ "0.8", "0.8", "0.8", + "0.8", "0.8" ], "retraction_minimum_travel": [ "1", "1", "1", + "1", "1" ], "retraction_speed": [ "30", "30", "30", + "30", "30" ], "travel_slope": [ "3", "3", "3", + "3", "3" ], "wipe": [ "1", "1", "1", + "1", "1" ], "wipe_distance": [ "1", "1", "1", + "1", "1" ], "z_hop": [ "0.4", "0.4", "0.4", + "0.4", "0.4" ], "z_hop_types": [ "Normal Lift", "Normal Lift", "Normal Lift", + "Normal Lift", "Normal Lift" ], "purge_in_prime_tower": "0", "machine_pause_gcode": "M601", "change_filament_gcode": "", - "machine_start_gcode": "PRINT_START TOOL_TEMP={first_layer_temperature[initial_tool]} {if is_extruder_used[0]}T0_TEMP={first_layer_temperature[0]}{endif} {if is_extruder_used[1]}T1_TEMP={first_layer_temperature[1]}{endif} {if is_extruder_used[2]}T2_TEMP={first_layer_temperature[2]}{endif} {if is_extruder_used[3]}T3_TEMP={first_layer_temperature[3]}{endif} {if is_extruder_used[4]}T4_TEMP={first_layer_temperature[4]}{endif} {if is_extruder_used[5]}T5_TEMP={first_layer_temperature[5]}{endif} BED_TEMP=[first_layer_bed_temperature] TOOL=[initial_tool]\n\n; Select purifier mode from the softening temperatures of used filaments.\n{if min_vitrification_temperature>70}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=600\n{elsif min_vitrification_temperature<=50}\nSET_PURIFIER_MODE MODE=1 DESIRE_TEMP=42 ALARM_TEMP=45 FAN_SPEED=0.6 DELAY_OFF=0\n{else}\nSET_PURIFIER_MODE MODE=3 DESIRE_TEMP=0 FAN_SPEED=0.6 DELAY_OFF=600\n{endif}\n\n\nM83\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nM109 T0 S{first_layer_temperature[0]}\nT0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 0 : (0 == 1 ? 120 : (0 == 2 ? 180 : 300)))} Y{(0 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[0]}10{else}30{endif} X40 Z0.2 F{if filament_multitool_ramming[0]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X40 E9 F800 ; continue purging and wipe the nozzle\nG0 X{40 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{40 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[0] == 0 ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nM109 T1 S{first_layer_temperature[1]}\nT1; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 0 : (1 == 1 ? 120 : (1 == 2 ? 180 : 300)))} Y{(1 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[1]}10{else}30{endif} X120 Z0.2 F{if filament_multitool_ramming[1]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X80 E9 F800 ; continue purging and wipe the nozzle\nG0 X{80 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{80 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[1] == 0 ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nM109 T2 S{first_layer_temperature[2]}\nT2; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 0 : (2 == 1 ? 120 : (2 == 2 ? 180 : 300)))} Y{(2 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[2]}10{else}30{endif} X220 Z0.2 F{if filament_multitool_ramming[2]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X220 E9 F800 ; continue purging and wipe the nozzle\nG0 X{220 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{220 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[2] == 0 ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nM109 T3 S{first_layer_temperature[3]}\nT3; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 0 : (3 == 1 ? 120 : (3 == 2 ? 180 : 300)))} Y{(3 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[3]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[3]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[3] == 0 ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nM109 T4 S{first_layer_temperature[4]}\nT4; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 0 : (4 == 1 ? 120 : (4 == 2 ? 180 : 300)))} Y{(4 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[4]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[4]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[4] == 0 ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool}; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300)))} Y{(initial_tool < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[initial_tool]}10{else}30{endif} X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if filament_multitool_ramming[initial_tool]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n", + "machine_start_gcode": "PRINT_START TOOL_TEMP={first_layer_temperature[initial_tool]} {if is_extruder_used[0]}T0_TEMP={first_layer_temperature[0]}{endif} {if is_extruder_used[1]}T1_TEMP={first_layer_temperature[1]}{endif} {if is_extruder_used[2]}T2_TEMP={first_layer_temperature[2]}{endif} {if is_extruder_used[3]}T3_TEMP={first_layer_temperature[3]}{endif} {if is_extruder_used[4]}T4_TEMP={first_layer_temperature[4]}{endif} {if is_extruder_used[5]}T5_TEMP={first_layer_temperature[5]}{endif} BED_TEMP=[first_layer_bed_temperature] TOOL=[initial_tool]\n\nM83\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nM109 T0 S{first_layer_temperature[0]}\nT0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 0 : (0 == 1 ? 120 : (0 == 2 ? 180 : 300)))} Y{(0 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[0]}10{else}30{endif} X40 Z0.2 F{if filament_multitool_ramming[0]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X40 E9 F800 ; continue purging and wipe the nozzle\nG0 X{40 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{40 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[0] == 0 ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nM109 T1 S{first_layer_temperature[1]}\nT1; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 0 : (1 == 1 ? 120 : (1 == 2 ? 180 : 300)))} Y{(1 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[1]}10{else}30{endif} X120 Z0.2 F{if filament_multitool_ramming[1]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X80 E9 F800 ; continue purging and wipe the nozzle\nG0 X{80 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{80 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[1] == 0 ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nM109 T2 S{first_layer_temperature[2]}\nT2; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 0 : (2 == 1 ? 120 : (2 == 2 ? 180 : 300)))} Y{(2 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[2]}10{else}30{endif} X220 Z0.2 F{if filament_multitool_ramming[2]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X220 E9 F800 ; continue purging and wipe the nozzle\nG0 X{220 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{220 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[2] == 0 ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nM109 T3 S{first_layer_temperature[3]}\nT3; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 0 : (3 == 1 ? 120 : (3 == 2 ? 180 : 300)))} Y{(3 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[3]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[3]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[3] == 0 ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nM109 T4 S{first_layer_temperature[4]}\nT4; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 0 : (4 == 1 ? 120 : (4 == 2 ? 180 : 300)))} Y{(4 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[4]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[4]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[4] == 0 ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool}; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300)))} Y{(initial_tool < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[initial_tool]}10{else}30{endif} X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if filament_multitool_ramming[initial_tool]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n", "scan_first_layer": "0", "nozzle_type": "undefine", "auxiliary_fan": "0", diff --git a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json index 73cee4710b..15546ce4bb 100644 --- a/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.06 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -1,47 +1,18 @@ { "type": "process", "name": "0.06 High Quality @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.06 High Quality Snapmaker U1 (0.2 nozzle);0.06 High Quality @Snapmaker U1 (0.2 High Flow nozzle);0.06 High Quality Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.06_nozzle_0.2", "from": "system", "setting_id": "3SCofR6VrVyo3vJp", "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in minimal layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": [ - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000" - ], + "default_acceleration": "4000", "elefant_foot_compensation": "0.15", - "outer_wall_acceleration": [ - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000" - ], - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "outer_wall_acceleration": "2000", + "outer_wall_speed": "60", "sparse_infill_pattern": "gyroid", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -49,69 +20,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "13.5", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json index 98f28fe3d5..3881460bda 100644 --- a/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.06 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.06 Standard @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.06 Standard Snapmaker U1 (0.2 nozzle);0.06 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.06 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.06_nozzle_0.2", "from": "system", "setting_id": "997RaiNHd2YhpaRB", @@ -9,8 +8,7 @@ "description": "Compared with the default profile of 0.2 mm nozzle, it has a smaller layer height, and results in minimal layer lines and higher printing quality, but shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -18,79 +16,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "13.5", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json index 8f8d3aa158..0a3ba43c86 100644 --- a/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 Extra Fine @Snapmaker U1 (0.4 nozzle).json @@ -1,98 +1,28 @@ { "type": "process", "name": "0.08 Extra Fine @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.08 Extra Fine Snapmaker U1 (0.4 nozzle);0.08 Extra Fine @Snapmaker U1 (0.4 High Flow nozzle);0.08 Extra Fine Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.08", "from": "system", "setting_id": "Tayg0K1WRyJMLoke", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, and results in almost negligible layer lines and longer printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", "wipe_tower_cone_angle": "15", "wipe_tower_extra_spacing": "120%", "enable_arc_fitting": "0", - "outer_wall_speed": [ - "100", - "500", - "100", - "500", - "100", - "500", - "100", - "500" - ], + "outer_wall_speed": "100", "precise_outer_wall": "1", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "450", - "600", - "450", - "600", - "450", - "600", - "450", - "600" - ], - "inner_wall_speed": [ - "350", - "600", - "350", - "600", - "350", - "600", - "350", - "600" - ], - "internal_solid_infill_speed": [ - "350", - "600", - "350", - "600", - "350", - "600", - "350", - "600" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json index 1ae8b8aba2..2aeb309d58 100644 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -1,47 +1,18 @@ { "type": "process", "name": "0.08 High Quality @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.08 High Quality Snapmaker U1 (0.2 nozzle);0.08 High Quality @Snapmaker U1 (0.2 High Flow nozzle);0.08 High Quality Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.08_nozzle_0.2", "from": "system", "setting_id": "6ExpMU3Wq4J1R7wy", "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer lines, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost invisible layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": [ - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000" - ], + "default_acceleration": "4000", "elefant_foot_compensation": "0.15", - "outer_wall_acceleration": [ - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000" - ], - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "outer_wall_acceleration": "2000", + "outer_wall_speed": "60", "sparse_infill_pattern": "gyroid", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -49,69 +20,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json index 6ba52ed409..107731b2cb 100644 --- a/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,110 +1,27 @@ { "type": "process", "name": "0.08 High Quality @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.08 High Quality Snapmaker U1 (0.4 nozzle);0.08 High Quality @Snapmaker U1 (0.4 High Flow nozzle);0.08 High Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.08", "from": "system", "setting_id": "Bld9ulv0wFza7OCb", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost negligible layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": [ - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000" - ], - "gap_infill_speed": [ - "210", - "210", - "210", - "210", - "210", - "210", - "210", - "210" - ], - "inner_wall_speed": [ - "120", - "600", - "120", - "600", - "120", - "600", - "120", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "outer_wall_acceleration": [ - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000" - ], - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "default_acceleration": "4000", + "gap_infill_speed": "210", + "inner_wall_speed": "120", + "internal_solid_infill_speed": "150", + "outer_wall_acceleration": "2000", + "outer_wall_speed": "60", "sparse_infill_pattern": "gyroid", - "sparse_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "top_surface_speed": [ - "150", - "150", - "150", - "150", - "150", - "150", - "150", - "150" - ], + "sparse_infill_speed": "150", + "top_surface_speed": "150", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -115,25 +32,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json index 0d17be8403..ea8c719085 100644 --- a/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.08 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.08 Standard @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.08 Standard Snapmaker U1 (0.2 nozzle);0.08 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.08 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.08_nozzle_0.2", "from": "system", "setting_id": "loFrCYH9ux2L5JpZ", @@ -9,8 +8,7 @@ "description": "Compared with the default profile of a 0.2 mm nozzle, it has a smaller layer height, and results in almost invisible layer lines and higher printing quality, but shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -18,79 +16,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "18", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json index d4b6f44745..6fc26cd55f 100644 --- a/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.10 High Quality @Snapmaker U1 (0.2 nozzle).json @@ -1,47 +1,18 @@ { "type": "process", "name": "0.10 High Quality @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.10 High Quality Snapmaker U1 (0.2 nozzle);0.10 High Quality @Snapmaker U1 (0.2 High Flow nozzle);0.10 High Quality Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.10_nozzle_0.2", "from": "system", "setting_id": "BfLpmnJnSBYBFInR", "instantiation": "true", "description": "Compared with the default profile of a 0.2 mm nozzle, it has lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in much higher printing quality, but a much longer printing time.", - "default_acceleration": [ - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000" - ], + "default_acceleration": "4000", "elefant_foot_compensation": "0.15", - "outer_wall_acceleration": [ - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000" - ], - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "outer_wall_acceleration": "2000", + "outer_wall_speed": "60", "sparse_infill_pattern": "gyroid", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -49,69 +20,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "28", "prime_volume": "25", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json index e6486cee9b..d33ce1c083 100644 --- a/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.10 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.10 Standard @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.10 Standard Snapmaker U1 (0.2 nozzle);0.10 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.10 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.10_nozzle_0.2", "from": "system", "setting_id": "SOq962UWv1ml1Mk0", @@ -9,8 +8,7 @@ "description": "It has a small layer height, and results in almost negligible layer lines and high printing quality. It is suitable for most general printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -25,74 +23,5 @@ "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", "wipe_tower_wall_type": "rib", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "slowdown_for_curled_perimeters": "0" } diff --git a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json index f72b90e84b..6de6d483da 100644 --- a/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 Fine @Snapmaker U1 (0.4 nozzle).json @@ -1,27 +1,16 @@ { "type": "process", "name": "0.12 Fine @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.12 Fine Snapmaker U1 (0.4 nozzle);0.12 Fine @Snapmaker U1 (0.4 High Flow nozzle);0.12 Fine Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.12", "from": "system", "setting_id": "xBU1j8ldOefYfosp", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, and results in almost negligible layer lines and higher printing quality, but longer printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -35,65 +24,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "430", - "600", - "430", - "600", - "430", - "600", - "430", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "350", - "600", - "350", - "600", - "350", - "600", - "350", - "600" - ], - "internal_solid_infill_speed": [ - "350", - "600", - "350", - "600", - "350", - "600", - "350", - "600" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json index d4a347bc1f..a5f215d7f3 100644 --- a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,109 +1,26 @@ { "type": "process", "name": "0.12 High Quality @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.12 High Quality Snapmaker U1 (0.4 nozzle);0.12 High Quality @Snapmaker U1 (0.4 High Flow nozzle);0.12 High Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.12", "from": "system", "setting_id": "WNWFsx5DJtCRAlFl", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in almost negligible layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": [ - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000" - ], - "gap_infill_speed": [ - "230", - "230", - "230", - "230", - "230", - "230", - "230", - "230" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "180", - "600", - "180", - "600", - "180", - "600", - "180", - "600" - ], - "outer_wall_acceleration": [ - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000" - ], - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "default_acceleration": "4000", + "gap_infill_speed": "230", + "inner_wall_speed": "150", + "internal_solid_infill_speed": "180", + "outer_wall_acceleration": "2000", + "outer_wall_speed": "60", "sparse_infill_pattern": "gyroid", - "sparse_infill_speed": [ - "180", - "600", - "180", - "600", - "180", - "600", - "180", - "600" - ], - "top_surface_speed": [ - "150", - "150", - "150", - "150", - "150", - "150", - "150", - "150" - ], + "sparse_infill_speed": "180", + "top_surface_speed": "150", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "prime_tower_width": "30", "prime_volume": "27", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", "wipe_tower_cone_angle": "15", @@ -116,25 +33,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json index c1108faea3..632d9614f7 100644 --- a/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.12 Standard @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.12 Standard Snapmaker U1 (0.2 nozzle);0.12 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.12 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.12_nozzle_0.2", "from": "system", "setting_id": "KHC1zpxpuvaUEzvf", @@ -9,8 +8,7 @@ "description": "Compared with the default profile of a 0.2 mm nozzle, it has a slightly bigger layer height, and results in almost negligible layer lines, and slightly shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -18,79 +16,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "28", "prime_volume": "27", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json index 5735194c70..0de823db1f 100644 --- a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.14 Standard @Snapmaker U1 (0.2 nozzle)", - "renamed_from": "0.14 Standard Snapmaker U1 (0.2 nozzle);0.14 Standard @Snapmaker U1 (0.2 High Flow nozzle);0.14 Standard Snapmaker U1 (0.2 High Flow nozzle)", "inherits": "fdm_process_U1_0.14_nozzle_0.2", "from": "system", "setting_id": "NGJoV0n6T510y9wM", @@ -9,8 +8,7 @@ "description": "Compared with the default profile of a 0.2 mm nozzle, it has a bigger layer height, and results in slightly visible layer lines, but shorter printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)", - "Snapmaker U1 (0.2 High Flow nozzle)" + "Snapmaker U1 (0.2 nozzle)" ], "enable_arc_fitting": "0", "ooze_prevention": "1", @@ -18,79 +16,10 @@ "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "32", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_wall_type": "rib" } diff --git a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json index 5126a45f91..1306485d6f 100644 --- a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,110 +1,27 @@ { "type": "process", "name": "0.16 High Quality @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.16 High Quality Snapmaker U1 (0.4 nozzle);0.16 High Quality @Snapmaker U1 (0.4 High Flow nozzle);0.16 High Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.16", "from": "system", "setting_id": "Ofe17WrGQ3MOrqNe", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, lower speeds and acceleration, and the sparse infill pattern is Gyroid. So, it results in less apparent layer lines and much higher printing quality, but much longer printing time.", - "default_acceleration": [ - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000", - "4000" - ], - "gap_infill_speed": [ - "250", - "250", - "250", - "250", - "250", - "250", - "250", - "250" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "outer_wall_acceleration": [ - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000", - "2000" - ], - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "default_acceleration": "4000", + "gap_infill_speed": "250", + "inner_wall_speed": "150", + "internal_solid_infill_speed": "200", + "outer_wall_acceleration": "2000", + "outer_wall_speed": "60", "sparse_infill_pattern": "gyroid", - "sparse_infill_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "top_surface_speed": [ - "150", - "150", - "150", - "150", - "150", - "150", - "150", - "150" - ], + "sparse_infill_speed": "200", + "top_surface_speed": "150", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "36", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -116,25 +33,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json index c5f4c492da..838f250e8c 100644 --- a/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 Optimal @Snapmaker U1 (0.4 nozzle).json @@ -1,29 +1,18 @@ { "type": "process", "name": "0.16 Optimal @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.16 Optimal Snapmaker U1 (0.4 nozzle);0.16 Optimal @Snapmaker U1 (0.4 High Flow nozzle);0.16 Optimal Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.16", "from": "system", "setting_id": "ERcgRLp5bQo1hgz8", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and higher printing quality, but longer printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "36", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -34,65 +23,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "330", - "600", - "330", - "600", - "330", - "600", - "330", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json index c9a5216347..88fbb33812 100644 --- a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.18 Standard @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.18 Standard Snapmaker U1 (0.6 nozzle);0.18 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.18 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.18_nozzle_0.6", "from": "system", "setting_id": "XGSrVgnsUFv6uCRs", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and higher printing quality, but longer printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "layer_height": "0.25", "prime_tower_brim_width": "5", "prime_tower_width": "30", @@ -35,76 +24,7 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json index 701b719713..be894aa221 100644 --- a/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Quality @Snapmaker U1 (0.4 nozzle).json @@ -1,92 +1,21 @@ { "type": "process", "name": "0.20 Quality @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.20 Quality Snapmaker U1 (0.4 nozzle);0.20 Quality @Snapmaker U1 (0.4 High Flow nozzle);0.20 Quality Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "BBXxXQCXCme2Mvy0", "instantiation": "true", "description": "It has a general layer height, and results in general layer lines and printing quality. It is suitable for most general printing cases.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", "wipe_tower_cone_angle": "15", "prime_tower_brim_width": "5", - "initial_layer_print_height": "0.25", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "270", - "600", - "270", - "600", - "270", - "600", - "270", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "250", - "600", - "250", - "600", - "250", - "600", - "250", - "600" - ] + "initial_layer_print_height": "0.25" } diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json index 75b4c935d5..443afe55dd 100644 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.4 nozzle).json @@ -1,93 +1,22 @@ { "type": "process", "name": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.20 Standard Snapmaker U1 (0.4 nozzle);0.20 Standard @Snapmaker U1 (0.4 High Flow nozzle);0.20 Standard Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "XR5r9SuWmSag5Qjo", "instantiation": "true", "description": "It has a general layer height, and results in general layer lines and printing quality. It is suitable for most general printing cases.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "45", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", "wipe_tower_cone_angle": "15", "prime_tower_brim_width": "5", - "initial_layer_print_height": "0.25", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "270", - "600", - "270", - "600", - "270", - "600", - "270", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "250", - "600", - "250", - "600", - "250", - "600", - "250", - "600" - ] + "initial_layer_print_height": "0.25" } diff --git a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json index 8dcbb68fa5..5c87ff68d0 100644 --- a/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.20 Standard @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.20 Standard Snapmaker U1 (0.6 nozzle);0.20 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.20 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.6_common", "from": "system", "setting_id": "6VM4QhgvHUmyf2um", @@ -9,21 +8,11 @@ "description": "Standard 0.2 mm layer height profile for the Snapmaker U1 with 0.6 mm nozzles. Balances print speed and surface quality for everyday prints.", "layer_height": "0.20", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -31,65 +20,5 @@ "prime_tower_brim_width": "5", "initial_layer_print_height": "0.25", "top_shell_layers": "4", - "bottom_shell_layers": "3", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "250", - "600", - "250", - "600", - "250", - "600", - "250", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "bottom_shell_layers": "3" } diff --git a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json index 157333ffad..1d051fc801 100644 --- a/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Strength @Snapmaker U1 (0.4 nozzle).json @@ -1,41 +1,21 @@ { "type": "process", "name": "0.20 Strength @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.20 Strength Snapmaker U1 (0.4 nozzle);0.20 Strength @Snapmaker U1 (0.4 High Flow nozzle);0.20 Strength Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "bYnMOZxTq83EwIyd", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has more wall loops and a higher sparse infill density. So, it results in higher strength of the prints, but more filament consumption and longer printing time.", - "outer_wall_speed": [ - "60", - "500", - "60", - "500", - "60", - "500", - "60", - "500" - ], + "outer_wall_speed": "60", "sparse_infill_density": "25%", "wall_loops": "6", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "45", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -46,55 +26,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "270", - "600", - "270", - "600", - "270", - "600", - "270", - "600" - ], - "inner_wall_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "250", - "600", - "250", - "600", - "250", - "600", - "250", - "600" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json index 5e031b3517..577065ca3f 100644 --- a/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Support @Snapmaker U1 (0.4 nozzle).json @@ -1,29 +1,18 @@ { "type": "process", "name": "0.20 Support @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.20 Support Snapmaker U1 (0.4 nozzle);0.20 Support @Snapmaker U1 (0.4 High Flow nozzle);0.20 Support Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "4whjHJVCN44w8SPE", "instantiation": "true", "description": "It has a general layer height, and results in general layer lines and printing quality. It is suitable for most general printing cases.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "40", "prime_volume": "15", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -32,65 +21,5 @@ "enable_support": "1", "initial_layer_print_height": "0.25", "support_interface_spacing": "0", - "support_interface_top_layers": "3", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "270", - "600", - "270", - "600", - "270", - "600", - "270", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "250", - "600", - "250", - "600", - "250", - "600", - "250", - "600" - ] + "support_interface_top_layers": "3" } diff --git a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json index 57a763a355..3d1887bea9 100644 --- a/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.20 Support W @Snapmaker U1 (0.4 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.20 Support W @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.20 Support W Snapmaker U1 (0.4 nozzle);0.20 Support W @Snapmaker U1 (0.4 High Flow nozzle);0.20 Support W Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.20", "from": "system", "setting_id": "M9JhI2roLcP4jYks", @@ -11,85 +10,16 @@ "support_top_z_distance": "0.2", "support_interface_loop_pattern": "1", "support_interface_spacing": "0", + "support_interface_speed": "80", "support_filament": "0", "support_interface_filament": "0", "enable_prime_tower": "1", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "50", "prime_volume": "38", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], - "standby_temperature_delta": "-150", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "270", - "600", - "270", - "600", - "270", - "600", - "270", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "250", - "600", - "250", - "600", - "250", - "600", - "250", - "600" - ] + "slowdown_for_curled_perimeters": "0", + "standby_temperature_delta": "-150" } diff --git a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json index b1ad97d537..8e09376daf 100644 --- a/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Draft @Snapmaker U1 (0.4 nozzle).json @@ -1,29 +1,18 @@ { "type": "process", "name": "0.24 Draft @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.24 Draft Snapmaker U1 (0.4 nozzle);0.24 Draft @Snapmaker U1 (0.4 High Flow nozzle);0.24 Draft Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.24", "from": "system", "setting_id": "MYRo7jTenu7F0Zv4", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a bigger layer height, and results in more apparent layer lines and lower printing quality, but slightly shorter printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "54", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", @@ -34,65 +23,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "230", - "600", - "230", - "600", - "230", - "600", - "230", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "230", - "600", - "230", - "600", - "230", - "600", - "230", - "600" - ], - "internal_solid_infill_speed": [ - "230", - "600", - "230", - "600", - "230", - "600", - "230", - "600" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json index 8aae7fa25c..2fa2eeabdb 100644 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.24 Standard @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.24 Standard Snapmaker U1 (0.6 nozzle);0.24 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.24 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.24_nozzle_0.6", "from": "system", "setting_id": "BziJYuA5U5gWm6FM", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and slight higher printing quality, but longer printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "55", @@ -34,77 +23,8 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json index 1c08f23f8e..316bb0dbee 100644 --- a/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.24 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.24 Standard @Snapmaker U1 (0.8 nozzle)", - "renamed_from": "0.24 Standard Snapmaker U1 (0.8 nozzle);0.24 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.24 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.24_nozzle_0.8", "from": "system", "setting_id": "QcJ5p9h0eYUb91ak", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and slight higher printing quality, but longer printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "min_width_top_surface": "90", "prime_tower_brim_width": "5", "prime_tower_width": "30", @@ -35,76 +24,7 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json index f5c9737efd..2dcf03d540 100644 --- a/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.25 Benchy @Snapmaker U1 (0.4 nozzle).json @@ -1,144 +1,35 @@ { "type": "process", "name": "0.25 Benchy @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.25 Benchy Snapmaker U1 (0.4 nozzle);0.25 Benchy @Snapmaker U1 (0.4 High Flow nozzle);0.25 Benchy Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1", "from": "system", "setting_id": "deA4Xg6QxFTKOYZy", "instantiation": "true", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "elefant_foot_compensation": "0.15", "top_shell_layers": "3", "top_shell_thickness": "0.5", - "initial_layer_speed": [ - "100", - "100", - "100", - "100", - "100", - "100", - "100", - "100" - ], - "initial_layer_infill_speed": [ - "180", - "180", - "180", - "180", - "180", - "180", - "180", - "180" - ], - "outer_wall_speed": [ - "180", - "500", - "180", - "500", - "180", - "500", - "180", - "500" - ], - "inner_wall_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "sparse_infill_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "internal_solid_infill_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "top_surface_speed": [ - "180", - "180", - "180", - "180", - "180", - "180", - "180", - "180" - ], - "gap_infill_speed": [ - "200", - "200", - "200", - "200", - "200", - "200", - "200", - "200" - ], + "initial_layer_speed": "100", + "initial_layer_infill_speed": "180", + "outer_wall_speed": "180", + "inner_wall_speed": "200", + "sparse_infill_speed": "200", + "internal_solid_infill_speed": "200", + "top_surface_speed": "180", + "gap_infill_speed": "200", "ironing_speed": "30", - "enable_overhang_speed": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], - "overhang_1_4_speed": [ - "180", - "180", - "180", - "180", - "180", - "180", - "180", - "180" - ], - "overhang_2_4_speed": [ - "20", - "20", - "20", - "20", - "20", - "20", - "20", - "20" - ], - "overhang_3_4_speed": [ - "10", - "10", - "10", - "10", - "10", - "10", - "10", - "10" - ], + "enable_overhang_speed": "0", + "overhang_1_4_speed": "180", + "overhang_2_4_speed": "20", + "overhang_3_4_speed": "10", "layer_height": "0.25", "initial_layer_print_height": "0.25", "detect_overhang_wall": "0", "reduce_crossing_wall": "0", "ensure_vertical_shell_thickness": "none", + "filename_format": "testBenchy_{print_time}.gcode", "sparse_infill_density": "10%", "sparse_infill_pattern": "alignedrectilinear", "wall_generator": "classic", @@ -155,134 +46,15 @@ "sparse_infill_line_width": "0.5", "top_surface_line_width": "0.5", "bottom_shell_layers": "2", - "initial_layer_travel_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], - "bridge_acceleration": [ - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000" - ], - "bridge_speed": [ - "100", - "100", - "100", - "100", - "100", - "100", - "100", - "100" - ], - "default_acceleration": [ - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000" - ], - "inner_wall_acceleration": [ - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000" - ], - "internal_solid_infill_acceleration": [ - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000" - ], - "outer_wall_acceleration": [ - "1600", - "1600", - "1600", - "1600", - "1600", - "1600", - "1600", - "1600" - ], - "small_perimeter_speed": [ - "200", - "200", - "200", - "200", - "200", - "200", - "200", - "200" - ], - "sparse_infill_acceleration": [ - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000" - ], - "top_surface_acceleration": [ - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000", - "3000" - ], - "initial_layer_acceleration": [ - "1600", - "1600", - "1600", - "1600", - "1600", - "1600", - "1600", - "1600" - ], - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ] + "initial_layer_travel_speed": "100%", + "bridge_acceleration": "3000", + "bridge_speed": "100", + "default_acceleration": "3000", + "inner_wall_acceleration": "3000", + "internal_solid_infill_acceleration": "3000", + "outer_wall_acceleration": "1600", + "small_perimeter_speed": "200", + "sparse_infill_acceleration": "3000", + "top_surface_acceleration": "3000", + "initial_layer_acceleration": "1600" } diff --git a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json index 3b2e7f8746..8829efafdb 100644 --- a/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.28 Extra Draft @Snapmaker U1 (0.4 nozzle).json @@ -1,28 +1,17 @@ { "type": "process", "name": "0.28 Extra Draft @Snapmaker U1 (0.4 nozzle)", - "renamed_from": "0.28 Extra Draft Snapmaker U1 (0.4 nozzle);0.28 Extra Draft @Snapmaker U1 (0.4 High Flow nozzle);0.28 Extra Draft Snapmaker U1 (0.4 High Flow nozzle)", "inherits": "fdm_process_U1_0.28", "from": "system", "setting_id": "0fSeyAIS6z75A1r3", "instantiation": "true", "description": "Compared with the default profile of a 0.4 mm nozzle, it has a bigger layer height, and results in more apparent layer lines and lower printing quality, but shorter printing time.", "compatible_printers": [ - "Snapmaker U1 (0.4 nozzle)", - "Snapmaker U1 (0.4 High Flow nozzle)" + "Snapmaker U1 (0.4 nozzle)" ], "prime_tower_width": "30", "prime_volume": "63", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "wipe_tower_filament": "0", "prime_tower_brim_width": "5", "wipe_tower_cone_angle": "15", @@ -34,65 +23,5 @@ "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", "wipe_tower_wall_type": "rib", - "gap_fill_target": "topbottom", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "outer_wall_speed": [ - "200", - "500", - "200", - "500", - "200", - "500", - "200", - "500" - ], - "inner_wall_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ], - "internal_solid_infill_speed": [ - "200", - "600", - "200", - "600", - "200", - "600", - "200", - "600" - ] + "gap_fill_target": "topbottom" } diff --git a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json index 1ca162e67a..defbc6740b 100644 --- a/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Draft @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.30 Draft @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.30 Draft Snapmaker U1 (0.6 nozzle);0.30 Draft @Snapmaker U1 (0.6 High Flow nozzle);0.30 Draft Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.6_common", "from": "system", "setting_id": "Tk3tG3RGJ8dwXFKf", @@ -9,22 +8,12 @@ "description": "Fast draft profile for the Snapmaker U1 with 0.6 mm nozzles. 0.3 mm layer height for quick prototypes and functional parts.", "layer_height": "0.30", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "68", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -33,64 +22,8 @@ "initial_layer_print_height": "0.30", "top_shell_layers": "3", "bottom_shell_layers": "3", - "outer_wall_speed": [ - "220", - "500", - "220", - "500", - "220", - "500", - "220", - "500" - ], - "inner_wall_speed": [ - "330", - "600", - "330", - "600", - "330", - "600", - "330", - "600" - ], - "sparse_infill_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "internal_solid_infill_speed": [ - "280", - "600", - "280", - "600", - "280", - "600", - "280", - "600" - ], - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ] + "outer_wall_speed": "220", + "inner_wall_speed": "330", + "sparse_infill_speed": "300", + "internal_solid_infill_speed": "280" } diff --git a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json index b84b343dcc..92bebf6219 100644 --- a/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.30 Standard @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.30 Standard Snapmaker U1 (0.6 nozzle);0.30 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.30 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.30_nozzle_0.6", "from": "system", "setting_id": "TWxp6qgz1DJa8Ngo", @@ -9,21 +8,11 @@ "description": "It has a big layer height, and results in apparent layer lines and ordinary printing quality and printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "68", @@ -33,77 +22,8 @@ "wipe_tower_wall_type": "rib", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json index 470490fb9f..f268a92446 100644 --- a/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.30 Strength @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.30 Strength @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.30 Strength Snapmaker U1 (0.6 nozzle);0.30 Strength @Snapmaker U1 (0.6 High Flow nozzle);0.30 Strength Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.30_nozzle_0.6", "from": "system", "setting_id": "CBaL0vgwtgsd6Snj", @@ -11,21 +10,11 @@ "sparse_infill_density": "25%", "wall_loops": "4", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_width": "30", "prime_volume": "68", "seam_gap": "15%", @@ -35,77 +24,8 @@ "ooze_prevention": "1", "precise_outer_wall": "1", "prime_tower_brim_width": "5", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_type": "tree(auto)", - "wipe_tower_extra_spacing": "120%", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "wipe_tower_extra_spacing": "120%" } diff --git a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json index 97ae45ed4e..de7559f827 100644 --- a/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.32 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.32 Standard @Snapmaker U1 (0.8 nozzle)", - "renamed_from": "0.32 Standard Snapmaker U1 (0.8 nozzle);0.32 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.32 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.32_nozzle_0.8", "from": "system", "setting_id": "iVRz2B0VBIBL1xVd", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.8 mm nozzle, it has a slightly smaller layer height, and results in slightly less but still apparent layer lines and slightly higher printing quality, but longer printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "72", @@ -36,77 +25,8 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json index e964e08e5d..6dc9f26329 100644 --- a/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.36 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.36 Standard @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.36 Standard Snapmaker U1 (0.6 nozzle);0.36 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.36 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.36_nozzle_0.6", "from": "system", "setting_id": "C0PiCFd3P222knX9", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a bigger layer height, and results in more apparent layer lines and lower printing quality, but shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "81", @@ -34,77 +23,8 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json index 51b76f6e33..b18e30c210 100644 --- a/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.40 Extra Draft @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.40 Extra Draft @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.40 Extra Draft Snapmaker U1 (0.6 nozzle);0.40 Extra Draft @Snapmaker U1 (0.6 High Flow nozzle);0.40 Extra Draft Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.6_common", "from": "system", "setting_id": "rAUffOThYmj9FVTw", @@ -9,22 +8,12 @@ "description": "Maximum-speed profile for the Snapmaker U1 with 0.6 mm nozzles. 0.4 mm layer height for rapid prototyping where surface finish is not critical.", "layer_height": "0.40", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "ooze_prevention": "1", "prime_tower_width": "30", "prime_volume": "90", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "wipe_tower_filament": "0", "preheat_time": "30", @@ -33,65 +22,9 @@ "initial_layer_print_height": "0.35", "top_shell_layers": "3", "bottom_shell_layers": "3", - "outer_wall_speed": [ - "220", - "500", - "220", - "500", - "220", - "500", - "220", - "500" - ], - "inner_wall_speed": [ - "330", - "600", - "330", - "600", - "330", - "600", - "330", - "600" - ], - "sparse_infill_speed": [ - "320", - "600", - "320", - "600", - "320", - "600", - "320", - "600" - ], - "internal_solid_infill_speed": [ - "300", - "600", - "300", - "600", - "300", - "600", - "300", - "600" - ], - "wall_loops": "2", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ] + "outer_wall_speed": "220", + "inner_wall_speed": "330", + "sparse_infill_speed": "320", + "internal_solid_infill_speed": "300", + "wall_loops": "2" } diff --git a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json index bf9f2644d6..b0cf935465 100644 --- a/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.40 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.40 Standard @Snapmaker U1 (0.8 nozzle)", - "renamed_from": "0.40 Standard Snapmaker U1 (0.8 nozzle);0.40 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.40 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.40_nozzle_0.8", "from": "system", "setting_id": "VqkPgUtZi159BKpJ", @@ -9,21 +8,11 @@ "description": "It has a very big layer height, and results in very apparent layer lines, low printing quality and general printing time.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "90", @@ -37,77 +26,8 @@ "min_width_top_surface": "200%", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "40", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json index 4338dccaa8..eaa04faadc 100644 --- a/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.42 Standard @Snapmaker U1 (0.6 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.42 Standard @Snapmaker U1 (0.6 nozzle)", - "renamed_from": "0.42 Standard Snapmaker U1 (0.6 nozzle);0.42 Standard @Snapmaker U1 (0.6 High Flow nozzle);0.42 Standard Snapmaker U1 (0.6 High Flow nozzle)", "inherits": "fdm_process_U1_0.42_nozzle_0.6", "from": "system", "setting_id": "SVJzhQA3KQetRZmo", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.6 mm nozzle, it has a bigger layer height, and results in much more apparent layer lines and much lower printing quality, but shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)", - "Snapmaker U1 (0.6 High Flow nozzle)" + "Snapmaker U1 (0.6 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "precise_z_height": "0", "prime_tower_brim_width": "5", "prime_tower_width": "30", @@ -34,77 +23,8 @@ "wipe_tower_wall_type": "rib", "enable_arc_fitting": "0", "ooze_prevention": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json index 45b2d9431b..599cf24db5 100644 --- a/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.48 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.48 Standard @Snapmaker U1 (0.8 nozzle)", - "renamed_from": "0.48 Standard Snapmaker U1 (0.8 nozzle);0.48 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.48 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.48_nozzle_0.8", "from": "system", "setting_id": "5H6JWChFEo3zqoHQ", @@ -9,21 +8,11 @@ "description": "Compared with the default profile of a 0.8 mm nozzle, it has a bigger layer height, and results in very apparent layer lines and much lower printing quality, but shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", - "internal_bridge_speed": [ - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%", - "100%" - ], + "internal_bridge_speed": "100%", "prime_tower_brim_width": "5", "prime_tower_width": "30", "prime_volume": "108", @@ -34,77 +23,8 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json index b2fb877e5f..ca728fe66a 100644 --- a/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.56 Standard @Snapmaker U1 (0.8 nozzle).json @@ -1,7 +1,6 @@ { "type": "process", "name": "0.56 Standard @Snapmaker U1 (0.8 nozzle)", - "renamed_from": "0.56 Standard Snapmaker U1 (0.8 nozzle);0.56 Standard @Snapmaker U1 (0.8 High Flow nozzle);0.56 Standard Snapmaker U1 (0.8 High Flow nozzle)", "inherits": "fdm_process_U1_0.56_nozzle_0.8", "from": "system", "setting_id": "JZEIXOUT33c7MrZj", @@ -9,8 +8,7 @@ "description": "Compared with the default profile of a 0.8 mm nozzle, it has a much bigger layer height, and results in extremely apparent layer lines and much lower printing quality, but much shorter printing time in some printing cases.", "elefant_foot_compensation": "0.15", "compatible_printers": [ - "Snapmaker U1 (0.8 nozzle)", - "Snapmaker U1 (0.8 High Flow nozzle)" + "Snapmaker U1 (0.8 nozzle)" ], "filter_out_gap_fill": "1", "gap_fill_target": "topbottom", @@ -24,77 +22,8 @@ "enable_arc_fitting": "0", "ooze_prevention": "1", "precise_outer_wall": "1", - "slowdown_for_curled_perimeters": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ], + "slowdown_for_curled_perimeters": "0", "standby_temperature_delta": "-150", "support_threshold_angle": "35", - "support_type": "tree(auto)", - "print_extruder_variant": [ - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow", - "Direct Drive Standard", - "Direct Drive High Flow" - ], - "print_extruder_id": [ - "1", - "1", - "2", - "2", - "3", - "3", - "4", - "4" - ], - "sparse_infill_speed": [ - "100", - "600", - "100", - "600", - "100", - "600", - "100", - "600" - ], - "outer_wall_speed": [ - "120", - "500", - "120", - "500", - "120", - "500", - "120", - "500" - ], - "inner_wall_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ], - "internal_solid_infill_speed": [ - "150", - "600", - "150", - "600", - "150", - "600", - "150", - "600" - ] + "support_type": "tree(auto)" } diff --git a/resources/profiles/Snapmaker/process/fdm_process_U1.json b/resources/profiles/Snapmaker/process/fdm_process_U1.json index c05db3013a..4e0d7bf6e6 100644 --- a/resources/profiles/Snapmaker/process/fdm_process_U1.json +++ b/resources/profiles/Snapmaker/process/fdm_process_U1.json @@ -28,7 +28,7 @@ "interface_shells": "0", "detect_overhang_wall": "0", "reduce_infill_retraction": "0", - "filename_format": "{input_filename_base}_{filament_type[initial_no_support_extruder]}_{int(total_weight*10) / 10.0}g_{print_time}.gcode", + "filename_format": "{input_filename_base}.gcode", "wall_loops": "2", "inner_wall_line_width": "0.45", "inner_wall_speed": "40", From ffbbd62355d3be161043073f800a5f33604cdb33 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Wed, 30 Sep 2026 14:33:35 -0300 Subject: [PATCH 81/89] Clean design Docs and move context (#15803) --- deps/OCCT/OCCT.cmake | 2 +- docs/CAD/GAP_ANALYSIS_vs_ONSHAPE.md | 160 -- docs/CAD/cad_dependency_weight.md | 136 -- docs/CAD/cad_ux_guidelines.md | 704 -------- .../mate-connectors/BEAR_CONNECTOR_REVIEW.md | 169 -- .../mate-connectors/BearConnector_Cutter.step | 998 ---------- .../mate-connectors/BearConnector_Female.step | 1173 ------------ .../BearConnector_Female_Trimmed.step | 1048 ----------- .../BearConnector_Female_Trimmed.stl | Bin 74884 -> 0 bytes .../BearConnector_Female_Trimmed_wall.step | 1604 ----------------- .../BearConnector_Female_Trimmed_wall.stl | Bin 97684 -> 0 bytes .../mate-connectors/DESIGN_MATE_CONNECTORS.md | 922 ---------- .../design/mate-connectors/bear-01-view.png | Bin 100101 -> 0 bytes .../design/mate-connectors/bear-02-view.png | Bin 96449 -> 0 bytes .../design/mate-connectors/bear-02-zoom.png | Bin 276302 -> 0 bytes docs/CAD/design/mate-connectors/bear-flat.png | Bin 20664 -> 0 bytes .../design/mate-connectors/bear-sil-cmp.png | Bin 1330 -> 0 bytes docs/CAD/design/mate-connectors/bear-sil.png | Bin 1688 -> 0 bytes .../design/mate-connectors/bear_glyph_table.h | 30 - .../CAD/design/mate-connectors/bear_mesh.json | 1 - .../circularmateconnectors.png | Bin 13095 -> 0 bytes docs/CAD/design/mate-connectors/cmp-sheet.png | Bin 22841 -> 0 bytes .../connector-glyph-proposal.html | 299 --- .../design/mate-connectors/coplanar_test.py | 68 - .../cylindricalmateconnectors.png | Bin 17385 -> 0 bytes .../mate-connectors/faceted_ridge_key.scad | 140 -- docs/CAD/design/mate-connectors/fem-0.png | Bin 5985 -> 0 bytes docs/CAD/design/mate-connectors/fem-1.png | Bin 4068 -> 0 bytes docs/CAD/design/mate-connectors/fem-2.png | Bin 4274 -> 0 bytes docs/CAD/design/mate-connectors/fem-3.png | Bin 5767 -> 0 bytes docs/CAD/design/mate-connectors/fem-sheet.png | Bin 26495 -> 0 bytes .../CAD/design/mate-connectors/fem-sheet2.png | Bin 26906 -> 0 bytes docs/CAD/design/mate-connectors/female.stl | 226 --- .../design/mate-connectors/female_only.scad | 12 - docs/CAD/design/mate-connectors/fit_check.py | 20 - .../design/mate-connectors/glyph-preview.png | Bin 12890 -> 0 bytes .../design/mate-connectors/glyph_preview.py | 99 - .../CAD/design/mate-connectors/glyph_probe.py | 143 -- .../mate-connectors/handedness-sheet.png | Bin 35843 -> 0 bytes docs/CAD/design/mate-connectors/handedness.py | 112 -- .../linearedgemateconnectors.png | Bin 20301 -> 0 bytes .../CAD/design/mate-connectors/make_female.py | 135 -- .../mate-connector-virtual-sharp.png | Bin 31835 -> 0 bytes .../mateconnector-planarcentroid.png | Bin 26069 -> 0 bytes .../mateconnector-planarpoints.png | Bin 27133 -> 0 bytes .../mate-connectors/matepointiconLG.png | Bin 2368 -> 0 bytes .../matepointreorientsecondaryaxis.png | Bin 10329 -> 0 bytes .../design/mate-connectors/muzzle_variants.py | 71 - .../planarfacemateconnectors.png | Bin 5679 -> 0 bytes .../design/mate-connectors/relief-sheet.png | Bin 24238 -> 0 bytes .../design/mate-connectors/relief_sheet.py | 99 - .../CAD/design/mate-connectors/relief_test.py | 9 - docs/CAD/design/mate-connectors/render_key.py | 85 - docs/CAD/design/mate-connectors/render_stl.py | 76 - docs/CAD/design/mate-connectors/rk-0.png | Bin 4974 -> 0 bytes docs/CAD/design/mate-connectors/rk-1.png | Bin 5548 -> 0 bytes docs/CAD/design/mate-connectors/rk-2.png | Bin 4940 -> 0 bytes docs/CAD/design/mate-connectors/rk-3.png | Bin 6240 -> 0 bytes docs/CAD/design/mate-connectors/rk-4.png | Bin 4992 -> 0 bytes docs/CAD/design/mate-connectors/rk-back.png | 0 docs/CAD/design/mate-connectors/rk-front.png | 0 docs/CAD/design/mate-connectors/rk-iso.png | 0 docs/CAD/design/mate-connectors/rk-sheet.png | Bin 22160 -> 0 bytes .../design/mate-connectors/simplify-sheet.png | Bin 50634 -> 0 bytes .../design/mate-connectors/simplify_study.py | 142 -- docs/CAD/design/mate-connectors/size-test.png | Bin 9289 -> 0 bytes .../design/mate-connectors/sz-22-down+Z.png | Bin 293 -> 0 bytes .../design/mate-connectors/sz-22-grazing.png | Bin 204 -> 0 bytes docs/CAD/design/mate-connectors/sz-22-iso.png | Bin 238 -> 0 bytes .../design/mate-connectors/sz-32-down+Z.png | Bin 359 -> 0 bytes .../design/mate-connectors/sz-32-grazing.png | Bin 263 -> 0 bytes docs/CAD/design/mate-connectors/sz-32-iso.png | Bin 314 -> 0 bytes .../design/mate-connectors/sz-48-down+Z.png | Bin 502 -> 0 bytes .../design/mate-connectors/sz-48-grazing.png | Bin 344 -> 0 bytes docs/CAD/design/mate-connectors/sz-48-iso.png | Bin 464 -> 0 bytes docs/CAD/design/mate-connectors/t-+0.1-0.png | Bin 5951 -> 0 bytes docs/CAD/design/mate-connectors/t-+0.1-1.png | Bin 4786 -> 0 bytes docs/CAD/design/mate-connectors/t-+1.6-0.png | Bin 7205 -> 0 bytes docs/CAD/design/mate-connectors/t-+1.6-1.png | Bin 5871 -> 0 bytes docs/CAD/design/mate-connectors/trim-cmp.png | Bin 19567 -> 0 bytes .../CAD/design/mate-connectors/trim_female.py | 74 - .../design/mate-connectors/verify_trimmed.py | 25 - docs/CAD/design_tab.md | 333 ---- docs/CAD/design_tab_pr_description.md | 73 - docs/CAD/design_tab_upstream_portability.md | 162 -- docs/CAD/ux/interaction-model.md | 144 -- docs/CAD/ux/mockups/cmp_12__edge_str.svg | 1 - docs/CAD/ux/mockups/cmp_12__face_planar.svg | 1 - docs/CAD/ux/mockups/cmp_12__sk_line.svg | 1 - docs/CAD/ux/mockups/cmp_8__edge_str.svg | 1 - docs/CAD/ux/mockups/cmp_8__face_planar.svg | 1 - docs/CAD/ux/mockups/cmp_8__sk_line.svg | 1 - docs/CAD/ux/mockups/fresh__art.svg | 1 - docs/CAD/ux/mockups/fresh__art__modify.svg | 1 - docs/CAD/ux/mockups/fresh__bodies_2.svg | 1 - docs/CAD/ux/mockups/fresh__body_sheet.svg | 1 - .../ux/mockups/fresh__body_sheet__modify.svg | 1 - docs/CAD/ux/mockups/fresh__body_solid.svg | 1 - .../ux/mockups/fresh__body_solid__modify.svg | 1 - docs/CAD/ux/mockups/fresh__coordsys.svg | 1 - .../ux/mockups/fresh__coordsys__modify.svg | 1 - docs/CAD/ux/mockups/fresh__datum_axis.svg | 1 - .../ux/mockups/fresh__datum_axis__modify.svg | 1 - docs/CAD/ux/mockups/fresh__datum_plane.svg | 1 - .../ux/mockups/fresh__datum_plane__modify.svg | 1 - .../mockups/fresh__datum_plane__reference.svg | 1 - docs/CAD/ux/mockups/fresh__edge_circ.svg | 1 - docs/CAD/ux/mockups/fresh__edge_str.svg | 1 - .../ux/mockups/fresh__edge_str__reference.svg | 1 - docs/CAD/ux/mockups/fresh__face_cyl.svg | 1 - .../ux/mockups/fresh__face_cyl__reference.svg | 1 - docs/CAD/ux/mockups/fresh__face_other.svg | 1 - docs/CAD/ux/mockups/fresh__face_planar.svg | 1 - .../mockups/fresh__face_planar__reference.svg | 1 - docs/CAD/ux/mockups/fresh__none.svg | 1 - .../CAD/ux/mockups/fresh__none__reference.svg | 1 - docs/CAD/ux/mockups/fresh__sk_2ent.svg | 1 - .../CAD/ux/mockups/fresh__sk_2ent__create.svg | 1 - .../ux/mockups/fresh__sk_2ent__dressup.svg | 1 - .../CAD/ux/mockups/fresh__sk_2ent__modify.svg | 1 - .../ux/mockups/fresh__sk_2ent__reference.svg | 1 - docs/CAD/ux/mockups/fresh__sk_arc.svg | 1 - docs/CAD/ux/mockups/fresh__sk_arc__create.svg | 1 - docs/CAD/ux/mockups/fresh__sk_arc__modify.svg | 1 - .../ux/mockups/fresh__sk_arc__reference.svg | 1 - docs/CAD/ux/mockups/fresh__sk_line.svg | 1 - .../CAD/ux/mockups/fresh__sk_line__create.svg | 1 - .../ux/mockups/fresh__sk_line__dressup.svg | 1 - .../CAD/ux/mockups/fresh__sk_line__modify.svg | 1 - .../ux/mockups/fresh__sk_line__reference.svg | 1 - docs/CAD/ux/mockups/fresh__sk_loop.svg | 1 - docs/CAD/ux/mockups/fresh__sk_loop__add.svg | 1 - .../CAD/ux/mockups/fresh__sk_loop__modify.svg | 1 - docs/CAD/ux/mockups/fresh__sk_none.svg | 1 - .../CAD/ux/mockups/fresh__sk_none__create.svg | 1 - .../ux/mockups/fresh__sk_none__reference.svg | 1 - docs/CAD/ux/mockups/fresh__sk_point.svg | 1 - .../ux/mockups/fresh__sk_point__create.svg | 1 - .../ux/mockups/fresh__sk_point__reference.svg | 1 - docs/CAD/ux/mockups/fresh__vertex.svg | 1 - .../ux/mockups/fresh__vertex__reference.svg | 1 - docs/CAD/ux/mockups/gen_offer_mockups.py | 884 --------- docs/CAD/ux/mockups/list__body_solid.svg | 1 - docs/CAD/ux/mockups/list__edge_str.svg | 1 - docs/CAD/ux/mockups/list__face_planar.svg | 1 - .../CAD/ux/mockups/list__face_planar__add.svg | 1 - docs/CAD/ux/mockups/list__none.svg | 1 - docs/CAD/ux/mockups/list__sk_line.svg | 1 - docs/CAD/ux/mockups/list__sk_none__create.svg | 1 - docs/CAD/ux/mockups/rich__art.svg | 1 - docs/CAD/ux/mockups/rich__art__modify.svg | 1 - docs/CAD/ux/mockups/rich__bodies_2.svg | 1 - .../ux/mockups/rich__bodies_2__reference.svg | 1 - docs/CAD/ux/mockups/rich__body_sheet.svg | 1 - .../ux/mockups/rich__body_sheet__modify.svg | 1 - docs/CAD/ux/mockups/rich__body_solid.svg | 1 - .../ux/mockups/rich__body_solid__dressup.svg | 1 - .../ux/mockups/rich__body_solid__modify.svg | 1 - .../mockups/rich__body_solid__reference.svg | 1 - .../ux/mockups/rich__body_solid__remove.svg | 1 - .../ux/mockups/rich__body_solid__repeat.svg | 1 - docs/CAD/ux/mockups/rich__coordsys.svg | 1 - .../CAD/ux/mockups/rich__coordsys__modify.svg | 1 - docs/CAD/ux/mockups/rich__datum_axis.svg | 1 - .../ux/mockups/rich__datum_axis__modify.svg | 1 - docs/CAD/ux/mockups/rich__datum_plane.svg | 1 - .../ux/mockups/rich__datum_plane__modify.svg | 1 - .../mockups/rich__datum_plane__reference.svg | 1 - .../ux/mockups/rich__datum_plane__remove.svg | 1 - docs/CAD/ux/mockups/rich__edge_circ.svg | 1 - .../ux/mockups/rich__edge_circ__dressup.svg | 1 - docs/CAD/ux/mockups/rich__edge_str.svg | 1 - .../ux/mockups/rich__edge_str__dressup.svg | 1 - .../ux/mockups/rich__edge_str__reference.svg | 1 - docs/CAD/ux/mockups/rich__face_cyl.svg | 1 - .../CAD/ux/mockups/rich__face_cyl__modify.svg | 1 - .../ux/mockups/rich__face_cyl__reference.svg | 1 - docs/CAD/ux/mockups/rich__face_other.svg | 1 - .../ux/mockups/rich__face_other__modify.svg | 1 - docs/CAD/ux/mockups/rich__face_planar.svg | 1 - .../CAD/ux/mockups/rich__face_planar__add.svg | 1 - .../ux/mockups/rich__face_planar__dressup.svg | 1 - .../ux/mockups/rich__face_planar__modify.svg | 1 - .../mockups/rich__face_planar__reference.svg | 1 - .../ux/mockups/rich__face_planar__remove.svg | 1 - .../mockups/rich__face_planar__transform.svg | 1 - docs/CAD/ux/mockups/rich__none.svg | 1 - docs/CAD/ux/mockups/rich__none__reference.svg | 1 - docs/CAD/ux/mockups/rich__sk_2ent.svg | 1 - docs/CAD/ux/mockups/rich__sk_2ent__create.svg | 1 - .../CAD/ux/mockups/rich__sk_2ent__dressup.svg | 1 - docs/CAD/ux/mockups/rich__sk_2ent__modify.svg | 1 - .../ux/mockups/rich__sk_2ent__reference.svg | 1 - docs/CAD/ux/mockups/rich__sk_arc.svg | 1 - docs/CAD/ux/mockups/rich__sk_arc__create.svg | 1 - docs/CAD/ux/mockups/rich__sk_arc__modify.svg | 1 - .../ux/mockups/rich__sk_arc__reference.svg | 1 - docs/CAD/ux/mockups/rich__sk_line.svg | 1 - docs/CAD/ux/mockups/rich__sk_line__create.svg | 1 - .../CAD/ux/mockups/rich__sk_line__dressup.svg | 1 - docs/CAD/ux/mockups/rich__sk_line__modify.svg | 1 - .../ux/mockups/rich__sk_line__reference.svg | 1 - docs/CAD/ux/mockups/rich__sk_loop.svg | 1 - docs/CAD/ux/mockups/rich__sk_loop__add.svg | 1 - docs/CAD/ux/mockups/rich__sk_loop__modify.svg | 1 - docs/CAD/ux/mockups/rich__sk_none.svg | 1 - docs/CAD/ux/mockups/rich__sk_none__create.svg | 1 - .../ux/mockups/rich__sk_none__reference.svg | 1 - docs/CAD/ux/mockups/rich__sk_point.svg | 1 - .../CAD/ux/mockups/rich__sk_point__create.svg | 1 - .../ux/mockups/rich__sk_point__reference.svg | 1 - docs/CAD/ux/mockups/rich__vertex.svg | 1 - .../ux/mockups/rich__vertex__reference.svg | 1 - docs/CAD/ux/offer_atlas.html | 78 - docs/CAD/ux/offer_atlas_inline.html | 77 - docs/HLSD/design-tab.md | 213 +++ scripts/CAD/README.md | 17 +- scripts/CAD/build-gui-incremental.sh | 2 +- scripts/CAD/build-gui.sh | 4 +- .../CAD}/gen_offer_table.py | 52 +- .../CAD/mate-glyph}/bear.step | 0 .../CAD/mate-glyph}/bear_outline.json | 0 .../CAD/mate-glyph}/emit_glyph_table.py | 0 .../CAD/mate-glyph}/extract_outline.py | 0 {docs => scripts}/CAD/rig_build_traps.md | 0 scripts/CAD/run-all-checks.sh | 7 +- scripts/CAD/run-kernel-tests.sh | 2 +- {docs/CAD/ux => scripts/CAD}/tool_atlas.json | 6 +- src/slic3r/GUI/CAD/DesignOffer.hpp | 2 +- src/slic3r/GUI/CAD/DesignPanel.cpp | 4 +- src/slic3r/GUI/CAD/DesignSketchTool.cpp | 4 +- 231 files changed, 281 insertions(+), 10691 deletions(-) delete mode 100644 docs/CAD/GAP_ANALYSIS_vs_ONSHAPE.md delete mode 100644 docs/CAD/cad_dependency_weight.md delete mode 100644 docs/CAD/cad_ux_guidelines.md delete mode 100644 docs/CAD/design/mate-connectors/BEAR_CONNECTOR_REVIEW.md delete mode 100644 docs/CAD/design/mate-connectors/BearConnector_Cutter.step delete mode 100644 docs/CAD/design/mate-connectors/BearConnector_Female.step delete mode 100644 docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.step delete mode 100644 docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.stl delete mode 100644 docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed_wall.step delete mode 100644 docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed_wall.stl delete mode 100644 docs/CAD/design/mate-connectors/DESIGN_MATE_CONNECTORS.md delete mode 100644 docs/CAD/design/mate-connectors/bear-01-view.png delete mode 100644 docs/CAD/design/mate-connectors/bear-02-view.png delete mode 100644 docs/CAD/design/mate-connectors/bear-02-zoom.png delete mode 100644 docs/CAD/design/mate-connectors/bear-flat.png delete mode 100644 docs/CAD/design/mate-connectors/bear-sil-cmp.png delete mode 100644 docs/CAD/design/mate-connectors/bear-sil.png delete mode 100644 docs/CAD/design/mate-connectors/bear_glyph_table.h delete mode 100644 docs/CAD/design/mate-connectors/bear_mesh.json delete mode 100644 docs/CAD/design/mate-connectors/circularmateconnectors.png delete mode 100644 docs/CAD/design/mate-connectors/cmp-sheet.png delete mode 100644 docs/CAD/design/mate-connectors/connector-glyph-proposal.html delete mode 100644 docs/CAD/design/mate-connectors/coplanar_test.py delete mode 100644 docs/CAD/design/mate-connectors/cylindricalmateconnectors.png delete mode 100644 docs/CAD/design/mate-connectors/faceted_ridge_key.scad delete mode 100644 docs/CAD/design/mate-connectors/fem-0.png delete mode 100644 docs/CAD/design/mate-connectors/fem-1.png delete mode 100644 docs/CAD/design/mate-connectors/fem-2.png delete mode 100644 docs/CAD/design/mate-connectors/fem-3.png delete mode 100644 docs/CAD/design/mate-connectors/fem-sheet.png delete mode 100644 docs/CAD/design/mate-connectors/fem-sheet2.png delete mode 100644 docs/CAD/design/mate-connectors/female.stl delete mode 100644 docs/CAD/design/mate-connectors/female_only.scad delete mode 100644 docs/CAD/design/mate-connectors/fit_check.py delete mode 100644 docs/CAD/design/mate-connectors/glyph-preview.png delete mode 100644 docs/CAD/design/mate-connectors/glyph_preview.py delete mode 100644 docs/CAD/design/mate-connectors/glyph_probe.py delete mode 100644 docs/CAD/design/mate-connectors/handedness-sheet.png delete mode 100644 docs/CAD/design/mate-connectors/handedness.py delete mode 100644 docs/CAD/design/mate-connectors/linearedgemateconnectors.png delete mode 100644 docs/CAD/design/mate-connectors/make_female.py delete mode 100644 docs/CAD/design/mate-connectors/mate-connector-virtual-sharp.png delete mode 100644 docs/CAD/design/mate-connectors/mateconnector-planarcentroid.png delete mode 100644 docs/CAD/design/mate-connectors/mateconnector-planarpoints.png delete mode 100644 docs/CAD/design/mate-connectors/matepointiconLG.png delete mode 100644 docs/CAD/design/mate-connectors/matepointreorientsecondaryaxis.png delete mode 100644 docs/CAD/design/mate-connectors/muzzle_variants.py delete mode 100644 docs/CAD/design/mate-connectors/planarfacemateconnectors.png delete mode 100644 docs/CAD/design/mate-connectors/relief-sheet.png delete mode 100644 docs/CAD/design/mate-connectors/relief_sheet.py delete mode 100644 docs/CAD/design/mate-connectors/relief_test.py delete mode 100644 docs/CAD/design/mate-connectors/render_key.py delete mode 100644 docs/CAD/design/mate-connectors/render_stl.py delete mode 100644 docs/CAD/design/mate-connectors/rk-0.png delete mode 100644 docs/CAD/design/mate-connectors/rk-1.png delete mode 100644 docs/CAD/design/mate-connectors/rk-2.png delete mode 100644 docs/CAD/design/mate-connectors/rk-3.png delete mode 100644 docs/CAD/design/mate-connectors/rk-4.png delete mode 100644 docs/CAD/design/mate-connectors/rk-back.png delete mode 100644 docs/CAD/design/mate-connectors/rk-front.png delete mode 100644 docs/CAD/design/mate-connectors/rk-iso.png delete mode 100644 docs/CAD/design/mate-connectors/rk-sheet.png delete mode 100644 docs/CAD/design/mate-connectors/simplify-sheet.png delete mode 100644 docs/CAD/design/mate-connectors/simplify_study.py delete mode 100644 docs/CAD/design/mate-connectors/size-test.png delete mode 100644 docs/CAD/design/mate-connectors/sz-22-down+Z.png delete mode 100644 docs/CAD/design/mate-connectors/sz-22-grazing.png delete mode 100644 docs/CAD/design/mate-connectors/sz-22-iso.png delete mode 100644 docs/CAD/design/mate-connectors/sz-32-down+Z.png delete mode 100644 docs/CAD/design/mate-connectors/sz-32-grazing.png delete mode 100644 docs/CAD/design/mate-connectors/sz-32-iso.png delete mode 100644 docs/CAD/design/mate-connectors/sz-48-down+Z.png delete mode 100644 docs/CAD/design/mate-connectors/sz-48-grazing.png delete mode 100644 docs/CAD/design/mate-connectors/sz-48-iso.png delete mode 100644 docs/CAD/design/mate-connectors/t-+0.1-0.png delete mode 100644 docs/CAD/design/mate-connectors/t-+0.1-1.png delete mode 100644 docs/CAD/design/mate-connectors/t-+1.6-0.png delete mode 100644 docs/CAD/design/mate-connectors/t-+1.6-1.png delete mode 100644 docs/CAD/design/mate-connectors/trim-cmp.png delete mode 100644 docs/CAD/design/mate-connectors/trim_female.py delete mode 100644 docs/CAD/design/mate-connectors/verify_trimmed.py delete mode 100644 docs/CAD/design_tab.md delete mode 100644 docs/CAD/design_tab_pr_description.md delete mode 100644 docs/CAD/design_tab_upstream_portability.md delete mode 100644 docs/CAD/ux/interaction-model.md delete mode 100644 docs/CAD/ux/mockups/cmp_12__edge_str.svg delete mode 100644 docs/CAD/ux/mockups/cmp_12__face_planar.svg delete mode 100644 docs/CAD/ux/mockups/cmp_12__sk_line.svg delete mode 100644 docs/CAD/ux/mockups/cmp_8__edge_str.svg delete mode 100644 docs/CAD/ux/mockups/cmp_8__face_planar.svg delete mode 100644 docs/CAD/ux/mockups/cmp_8__sk_line.svg delete mode 100644 docs/CAD/ux/mockups/fresh__art.svg delete mode 100644 docs/CAD/ux/mockups/fresh__art__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__bodies_2.svg delete mode 100644 docs/CAD/ux/mockups/fresh__body_sheet.svg delete mode 100644 docs/CAD/ux/mockups/fresh__body_sheet__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__body_solid.svg delete mode 100644 docs/CAD/ux/mockups/fresh__body_solid__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__coordsys.svg delete mode 100644 docs/CAD/ux/mockups/fresh__coordsys__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__datum_axis.svg delete mode 100644 docs/CAD/ux/mockups/fresh__datum_axis__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__datum_plane.svg delete mode 100644 docs/CAD/ux/mockups/fresh__datum_plane__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__datum_plane__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__edge_circ.svg delete mode 100644 docs/CAD/ux/mockups/fresh__edge_str.svg delete mode 100644 docs/CAD/ux/mockups/fresh__edge_str__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__face_cyl.svg delete mode 100644 docs/CAD/ux/mockups/fresh__face_cyl__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__face_other.svg delete mode 100644 docs/CAD/ux/mockups/fresh__face_planar.svg delete mode 100644 docs/CAD/ux/mockups/fresh__face_planar__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__none.svg delete mode 100644 docs/CAD/ux/mockups/fresh__none__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_2ent.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_2ent__create.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_2ent__dressup.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_2ent__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_2ent__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_arc.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_arc__create.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_arc__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_arc__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_line.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_line__create.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_line__dressup.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_line__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_line__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_loop.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_loop__add.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_loop__modify.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_none.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_none__create.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_none__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_point.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_point__create.svg delete mode 100644 docs/CAD/ux/mockups/fresh__sk_point__reference.svg delete mode 100644 docs/CAD/ux/mockups/fresh__vertex.svg delete mode 100644 docs/CAD/ux/mockups/fresh__vertex__reference.svg delete mode 100644 docs/CAD/ux/mockups/gen_offer_mockups.py delete mode 100644 docs/CAD/ux/mockups/list__body_solid.svg delete mode 100644 docs/CAD/ux/mockups/list__edge_str.svg delete mode 100644 docs/CAD/ux/mockups/list__face_planar.svg delete mode 100644 docs/CAD/ux/mockups/list__face_planar__add.svg delete mode 100644 docs/CAD/ux/mockups/list__none.svg delete mode 100644 docs/CAD/ux/mockups/list__sk_line.svg delete mode 100644 docs/CAD/ux/mockups/list__sk_none__create.svg delete mode 100644 docs/CAD/ux/mockups/rich__art.svg delete mode 100644 docs/CAD/ux/mockups/rich__art__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__bodies_2.svg delete mode 100644 docs/CAD/ux/mockups/rich__bodies_2__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_sheet.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_sheet__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_solid.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_solid__dressup.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_solid__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_solid__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_solid__remove.svg delete mode 100644 docs/CAD/ux/mockups/rich__body_solid__repeat.svg delete mode 100644 docs/CAD/ux/mockups/rich__coordsys.svg delete mode 100644 docs/CAD/ux/mockups/rich__coordsys__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__datum_axis.svg delete mode 100644 docs/CAD/ux/mockups/rich__datum_axis__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__datum_plane.svg delete mode 100644 docs/CAD/ux/mockups/rich__datum_plane__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__datum_plane__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__datum_plane__remove.svg delete mode 100644 docs/CAD/ux/mockups/rich__edge_circ.svg delete mode 100644 docs/CAD/ux/mockups/rich__edge_circ__dressup.svg delete mode 100644 docs/CAD/ux/mockups/rich__edge_str.svg delete mode 100644 docs/CAD/ux/mockups/rich__edge_str__dressup.svg delete mode 100644 docs/CAD/ux/mockups/rich__edge_str__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_cyl.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_cyl__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_cyl__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_other.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_other__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar__add.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar__dressup.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar__remove.svg delete mode 100644 docs/CAD/ux/mockups/rich__face_planar__transform.svg delete mode 100644 docs/CAD/ux/mockups/rich__none.svg delete mode 100644 docs/CAD/ux/mockups/rich__none__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_2ent.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_2ent__create.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_2ent__dressup.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_2ent__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_2ent__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_arc.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_arc__create.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_arc__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_arc__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_line.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_line__create.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_line__dressup.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_line__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_line__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_loop.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_loop__add.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_loop__modify.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_none.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_none__create.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_none__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_point.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_point__create.svg delete mode 100644 docs/CAD/ux/mockups/rich__sk_point__reference.svg delete mode 100644 docs/CAD/ux/mockups/rich__vertex.svg delete mode 100644 docs/CAD/ux/mockups/rich__vertex__reference.svg delete mode 100644 docs/CAD/ux/offer_atlas.html delete mode 100644 docs/CAD/ux/offer_atlas_inline.html create mode 100644 docs/HLSD/design-tab.md rename {docs/CAD/ux/mockups => scripts/CAD}/gen_offer_table.py (80%) rename {docs/CAD/design/mate-connectors => scripts/CAD/mate-glyph}/bear.step (100%) rename {docs/CAD/design/mate-connectors => scripts/CAD/mate-glyph}/bear_outline.json (100%) rename {docs/CAD/design/mate-connectors => scripts/CAD/mate-glyph}/emit_glyph_table.py (100%) rename {docs/CAD/design/mate-connectors => scripts/CAD/mate-glyph}/extract_outline.py (100%) rename {docs => scripts}/CAD/rig_build_traps.md (100%) rename {docs/CAD/ux => scripts/CAD}/tool_atlas.json (99%) diff --git a/deps/OCCT/OCCT.cmake b/deps/OCCT/OCCT.cmake index 45263d6ae3..e1a65beef7 100644 --- a/deps/OCCT/OCCT.cmake +++ b/deps/OCCT/OCCT.cmake @@ -24,7 +24,7 @@ endif() # On macOS/Linux OCCT links statically, so an unreferenced toolkit costs build time and no # shipped bytes. The Windows figure is a real DLL cost and has NOT been measured -- an # earlier "3.77 MiB, Windows only" note here covered only two of the three toolkits and is -# not a number to quote. See docs/cad_dependency_weight.md. +# not a number to quote. See docs/HLSD/design-tab.md. if (IN_GIT_REPO) set(OCCT_DIRECTORY_FLAG --directory ${BINARY_DIR_REL}/dep_OCCT-prefix/src/dep_OCCT) diff --git a/docs/CAD/GAP_ANALYSIS_vs_ONSHAPE.md b/docs/CAD/GAP_ANALYSIS_vs_ONSHAPE.md deleted file mode 100644 index bf603cf2c1..0000000000 --- a/docs/CAD/GAP_ANALYSIS_vs_ONSHAPE.md +++ /dev/null @@ -1,160 +0,0 @@ -# Orca-CAD vs Onshape — capability gap analysis - -Generated 2026-07-22 by enumerating the source, not from recollection: -`CadFeatureType` and `add_*` in `src/libslic3r/CAD/CadDocument.hpp`, `Tool` in -`src/slic3r/GUI/CAD/DesignPanel.hpp`, `Mode` in `src/slic3r/GUI/CAD/DesignSketchTool.hpp`, -`SketchConstraintType` + `SketchEntity::Type` in `src/libslic3r/CAD/SketchEngine.hpp`, -and the JSON-RPC dispatch in `src/slic3r/GUI/CAD/McpControl.cpp`. - -**Scope note.** Onshape is a cloud PLM platform; Orca is a Design tab inside a -slicer. A large share of Onshape's surface (release management, branching, real-time -collaboration, FEA, rendering, PDM) is out of scope by construction and is listed -separately at the bottom rather than counted as a "missing tool". - ---- - -## 1. What Orca already has - -### 2D sketcher — near parity with Onshape -This is the strongest area. Very little is missing. - -| Category | Orca | -|---|---| -| Entities | Line, Polyline, Arc (3-point / tangent / center), Circle (center / 2-point / 3-point), Point, Ellipse, Elliptical arc, B-spline | -| Shapes | Rectangle (corner / center / oblique / rounded), Slot, Arc-slot, Polygon | -| Edit ops | Fillet, Chamfer, Offset, Mirror, Trim, Extend | -| Transforms | Move, Rotate, Scale, Linear array, Polar array | -| Constraints (19) | Fix, Coincident, Horizontal, Vertical, Distance, LockX, LockY, EqualLength, Parallel, Perpendicular, Concentric, Tangent, Midpoint, Symmetric, Angle, Radius, Diameter, PointOnLine, PointOnObject | -| Dimensions | Length, Diameter, Radius, Angle, Distance, Distance-to-line | - -Solver: vendored SolveSpace (`libslvs`, GPL-3.0) — the same solver lineage as a -commercial-grade sketcher. - -### Part features - -| Present | Notes | -|---|---| -| Extrude | + up-to-face / up-to-point, taper, flip | -| Revolve | angle-arc gizmo | -| Sweep | along a path | -| Loft | multi-profile | -| Fillet / Chamfer | edge-level | -| Draft | face taper | -| Shell | wall thickness + open face | -| Hole / Thread | face-aware placement | -| Pattern | linear + circular | -| Boolean | New / Add / Cut / Intersect, with face-mating | -| Cut | plane-based, signed offset | -| Datum plane | offset / 2-face / 2-edge derived | -| Import | STEP (B-rep) + mesh→B-rep (native mesh2step port) | -| Export | STEP (native B-rep, not tessellated) | -| Multi-body | + per-body colour | -| Section view | with flip | -| Undo/redo | full feature-tree recompute | -| 3MF persistence | parametric recipe survives save/load | - -### Automation -9 MCP JSON-RPC methods: `describe_tools`, `describe_scene`, `query_topology`, -`measure`, `slice_body`, `import_step`, `import_mesh`, `validate_against`, plus -build actions `extrude`, `revolve`, `fillet`, `chamfer`, `hole`, `boolean`, `pattern`. -Onshape's equivalent is its REST API + FeatureScript. - ---- - -## 2. Missing tools — ranked by impact - -### Tier 1 — structural absences (whole subsystems) - -**1. Assemblies and mates.** Entirely absent. No assembly document, no mate -connectors, no fastened / revolute / slider / cylindrical / planar / ball / pin-slot -mates, no assembly patterns, no interference detection, no exploded views. -`bool_target_face` / `bool_tool_face` do face-to-face *mating* for a boolean, which -is geometric alignment, not a kinematic joint. -*Impact:* multi-part products cannot be positioned or validated as a mechanism. -*Note:* an MCP-side `align_instance_to_face` / `create_*_mate` vocabulary already -exists on the Onshape bridge in this workspace, so the target semantics are known. - -**2. Drawings / 2D documentation.** Absent. No drawing sheets, dimensioned views, -section/detail views, GD&T, title blocks, or BOM. -*Impact:* nothing manufacturable-by-a-third-party leaves the tool. For 3D printing -this matters less than for machining, which is the honest reason it is Tier 1 by -CAD convention but arguably Tier 3 for this product. - -**3. Variables, equations, configurations.** Absent — no `add_variable`, no -expression evaluation, no configuration table. Every dimension is a literal double. -*Impact:* this is the biggest *parametric* gap. "Make this bracket for an M4 vs M5 -bolt" requires re-editing every dependent feature by hand. Onshape's Variable -Studio + configurations are a core differentiator, and this is the cheapest Tier 1 -item to close for the size of the payoff. - -**4. Surface modelling.** Absent. No surface extrude/revolve/loft/sweep, no fill, -knit, trim/extend surface, offset surface, or thicken. Orca is solid-only. -*Impact:* organic/complex shapes and repair of imported junk geometry are impossible. -OCCT already provides all of it (`TKOffset`, `TKBRep`), so the kernel is not the -blocker — only UI and feature plumbing. - -**5. Sheet metal.** Absent. No flange, bend, tab, relief, or flat-pattern unfold. -*Impact:* arguably out of scope for an FDM slicer; listed for completeness. - -### Tier 2 — individual features with clear demand - -| Missing | Why it matters | Cheap? | -|---|---|---| -| **Mirror body** (part-level) | Sketch mirror exists; mirroring a *solid* about a plane does not. Extremely common. | Yes — OCCT `gp_Trsf` mirror + fuse | -| **Helix / spiral curve** | No helix ⇒ no springs, no custom threads, no spiral vase geometry. Sweep exists but has no helical path to sweep along. | Yes | -| **Move / rotate body as a real feature** | `m_body_xform` exists but is **display-only** (memory #1655) — it never enters the B-rep. Export/boolean see the original position. | Medium | -| **Split body** | Cut removes material; splitting one body into two independently-usable bodies is absent. Very relevant for print-in-parts. | Medium | -| **Thicken** | Solid from a surface/face offset. | Needs surfaces | -| **Rib** | Standard structural feature. | Medium | -| **Delete face / move face / replace face** | Direct/dumb-solid editing — the main tool for fixing imported STEP. Given Orca imports STEP *and* meshes, its absence is felt. | Medium | -| **Datum axis, coordinate system** | Only datum *planes* exist. Axes are needed for revolve/pattern references. | Yes | -| **Mass properties** | `GeometryEngine` computes a volume internally, but there is no volume/mass/COM/inertia readout. For print cost/time estimation this is nearly free to expose. | Yes — trivial | -| **Measure tool in the GUI** | `measure` exists over MCP but there is no interactive measure in the UI. | Yes | -| **Hole standards library** | Hole exists, but no counterbore/countersink/tapped standards (ISO/ANSI) with callouts. | Medium | -| **Project / convert edges into a sketch** | Cannot reference existing solid edges as sketch geometry ("Use" in SolidWorks). A significant sketcher gap given everything else is present. | Medium | -| **Construction geometry** | Could not confirm a construction/reference-line flag on sketch entities. | Yes if absent | -| **Curve tools** | Projected curve, bridging curve, composite curve, 3D fit spline. | Medium | -| **Pattern on curve / pattern faces** | Pattern is linear + circular of whole bodies only; no curve-driven pattern, no feature/face pattern. | Medium | -| **Wrap / emboss** | Text or sketch wrapped onto a curved face. | Hard | -| **Enclose** | Solid from bounded void regions. | Medium | - -### Tier 3 — platform capabilities (out of scope by construction) - -Version control with branching/merging, release management, real-time multi-user -collaboration, cloud PDM, FeatureScript custom-feature authoring, simulation/FEA, -photorealistic rendering, app store/integrations. These are Onshape-the-platform, -not Onshape-the-modeller. Not defects in Orca. - ---- - -## 3. Recommended priority - -If the goal is "credible parametric CAD inside a slicer", the ordering that buys -the most capability per unit of work: - -1. **Variables + expressions** — unlocks genuine parametric reuse; no new kernel work. -2. **Mass properties + GUI measure** — nearly free, immediately useful for printing. -3. **Mirror body, datum axis, helix** — small, self-contained, high-frequency features. -4. **Promote move/rotate body from display-only to a real B-rep feature** — closes a - correctness gap, not just a missing tool (exports currently disagree with the view). -5. **Split body** — high value for print-in-parts workflows. -6. **Project edges into sketch** — the sketcher's most conspicuous hole. -7. **Surface modelling** — large, but OCCT already ships the algorithms. -8. **Assemblies** — largest effort; only worth it if Orca targets multi-part products. - -Deliberately last: drawings and sheet metal — high cost, low relevance to an -FDM-oriented tool. - ---- - -## 4. Honest summary - -Orca's **sketcher is at or near Onshape parity**, and its **solid feature set -covers the mainstream modelling path** (sketch → extrude/revolve/sweep/loft → -dress-up → boolean/pattern). What is absent is *breadth*: assemblies, surfaces, -sheet metal, drawings, and — most importantly for a tool calling itself parametric — -**variables and configurations**. - -The single most defensible criticism is #3: without variables, the feature tree is -parametric in *structure* but not in *value*, so the promise of "change one number -and the model updates" is only half delivered. diff --git a/docs/CAD/cad_dependency_weight.md b/docs/CAD/cad_dependency_weight.md deleted file mode 100644 index 0b64ac8097..0000000000 --- a/docs/CAD/cad_dependency_weight.md +++ /dev/null @@ -1,136 +0,0 @@ -# Dependency weight of the Design/CAD subsystem - -What the Design tab actually costs a maintainer who merges it. Written to be checkable: -every number below is reproducible with the command that produced it, and the places where -a number is still missing say so instead of guessing. - -Measured on Linux x86_64, OCCT V7_6_0, in the `snapmaker-deps` build image. - -## Summary - -| | Cost | -|---|---| -| New third-party dependencies | **none** | -| OCCT build flag | `BUILD_MODULE_ModelingAlgorithms=ON` | -| Extra OCCT toolkits *built* | 3 (TKFillet, TKOffset, TKFeat) | -| Extra OCCT toolkits *linked* | 2 (TKFillet, TKOffset) | -| Vendored code | `src/libslic3r/slvs`, 9,339 lines, 380 KiB, GPLv3 | -| Own object code | 6.79 MiB unstripped `.o` (7.13 MiB with the solver) | - -OCCT is **already** an upstream dependency — Orca uses it for STEP import. The Design tab -does not add a library; it turns on one more OCCT module. - -## The OCCT module flag - -`deps/OCCT/OCCT.cmake` gates the module on `SLIC3R_CAD`: - -```cmake --DBUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD} # was hard-coded OFF -``` - -With `SLIC3R_CAD=OFF` the deps prefix matches upstream exactly. - -`ModelingAlgorithms` contains 12 toolkits, but **most were already being built**, because -`DataExchange` — the STEP path upstream already ships — depends on them. The honest delta is -only the toolkits that DataExchange's dependency closure does *not* reach: - -``` -ModelingAlgorithms = TKGeomAlgo TKTopAlgo TKPrim TKBO TKBool TKHLR - TKFillet TKOffset TKFeat TKMesh TKXMesh TKShHealing - -already required by DataExchange: TKBO TKBool TKGeomAlgo TKHLR TKMesh - TKPrim TKShHealing TKTopAlgo -true delta: TKFeat TKFillet TKOffset TKXMesh -``` - -Reproduce by walking `adm/MODULES` and each toolkit's `src//EXTERNLIB` in the OCCT -source tree. - -### Sizes of the delta toolkits - -Static archives in the deps prefix. These are *build artifacts*, not shipped bytes — a -static link pulls in only the objects it references: - -| Toolkit | Archive | Referenced by the Design tab? | -|---|---|---| -| TKFillet | 7.40 MiB | yes — `BRepFilletAPI` | -| TKOffset | 5.38 MiB | yes — `BRepOffsetAPI`, `BRepOffset_` | -| TKFeat | 4.42 MiB | **no** | -| TKXMesh | — | not produced at all | - -TKFeat is worth calling out: nothing in the Design tab references it, and it is absent from -the `TKFillet`/`TKOffset` dependency closure, so it is built for nothing. OCCT's module flag -is all-or-nothing per module, which is why it comes along. It costs build time and zero -shipped bytes on any platform that links OCCT statically. - -**A correction to the record.** The comment in `deps/OCCT/OCCT.cmake` and the earlier -summary both said the delta was "TKFillet + TKOffset — 3.77 MiB, Windows only". The toolkit -list was incomplete: TKFeat is built too. The 3.77 MiB figure covers 2 of the 3 built -toolkits and has not been re-derived here — see the gap below. - -## What is not measured yet - -Two numbers a maintainer may reasonably ask for are **not** in this document, because -producing them honestly needs a build this machine cannot do: - -1. **Windows DLL delta.** OCCT builds shared on Windows, so the shipped cost there is real - DLL bytes rather than linker-selected objects. That needs a Windows build to size — - tracked as the cross-platform build proof (`gix`). -2. **Clean-build time delta.** Measuring it means building the deps prefix twice, with the - flag ON and OFF, on the same machine. The incremental figures from day-to-day work do not - answer the question and are not offered as if they did. - -Do not quote a number for either until it has been measured. - -## Vendored solver - -`src/libslic3r/slvs` — the 2D sketch constraint solver extracted from SolveSpace. - -- 19 files: 8 `.cpp`, 11 `.h`, plus `LICENSE` -- 9,339 lines, 380 KiB of source, 0.34 MiB of object code -- **GPLv3**, `LICENSE` preserved verbatim in the vendored directory - -The fork is **AGPLv3**. GPLv3 code combines into an AGPLv3 work without difficulty: AGPLv3 -§13 provides explicit compatibility in that direction. No licence question to resolve. - -It is live code, not a carried corpse — `SketchSolver.cpp` is its only consumer and drives -every sketch constraint in the Design tab. - -## Own code - -Object sizes from the release build (unstripped, so these include debug information and -overstate the shipped contribution): - -| Object | Size | -|---|---| -| DesignPanel.o | 2.22 MiB | -| McpControl.o | 1.69 MiB | -| DesignSketchTool.o | 0.88 MiB | -| CadDocument.o | 0.76 MiB | -| SketchEngine.o | 0.40 MiB | -| DesignCanvas.o | 0.37 MiB | -| GeometryEngine.o | 0.32 MiB | -| SketchSolver.o | 0.15 MiB | -| slvs (all objects) | 0.34 MiB | -| **total** | **7.13 MiB** | - -For scale, the linked binary is 137.1 MiB. - -## Reproducing - -```bash -# toolkit membership and dependency closure -R= -cat $R/adm/MODULES # module -> toolkits -cat $R/src//EXTERNLIB # toolkit -> its dependencies - -# archive sizes -ls -l /lib/libTK{Fillet,Offset,Feat}.a - -# what the Design tab actually references -grep -rE 'BRepFilletAPI|BRepOffsetAPI|BRepOffset_|BRepFeat' src/libslic3r/ - -# vendored solver -wc -l src/libslic3r/slvs/*.cpp src/libslic3r/slvs/**/*.h -head -3 src/libslic3r/slvs/LICENSE -``` diff --git a/docs/CAD/cad_ux_guidelines.md b/docs/CAD/cad_ux_guidelines.md deleted file mode 100644 index d0290a1d3d..0000000000 --- a/docs/CAD/cad_ux_guidelines.md +++ /dev/null @@ -1,704 +0,0 @@ -# Orca-CAD — UX guidelines and design charter - -Status: proposed, v1. Owner: design working group. Applies to the Design tab — -the parametric CAD environment inside OrcaSlicer. - -This document is a **review instrument**, not an essay. Sections 3–9 are written -so that a reviewer can hold a pull request against them and get a yes or a no. -If a rule here cannot be failed, it is badly written and should be rewritten. - ---- - -## 1. Why this exists - -A CAD tool acquires its interface by accretion. Every feature arrives needing -"just one more field", the side panel is the cheapest place to put it, and after -forty features the product is FreeCAD: complete, respected, and abandoned by -almost everyone who opens it once. That end state is not a failure of any single -decision. It is the sum of forty locally reasonable ones taken without a written -rule to violate. - -So we write the rule down first, and we make additions argue against it. - -## 2. Product thesis - -**Orca-CAD is a modelling space for people who want a part, inside the tool that -prints it.** - -Three audiences, one interface: - -- **The fourteen-year-old on a school laptop.** Free software, on the machine - they already have, with no account, no subscription, no licence and no - tutorial. They open the tab because they want a bracket for a bike light, and - an hour later it is printing. This is not the charity case at the bottom of - the list — it is the reason the project is worth doing. A CAD tool that only - the equipped can run is a tool for people who were already going to design - something; this one has to be a creative instrument in the hands of someone - who did not yet know they could make things. Everything in §6.1 exists to - keep that door open, and nothing gets to close it for the convenience of the - other two audiences. -- **The maker** who has an idea and a printer, and who has bounced off FreeCAD. - They should be modelling something real within ten minutes of first opening - the tab, without a tutorial, without knowing the word "constraint". -- **The mechanical designer** who needs assemblies, mates, exploded views, - variables, and a feature history they can edit six months later. They should - not have to leave for SolidWorks the moment the work gets serious. - -The order matters. When a decision helps one audience and hurts another, the -earlier one wins unless there is a written argument for why not. - -The reference for *how it feels* is Shapr3D: direct, gestural, quiet, almost no -chrome, depth revealed by what you touch rather than by what is on screen. The -anti-references are Blender (a modal keyboard language you must learn before the -first success) and FreeCAD (a workbench-and-dialog architecture where the -geometry is a preview of a form you fill in elsewhere). - -We are not cloning Shapr3D's feature set. We are adopting its *interaction -economy*: the smallest number of visible controls that still makes an expert -fast. - -**And one thing neither reference has:** Orca-CAD lives inside a slicer. The -plate, the nozzle, the material and the print constraints are known to the -application at design time. Designing for print is not a plugin here, it is the -home advantage. Where a rule below trades generality for print-awareness, it -trades in favour of print-awareness. - -## 3. The laws - -Non-negotiable. A change that breaks one of these does not get merged on the -grounds that it was easier, that the alternative is more work, or that another -CAD does it that way. Each law carries a test — the question a reviewer asks. - -### L1 — Geometry first: you point, then you act - -Controls live **on the geometry**: handles, arrows, points, small circles and -boxes, with an inline label tab for typed values. Not in a side panel of combos -and spin fields. - -The canonical gesture: **select a face or plane in the viewport, then click the -sketch tool.** Never: click the sketch tool, then choose a plane from a list. -The tool consumes what you pointed at — and, better still, the thing you pointed -at offers the tool itself (§4). - -> **Test.** Can the operation be performed start to finish without the pointer -> leaving the viewport, except to press the tool itself? If a control had to be -> added to a panel to make it work, the design is not finished. - -This is the law the others serve. It was stated after two proposals in a row -reached for a dropdown, and the failure mode it names is real and recurrent: a -fix that "adds a row to the plane combo" is the side-panel pattern wearing a -different hat. - -### L2 — Everything draggable is typable, and everything typable is draggable - -Any value produced by direct manipulation (a fillet radius, an extrude depth, a -pattern spacing, a plane offset) shows a live label on the geometry, and that -label is an editable field. Any value entered numerically has a corresponding -handle in the viewport. - -Dragging is for finding the answer. Typing is for committing to it. A tool that -offers only one of the two is half a tool. - -> **Test.** Point at the number the tool produces. Can you drag it? Can you -> click it and type? Both must be yes. - -### L3 — Noun then verb, always the same way round - -Selection precedes action, without exception, across sketch tools, features, -dress-up, booleans and mates. There is no tool in the product that is armed -first and asks for its input afterwards. - -> **Test.** Does this tool work if the user has already selected the thing they -> want it applied to? Does it work *only* that way? - -### L4 — No modal dialog in the modelling loop - -Dialogs belong to document-level actions: open, save, import, export, preferences. -Modelling never opens one. A feature that needs three values gets three labels on -the geometry, not a form; a feature that needs confirming gets a ghost preview and -a confirm/cancel puck in the scene beside it (§4.2) — an object, not a window: the -camera still orbits, the values are still editable, nothing is blocked. - -> **Test.** Between starting an operation and seeing its result, does a window -> appear that must be dismissed? If yes, redesign. - -### L5 — One click, one visible change - -Every click either changes what is on screen or tells the user why it did not. -A click that opens something invisible, arms an invisible state, or requires a -second identical click to have any effect is a defect, not a design. - -This law exists because we shipped its violation twice. Sketch-tool family -buttons were flyouts whose first click only rendered a pressed state — three -separate sessions filed bugs against tools that were working. Solid picking used -a click *cycle* (first click selects the body, second refines to the face), so -sketching on a face appeared broken to anyone who clicked a face once, the way -every human does. - -> **Test.** Perform the gesture exactly once, as a first-time user would. Take a -> screenshot. Is the state visibly different, and is the difference the one the -> user intended? - -### L6 — The default is the answer four times out of five - -Every option that has a default must have the *common* answer as its default, -measured against real parts, not against generality. "New body" as the default -result of an extrude is wrong: most extrudes join. Radius as the input for a -circle is wrong: drawings give diameter. - -> **Test.** Take ten real parts. In how many is the default correct? Below eight, -> change the default or infer it from context. - -### L7 — Errors are caught before the commit, in the user's words - -A self-intersecting profile, a cut that removes no material, a wall thinner than -the nozzle: these are reported at the moment they become knowable, on the -geometry that is wrong, phrased as what happened and what to do — not as a kernel -exception after the fact, and never silently. - -> **Test.** Is the failure detectable before the user commits? Then it must be -> reported before the user commits. Read the message aloud: does it name a thing -> the user can see and an action they can take? - -### L8 — The camera is the application's job - -Selecting a sketch plane orients the view to it. Committing a feature does not -throw the camera away. Zoom-to-fit exists and is one keystroke. The user is never -required to fight the view in order to reach the geometry, and orbit is bound to -the gesture people actually try. - -> **Test.** Count camera manipulations in a representative modelling session. -> Any camera action the application could have performed for the user is a bug. - -### L9 — Accessible by construction, not by retrofit - -The floor, applied to every new interaction (details in §6.2): full keyboard -reach, no meaning carried by colour alone, hit targets that survive a shaky hand -and a HiDPI screen, legible labels over an arbitrary 3D background, no gesture -that depends on timing. - -> **Test.** Drive the whole interaction from the keyboard. Then drive it in -> greyscale. Both must work. - -### L10 — Vocabulary from the drawing office - -Names come from the language of people who make parts: fillet, chamfer, boss, -rib, counterbore, mate, exploded view. Not from the kernel (no "boolean -subtract", no "B-rep"), not from invented product-speak. Where the drawing-office -word and the beginner's word differ, use the drawing-office word and make the -tooltip teach it — an approachable tool that leaves the user unable to talk to a -machinist has failed them. - -> **Test.** Would a shop-floor engineer recognise this word? Would a first-time -> user be able to look it up and find a real definition? - -### L11 — The floor is a school laptop, and nothing is behind a door - -The product runs, completely, on a low-end laptop with integrated graphics and a -small screen, offline, with no account, no subscription and no feature withheld. -No capability in this document is reserved for a paid tier, a cloud service, a -plugin, or a machine with a discrete GPU — there is one product and everybody -gets all of it. - -> **Test.** On the reference low-end machine (§6.1), at 1366×768, with the -> network cable pulled and no account ever created: does this feature work, and -> is it usable at an honest frame rate? Any "no" is a defect, not a limitation. - -## 4. Interaction grammar — object-driven - -The rules above compose into one sentence the whole product obeys: - -> **Point at geometry → the geometry offers what can be done to it → choose the -> tool → manipulate handles and type exact values → confirm or cancel.** - -The selection does not merely feed the tool. **The selection determines which -tools exist.** Pick a planar face and the product shows you the small set of -things a planar face can become — sketch on it, extrude it, hole it, shell it, -put a datum on it. Pick an edge and that set is fillet, chamfer, and the sketch -tools that can use it as a reference. Nothing else is offered, because nothing -else is possible. - -This is the single largest thing we can do for a first-time user, and it is -worth stating as the reason: a beginner's difficulty is not operating a tool, -it is **not knowing which tools apply to what they are looking at**. A palette -of sixty icons answers a question they cannot yet ask. A face that offers its -own five verbs teaches the model of the product by using it. It also removes an -entire class of failure — a tool that silently does nothing because the -selection was wrong can no longer be reached. - -### 4.1 The offer, and the one thing that makes it work - -The flow, in full: - -> **left-click the geometry to select it → right-click to open the offer → a -> vertical list, always in the same order, each row an icon, a name and its -> keyboard shortcut → click.** - -- **Selecting and acting are separate gestures.** Left-click only ever selects, - so pointing at things is quiet — nothing pops up while you look around. - Right-click on the selection opens the offer, at the pointer, over the - geometry it acts on. -- **Order is fixed and it is the whole point.** A verb occupies one permanent - row, and that row is the same in every selection where the verb appears. - Dress-up is the fourth row on an edge, on a face, on a body, on the day the - product ships and two years later. The hand learns the position; the eye stops - being needed. -- **What does not apply is DISABLED IN PLACE, never removed.** This is the - single strongest thing the list does, and it is why it beat the radial we - drew first: a greyed row still carries its name *and the reason it is grey* — - "Create a sketch, or pick a solid face, first", "Create a solid body to - pattern first" — in the words the product already ships. On a first-run - document the offer is therefore not a mostly-empty control but a map of what - the product does and what you have to do first. -- **It is an accelerator, not a toll gate.** The toolbar and the single-letter - shortcuts keep working exactly as they do now, and pressing a tool directly - consumes the same selection (L3). An expert never has to open the offer; a - beginner never has to know the toolbar exists. Both routes land in the same - place — this is the only way one interface serves §2's three audiences. -- **Every row shows its keyboard shortcut**, right-aligned so the keys stack - into a column the eye learns without trying, beside the icon and the - drawing-office word (L10). This is deliberate: the offer is the path by which - a user stops needing the offer. You reach for fillet in its row, the row says - "F", and one day your hand types F before the menu has finished opening. A - menu that teaches its own shortcut is how a beginner becomes the power user - who never opens it — the same interface at two speeds, with no "advanced mode" - between them (§7). -- **A family with more than one applicable verb opens a submenu** to the side, - in its own fixed order. A family with exactly one shows that verb directly, so - the common path is never one click longer than it needs to be. -- **It never blocks the view of what it acts on**: it opens beside the pick, - never over it, with a thin leader back to the point it belongs to, and it - dismisses the moment the selection changes. -- **The header names what is selected** ("Top face · Body 1"), because a user - who mis-picked should find that out before choosing a verb, not after. - -#### Opening the offer on every machine - -Right-click is the primary gesture and every platform must have a first-class -equivalent — this is a reach requirement (L11), not a nicety: - -| Input | Gesture | -|---|---| -| Two-button mouse | right-click | -| Trackpad | two-finger tap (the OS-standard secondary click) | -| macOS, one-button mouse | **long-press**, and Ctrl-click, which is the platform convention | -| Keyboard | the Menu key, or Shift+F10, on the current selection | -| Touch / pen | long-press | - -The long-press is an **additional** route, never the only one — §6.2 forbids -press-and-hold as a sole path to a function, and it stays forbidden. Every -opening gesture is reachable at least two ways on every platform, and the -keyboard route exists everywhere. A long-press must show that it is charging -(a growing ring under the finger) so a user who holds too briefly learns why -nothing happened rather than concluding the product is broken (L5). - -#### The row-constancy invariant - -This is the rule that has to survive every future feature, so it is written as -an invariant rather than as advice: - -> **Every verb has exactly one row index in the offer. That index is identical -> for every selection type in which the verb appears. Verbs that do not apply to -> the current selection are DISABLED IN PLACE, with their reason — the offer is -> never compacted, re-sorted or re-ordered. Adding a verb never changes the -> index of an existing one.** - -Two consequences the group must accept together with the invariant: - -- **No adaptive ordering. Ever.** Not most-used-first, not recently-used-first, - not per-selection frequency. An offer that rearranges itself to be helpful - destroys the only thing that made it fast, and it does so precisely for the - user who has just started to learn it. (Office 2000's adaptive menus are the - textbook case; they were removed.) -- **Greyed rows are the price, and they are cheap.** A compacted menu is shorter - and unlearnable. A constant one is a few rows longer, teaches while it waits, - and is memorised in a week. - -#### The map — RATIFIED 2026-07-31 - -The invariant is not negotiable, and as of 2026-07-31 neither is the assignment: -the row order below is **ratified**. It was argued once; it is not argued again. -Changing an index from here on is a breaking change to every user's muscle -memory and needs the group, not a pull request (§9 q12). - -Eight families, ordered so the sequence itself has a logic: material is created, -grows, is taken away, is refined, is repeated, is moved, is referred to, is -edited. - -| Row | Family | On a face | On an edge | On a body | On text/art | -|---|---|---|---|---|---| -| **1** | Create | Sketch on it | — | — | Edit text | -| **2** | Add material | Extrude, thicken | — | Combine, thicken | Extrude | -| **3** | Remove | Hole, shell | Thread | Shell, cut, split | — | -| **4** | Dress-up | Draft | Fillet, chamfer | Fillet, chamfer | — | -| **5** | Repeat | Pattern | Pattern along it | Pattern, mirror | Pattern | -| **6** | Transform | Align to, mate | — | Move, mate | Move, size | -| **7** | Reference | Plane, axis, measure | Axis, measure | Project, measure, mass | — | -| **8** | Modify | Delete face, edit | — | Edit, colour, delete | Replace art | - -A dash means the row is drawn greyed for that selection, with its reason. - -The authoritative version of this table is **`docs/ux/tool_atlas.json`**, which -carries all 52 verbs with their preconditions and their refusal strings, taken -from the code rather than from memory. Every state it produces — 20 selection -kinds × 2 document states, 40 primary menus and 73 submenus — is rendered by -`docs/ux/mockups/gen_offer_mockups.py` into `docs/ux/offer_atlas.html`. Read the -atlas before proposing a change to the map; the generator refuses to render an -address collision, so the map cannot silently rot. - -#### Rejected: the radial ring - -The first design put the eight families at eight compass points around the pick. -It is recorded here because it is a good idea that loses on evidence, and -someone will propose it again: - -- an inapplicable slot could only be drawn empty, and **an empty slot says - nothing** — the reason text above has nowhere to live; -- the measured fill was **3.45 of 8 slots**, so most of the control was blank - most of the time, and on a fresh document only two of eight were live; -- sketch-mode *Create* needs **nine** addresses; eight forced two primitives - behind a "More" slot, and a ninth position costs the 45° spacing that made the - ring worth having; -- long translated names do not fit around a circle, and screen readers and arrow - keys need bespoke handling a list gets for free; -- a 380 px disc over the model costs more on a 1366×768 screen than a 324 px - list beside it (§6.1). - -What it kept — equidistant targets and a future flick gesture — buys little in a -product whose experts live on the keyboard by design. - -### 4.2 Confirm and cancel are objects, not gestures - -The old rule — click empty space to commit — is withdrawn. It was an invisible -gesture with a destructive meaning: nothing on screen said it, and a stray click -committed a feature the user was still adjusting. That is exactly what L5 -forbids, and it is hostile to the audience §6.1 exists for. - -- **A pending feature carries a confirm/cancel puck**, attached to the geometry - it is editing, next to its handles: ✓ commits, ✗ discards. Enter and Escape - mirror them for the keyboard (L9). It is drawn where the user's attention - already is, and it is the only thing in the viewport that commits. -- **Empty space now means "clear the selection"** — the safe meaning, and the - same meaning everywhere. -- **This is not a dialog** (L4). It is two objects in the scene, on the - geometry, non-modal: the camera still orbits, the tree is still there, the - values are still editable while it waits. -- **Continuous tools do not ask.** Drawing a line, a rectangle, a circle commits - each entity as its own gesture completes — a ✓ per line would destroy the - inner loop. The puck belongs to *features* (extrude, fillet, hole, pattern, - mate) and to sketch edits that hold a pending state. Enter/Escape end a - continuous tool rather than confirming an entity. -- **Ambiguity resolves toward keeping work, never toward losing it.** Starting - another operation while a valid feature is pending commits it rather than - discarding it; if it is not valid, the product says why (L7) and keeps it - pending. Since undo reaches everything (§6.1), the recoverable direction is - always the right default. - -### 4.3 The rest of the grammar - -- **The status line is one imperative sentence** naming what the tool wants - next, and it names the target when the target came from a selection - ("Circle — click centre, then radius · on the picked face"). It is the - authoritative feedback surface for the armed tool; the toolbar is not. -- **Hover previews, click commits.** A hover shows the ghost of what a click - would do wherever this is cheap to compute. -- **Selection is persistent and visible** until consumed or cleared. A tool that - consumes a selection clears it, so the next feature cannot silently inherit it. -- **Every gesture is undoable**, and the feature tree is editable history, not a - log. Re-editing a feature re-enters the same on-geometry interaction that - created it — including its offer and its puck. - -## 5. Layout and screen budget - -The viewport is the application. Chrome is a tax on it. - -- **One toolbar**, contextual to the mode (model / sketch). Tools are grouped by - what they make, not by which subsystem implements them. -- **A left rail for the document, not for parameters**: feature tree, bodies, - variables. It answers "what exists", never "what value should this be". -- **No parameter panel.** Where one exists today it is technical debt with a - scheduled removal (§10). -- **Print context is ambient**, not a panel: the plate is visible in the design - space, and print-domain warnings appear on the geometry that will fail. -- **Nothing is added to permanent chrome without removing something**, or - demonstrating that the addition is used in the majority of sessions. -- **The budget is set by the smallest screen we serve**, 1366×768 (§6.1) — not - by the reviewer's monitor. Chrome that fits a 27-inch display and swallows a - laptop's has not fitted, it has just failed somewhere the author cannot see. - -## 6. Accessibility — reach first, then the assistive floor - -"Accessible" means two different things and the product owes both. §6.1 is about -**who can get in at all**; §6.2 is about **who can operate it once inside**. -Neither is a phase. Both are merge requirements. - -### 6.1 Reach — the door has to be open - -The premise of the whole project: someone with no money, no licence, no account, -no fast machine and no teacher can open this and make a real thing. Free -software on a school laptop is the only path to a CAD tool that reaches people -who were never going to be handed one. If a design decision quietly raises the -cost of entry, it has broken the premise, however elegant it is. - -- **The reference machine.** A 5-year-old laptop: dual/quad-core CPU, - **integrated graphics**, 8 GB RAM, **1366×768** screen, no discrete GPU. The - Design tab must be usable there, and any interaction that needs more is a - design failure to be solved, not a requirement to be documented. The GPU path - degrades gracefully to software rendering rather than refusing to start; the - viewport stays interactive while the kernel thinks. -- **1366×768 is the layout target, not the stretch case.** A form-heavy side - panel is not merely inelegant on that screen — it takes the model off it. - This is the second, independent argument for the whole of L1 and §5. -- **No account, no cloud, no connection.** The product works forever with the - network unplugged. Nothing is uploaded, no sign-in gates any feature, no - telemetry is required to use it. A school network that blocks everything must - not be able to block this. -- **No tier, no plugin wall, no "pro".** Every feature named in this document is - in the product everyone downloads. Assemblies and exploded views are not the - paid half. -- **Files belong to the user**, on their disk, in a format that outlives the - project: the design travels inside the ordinary project file, and the geometry - exports to STEP and mesh formats anyone can open. -- **Learnable without instruction.** The first solid comes with no - documentation, no video and no tutorial mode — from noticing that a face can - be clicked. Tooltips teach the vocabulary (L10) at the moment it is needed; - nothing is explained in a manual the user will never open. -- **Plain language at the entry tier.** The Make tier speaks in words a - thirteen-year-old reads without stopping. Precision comes with the tier that - needs it, and everything is translated, because "accessible" in English only - is not accessible. -- **Exploration must be free.** Undo reaches everything, work is never lost to a - wrong click, and no dialog ever asks the user to be sure. A tool that punishes - experiments teaches people to stop experimenting, which is the one thing this - audience cannot afford to learn. -- **The product never blames the user.** Failures are stated as what happened - and what to do (L7). "Invalid input" is not an acceptable sentence anywhere. - -### 6.2 Assistive floor - -- **Keyboard**: every operation reachable and completable without a pointer. - Single-letter shortcuts for sketch tools, shown in the offer itself (§4.1) as - well as in the tooltip. The offer opens from the keyboard (Menu key or - Shift+F10) and walks by arrow key and by type-ahead, so the row map works for - someone who never touches the pointer. A visible focus state on every - focusable element. No shortcut that only works while the pointer happens to be - over the canvas. -- **Colour**: never the sole carrier of meaning. Selection is colour *and* - outline; an error is colour *and* an icon *and* text. Verify in greyscale. -- **Contrast**: labels over the 3D viewport get a scrim or halo so 4.5:1 holds - against any background the model can produce, including a white body under a - white plate. -- **Targets**: handles and grips no smaller than 32 px at 100 % scale, scaling - with the OS factor; the grab tolerance is larger than the drawn glyph. -- **Timing**: no double-click-to-mean-something-else, no press-and-hold as the - only route to a function, no cycle that depends on repeated clicks - (see L5). The long-press that opens the offer on a one-button Mac and on touch - (§4.1) is explicitly an *additional* route — Ctrl-click, two-finger tap and - the keyboard all reach the same place — and it shows its own progress while - charging, so it never fails silently. -- **Motion**: animation is functional (showing where a thing went), never - decorative, and it respects the reduced-motion preference. -- **Text**: no fixed-width assumptions; the UI holds together in German and in - Chinese, at 125 % and 200 % scale. Every string routed through the normal - translation path. - -## 7. Depth without clutter — the three tiers - -Power for experts is delivered by **progressive disclosure of tools, never by -relocation of tools**. A tool that appears in a later tier is in the same place -it will always be; it is simply not shown yet. - -| Tier | Who | What appears | -|---|---|---| -| **Make** | first hour | Sketch, extrude, revolve, hole, fillet/chamfer, move, commit to plate | -| **Model** | competent user | Patterns, shell, draft, sweep/loft, booleans, reference geometry, variables, import/export | -| **Mechanism** | mechanical designer | Assemblies and mates, exploded views, interference detection, surfaces, feature-level editing of imported solids | - -Rules that keep this honest: - -1. **Tiers are non-modal.** No mode switch, no workbench selector, no "advanced - mode" toggle that changes the meaning of anything. The tier only governs what - is *offered*. -2. **A tier reveals itself by use.** Using a body reveals boolean tools; adding - a second body reveals assembly tools. The product notices what you are doing. -3. **Nothing moves when a tier appears.** A user who learned where fillet lives - finds it in the same place forever. -4. **An expert tool obeys the same grammar** as a beginner tool. Mates are - picked in 3D like everything else, not configured in a table. -5. **Exploded views are a view state**, not a document mode — reversible, - draggable along mate axes, and never a separate file. - -## 8. Designing for print — the home advantage - -Design-time knowledge the application already has, and must use: - -- **The plate is present** in the design space, at the real size, with the real - origin. Committing a body to the plate is one action and preserves placement. -- **Print-domain checks run on the model, on the geometry, before slicing**: - walls thinner than the nozzle, unsupported overhangs beyond the material's - angle, features smaller than the layer height, a part that does not fit the - build volume. -- **These are warnings on the geometry, never a report.** The thin wall glows; - the tooltip says how thin and what the nozzle is. -- **Material and machine context is inherited** from the active slicer profile, - not re-entered in the Design tab. -- **The round trip is preserved**: editing a design after slicing returns to the - feature history, not to a mesh. - -## 9. The review gate - -Every pull request that touches the Design tab UI answers these, in the PR body. -A "no" that is not accompanied by an argument is a request for changes. - -1. Which law (L1–L11) does the change most directly serve? -2. Can the whole operation be completed without the pointer leaving the - viewport? If not, why is this the exception? - And: does the relevant selection *offer* this tool (§4.1), or must the user - already know it exists? -3. Are the values draggable *and* typable? -4. Screenshot of the state after **exactly one** click of the new gesture, - performed as a first-time user. -5. Keyboard-only walkthrough: does it complete? -6. Greyscale screenshot: is every state still distinguishable? -7. What was **removed**? (Net additions to permanent chrome require an argument.) -8. Which tier does it belong to, and does it appear without moving anything else? -9. What does it do when the geometry is invalid, and is that reported before the - commit? -10. Interaction cost: actions required for the canonical task it addresses, - before and after. -11. Reach (L11): screenshot at 1366×768 with the panel open — is the model still - on screen? Does it run on integrated graphics? Does it need the network, an - account, or a file the user cannot keep? -12. If the change adds or moves a verb in the offer: which row, and is it that - verb's row in **every** selection where it appears? Did any existing verb's - index change? (If yes, this is not a UI change, it is a breaking change to - every user's muscle memory, and it needs the group — see §4.1.) Was - `docs/ux/tool_atlas.json` updated and the atlas regenerated? -13. If the change adds a pointer gesture: what is its keyboard equivalent, and - what does a one-button Mac, a trackpad and a touch screen do (§4.1)? - -## 10. Where we stand today — honest inventory - -Complying with the laws already: - -- Sketch inline editors — draw an entity and its dimension tab opens on the - geometry; Tab walks Length → Width → Angle. -- Fillet/chamfer draggable radius arrow with an editable value label. -- Extrude depth arrow; move-body three-axis arrows. -- Datum-plane resize handles and offset arrow; ghost reference planes picked in - 3D. -- Imported-art place/size gizmo. -- Sketch plane taken from the picked face, with the target named in the status - line, and the sketch-plane dropdown deleted outright. - -Violating them, with removal scheduled: - -- **Every tool card is a two-column form** of combos and spin fields in the left - panel. This is the single largest debt in the product and the reason this - document exists. Tracked as an epic; each card is replaced by its on-geometry - equivalent, not improved in place. It fails L1 and it fails L11 twice over — - on a 1366×768 screen the cards leave the model a strip. -- Seven remaining plane pickers still populate a combo instead of consuming a - viewport selection. -- Pattern has no on-geometry spacing arrow or count badge. -- Hole is positioned by X/Y fields rather than by a point on a face. -- Booleans and cuts pick their operands from lists rather than in 3D. -- Fillet/chamfer edge selection still requires the click cycle L5 forbids. -- **Selecting geometry offers nothing.** There is no contextual offer (§4.1): - the user faces the full toolbar whatever they have picked, and finds out that - a tool did not apply by it doing nothing. This is the largest single item of - new work the charter asks for. The map and every state of it are already - drawn (`docs/ux/offer_atlas.html`); what the group owes itself before the code - is ratifying the row order, since every verb built before that lands has to be - addressed afterwards anyway. -- **Committing is an invisible click in empty space** rather than the - confirm/cancel puck of §4.2 — the exact gesture that rule withdraws. - -Nothing on the violating list is defended. The only open question for each is -what its on-geometry replacement should be. - -## 11. How the group works - -**Roles.** Product/UX lead (owns this document and casts the tie-break vote on -interaction questions); kernel maintainer; GUI maintainer; a print-domain -reviewer; a mechanical-design reviewer who uses the product on real work; an -accessibility reviewer covering both senses of §6 — reach and assistive — who -owns the reference machine and actually runs on it. One person may hold more -than one role; the UX lead and the mechanical-design reviewer should not be the -same person, and nobody reviews reach from a workstation. - -**The absent audience needs a seat.** The fourteen-year-old is not in the room -and cannot file an issue. Someone in the group is accountable for B5 and B6, and -the group watches real first-timers use the product on the reference machine at -least once a quarter — school, makerspace, or a friend's kid. Everything else in -this document can be argued from principle; approachability can only be -observed. - -**Cadence.** A short weekly review of open interaction proposals. A monthly pass -over the violating inventory in §10 — anything that has not moved in two months -is either scheduled or explicitly accepted as permanent, with a reason written -into this document. - -**How a change moves.** - -1. *Problem* — a described user difficulty, ideally with an interaction-cost - measurement, never a solution in disguise. -2. *Sketch* — one or two on-geometry interaction proposals, drawn or described - as a gesture sequence. Reviewed against §3 before any code. -3. *Prototype* — built behind whatever the smallest safe path is, driven end to - end on a real display, and screenshotted at each state. -4. *Gate* — §9 answered in the PR. -5. *Merge*, then update §10. - -**Decisions are written down.** Any resolution that constrains future work is -appended to this document as a numbered law or as an accepted exception with its -reasoning. A decision that lives only in a call is not a decision. - -**How disagreements resolve.** Against the laws first. If the laws do not decide -it, the tie-break is the interaction cost measured on the canonical tasks in -§12; if that does not decide it, the UX lead chooses and records why. - -## 12. Canonical tasks — the benchmark - -The measure of every UX change is the cost of these five tasks. Each is timed and -counted (clicks, keystrokes, camera actions, mode switches) on the headless rig -and, periodically, with real users who have not seen the product. - -| # | Task | What it exercises | -|---|---|---| -| **B1** | Bracket: sketch an L, extrude, two holes, fillet the inside corner, send to plate | The inner loop | -| **B2** | Change a hole diameter and the plate thickness, six features deep, and rebuild | Parametric editability | -| **B3** | Take an imported STEP, delete a boss, close the face, thicken a wall to nozzle width | Direct editing + print awareness | -| **B4** | Two parts, one revolute mate, check interference, produce an exploded view | The Mechanism tier | -| **B5** | First-run: from opening the Design tab to a print-ready solid, no documentation | Approachability | -| **B6** | B1 again, on the reference machine at 1366×768, offline, on a fresh account-less install | Reach (L11) | - -Every task is run on the reference machine of §6.1, not on a workstation — a -number measured on a fast desktop describes an experience most of our users will -never have. B6 repeats the inner loop under the full entry conditions so that -reach is a measured quantity and not an intention. - -Targets are set once each task has been measured on the current build. B5's -target is expressed in minutes-to-first-solid **by someone who has never seen a -CAD program**, and it is the number this project is ultimately judged by. - ---- - -### Appendix — anti-patterns we have already paid for - -Kept because each cost real time and each is easy to reintroduce. - -- **The dropdown that grew a row.** Fixing "cannot sketch on a face" by adding a - "Face of Body 1" entry to a plane combo. It reads as a small fix and it is the - side-panel architecture reproducing itself. -- **The invisible first click.** Flyout buttons and pick cycles whose first click - changes nothing meaningful. Filed as bugs three separate times against working - code, and made a real bug look fixed when it was not. -- **The fix verified through a path the user will never take.** A face-sketch fix - confirmed by double-clicking to reach face level. Users click once. A fix - reachable only by an undiscoverable gesture is indistinguishable from no fix. -- **The wrong feedback surface.** Measuring an armed tool by the toolbar, which - never renders keyboard-armed state. The status line is the surface that - answers. -- **The silent success.** A cut that removed no material, reported as done. Now - an error naming the likely cause. diff --git a/docs/CAD/design/mate-connectors/BEAR_CONNECTOR_REVIEW.md b/docs/CAD/design/mate-connectors/BEAR_CONNECTOR_REVIEW.md deleted file mode 100644 index cc2d4b22aa..0000000000 --- a/docs/CAD/design/mate-connectors/BEAR_CONNECTOR_REVIEW.md +++ /dev/null @@ -1,169 +0,0 @@ -# BearConnector.step — examination - -> **Scope.** One file was supplied and it contains **one object: the male.** Everything below is -> measured from that single solid. Earlier drafts of this note reasoned about a female pocket and a -> mating pair — those objects were never supplied, so any statement about them was speculation and -> has been removed. The clearance, the fit, and the pocket's legibility are all **unassessed**. - -Measured, not eyeballed. Imported into the Design tab's own OpenCascade kernel -(`import_step` → one valid closed solid), topology queried, geometry checked numerically. -Flat drawing: `artifacts/shots/bear-flat.png`. Viewport: `artifacts/shots/bear-02-zoom.png`. - -**File:** AP242 Edition 2, ST-Developer. 1 `MANIFOLD_SOLID_BREP`, 1 `CLOSED_SHELL`. -**Size:** 83.06 × 66.69 × 17.27 mm. **Faces:** 30 — 24 planar + 6 cylindrical. -**Curves:** 69 lines + 12 circles. **No** splines, spheres, tori or cones. -**Relief:** only four Z levels — 0, 3.00, 10.66, 17.27. - ---- - -## What is right, and precisely so - -**The sloping ridge is implemented exactly as briefed.** From (0.00, 18.40, 17.27) to -(0.00, 46.72, 10.66): 28.3 mm long, 6.61 mm drop, **13.1° slope**, and both ends sit dead on -x = 0.00. It breaks 180° rotation on its own. - -**20.0° uniform draft on all four snout flanks**, identical to within 0.1°: -`(0,−0.94,0.342) (0.936,0.08,0.342) (0,0.94,0.342) (−0.936,0.08,0.342)`. That is a real, -deliberate lead-in — it self-centres into a matching pocket, and it demoulds and prints. - -**The eyes are exactly symmetric**: Ø9.87 at x = ±16.43, y = 48.01, matching to 0.01 mm. -Someone mirrored those on purpose. - -**The mating feature is extremely economical**: only **five edges** exist above the 3 mm plate — -the ridge plus two flank edges at each end. Base plate is exactly 3.00 mm. - -The low-poly constraint is honoured. All six cylinders are outline rounds and eye holes; none of -them is a mating surface. - ---- - -## The asymmetry is deliberate, and it is complete - -**Correction.** A first pass read the left/right differences as an unfinished mirror. That was wrong: -the asymmetry is intentional. Tested properly — every candidate self-symmetry, in the part's own -centred frame, with a generous 0.1 mm tolerance: - -| operation | edges mapped onto the part | -|---|---| -| identity | 81 / 81 — 100 % | -| mirror about x = 0 (left/right) | **0 / 81** | -| mirror about y = 0 (top/bottom) | **0 / 81** | -| rotate 180° about Z | **0 / 81** | -| rotate 90° about Z | **0 / 81** | -| mirror about the diagonal | **0 / 81** | - -**The symmetry group is trivial.** No rigid motion or reflection maps this part onto itself, so -**every partial view determines the orientation uniquely** — you never need to see the whole face to -know which way round it goes. That is the strongest possible result for a keying interface and it is -exactly what the earlier abstract glyph work kept failing to achieve: a symmetric shape seen at a -grazing angle, or half-occluded, gives an ambiguous read. - -### Does it let you GRASP the orientation? Measured, not asserted. - -Unique-in-principle and graspable-at-a-glance are different claims. The symmetry table proves the -first. For the second, the front-on picture (outline + eyes + mouth, filled) was rasterised and -compared against its own mirror and its own 180° rotation — the two ways a person can get it wrong. - -**By size** (percentage of pixels that differ): - -| width | vs mirror | vs rotated 180° | -|---|---|---| -| 16 px | 20.7 % | 26.0 % | -| 24 px | 21.9 % | 30.9 % | -| 32 px | 23.0 % | 28.1 % | -| 48 px | 22.4 % | 30.6 % | -| 80 px | 24.7 % | 31.0 % | -| 160 px | 23.6 % | 31.0 % | - -**The curve is flat.** The full signal is already there at 16 pixels and more resolution adds -nothing. That is the whole result: **the orientation cue lives at low spatial frequency**, carried by -the overall shape rather than by any detail. It therefore survives distance, blur, poor light, -peripheral vision, a small print and a low-resolution screen. It is the exact opposite of the abstract -disc glyph, whose roll cue was a small high-frequency feature and died at a grazing angle. - -**Partial views — a claim I made and then withdrew.** I ran a masked-window test and concluded that -a single quarter of the face was enough to read the orientation. **That test was invalid and the -conclusion is wrong.** It compared a window of the original against *the same window* of the mirrored -and rotated versions — which silently hands the observer the registration. It assumes you already -know that the patch you are looking at is the top-left quarter, which is exactly the thing you would -not know if you could only see a quarter. - -**You need to see the whole face.** The cues here are *relational*: the big ear only means something -next to the small ear, and the mouth offset only means something relative to the centreline. None of -them is self-locating. Whole-face is the operating condition, and the design should be judged and -used on that basis. - -That does not weaken the size result above, which always used the complete silhouette: the whole face -reads at 16 px. Needing all of it, and needing very little resolution of it, are compatible — and for -a part held in a hand, seeing all of it is the normal case. - -**The signal is allocated to the right risks.** The strongest cue (up to 41.7 %) guards against -inserting it upside down — the mistake people actually make. The weakest (~23 %) guards the mirror -case, which needs the part flipped over and which the protrusion already prevents mechanically. - -It also does mechanical work beyond the ridge. The ridge alone breaks 180° rotation; the asymmetric -outline additionally defeats the **mirrored-part** case — a mirror-image copy will not fit, so a -modelling or printing mirror is caught at assembly rather than three steps later. - -And for children specifically, a symmetric cartoon face reads as a mask; illustrators asymmetrise -deliberately so a face reads as a *character*. The asymmetry is earning its keep three ways at once. - -### What is worth keeping in mind anyway - -**The ears differ by 42 %** — left 8.33 mm wide (top y 65.68), right 11.81 mm (top y 66.69). Both -start at the same y = 60.79, so they read as a deliberate pair rather than an error. 42 % is well -above the perceptual threshold: you see it instantly. Good cue. - -**The mouth is a smirk** — x −21.93 … 0.00, centred at x = −10.96, stopping on the centreline. A -classic character device and a strong asymmetry. - -**The rounds are the best cue and the one safety question.** All four are on the left — Ø11.71 at -(−40.82, 7.38), Ø11.71 at (−34.76, 0.58), Ø10.00 at (−29.85, 60.83), Ø2.90 at (−26.70, 65.95) — and -the right side is entirely sharp. This is the *most locally readable* cue in the design: the ears -differ only by comparison (you must see both to know which is which), whereas a rounded corner tells -you "this is the left" from that corner alone, by eye **or by fingertip**. For children assembling by -feel that is the cue doing the real work. - -The tension is that "sharp" on a children's part is a hazard, and the obvious safety fix — round -everything — destroys the cue. The resolution is not round-vs-sharp but **large-vs-small radius**: -keep R≈6 on the left and give the right R≈1. R1 still reads and feels sharp locally, so the cue -survives, and the actual edge hazard goes away. That is the one recommendation that outlives the -correction. - -**One measurement that does not fit the story:** the outline is off-centre by **0.54 mm** (left reach -40.99, right reach 42.07). A deliberate cue should be unmissable; 0.54 mm is invisible. It is -probably a by-product of the other features rather than intent — worth a look, not a defect. - ---- - -## Two judgement calls, not defects - -**The snout is highest at the nose tip and slopes down toward the brow** — a real bear's muzzle -does the opposite. Anatomically it reads more like a beak or a horn than a snout. But mechanically -it is the better choice: the nose tip enters the pocket first and does the finding. Keep it if the -lead-in matters more than the likeness; flip it if "it must look like a bear" wins. - -**Only the male was supplied**, so the clearance, the fit and the pocket are unassessed. Nothing in -this note should be read as a judgement on them. - ---- - -## The strategic point, which is the real reason this design is good - -It gives orientation **a name**. "Ears up, nose down" needs no legend, no convention and no -documentation. Face recognition is the most robust pattern-matching humans have: it survives low -resolution, poor light, partial occlusion and peripheral vision. That is exactly the robustness the -abstract ridge key was reaching for, and here it comes for free. - -**One earlier objection does not transfer — noting it only so it is not carried over by mistake.** -In §8c of the design doc a female *pocket* measured as visually invisible — flat-shaded, a recess -reads as a blank rectangle — and I concluded male/female -is the wrong polarity cue. **That was a viewport finding, and it does not apply to a physical part.** -Nobody looks into the pocket of a toy; they feel it. For a part in a child's hands, male/female is -exactly the right polarity language. The earlier conclusion stands for the on-screen glyph and must -not be carried over to this. - -**The one rule to write down now:** the face and the key must never be allowed to disagree. People -will trust the face over the mechanics every time. Here they agree — ridge on the centreline, ears -up. If the face is ever restyled independently of the key, a user will orient by the bear and be -wrong. Tie them permanently, in the model and in whatever generates it. diff --git a/docs/CAD/design/mate-connectors/BearConnector_Cutter.step b/docs/CAD/design/mate-connectors/BearConnector_Cutter.step deleted file mode 100644 index c78e19e536..0000000000 --- a/docs/CAD/design/mate-connectors/BearConnector_Cutter.step +++ /dev/null @@ -1,998 +0,0 @@ -ISO-10303-21; -HEADER; -FILE_DESCRIPTION(('FreeCAD Model'),'2;1'); -FILE_NAME('Open CASCADE Shape Model','2026-08-05T12:46:26',('FreeCAD'),( - 'FreeCAD'),'Open CASCADE STEP processor 7.8','FreeCAD','Unknown'); -FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }')); -ENDSEC; -DATA; -#1 = APPLICATION_PROTOCOL_DEFINITION('international standard', - 'automotive_design',2000,#2); -#2 = APPLICATION_CONTEXT( - 'core data for automotive mechanical design processes'); -#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10); -#4 = PRODUCT_DEFINITION_SHAPE('','',#5); -#5 = PRODUCT_DEFINITION('design','',#6,#9); -#6 = PRODUCT_DEFINITION_FORMATION('','',#7); -#7 = PRODUCT('Open CASCADE STEP translator 7.8 1', - 'Open CASCADE STEP translator 7.8 1','',(#8)); -#8 = PRODUCT_CONTEXT('',#2,'mechanical'); -#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design'); -#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#958); -#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14); -#12 = CARTESIAN_POINT('',(0.,0.,0.)); -#13 = DIRECTION('',(0.,0.,1.)); -#14 = DIRECTION('',(1.,0.,-0.)); -#15 = MANIFOLD_SOLID_BREP('',#16); -#16 = CLOSED_SHELL('',(#17,#229,#260,#497,#514,#531,#548,#565,#582,#599, - #616,#633,#650,#667,#684,#701,#718,#735,#747,#770,#794,#810,#822, - #839,#856,#878,#895,#912,#929,#946)); -#17 = ADVANCED_FACE('',(#18,#68,#79,#213),#224,.F.); -#18 = FACE_BOUND('',#19,.F.); -#19 = EDGE_LOOP('',(#20,#30,#38,#46,#54,#62)); -#20 = ORIENTED_EDGE('',*,*,#21,.F.); -#21 = EDGE_CURVE('',#22,#24,#26,.T.); -#22 = VERTEX_POINT('',#23); -#23 = CARTESIAN_POINT('',(19.029295926024,-0.2,-17.63009960955)); -#24 = VERTEX_POINT('',#25); -#25 = CARTESIAN_POINT('',(16.626582997737,-0.2,-8.940188245231)); -#26 = LINE('',#27,#28); -#27 = CARTESIAN_POINT('',(19.849519003668,-0.2,-20.59660707692)); -#28 = VECTOR('',#29,1.); -#29 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#30 = ORIENTED_EDGE('',*,*,#31,.F.); -#31 = EDGE_CURVE('',#32,#22,#34,.T.); -#32 = VERTEX_POINT('',#33); -#33 = CARTESIAN_POINT('',(22.059435554995,-0.2,-3.734519760785)); -#34 = LINE('',#35,#36); -#35 = CARTESIAN_POINT('',(17.698510515043,-0.2,-23.73280021221)); -#36 = VECTOR('',#37,1.); -#37 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#38 = ORIENTED_EDGE('',*,*,#39,.F.); -#39 = EDGE_CURVE('',#40,#32,#42,.T.); -#40 = VERTEX_POINT('',#41); -#41 = CARTESIAN_POINT('',(-21.72552223146,-0.2,-3.734519760785)); -#42 = LINE('',#43,#44); -#43 = CARTESIAN_POINT('',(0.297084840953,-0.2,-3.734519760785)); -#44 = VECTOR('',#45,1.); -#45 = DIRECTION('',(1.,0.,0.)); -#46 = ORIENTED_EDGE('',*,*,#47,.F.); -#47 = EDGE_CURVE('',#48,#40,#50,.T.); -#48 = VERTEX_POINT('',#49); -#49 = CARTESIAN_POINT('',(-21.72552223146,-0.2,-8.903751135252)); -#50 = LINE('',#51,#52); -#51 = CARTESIAN_POINT('',(-21.72552223146,-0.2,-19.83215600037)); -#52 = VECTOR('',#53,1.); -#53 = DIRECTION('',(0.,0.,1.)); -#54 = ORIENTED_EDGE('',*,*,#55,.F.); -#55 = EDGE_CURVE('',#56,#48,#58,.T.); -#56 = VERTEX_POINT('',#57); -#57 = CARTESIAN_POINT('',(4.383041634064E-04,-0.2,-8.903751615529)); -#58 = LINE('',#59,#60); -#59 = CARTESIAN_POINT('',(-5.279852300138,-0.2,-8.903751135252)); -#60 = VECTOR('',#61,1.); -#61 = DIRECTION('',(-1.,0.,0.)); -#62 = ORIENTED_EDGE('',*,*,#63,.F.); -#63 = EDGE_CURVE('',#24,#56,#64,.T.); -#64 = LINE('',#65,#66); -#65 = CARTESIAN_POINT('',(4.347099726942,-0.2,-8.913277437397)); -#66 = VECTOR('',#67,1.); -#67 = DIRECTION('',(-0.999997598615,0.,2.191520817069E-03)); -#68 = FACE_BOUND('',#69,.F.); -#69 = EDGE_LOOP('',(#70)); -#70 = ORIENTED_EDGE('',*,*,#71,.F.); -#71 = EDGE_CURVE('',#72,#72,#74,.T.); -#72 = VERTEX_POINT('',#73); -#73 = CARTESIAN_POINT('',(21.163799345768,-0.2,-48.00951684793)); -#74 = CIRCLE('',#75,4.735522705283); -#75 = AXIS2_PLACEMENT_3D('',#76,#77,#78); -#76 = CARTESIAN_POINT('',(16.428276640485,-0.2,-48.00951684793)); -#77 = DIRECTION('',(-0.,1.,0.)); -#78 = DIRECTION('',(1.,0.,0.)); -#79 = FACE_BOUND('',#80,.F.); -#80 = EDGE_LOOP('',(#81,#91,#100,#108,#116,#125,#133,#142,#150,#158,#166 - ,#174,#182,#190,#198,#206)); -#81 = ORIENTED_EDGE('',*,*,#82,.T.); -#82 = EDGE_CURVE('',#83,#85,#87,.T.); -#83 = VERTEX_POINT('',#84); -#84 = CARTESIAN_POINT('',(-27.86158468659,-0.2,-65.78852163128)); -#85 = VERTEX_POINT('',#86); -#86 = CARTESIAN_POINT('',(-29.08766684168,-0.2,-64.52156932717)); -#87 = LINE('',#88,#89); -#88 = CARTESIAN_POINT('',(-29.44004200272,-0.2,-64.15744811364)); -#89 = VECTOR('',#90,1.); -#90 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#91 = ORIENTED_EDGE('',*,*,#92,.F.); -#92 = EDGE_CURVE('',#93,#85,#95,.T.); -#93 = VERTEX_POINT('',#94); -#94 = CARTESIAN_POINT('',(-28.96712497021,-0.2,-57.16864680227)); -#95 = CIRCLE('',#96,5.2); -#96 = AXIS2_PLACEMENT_3D('',#97,#98,#99); -#97 = CARTESIAN_POINT('',(-25.35093464349,-0.2,-60.90537900045)); -#98 = DIRECTION('',(0.,-1.,0.)); -#99 = DIRECTION('',(-1.,0.,0.)); -#100 = ORIENTED_EDGE('',*,*,#101,.T.); -#101 = EDGE_CURVE('',#93,#102,#104,.T.); -#102 = VERTEX_POINT('',#103); -#103 = CARTESIAN_POINT('',(-26.93875323652,-0.2,-55.20570756731)); -#104 = LINE('',#105,#106); -#105 = CARTESIAN_POINT('',(-14.90185526362,-0.2,-43.55710346492)); -#106 = VECTOR('',#107,1.); -#107 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#108 = ORIENTED_EDGE('',*,*,#109,.T.); -#109 = EDGE_CURVE('',#102,#110,#112,.T.); -#110 = VERTEX_POINT('',#111); -#111 = CARTESIAN_POINT('',(-41.17904151244,-0.2,-10.52828909594)); -#112 = LINE('',#113,#114); -#113 = CARTESIAN_POINT('',(-32.39120436163,-0.2,-38.09921113329)); -#114 = VECTOR('',#115,1.); -#115 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#116 = ORIENTED_EDGE('',*,*,#117,.F.); -#117 = EDGE_CURVE('',#118,#110,#120,.T.); -#118 = VERTEX_POINT('',#119); -#119 = CARTESIAN_POINT('',(-39.69176606491,-0.2,-4.408923352436)); -#120 = CIRCLE('',#121,6.054044962965); -#121 = AXIS2_PLACEMENT_3D('',#122,#123,#124); -#122 = CARTESIAN_POINT('',(-35.41090981799,-0.2,-8.689779599357)); -#123 = DIRECTION('',(0.,-1.,0.)); -#124 = DIRECTION('',(-1.,0.,0.)); -#125 = ORIENTED_EDGE('',*,*,#126,.T.); -#126 = EDGE_CURVE('',#118,#127,#129,.T.); -#127 = VERTEX_POINT('',#128); -#128 = CARTESIAN_POINT('',(-36.85603142851,-0.2,-1.573188716044)); -#129 = LINE('',#130,#131); -#130 = CARTESIAN_POINT('',(-36.19319006079,-0.2,-0.910347348321)); -#131 = VECTOR('',#132,1.); -#132 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#133 = ORIENTED_EDGE('',*,*,#134,.F.); -#134 = EDGE_CURVE('',#135,#127,#137,.T.); -#135 = VERTEX_POINT('',#136); -#136 = CARTESIAN_POINT('',(-32.57517518159,-0.2,0.2)); -#137 = CIRCLE('',#138,6.054044962965); -#138 = AXIS2_PLACEMENT_3D('',#139,#140,#141); -#139 = CARTESIAN_POINT('',(-32.57517518159,-0.2,-5.854044962965)); -#140 = DIRECTION('',(0.,-1.,0.)); -#141 = DIRECTION('',(-1.,0.,0.)); -#142 = ORIENTED_EDGE('',*,*,#143,.T.); -#143 = EDGE_CURVE('',#135,#144,#146,.T.); -#144 = VERTEX_POINT('',#145); -#145 = CARTESIAN_POINT('',(35.082842712475,-0.2,0.2)); -#146 = LINE('',#147,#148); -#147 = CARTESIAN_POINT('',(-7.942265537672,-0.2,0.2)); -#148 = VECTOR('',#149,1.); -#149 = DIRECTION('',(1.,0.,0.)); -#150 = ORIENTED_EDGE('',*,*,#151,.F.); -#151 = EDGE_CURVE('',#152,#144,#154,.T.); -#152 = VERTEX_POINT('',#153); -#153 = CARTESIAN_POINT('',(42.298608189024,-0.2,-7.015765476549)); -#154 = LINE('',#155,#156); -#155 = CARTESIAN_POINT('',(36.596246576251,-0.2,-1.313403863776)); -#156 = VECTOR('',#157,1.); -#157 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#158 = ORIENTED_EDGE('',*,*,#159,.F.); -#159 = EDGE_CURVE('',#160,#152,#162,.T.); -#160 = VERTEX_POINT('',#161); -#161 = CARTESIAN_POINT('',(26.938753236523,-0.2,-55.20570756731)); -#162 = LINE('',#163,#164); -#163 = CARTESIAN_POINT('',(32.699020781567,-0.2,-37.13346923684)); -#164 = VECTOR('',#165,1.); -#165 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#166 = ORIENTED_EDGE('',*,*,#167,.F.); -#167 = EDGE_CURVE('',#168,#160,#170,.T.); -#168 = VERTEX_POINT('',#169); -#169 = CARTESIAN_POINT('',(32.703857168398,-0.2,-60.78483712899)); -#170 = LINE('',#171,#172); -#171 = CARTESIAN_POINT('',(16.008242280412,-0.2,-44.62779994931)); -#172 = VECTOR('',#173,1.); -#173 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#174 = ORIENTED_EDGE('',*,*,#175,.F.); -#175 = EDGE_CURVE('',#176,#168,#178,.T.); -#176 = VERTEX_POINT('',#177); -#177 = CARTESIAN_POINT('',(26.715163243538,-0.2,-66.97315781796)); -#178 = LINE('',#179,#180); -#179 = CARTESIAN_POINT('',(30.25240665457,-0.2,-63.31800414721)); -#180 = VECTOR('',#181,1.); -#181 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#182 = ORIENTED_EDGE('',*,*,#183,.F.); -#183 = EDGE_CURVE('',#184,#176,#186,.T.); -#184 = VERTEX_POINT('',#185); -#185 = CARTESIAN_POINT('',(20.532019001374,-0.2,-60.98947335481)); -#186 = LINE('',#187,#188); -#187 = CARTESIAN_POINT('',(9.922784884512,-0.2,-50.72247862452)); -#188 = VECTOR('',#189,1.); -#189 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#190 = ORIENTED_EDGE('',*,*,#191,.F.); -#191 = EDGE_CURVE('',#192,#184,#194,.T.); -#192 = VERTEX_POINT('',#193); -#193 = CARTESIAN_POINT('',(-20.53201900137,-0.2,-60.98947335481)); -#194 = LINE('',#195,#196); -#195 = CARTESIAN_POINT('',(5.354765181569,-0.2,-60.98947335481)); -#196 = VECTOR('',#197,1.); -#197 = DIRECTION('',(1.,0.,0.)); -#198 = ORIENTED_EDGE('',*,*,#199,.T.); -#199 = EDGE_CURVE('',#192,#200,#202,.T.); -#200 = VERTEX_POINT('',#201); -#201 = CARTESIAN_POINT('',(-25.53052705686,-0.2,-65.82673637491)); -#202 = LINE('',#203,#204); -#203 = CARTESIAN_POINT('',(-9.454421870603,-0.2,-50.26922436104)); -#204 = VECTOR('',#205,1.); -#205 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#206 = ORIENTED_EDGE('',*,*,#207,.F.); -#207 = EDGE_CURVE('',#83,#200,#208,.T.); -#208 = CIRCLE('',#209,1.648528137424); -#209 = AXIS2_PLACEMENT_3D('',#210,#211,#212); -#210 = CARTESIAN_POINT('',(-26.67694849991,-0.2,-64.64210018823)); -#211 = DIRECTION('',(0.,-1.,0.)); -#212 = DIRECTION('',(-1.,0.,0.)); -#213 = FACE_BOUND('',#214,.F.); -#214 = EDGE_LOOP('',(#215)); -#215 = ORIENTED_EDGE('',*,*,#216,.F.); -#216 = EDGE_CURVE('',#217,#217,#219,.T.); -#217 = VERTEX_POINT('',#218); -#218 = CARTESIAN_POINT('',(-11.6927539352,-0.2,-48.00951684793)); -#219 = CIRCLE('',#220,4.735522705283); -#220 = AXIS2_PLACEMENT_3D('',#221,#222,#223); -#221 = CARTESIAN_POINT('',(-16.42827664048,-0.2,-48.00951684793)); -#222 = DIRECTION('',(-0.,1.,0.)); -#223 = DIRECTION('',(1.,0.,0.)); -#224 = PLANE('',#225); -#225 = AXIS2_PLACEMENT_3D('',#226,#227,#228); -#226 = CARTESIAN_POINT('',(0.403056515455,-0.2,-33.34517655273)); -#227 = DIRECTION('',(0.,1.,0.)); -#228 = DIRECTION('',(1.,0.,0.)); -#229 = ADVANCED_FACE('',(#230),#255,.F.); -#230 = FACE_BOUND('',#231,.F.); -#231 = EDGE_LOOP('',(#232,#240,#241,#249)); -#232 = ORIENTED_EDGE('',*,*,#233,.T.); -#233 = EDGE_CURVE('',#234,#160,#236,.T.); -#234 = VERTEX_POINT('',#235); -#235 = CARTESIAN_POINT('',(26.938753236523,3.2,-55.20570756731)); -#236 = LINE('',#237,#238); -#237 = CARTESIAN_POINT('',(26.938753236523,3.,-55.20570756731)); -#238 = VECTOR('',#239,1.); -#239 = DIRECTION('',(0.,-1.,0.)); -#240 = ORIENTED_EDGE('',*,*,#159,.T.); -#241 = ORIENTED_EDGE('',*,*,#242,.F.); -#242 = EDGE_CURVE('',#243,#152,#245,.T.); -#243 = VERTEX_POINT('',#244); -#244 = CARTESIAN_POINT('',(42.298608189024,3.2,-7.015765476549)); -#245 = LINE('',#246,#247); -#246 = CARTESIAN_POINT('',(42.298608189024,3.,-7.015765476549)); -#247 = VECTOR('',#248,1.); -#248 = DIRECTION('',(0.,-1.,0.)); -#249 = ORIENTED_EDGE('',*,*,#250,.F.); -#250 = EDGE_CURVE('',#234,#243,#251,.T.); -#251 = LINE('',#252,#253); -#252 = CARTESIAN_POINT('',(32.699020781567,3.2,-37.13346923684)); -#253 = VECTOR('',#254,1.); -#254 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#255 = PLANE('',#256); -#256 = AXIS2_PLACEMENT_3D('',#257,#258,#259); -#257 = CARTESIAN_POINT('',(34.581352051556,3.,-31.22785130119)); -#258 = DIRECTION('',(-0.952773183837,0.,0.30368282823)); -#259 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#260 = ADVANCED_FACE('',(#261,#311,#322,#447,#481),#492,.T.); -#261 = FACE_BOUND('',#262,.T.); -#262 = EDGE_LOOP('',(#263,#273,#281,#289,#297,#305)); -#263 = ORIENTED_EDGE('',*,*,#264,.F.); -#264 = EDGE_CURVE('',#265,#267,#269,.T.); -#265 = VERTEX_POINT('',#266); -#266 = CARTESIAN_POINT('',(16.626582997737,3.2,-8.940188245231)); -#267 = VERTEX_POINT('',#268); -#268 = CARTESIAN_POINT('',(4.383041634064E-04,3.2,-8.903751615529)); -#269 = LINE('',#270,#271); -#270 = CARTESIAN_POINT('',(4.347099726942,3.2,-8.913277437397)); -#271 = VECTOR('',#272,1.); -#272 = DIRECTION('',(-0.999997598615,0.,2.191520817069E-03)); -#273 = ORIENTED_EDGE('',*,*,#274,.F.); -#274 = EDGE_CURVE('',#275,#265,#277,.T.); -#275 = VERTEX_POINT('',#276); -#276 = CARTESIAN_POINT('',(19.029295926024,3.2,-17.63009960955)); -#277 = LINE('',#278,#279); -#278 = CARTESIAN_POINT('',(19.849519003668,3.2,-20.59660707692)); -#279 = VECTOR('',#280,1.); -#280 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#281 = ORIENTED_EDGE('',*,*,#282,.F.); -#282 = EDGE_CURVE('',#283,#275,#285,.T.); -#283 = VERTEX_POINT('',#284); -#284 = CARTESIAN_POINT('',(22.059435554995,3.2,-3.734519760785)); -#285 = LINE('',#286,#287); -#286 = CARTESIAN_POINT('',(17.698510515043,3.2,-23.73280021221)); -#287 = VECTOR('',#288,1.); -#288 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#289 = ORIENTED_EDGE('',*,*,#290,.F.); -#290 = EDGE_CURVE('',#291,#283,#293,.T.); -#291 = VERTEX_POINT('',#292); -#292 = CARTESIAN_POINT('',(-21.72552223146,3.2,-3.734519760785)); -#293 = LINE('',#294,#295); -#294 = CARTESIAN_POINT('',(0.297084840953,3.2,-3.734519760785)); -#295 = VECTOR('',#296,1.); -#296 = DIRECTION('',(1.,0.,0.)); -#297 = ORIENTED_EDGE('',*,*,#298,.F.); -#298 = EDGE_CURVE('',#299,#291,#301,.T.); -#299 = VERTEX_POINT('',#300); -#300 = CARTESIAN_POINT('',(-21.72552223146,3.2,-8.903751135252)); -#301 = LINE('',#302,#303); -#302 = CARTESIAN_POINT('',(-21.72552223146,3.2,-19.83215600037)); -#303 = VECTOR('',#304,1.); -#304 = DIRECTION('',(0.,0.,1.)); -#305 = ORIENTED_EDGE('',*,*,#306,.F.); -#306 = EDGE_CURVE('',#267,#299,#307,.T.); -#307 = LINE('',#308,#309); -#308 = CARTESIAN_POINT('',(-5.279852300138,3.2,-8.903751135252)); -#309 = VECTOR('',#310,1.); -#310 = DIRECTION('',(-1.,0.,0.)); -#311 = FACE_BOUND('',#312,.T.); -#312 = EDGE_LOOP('',(#313)); -#313 = ORIENTED_EDGE('',*,*,#314,.F.); -#314 = EDGE_CURVE('',#315,#315,#317,.T.); -#315 = VERTEX_POINT('',#316); -#316 = CARTESIAN_POINT('',(21.163799345768,3.2,-48.00951684793)); -#317 = CIRCLE('',#318,4.735522705283); -#318 = AXIS2_PLACEMENT_3D('',#319,#320,#321); -#319 = CARTESIAN_POINT('',(16.428276640485,3.2,-48.00951684793)); -#320 = DIRECTION('',(-0.,1.,0.)); -#321 = DIRECTION('',(1.,0.,0.)); -#322 = FACE_BOUND('',#323,.T.); -#323 = EDGE_LOOP('',(#324,#334,#343,#351,#360,#368,#374,#375,#383,#391, - #399,#407,#415,#424,#432,#441)); -#324 = ORIENTED_EDGE('',*,*,#325,.T.); -#325 = EDGE_CURVE('',#326,#328,#330,.T.); -#326 = VERTEX_POINT('',#327); -#327 = CARTESIAN_POINT('',(-26.93875323652,3.2,-55.20570756731)); -#328 = VERTEX_POINT('',#329); -#329 = CARTESIAN_POINT('',(-41.17904151244,3.2,-10.52828909594)); -#330 = LINE('',#331,#332); -#331 = CARTESIAN_POINT('',(-32.39120436163,3.2,-38.09921113329)); -#332 = VECTOR('',#333,1.); -#333 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#334 = ORIENTED_EDGE('',*,*,#335,.F.); -#335 = EDGE_CURVE('',#336,#328,#338,.T.); -#336 = VERTEX_POINT('',#337); -#337 = CARTESIAN_POINT('',(-39.69176606491,3.2,-4.408923352436)); -#338 = CIRCLE('',#339,6.054044962965); -#339 = AXIS2_PLACEMENT_3D('',#340,#341,#342); -#340 = CARTESIAN_POINT('',(-35.41090981799,3.2,-8.689779599357)); -#341 = DIRECTION('',(0.,-1.,0.)); -#342 = DIRECTION('',(-1.,0.,0.)); -#343 = ORIENTED_EDGE('',*,*,#344,.T.); -#344 = EDGE_CURVE('',#336,#345,#347,.T.); -#345 = VERTEX_POINT('',#346); -#346 = CARTESIAN_POINT('',(-36.85603142851,3.2,-1.573188716044)); -#347 = LINE('',#348,#349); -#348 = CARTESIAN_POINT('',(-36.19319006079,3.2,-0.910347348321)); -#349 = VECTOR('',#350,1.); -#350 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#351 = ORIENTED_EDGE('',*,*,#352,.F.); -#352 = EDGE_CURVE('',#353,#345,#355,.T.); -#353 = VERTEX_POINT('',#354); -#354 = CARTESIAN_POINT('',(-32.57517518159,3.2,0.2)); -#355 = CIRCLE('',#356,6.054044962965); -#356 = AXIS2_PLACEMENT_3D('',#357,#358,#359); -#357 = CARTESIAN_POINT('',(-32.57517518159,3.2,-5.854044962965)); -#358 = DIRECTION('',(0.,-1.,0.)); -#359 = DIRECTION('',(-1.,0.,0.)); -#360 = ORIENTED_EDGE('',*,*,#361,.T.); -#361 = EDGE_CURVE('',#353,#362,#364,.T.); -#362 = VERTEX_POINT('',#363); -#363 = CARTESIAN_POINT('',(35.082842712475,3.2,0.2)); -#364 = LINE('',#365,#366); -#365 = CARTESIAN_POINT('',(-7.942265537672,3.2,0.2)); -#366 = VECTOR('',#367,1.); -#367 = DIRECTION('',(1.,0.,0.)); -#368 = ORIENTED_EDGE('',*,*,#369,.F.); -#369 = EDGE_CURVE('',#243,#362,#370,.T.); -#370 = LINE('',#371,#372); -#371 = CARTESIAN_POINT('',(36.596246576251,3.2,-1.313403863776)); -#372 = VECTOR('',#373,1.); -#373 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#374 = ORIENTED_EDGE('',*,*,#250,.F.); -#375 = ORIENTED_EDGE('',*,*,#376,.F.); -#376 = EDGE_CURVE('',#377,#234,#379,.T.); -#377 = VERTEX_POINT('',#378); -#378 = CARTESIAN_POINT('',(32.703857168398,3.2,-60.78483712899)); -#379 = LINE('',#380,#381); -#380 = CARTESIAN_POINT('',(16.008242280412,3.2,-44.62779994931)); -#381 = VECTOR('',#382,1.); -#382 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#383 = ORIENTED_EDGE('',*,*,#384,.F.); -#384 = EDGE_CURVE('',#385,#377,#387,.T.); -#385 = VERTEX_POINT('',#386); -#386 = CARTESIAN_POINT('',(26.715163243538,3.2,-66.97315781796)); -#387 = LINE('',#388,#389); -#388 = CARTESIAN_POINT('',(30.25240665457,3.2,-63.31800414721)); -#389 = VECTOR('',#390,1.); -#390 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#391 = ORIENTED_EDGE('',*,*,#392,.F.); -#392 = EDGE_CURVE('',#393,#385,#395,.T.); -#393 = VERTEX_POINT('',#394); -#394 = CARTESIAN_POINT('',(20.532019001374,3.2,-60.98947335481)); -#395 = LINE('',#396,#397); -#396 = CARTESIAN_POINT('',(9.922784884512,3.2,-50.72247862452)); -#397 = VECTOR('',#398,1.); -#398 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#399 = ORIENTED_EDGE('',*,*,#400,.F.); -#400 = EDGE_CURVE('',#401,#393,#403,.T.); -#401 = VERTEX_POINT('',#402); -#402 = CARTESIAN_POINT('',(-20.53201900137,3.2,-60.98947335481)); -#403 = LINE('',#404,#405); -#404 = CARTESIAN_POINT('',(5.354765181569,3.2,-60.98947335481)); -#405 = VECTOR('',#406,1.); -#406 = DIRECTION('',(1.,0.,0.)); -#407 = ORIENTED_EDGE('',*,*,#408,.T.); -#408 = EDGE_CURVE('',#401,#409,#411,.T.); -#409 = VERTEX_POINT('',#410); -#410 = CARTESIAN_POINT('',(-25.53052705686,3.2,-65.82673637491)); -#411 = LINE('',#412,#413); -#412 = CARTESIAN_POINT('',(-9.454421870603,3.2,-50.26922436104)); -#413 = VECTOR('',#414,1.); -#414 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#415 = ORIENTED_EDGE('',*,*,#416,.F.); -#416 = EDGE_CURVE('',#417,#409,#419,.T.); -#417 = VERTEX_POINT('',#418); -#418 = CARTESIAN_POINT('',(-27.86158468659,3.2,-65.78852163128)); -#419 = CIRCLE('',#420,1.648528137424); -#420 = AXIS2_PLACEMENT_3D('',#421,#422,#423); -#421 = CARTESIAN_POINT('',(-26.67694849991,3.2,-64.64210018823)); -#422 = DIRECTION('',(0.,-1.,0.)); -#423 = DIRECTION('',(-1.,0.,0.)); -#424 = ORIENTED_EDGE('',*,*,#425,.T.); -#425 = EDGE_CURVE('',#417,#426,#428,.T.); -#426 = VERTEX_POINT('',#427); -#427 = CARTESIAN_POINT('',(-29.08766684168,3.2,-64.52156932717)); -#428 = LINE('',#429,#430); -#429 = CARTESIAN_POINT('',(-29.44004200272,3.2,-64.15744811364)); -#430 = VECTOR('',#431,1.); -#431 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#432 = ORIENTED_EDGE('',*,*,#433,.F.); -#433 = EDGE_CURVE('',#434,#426,#436,.T.); -#434 = VERTEX_POINT('',#435); -#435 = CARTESIAN_POINT('',(-28.96712497021,3.2,-57.16864680227)); -#436 = CIRCLE('',#437,5.2); -#437 = AXIS2_PLACEMENT_3D('',#438,#439,#440); -#438 = CARTESIAN_POINT('',(-25.35093464349,3.2,-60.90537900045)); -#439 = DIRECTION('',(0.,-1.,0.)); -#440 = DIRECTION('',(-1.,0.,0.)); -#441 = ORIENTED_EDGE('',*,*,#442,.T.); -#442 = EDGE_CURVE('',#434,#326,#443,.T.); -#443 = LINE('',#444,#445); -#444 = CARTESIAN_POINT('',(-14.90185526362,3.2,-43.55710346492)); -#445 = VECTOR('',#446,1.); -#446 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#447 = FACE_BOUND('',#448,.T.); -#448 = EDGE_LOOP('',(#449,#459,#467,#475)); -#449 = ORIENTED_EDGE('',*,*,#450,.F.); -#450 = EDGE_CURVE('',#451,#453,#455,.T.); -#451 = VERTEX_POINT('',#452); -#452 = CARTESIAN_POINT('',(5.809375885494,3.2,-13.06417917474)); -#453 = VERTEX_POINT('',#454); -#454 = CARTESIAN_POINT('',(2.688069798796,3.2,-49.64588621989)); -#455 = LINE('',#456,#457); -#456 = CARTESIAN_POINT('',(4.914157977861,3.2,-23.55613296875)); -#457 = VECTOR('',#458,1.); -#458 = DIRECTION('',(-8.501532861635E-02,0.,-0.996379643459)); -#459 = ORIENTED_EDGE('',*,*,#460,.F.); -#460 = EDGE_CURVE('',#461,#451,#463,.T.); -#461 = VERTEX_POINT('',#462); -#462 = CARTESIAN_POINT('',(-5.809375885494,3.2,-13.06417917474)); -#463 = LINE('',#464,#465); -#464 = CARTESIAN_POINT('',(1.615747408047,3.2,-13.06417917474)); -#465 = VECTOR('',#466,1.); -#466 = DIRECTION('',(1.,0.,-3.066574716487E-16)); -#467 = ORIENTED_EDGE('',*,*,#468,.F.); -#468 = EDGE_CURVE('',#469,#461,#471,.T.); -#469 = VERTEX_POINT('',#470); -#470 = CARTESIAN_POINT('',(-2.688069798796,3.2,-49.64588621989)); -#471 = LINE('',#472,#473); -#472 = CARTESIAN_POINT('',(-4.890802006217,3.2,-23.82986495424)); -#473 = VECTOR('',#474,1.); -#474 = DIRECTION('',(-8.501532861635E-02,0.,0.996379643459)); -#475 = ORIENTED_EDGE('',*,*,#476,.F.); -#476 = EDGE_CURVE('',#453,#469,#477,.T.); -#477 = LINE('',#478,#479); -#478 = CARTESIAN_POINT('',(1.615747408047,3.2,-49.64588621989)); -#479 = VECTOR('',#480,1.); -#480 = DIRECTION('',(-1.,0.,0.)); -#481 = FACE_BOUND('',#482,.T.); -#482 = EDGE_LOOP('',(#483)); -#483 = ORIENTED_EDGE('',*,*,#484,.F.); -#484 = EDGE_CURVE('',#485,#485,#487,.T.); -#485 = VERTEX_POINT('',#486); -#486 = CARTESIAN_POINT('',(-11.6927539352,3.2,-48.00951684793)); -#487 = CIRCLE('',#488,4.735522705283); -#488 = AXIS2_PLACEMENT_3D('',#489,#490,#491); -#489 = CARTESIAN_POINT('',(-16.42827664048,3.2,-48.00951684793)); -#490 = DIRECTION('',(-0.,1.,0.)); -#491 = DIRECTION('',(1.,0.,0.)); -#492 = PLANE('',#493); -#493 = AXIS2_PLACEMENT_3D('',#494,#495,#496); -#494 = CARTESIAN_POINT('',(0.403056515455,3.2,-33.34517655273)); -#495 = DIRECTION('',(0.,1.,0.)); -#496 = DIRECTION('',(1.,0.,0.)); -#497 = ADVANCED_FACE('',(#498),#509,.F.); -#498 = FACE_BOUND('',#499,.F.); -#499 = EDGE_LOOP('',(#500,#506,#507,#508)); -#500 = ORIENTED_EDGE('',*,*,#501,.F.); -#501 = EDGE_CURVE('',#168,#377,#502,.T.); -#502 = LINE('',#503,#504); -#503 = CARTESIAN_POINT('',(32.703857168398,3.,-60.78483712899)); -#504 = VECTOR('',#505,1.); -#505 = DIRECTION('',(0.,1.,0.)); -#506 = ORIENTED_EDGE('',*,*,#167,.T.); -#507 = ORIENTED_EDGE('',*,*,#233,.F.); -#508 = ORIENTED_EDGE('',*,*,#376,.F.); -#509 = PLANE('',#510); -#510 = AXIS2_PLACEMENT_3D('',#511,#512,#513); -#511 = CARTESIAN_POINT('',(29.704873980143,3.,-57.88259703786)); -#512 = DIRECTION('',(-0.695421216677,0.,-0.718602345805)); -#513 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#514 = ADVANCED_FACE('',(#515),#526,.F.); -#515 = FACE_BOUND('',#516,.F.); -#516 = EDGE_LOOP('',(#517,#523,#524,#525)); -#517 = ORIENTED_EDGE('',*,*,#518,.F.); -#518 = EDGE_CURVE('',#176,#385,#519,.T.); -#519 = LINE('',#520,#521); -#520 = CARTESIAN_POINT('',(26.715163243538,3.,-66.97315781796)); -#521 = VECTOR('',#522,1.); -#522 = DIRECTION('',(0.,1.,0.)); -#523 = ORIENTED_EDGE('',*,*,#175,.T.); -#524 = ORIENTED_EDGE('',*,*,#501,.T.); -#525 = ORIENTED_EDGE('',*,*,#384,.F.); -#526 = PLANE('',#527); -#527 = AXIS2_PLACEMENT_3D('',#528,#529,#530); -#528 = CARTESIAN_POINT('',(29.709510205968,3.,-63.87899747347)); -#529 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#530 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#531 = ADVANCED_FACE('',(#532),#543,.F.); -#532 = FACE_BOUND('',#533,.F.); -#533 = EDGE_LOOP('',(#534,#540,#541,#542)); -#534 = ORIENTED_EDGE('',*,*,#535,.T.); -#535 = EDGE_CURVE('',#393,#184,#536,.T.); -#536 = LINE('',#537,#538); -#537 = CARTESIAN_POINT('',(20.532019001374,3.,-60.98947335481)); -#538 = VECTOR('',#539,1.); -#539 = DIRECTION('',(0.,-1.,0.)); -#540 = ORIENTED_EDGE('',*,*,#183,.T.); -#541 = ORIENTED_EDGE('',*,*,#518,.T.); -#542 = ORIENTED_EDGE('',*,*,#392,.F.); -#543 = PLANE('',#544); -#544 = AXIS2_PLACEMENT_3D('',#545,#546,#547); -#545 = CARTESIAN_POINT('',(23.522653113203,3.,-63.8836336993)); -#546 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#547 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#548 = ADVANCED_FACE('',(#549),#560,.F.); -#549 = FACE_BOUND('',#550,.F.); -#550 = EDGE_LOOP('',(#551,#557,#558,#559)); -#551 = ORIENTED_EDGE('',*,*,#552,.F.); -#552 = EDGE_CURVE('',#192,#401,#553,.T.); -#553 = LINE('',#554,#555); -#554 = CARTESIAN_POINT('',(-20.53201900137,3.,-60.98947335481)); -#555 = VECTOR('',#556,1.); -#556 = DIRECTION('',(0.,1.,0.)); -#557 = ORIENTED_EDGE('',*,*,#191,.T.); -#558 = ORIENTED_EDGE('',*,*,#535,.F.); -#559 = ORIENTED_EDGE('',*,*,#400,.F.); -#560 = PLANE('',#561); -#561 = AXIS2_PLACEMENT_3D('',#562,#563,#564); -#562 = CARTESIAN_POINT('',(10.306473847682,3.,-60.98947335481)); -#563 = DIRECTION('',(0.,0.,1.)); -#564 = DIRECTION('',(0.,-1.,0.)); -#565 = ADVANCED_FACE('',(#566),#577,.T.); -#566 = FACE_BOUND('',#567,.T.); -#567 = EDGE_LOOP('',(#568,#574,#575,#576)); -#568 = ORIENTED_EDGE('',*,*,#569,.F.); -#569 = EDGE_CURVE('',#409,#200,#570,.T.); -#570 = LINE('',#571,#572); -#571 = CARTESIAN_POINT('',(-25.53052705686,3.,-65.82673637491)); -#572 = VECTOR('',#573,1.); -#573 = DIRECTION('',(0.,-1.,0.)); -#574 = ORIENTED_EDGE('',*,*,#408,.F.); -#575 = ORIENTED_EDGE('',*,*,#552,.F.); -#576 = ORIENTED_EDGE('',*,*,#199,.T.); -#577 = PLANE('',#578); -#578 = AXIS2_PLACEMENT_3D('',#579,#580,#581); -#579 = CARTESIAN_POINT('',(-23.00219525444,3.,-63.37996509944)); -#580 = DIRECTION('',(0.695421216677,0.,-0.718602345805)); -#581 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#582 = ADVANCED_FACE('',(#583),#594,.T.); -#583 = FACE_BOUND('',#584,.T.); -#584 = EDGE_LOOP('',(#585,#591,#592,#593)); -#585 = ORIENTED_EDGE('',*,*,#586,.F.); -#586 = EDGE_CURVE('',#417,#83,#587,.T.); -#587 = LINE('',#588,#589); -#588 = CARTESIAN_POINT('',(-27.86158468659,3.,-65.78852163128)); -#589 = VECTOR('',#590,1.); -#590 = DIRECTION('',(0.,-1.,0.)); -#591 = ORIENTED_EDGE('',*,*,#416,.T.); -#592 = ORIENTED_EDGE('',*,*,#569,.T.); -#593 = ORIENTED_EDGE('',*,*,#207,.F.); -#594 = CYLINDRICAL_SURFACE('',#595,1.648528137424); -#595 = AXIS2_PLACEMENT_3D('',#596,#597,#598); -#596 = CARTESIAN_POINT('',(-26.67694849991,3.,-64.64210018823)); -#597 = DIRECTION('',(0.,-1.,0.)); -#598 = DIRECTION('',(-1.,0.,0.)); -#599 = ADVANCED_FACE('',(#600),#611,.T.); -#600 = FACE_BOUND('',#601,.T.); -#601 = EDGE_LOOP('',(#602,#608,#609,#610)); -#602 = ORIENTED_EDGE('',*,*,#603,.F.); -#603 = EDGE_CURVE('',#426,#85,#604,.T.); -#604 = LINE('',#605,#606); -#605 = CARTESIAN_POINT('',(-29.08766684168,3.,-64.52156932717)); -#606 = VECTOR('',#607,1.); -#607 = DIRECTION('',(0.,-1.,0.)); -#608 = ORIENTED_EDGE('',*,*,#425,.F.); -#609 = ORIENTED_EDGE('',*,*,#586,.T.); -#610 = ORIENTED_EDGE('',*,*,#82,.T.); -#611 = PLANE('',#612); -#612 = AXIS2_PLACEMENT_3D('',#613,#614,#615); -#613 = CARTESIAN_POINT('',(-28.47462576413,3.,-65.15504547923)); -#614 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#615 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#616 = ADVANCED_FACE('',(#617),#628,.T.); -#617 = FACE_BOUND('',#618,.T.); -#618 = EDGE_LOOP('',(#619,#625,#626,#627)); -#619 = ORIENTED_EDGE('',*,*,#620,.F.); -#620 = EDGE_CURVE('',#434,#93,#621,.T.); -#621 = LINE('',#622,#623); -#622 = CARTESIAN_POINT('',(-28.96712497021,3.,-57.16864680227)); -#623 = VECTOR('',#624,1.); -#624 = DIRECTION('',(0.,-1.,0.)); -#625 = ORIENTED_EDGE('',*,*,#433,.T.); -#626 = ORIENTED_EDGE('',*,*,#603,.T.); -#627 = ORIENTED_EDGE('',*,*,#92,.F.); -#628 = CYLINDRICAL_SURFACE('',#629,5.2); -#629 = AXIS2_PLACEMENT_3D('',#630,#631,#632); -#630 = CARTESIAN_POINT('',(-25.35093464349,3.,-60.90537900045)); -#631 = DIRECTION('',(0.,-1.,0.)); -#632 = DIRECTION('',(-1.,0.,0.)); -#633 = ADVANCED_FACE('',(#634),#645,.T.); -#634 = FACE_BOUND('',#635,.T.); -#635 = EDGE_LOOP('',(#636,#642,#643,#644)); -#636 = ORIENTED_EDGE('',*,*,#637,.F.); -#637 = EDGE_CURVE('',#326,#102,#638,.T.); -#638 = LINE('',#639,#640); -#639 = CARTESIAN_POINT('',(-26.93875323652,3.,-55.20570756731)); -#640 = VECTOR('',#641,1.); -#641 = DIRECTION('',(0.,-1.,0.)); -#642 = ORIENTED_EDGE('',*,*,#442,.F.); -#643 = ORIENTED_EDGE('',*,*,#620,.T.); -#644 = ORIENTED_EDGE('',*,*,#101,.T.); -#645 = PLANE('',#646); -#646 = AXIS2_PLACEMENT_3D('',#647,#648,#649); -#647 = CARTESIAN_POINT('',(-27.90836811563,3.,-56.14404399617)); -#648 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#649 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#650 = ADVANCED_FACE('',(#651),#662,.T.); -#651 = FACE_BOUND('',#652,.T.); -#652 = EDGE_LOOP('',(#653,#659,#660,#661)); -#653 = ORIENTED_EDGE('',*,*,#654,.F.); -#654 = EDGE_CURVE('',#328,#110,#655,.T.); -#655 = LINE('',#656,#657); -#656 = CARTESIAN_POINT('',(-41.17904151244,3.,-10.52828909594)); -#657 = VECTOR('',#658,1.); -#658 = DIRECTION('',(0.,-1.,0.)); -#659 = ORIENTED_EDGE('',*,*,#325,.F.); -#660 = ORIENTED_EDGE('',*,*,#637,.T.); -#661 = ORIENTED_EDGE('',*,*,#109,.T.); -#662 = PLANE('',#663); -#663 = AXIS2_PLACEMENT_3D('',#664,#665,#666); -#664 = CARTESIAN_POINT('',(-34.04006158346,3.,-32.92609366041)); -#665 = DIRECTION('',(-0.952773183837,0.,-0.30368282823)); -#666 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#667 = ADVANCED_FACE('',(#668),#679,.T.); -#668 = FACE_BOUND('',#669,.T.); -#669 = EDGE_LOOP('',(#670,#676,#677,#678)); -#670 = ORIENTED_EDGE('',*,*,#671,.F.); -#671 = EDGE_CURVE('',#336,#118,#672,.T.); -#672 = LINE('',#673,#674); -#673 = CARTESIAN_POINT('',(-39.69176606491,3.,-4.408923352436)); -#674 = VECTOR('',#675,1.); -#675 = DIRECTION('',(0.,-1.,0.)); -#676 = ORIENTED_EDGE('',*,*,#335,.T.); -#677 = ORIENTED_EDGE('',*,*,#654,.T.); -#678 = ORIENTED_EDGE('',*,*,#117,.F.); -#679 = CYLINDRICAL_SURFACE('',#680,6.054044962965); -#680 = AXIS2_PLACEMENT_3D('',#681,#682,#683); -#681 = CARTESIAN_POINT('',(-35.41090981799,3.,-8.689779599357)); -#682 = DIRECTION('',(0.,-1.,0.)); -#683 = DIRECTION('',(-1.,0.,0.)); -#684 = ADVANCED_FACE('',(#685),#696,.T.); -#685 = FACE_BOUND('',#686,.T.); -#686 = EDGE_LOOP('',(#687,#693,#694,#695)); -#687 = ORIENTED_EDGE('',*,*,#688,.F.); -#688 = EDGE_CURVE('',#345,#127,#689,.T.); -#689 = LINE('',#690,#691); -#690 = CARTESIAN_POINT('',(-36.85603142851,3.,-1.573188716044)); -#691 = VECTOR('',#692,1.); -#692 = DIRECTION('',(0.,-1.,0.)); -#693 = ORIENTED_EDGE('',*,*,#344,.F.); -#694 = ORIENTED_EDGE('',*,*,#671,.T.); -#695 = ORIENTED_EDGE('',*,*,#126,.T.); -#696 = PLANE('',#697); -#697 = AXIS2_PLACEMENT_3D('',#698,#699,#700); -#698 = CARTESIAN_POINT('',(-38.27389874671,3.,-2.99105603424)); -#699 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#700 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#701 = ADVANCED_FACE('',(#702),#713,.T.); -#702 = FACE_BOUND('',#703,.T.); -#703 = EDGE_LOOP('',(#704,#710,#711,#712)); -#704 = ORIENTED_EDGE('',*,*,#705,.F.); -#705 = EDGE_CURVE('',#353,#135,#706,.T.); -#706 = LINE('',#707,#708); -#707 = CARTESIAN_POINT('',(-32.57517518159,3.,0.2)); -#708 = VECTOR('',#709,1.); -#709 = DIRECTION('',(0.,-1.,0.)); -#710 = ORIENTED_EDGE('',*,*,#352,.T.); -#711 = ORIENTED_EDGE('',*,*,#688,.T.); -#712 = ORIENTED_EDGE('',*,*,#134,.F.); -#713 = CYLINDRICAL_SURFACE('',#714,6.054044962965); -#714 = AXIS2_PLACEMENT_3D('',#715,#716,#717); -#715 = CARTESIAN_POINT('',(-32.57517518159,3.,-5.854044962965)); -#716 = DIRECTION('',(0.,-1.,0.)); -#717 = DIRECTION('',(-1.,0.,0.)); -#718 = ADVANCED_FACE('',(#719),#730,.T.); -#719 = FACE_BOUND('',#720,.T.); -#720 = EDGE_LOOP('',(#721,#727,#728,#729)); -#721 = ORIENTED_EDGE('',*,*,#722,.F.); -#722 = EDGE_CURVE('',#362,#144,#723,.T.); -#723 = LINE('',#724,#725); -#724 = CARTESIAN_POINT('',(35.082842712475,3.,0.2)); -#725 = VECTOR('',#726,1.); -#726 = DIRECTION('',(0.,-1.,0.)); -#727 = ORIENTED_EDGE('',*,*,#361,.F.); -#728 = ORIENTED_EDGE('',*,*,#705,.T.); -#729 = ORIENTED_EDGE('',*,*,#143,.T.); -#730 = PLANE('',#731); -#731 = AXIS2_PLACEMENT_3D('',#732,#733,#734); -#732 = CARTESIAN_POINT('',(-16.28758759079,3.,0.2)); -#733 = DIRECTION('',(0.,0.,1.)); -#734 = DIRECTION('',(0.,-1.,0.)); -#735 = ADVANCED_FACE('',(#736),#742,.F.); -#736 = FACE_BOUND('',#737,.F.); -#737 = EDGE_LOOP('',(#738,#739,#740,#741)); -#738 = ORIENTED_EDGE('',*,*,#151,.T.); -#739 = ORIENTED_EDGE('',*,*,#722,.F.); -#740 = ORIENTED_EDGE('',*,*,#369,.F.); -#741 = ORIENTED_EDGE('',*,*,#242,.T.); -#742 = PLANE('',#743); -#743 = AXIS2_PLACEMENT_3D('',#744,#745,#746); -#744 = CARTESIAN_POINT('',(38.67695526217,3.,-3.394112549695)); -#745 = DIRECTION('',(-0.707106781187,0.,-0.707106781187)); -#746 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#747 = ADVANCED_FACE('',(#748),#765,.T.); -#748 = FACE_BOUND('',#749,.T.); -#749 = EDGE_LOOP('',(#750,#758,#764)); -#750 = ORIENTED_EDGE('',*,*,#751,.T.); -#751 = EDGE_CURVE('',#451,#752,#754,.T.); -#752 = VERTEX_POINT('',#753); -#753 = CARTESIAN_POINT('',(3.256654205567E-15,17.8572529153, - -18.39898295202)); -#754 = LINE('',#755,#756); -#755 = CARTESIAN_POINT('',(5.649679875255,3.602918464526,-13.21082950266 - )); -#756 = VECTOR('',#757,1.); -#757 = DIRECTION('',(-0.349023821871,0.880598971639,-0.320511814002)); -#758 = ORIENTED_EDGE('',*,*,#759,.T.); -#759 = EDGE_CURVE('',#752,#461,#760,.T.); -#760 = LINE('',#761,#762); -#761 = CARTESIAN_POINT('',(-5.275833888477,4.546144602338, - -13.55413574101)); -#762 = VECTOR('',#763,1.); -#763 = DIRECTION('',(-0.349023821871,-0.880598971639,0.320511814002)); -#764 = ORIENTED_EDGE('',*,*,#460,.T.); -#765 = PLANE('',#766); -#766 = AXIS2_PLACEMENT_3D('',#767,#768,#769); -#767 = CARTESIAN_POINT('',(2.828438300639,3.068404028665,-13.01628215822 - )); -#768 = DIRECTION('',(2.881637632171E-16,0.342020143326,0.939692620786)); -#769 = DIRECTION('',(-1.048830324052E-16,0.939692620786,-0.342020143326) - ); -#770 = ADVANCED_FACE('',(#771),#789,.T.); -#771 = FACE_BOUND('',#772,.T.); -#772 = EDGE_LOOP('',(#773,#781,#782,#783)); -#773 = ORIENTED_EDGE('',*,*,#774,.T.); -#774 = EDGE_CURVE('',#775,#752,#777,.T.); -#775 = VERTEX_POINT('',#776); -#776 = CARTESIAN_POINT('',(1.480297366167E-15,11.242400581089, - -46.71869179633)); -#777 = LINE('',#778,#779); -#778 = CARTESIAN_POINT('',(2.6645352591E-15,18.133069549222, - -17.21814831317)); -#779 = VECTOR('',#780,1.); -#780 = DIRECTION('',(5.275122655166E-17,0.227455280238,0.97378852709)); -#781 = ORIENTED_EDGE('',*,*,#751,.F.); -#782 = ORIENTED_EDGE('',*,*,#450,.T.); -#783 = ORIENTED_EDGE('',*,*,#784,.T.); -#784 = EDGE_CURVE('',#453,#775,#785,.T.); -#785 = LINE('',#786,#787); -#786 = CARTESIAN_POINT('',(1.103762571829,7.940067898719,-47.92064259636 - )); -#787 = VECTOR('',#788,1.); -#788 = DIRECTION('',(-0.299648208284,0.896513522642,0.326304236859)); -#789 = PLANE('',#790); -#790 = AXIS2_PLACEMENT_3D('',#791,#792,#793); -#791 = CARTESIAN_POINT('',(5.823691883056,3.068404028665,-13.45978839622 - )); -#792 = DIRECTION('',(0.93629059846,0.342020143326,-7.988827695448E-02)); -#793 = DIRECTION('',(-0.340781908463,0.939692620786,2.907695487824E-02) - ); -#794 = ADVANCED_FACE('',(#795),#805,.T.); -#795 = FACE_BOUND('',#796,.T.); -#796 = EDGE_LOOP('',(#797,#803,#804)); -#797 = ORIENTED_EDGE('',*,*,#798,.T.); -#798 = EDGE_CURVE('',#469,#775,#799,.T.); -#799 = LINE('',#800,#801); -#800 = CARTESIAN_POINT('',(-0.871390517001,8.635298785064, - -47.66759924779)); -#801 = VECTOR('',#802,1.); -#802 = DIRECTION('',(0.299648208284,0.896513522642,0.326304236859)); -#803 = ORIENTED_EDGE('',*,*,#784,.F.); -#804 = ORIENTED_EDGE('',*,*,#476,.T.); -#805 = PLANE('',#806); -#806 = AXIS2_PLACEMENT_3D('',#807,#808,#809); -#807 = CARTESIAN_POINT('',(2.828438300639,3.068404028665,-49.69378323641 - )); -#808 = DIRECTION('',(0.,0.342020143326,-0.939692620786)); -#809 = DIRECTION('',(0.,0.939692620786,0.342020143326)); -#810 = ADVANCED_FACE('',(#811),#817,.T.); -#811 = FACE_BOUND('',#812,.T.); -#812 = EDGE_LOOP('',(#813,#814,#815,#816)); -#813 = ORIENTED_EDGE('',*,*,#468,.T.); -#814 = ORIENTED_EDGE('',*,*,#759,.F.); -#815 = ORIENTED_EDGE('',*,*,#774,.F.); -#816 = ORIENTED_EDGE('',*,*,#798,.F.); -#817 = PLANE('',#818); -#818 = AXIS2_PLACEMENT_3D('',#819,#820,#821); -#819 = CARTESIAN_POINT('',(-5.782806207227,3.068404028665, - -13.93896851312)); -#820 = DIRECTION('',(-0.93629059846,0.342020143326,-7.988827695448E-02) - ); -#821 = DIRECTION('',(0.340781908463,0.939692620786,2.907695487824E-02)); -#822 = ADVANCED_FACE('',(#823),#834,.F.); -#823 = FACE_BOUND('',#824,.F.); -#824 = EDGE_LOOP('',(#825,#831,#832,#833)); -#825 = ORIENTED_EDGE('',*,*,#826,.F.); -#826 = EDGE_CURVE('',#217,#485,#827,.T.); -#827 = LINE('',#828,#829); -#828 = CARTESIAN_POINT('',(-11.6927539352,-22.,-48.00951684793)); -#829 = VECTOR('',#830,1.); -#830 = DIRECTION('',(0.,1.,0.)); -#831 = ORIENTED_EDGE('',*,*,#216,.T.); -#832 = ORIENTED_EDGE('',*,*,#826,.T.); -#833 = ORIENTED_EDGE('',*,*,#484,.F.); -#834 = CYLINDRICAL_SURFACE('',#835,4.735522705283); -#835 = AXIS2_PLACEMENT_3D('',#836,#837,#838); -#836 = CARTESIAN_POINT('',(-16.42827664048,-22.,-48.00951684793)); -#837 = DIRECTION('',(0.,1.,0.)); -#838 = DIRECTION('',(1.,0.,0.)); -#839 = ADVANCED_FACE('',(#840),#851,.F.); -#840 = FACE_BOUND('',#841,.F.); -#841 = EDGE_LOOP('',(#842,#848,#849,#850)); -#842 = ORIENTED_EDGE('',*,*,#843,.F.); -#843 = EDGE_CURVE('',#72,#315,#844,.T.); -#844 = LINE('',#845,#846); -#845 = CARTESIAN_POINT('',(21.163799345768,-22.,-48.00951684793)); -#846 = VECTOR('',#847,1.); -#847 = DIRECTION('',(0.,1.,0.)); -#848 = ORIENTED_EDGE('',*,*,#71,.T.); -#849 = ORIENTED_EDGE('',*,*,#843,.T.); -#850 = ORIENTED_EDGE('',*,*,#314,.F.); -#851 = CYLINDRICAL_SURFACE('',#852,4.735522705283); -#852 = AXIS2_PLACEMENT_3D('',#853,#854,#855); -#853 = CARTESIAN_POINT('',(16.428276640485,-22.,-48.00951684793)); -#854 = DIRECTION('',(0.,1.,0.)); -#855 = DIRECTION('',(1.,0.,0.)); -#856 = ADVANCED_FACE('',(#857),#873,.F.); -#857 = FACE_BOUND('',#858,.F.); -#858 = EDGE_LOOP('',(#859,#865,#866,#872)); -#859 = ORIENTED_EDGE('',*,*,#860,.F.); -#860 = EDGE_CURVE('',#24,#265,#861,.T.); -#861 = LINE('',#862,#863); -#862 = CARTESIAN_POINT('',(16.626582997737,-22.,-8.940188245231)); -#863 = VECTOR('',#864,1.); -#864 = DIRECTION('',(0.,1.,0.)); -#865 = ORIENTED_EDGE('',*,*,#63,.T.); -#866 = ORIENTED_EDGE('',*,*,#867,.T.); -#867 = EDGE_CURVE('',#56,#267,#868,.T.); -#868 = LINE('',#869,#870); -#869 = CARTESIAN_POINT('',(-5.329070518201E-15,-22.,-8.903751135252)); -#870 = VECTOR('',#871,1.); -#871 = DIRECTION('',(0.,1.,0.)); -#872 = ORIENTED_EDGE('',*,*,#264,.F.); -#873 = PLANE('',#874); -#874 = AXIS2_PLACEMENT_3D('',#875,#876,#877); -#875 = CARTESIAN_POINT('',(8.237581109188,-22.,-8.921803528809)); -#876 = DIRECTION('',(-2.191520817069E-03,0.,-0.999997598615)); -#877 = DIRECTION('',(-0.999997598615,0.,2.191520817069E-03)); -#878 = ADVANCED_FACE('',(#879),#890,.F.); -#879 = FACE_BOUND('',#880,.F.); -#880 = EDGE_LOOP('',(#881,#887,#888,#889)); -#881 = ORIENTED_EDGE('',*,*,#882,.T.); -#882 = EDGE_CURVE('',#48,#299,#883,.T.); -#883 = LINE('',#884,#885); -#884 = CARTESIAN_POINT('',(-21.72552223146,-22.,-8.903751135252)); -#885 = VECTOR('',#886,1.); -#886 = DIRECTION('',(0.,1.,0.)); -#887 = ORIENTED_EDGE('',*,*,#306,.F.); -#888 = ORIENTED_EDGE('',*,*,#867,.F.); -#889 = ORIENTED_EDGE('',*,*,#55,.T.); -#890 = PLANE('',#891); -#891 = AXIS2_PLACEMENT_3D('',#892,#893,#894); -#892 = CARTESIAN_POINT('',(-10.96276111573,-22.,-8.903751135252)); -#893 = DIRECTION('',(0.,0.,-1.)); -#894 = DIRECTION('',(0.,1.,0.)); -#895 = ADVANCED_FACE('',(#896),#907,.F.); -#896 = FACE_BOUND('',#897,.F.); -#897 = EDGE_LOOP('',(#898,#904,#905,#906)); -#898 = ORIENTED_EDGE('',*,*,#899,.T.); -#899 = EDGE_CURVE('',#40,#291,#900,.T.); -#900 = LINE('',#901,#902); -#901 = CARTESIAN_POINT('',(-21.72552223146,-22.,-3.734519760785)); -#902 = VECTOR('',#903,1.); -#903 = DIRECTION('',(0.,1.,0.)); -#904 = ORIENTED_EDGE('',*,*,#298,.F.); -#905 = ORIENTED_EDGE('',*,*,#882,.F.); -#906 = ORIENTED_EDGE('',*,*,#47,.T.); -#907 = PLANE('',#908); -#908 = AXIS2_PLACEMENT_3D('',#909,#910,#911); -#909 = CARTESIAN_POINT('',(-21.72552223146,-22.,-6.319135448019)); -#910 = DIRECTION('',(-1.,0.,0.)); -#911 = DIRECTION('',(0.,1.,0.)); -#912 = ADVANCED_FACE('',(#913),#924,.F.); -#913 = FACE_BOUND('',#914,.F.); -#914 = EDGE_LOOP('',(#915,#921,#922,#923)); -#915 = ORIENTED_EDGE('',*,*,#916,.T.); -#916 = EDGE_CURVE('',#32,#283,#917,.T.); -#917 = LINE('',#918,#919); -#918 = CARTESIAN_POINT('',(22.059435554995,-22.,-3.734519760785)); -#919 = VECTOR('',#920,1.); -#920 = DIRECTION('',(0.,1.,0.)); -#921 = ORIENTED_EDGE('',*,*,#290,.F.); -#922 = ORIENTED_EDGE('',*,*,#899,.F.); -#923 = ORIENTED_EDGE('',*,*,#39,.T.); -#924 = PLANE('',#925); -#925 = AXIS2_PLACEMENT_3D('',#926,#927,#928); -#926 = CARTESIAN_POINT('',(0.19111316645,-22.,-3.734519760785)); -#927 = DIRECTION('',(0.,0.,1.)); -#928 = DIRECTION('',(0.,-1.,0.)); -#929 = ADVANCED_FACE('',(#930),#941,.F.); -#930 = FACE_BOUND('',#931,.F.); -#931 = EDGE_LOOP('',(#932,#938,#939,#940)); -#932 = ORIENTED_EDGE('',*,*,#933,.T.); -#933 = EDGE_CURVE('',#22,#275,#934,.T.); -#934 = LINE('',#935,#936); -#935 = CARTESIAN_POINT('',(19.029295926024,-22.,-17.63009960955)); -#936 = VECTOR('',#937,1.); -#937 = DIRECTION('',(0.,1.,0.)); -#938 = ORIENTED_EDGE('',*,*,#282,.F.); -#939 = ORIENTED_EDGE('',*,*,#916,.F.); -#940 = ORIENTED_EDGE('',*,*,#31,.T.); -#941 = PLANE('',#942); -#942 = AXIS2_PLACEMENT_3D('',#943,#944,#945); -#943 = CARTESIAN_POINT('',(20.484588228021,-22.,-10.95643672209)); -#944 = DIRECTION('',(0.977039526026,0.,-0.213058124893)); -#945 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#946 = ADVANCED_FACE('',(#947),#953,.F.); -#947 = FACE_BOUND('',#948,.F.); -#948 = EDGE_LOOP('',(#949,#950,#951,#952)); -#949 = ORIENTED_EDGE('',*,*,#21,.T.); -#950 = ORIENTED_EDGE('',*,*,#860,.T.); -#951 = ORIENTED_EDGE('',*,*,#274,.F.); -#952 = ORIENTED_EDGE('',*,*,#933,.F.); -#953 = PLANE('',#954); -#954 = AXIS2_PLACEMENT_3D('',#955,#956,#957); -#955 = CARTESIAN_POINT('',(17.956031892536,-22.,-13.7484168618)); -#956 = DIRECTION('',(-0.963836182336,0.,-0.26649542889)); -#957 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#958 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) -GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#962)) GLOBAL_UNIT_ASSIGNED_CONTEXT -((#959,#960,#961)) REPRESENTATION_CONTEXT('Context #1', - '3D Context with UNIT and UNCERTAINTY') ); -#959 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) ); -#960 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) ); -#961 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() ); -#962 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-05),#959, - 'distance_accuracy_value','confusion accuracy'); -#963 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7)); -ENDSEC; -END-ISO-10303-21; diff --git a/docs/CAD/design/mate-connectors/BearConnector_Female.step b/docs/CAD/design/mate-connectors/BearConnector_Female.step deleted file mode 100644 index 18e31c4b47..0000000000 --- a/docs/CAD/design/mate-connectors/BearConnector_Female.step +++ /dev/null @@ -1,1173 +0,0 @@ -ISO-10303-21; -HEADER; -FILE_DESCRIPTION(('FreeCAD Model'),'2;1'); -FILE_NAME('Open CASCADE Shape Model','2026-08-05T10:54:26',('FreeCAD'),( - 'FreeCAD'),'Open CASCADE STEP processor 7.8','FreeCAD','Unknown'); -FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }')); -ENDSEC; -DATA; -#1 = APPLICATION_PROTOCOL_DEFINITION('international standard', - 'automotive_design',2000,#2); -#2 = APPLICATION_CONTEXT( - 'core data for automotive mechanical design processes'); -#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10); -#4 = PRODUCT_DEFINITION_SHAPE('','',#5); -#5 = PRODUCT_DEFINITION('design','',#6,#9); -#6 = PRODUCT_DEFINITION_FORMATION('','',#7); -#7 = PRODUCT('Open CASCADE STEP translator 7.8 1', - 'Open CASCADE STEP translator 7.8 1','',(#8)); -#8 = PRODUCT_CONTEXT('',#2,'mechanical'); -#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design'); -#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#1118); -#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14); -#12 = CARTESIAN_POINT('',(0.,0.,0.)); -#13 = DIRECTION('',(0.,0.,1.)); -#14 = DIRECTION('',(1.,0.,-0.)); -#15 = MANIFOLD_SOLID_BREP('',#16); -#16 = CLOSED_SHELL('',(#17,#57,#222,#246,#270,#287,#299,#330,#354,#378, - #402,#426,#451,#475,#500,#524,#548,#573,#597,#622,#646,#670,#687, - #817,#848,#872,#896,#920,#944,#961,#986,#1017,#1033,#1050,#1061, - #1086,#1100,#1109)); -#17 = ADVANCED_FACE('',(#18),#52,.F.); -#18 = FACE_BOUND('',#19,.F.); -#19 = EDGE_LOOP('',(#20,#30,#38,#46)); -#20 = ORIENTED_EDGE('',*,*,#21,.F.); -#21 = EDGE_CURVE('',#22,#24,#26,.T.); -#22 = VERTEX_POINT('',#23); -#23 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#24 = VERTEX_POINT('',#25); -#25 = CARTESIAN_POINT('',(-45.46495478095,0.,4.2)); -#26 = LINE('',#27,#28); -#27 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#28 = VECTOR('',#29,1.); -#29 = DIRECTION('',(0.,0.,1.)); -#30 = ORIENTED_EDGE('',*,*,#31,.T.); -#31 = EDGE_CURVE('',#22,#32,#34,.T.); -#32 = VERTEX_POINT('',#33); -#33 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,-70.97315781796) - ); -#34 = LINE('',#35,#36); -#35 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#36 = VECTOR('',#37,1.); -#37 = DIRECTION('',(0.,1.,0.)); -#38 = ORIENTED_EDGE('',*,*,#39,.T.); -#39 = EDGE_CURVE('',#32,#40,#42,.T.); -#40 = VERTEX_POINT('',#41); -#41 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,4.2)); -#42 = LINE('',#43,#44); -#43 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,-70.97315781796) - ); -#44 = VECTOR('',#45,1.); -#45 = DIRECTION('',(0.,0.,1.)); -#46 = ORIENTED_EDGE('',*,*,#47,.F.); -#47 = EDGE_CURVE('',#24,#40,#48,.T.); -#48 = LINE('',#49,#50); -#49 = CARTESIAN_POINT('',(-45.46495478095,0.,4.2)); -#50 = VECTOR('',#51,1.); -#51 = DIRECTION('',(0.,1.,0.)); -#52 = PLANE('',#53); -#53 = AXIS2_PLACEMENT_3D('',#54,#55,#56); -#54 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#55 = DIRECTION('',(1.,0.,0.)); -#56 = DIRECTION('',(0.,0.,1.)); -#57 = ADVANCED_FACE('',(#58,#83),#217,.F.); -#58 = FACE_BOUND('',#59,.F.); -#59 = EDGE_LOOP('',(#60,#68,#69,#77)); -#60 = ORIENTED_EDGE('',*,*,#61,.F.); -#61 = EDGE_CURVE('',#22,#62,#64,.T.); -#62 = VERTEX_POINT('',#63); -#63 = CARTESIAN_POINT('',(46.298608189024,0.,-70.97315781796)); -#64 = LINE('',#65,#66); -#65 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#66 = VECTOR('',#67,1.); -#67 = DIRECTION('',(1.,0.,0.)); -#68 = ORIENTED_EDGE('',*,*,#21,.T.); -#69 = ORIENTED_EDGE('',*,*,#70,.T.); -#70 = EDGE_CURVE('',#24,#71,#73,.T.); -#71 = VERTEX_POINT('',#72); -#72 = CARTESIAN_POINT('',(46.298608189024,0.,4.2)); -#73 = LINE('',#74,#75); -#74 = CARTESIAN_POINT('',(-45.46495478095,0.,4.2)); -#75 = VECTOR('',#76,1.); -#76 = DIRECTION('',(1.,0.,0.)); -#77 = ORIENTED_EDGE('',*,*,#78,.F.); -#78 = EDGE_CURVE('',#62,#71,#79,.T.); -#79 = LINE('',#80,#81); -#80 = CARTESIAN_POINT('',(46.298608189024,0.,-70.97315781796)); -#81 = VECTOR('',#82,1.); -#82 = DIRECTION('',(0.,0.,1.)); -#83 = FACE_BOUND('',#84,.F.); -#84 = EDGE_LOOP('',(#85,#95,#103,#111,#119,#128,#136,#145,#153,#161,#170 - ,#178,#187,#195,#203,#211)); -#85 = ORIENTED_EDGE('',*,*,#86,.T.); -#86 = EDGE_CURVE('',#87,#89,#91,.T.); -#87 = VERTEX_POINT('',#88); -#88 = CARTESIAN_POINT('',(32.703857168398,0.,-60.78483712899)); -#89 = VERTEX_POINT('',#90); -#90 = CARTESIAN_POINT('',(26.938753236523,0.,-55.20570756731)); -#91 = LINE('',#92,#93); -#92 = CARTESIAN_POINT('',(13.567306766147,0.,-42.26560567724)); -#93 = VECTOR('',#94,1.); -#94 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#95 = ORIENTED_EDGE('',*,*,#96,.T.); -#96 = EDGE_CURVE('',#89,#97,#99,.T.); -#97 = VERTEX_POINT('',#98); -#98 = CARTESIAN_POINT('',(42.298608189024,0.,-7.015765476549)); -#99 = LINE('',#100,#101); -#100 = CARTESIAN_POINT('',(25.140315874087,-4.440892098501E-16, - -60.84811712245)); -#101 = VECTOR('',#102,1.); -#102 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#103 = ORIENTED_EDGE('',*,*,#104,.T.); -#104 = EDGE_CURVE('',#97,#105,#107,.T.); -#105 = VERTEX_POINT('',#106); -#106 = CARTESIAN_POINT('',(35.082842712475,0.,0.2)); -#107 = LINE('',#108,#109); -#108 = CARTESIAN_POINT('',(34.536239068456,0.,0.746603644019)); -#109 = VECTOR('',#110,1.); -#110 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#111 = ORIENTED_EDGE('',*,*,#112,.F.); -#112 = EDGE_CURVE('',#113,#105,#115,.T.); -#113 = VERTEX_POINT('',#114); -#114 = CARTESIAN_POINT('',(-32.57517518159,0.,0.2)); -#115 = LINE('',#116,#117); -#116 = CARTESIAN_POINT('',(-30.87627118587,0.,0.2)); -#117 = VECTOR('',#118,1.); -#118 = DIRECTION('',(1.,0.,0.)); -#119 = ORIENTED_EDGE('',*,*,#120,.T.); -#120 = EDGE_CURVE('',#113,#121,#123,.T.); -#121 = VERTEX_POINT('',#122); -#122 = CARTESIAN_POINT('',(-36.85603142851,0.,-1.573188716044)); -#123 = CIRCLE('',#124,6.054044962965); -#124 = AXIS2_PLACEMENT_3D('',#125,#126,#127); -#125 = CARTESIAN_POINT('',(-32.57517518159,0.,-5.854044962965)); -#126 = DIRECTION('',(0.,-1.,0.)); -#127 = DIRECTION('',(-1.,0.,0.)); -#128 = ORIENTED_EDGE('',*,*,#129,.F.); -#129 = EDGE_CURVE('',#130,#121,#132,.T.); -#130 = VERTEX_POINT('',#131); -#131 = CARTESIAN_POINT('',(-39.69176606491,0.,-4.408923352436)); -#132 = LINE('',#133,#134); -#133 = CARTESIAN_POINT('',(-57.0671882012,0.,-21.78434548873)); -#134 = VECTOR('',#135,1.); -#135 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#136 = ORIENTED_EDGE('',*,*,#137,.T.); -#137 = EDGE_CURVE('',#130,#138,#140,.T.); -#138 = VERTEX_POINT('',#139); -#139 = CARTESIAN_POINT('',(-41.17904151244,0.,-10.52828909594)); -#140 = CIRCLE('',#141,6.054044962965); -#141 = AXIS2_PLACEMENT_3D('',#142,#143,#144); -#142 = CARTESIAN_POINT('',(-35.41090981799,0.,-8.689779599357)); -#143 = DIRECTION('',(0.,-1.,0.)); -#144 = DIRECTION('',(-1.,0.,0.)); -#145 = ORIENTED_EDGE('',*,*,#146,.F.); -#146 = EDGE_CURVE('',#147,#138,#149,.T.); -#147 = VERTEX_POINT('',#148); -#148 = CARTESIAN_POINT('',(-26.93875323652,0.,-55.20570756731)); -#149 = LINE('',#150,#151); -#150 = CARTESIAN_POINT('',(-29.06259699304,-4.440892098501E-16, - -48.54236940733)); -#151 = VECTOR('',#152,1.); -#152 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#153 = ORIENTED_EDGE('',*,*,#154,.F.); -#154 = EDGE_CURVE('',#155,#147,#157,.T.); -#155 = VERTEX_POINT('',#156); -#156 = CARTESIAN_POINT('',(-28.96712497021,0.,-57.16864680227)); -#157 = LINE('',#158,#159); -#158 = CARTESIAN_POINT('',(-36.14667143517,0.,-64.11659091489)); -#159 = VECTOR('',#160,1.); -#160 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#161 = ORIENTED_EDGE('',*,*,#162,.T.); -#162 = EDGE_CURVE('',#155,#163,#165,.T.); -#163 = VERTEX_POINT('',#164); -#164 = CARTESIAN_POINT('',(-29.08766684168,0.,-64.52156932717)); -#165 = CIRCLE('',#166,5.2); -#166 = AXIS2_PLACEMENT_3D('',#167,#168,#169); -#167 = CARTESIAN_POINT('',(-25.35093464349,0.,-60.90537900045)); -#168 = DIRECTION('',(0.,-1.,0.)); -#169 = DIRECTION('',(-1.,0.,0.)); -#170 = ORIENTED_EDGE('',*,*,#171,.F.); -#171 = EDGE_CURVE('',#172,#163,#174,.T.); -#172 = VERTEX_POINT('',#173); -#173 = CARTESIAN_POINT('',(-27.86158468659,0.,-65.78852163128)); -#174 = LINE('',#175,#176); -#175 = CARTESIAN_POINT('',(-31.12923147938,0.,-62.41195129628)); -#176 = VECTOR('',#177,1.); -#177 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#178 = ORIENTED_EDGE('',*,*,#179,.T.); -#179 = EDGE_CURVE('',#172,#180,#182,.T.); -#180 = VERTEX_POINT('',#181); -#181 = CARTESIAN_POINT('',(-25.53052705686,0.,-65.82673637491)); -#182 = CIRCLE('',#183,1.648528137424); -#183 = AXIS2_PLACEMENT_3D('',#184,#185,#186); -#184 = CARTESIAN_POINT('',(-26.67694849991,0.,-64.64210018823)); -#185 = DIRECTION('',(0.,-1.,0.)); -#186 = DIRECTION('',(-1.,0.,0.)); -#187 = ORIENTED_EDGE('',*,*,#188,.F.); -#188 = EDGE_CURVE('',#189,#180,#191,.T.); -#189 = VERTEX_POINT('',#190); -#190 = CARTESIAN_POINT('',(-20.53201900137,0.,-60.98947335481)); -#191 = LINE('',#192,#193); -#192 = CARTESIAN_POINT('',(-30.69923804215,0.,-70.82871181101)); -#193 = VECTOR('',#194,1.); -#194 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#195 = ORIENTED_EDGE('',*,*,#196,.T.); -#196 = EDGE_CURVE('',#189,#197,#199,.T.); -#197 = VERTEX_POINT('',#198); -#198 = CARTESIAN_POINT('',(20.532019001374,0.,-60.98947335481)); -#199 = LINE('',#200,#201); -#200 = CARTESIAN_POINT('',(-17.57924046663,0.,-60.98947335481)); -#201 = VECTOR('',#202,1.); -#202 = DIRECTION('',(1.,0.,0.)); -#203 = ORIENTED_EDGE('',*,*,#204,.T.); -#204 = EDGE_CURVE('',#197,#205,#207,.T.); -#205 = VERTEX_POINT('',#206); -#206 = CARTESIAN_POINT('',(26.715163243538,0.,-66.97315781796)); -#207 = LINE('',#208,#209); -#208 = CARTESIAN_POINT('',(7.481849370247,-4.440892098501E-16, - -48.36028435244)); -#209 = VECTOR('',#210,1.); -#210 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#211 = ORIENTED_EDGE('',*,*,#212,.T.); -#212 = EDGE_CURVE('',#205,#87,#213,.T.); -#213 = LINE('',#214,#215); -#214 = CARTESIAN_POINT('',(9.75933652063,0.,-84.4941890519)); -#215 = VECTOR('',#216,1.); -#216 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#217 = PLANE('',#218); -#218 = AXIS2_PLACEMENT_3D('',#219,#220,#221); -#219 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#220 = DIRECTION('',(0.,1.,0.)); -#221 = DIRECTION('',(0.,0.,1.)); -#222 = ADVANCED_FACE('',(#223),#241,.T.); -#223 = FACE_BOUND('',#224,.T.); -#224 = EDGE_LOOP('',(#225,#226,#227,#235)); -#225 = ORIENTED_EDGE('',*,*,#47,.F.); -#226 = ORIENTED_EDGE('',*,*,#70,.T.); -#227 = ORIENTED_EDGE('',*,*,#228,.T.); -#228 = EDGE_CURVE('',#71,#229,#231,.T.); -#229 = VERTEX_POINT('',#230); -#230 = CARTESIAN_POINT('',(46.298608189024,20.8572529153,4.2)); -#231 = LINE('',#232,#233); -#232 = CARTESIAN_POINT('',(46.298608189024,0.,4.2)); -#233 = VECTOR('',#234,1.); -#234 = DIRECTION('',(0.,1.,0.)); -#235 = ORIENTED_EDGE('',*,*,#236,.F.); -#236 = EDGE_CURVE('',#40,#229,#237,.T.); -#237 = LINE('',#238,#239); -#238 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,4.2)); -#239 = VECTOR('',#240,1.); -#240 = DIRECTION('',(1.,0.,0.)); -#241 = PLANE('',#242); -#242 = AXIS2_PLACEMENT_3D('',#243,#244,#245); -#243 = CARTESIAN_POINT('',(-45.46495478095,0.,4.2)); -#244 = DIRECTION('',(0.,0.,1.)); -#245 = DIRECTION('',(1.,0.,0.)); -#246 = ADVANCED_FACE('',(#247),#265,.T.); -#247 = FACE_BOUND('',#248,.T.); -#248 = EDGE_LOOP('',(#249,#257,#258,#259)); -#249 = ORIENTED_EDGE('',*,*,#250,.F.); -#250 = EDGE_CURVE('',#32,#251,#253,.T.); -#251 = VERTEX_POINT('',#252); -#252 = CARTESIAN_POINT('',(46.298608189024,20.8572529153,-70.97315781796 - )); -#253 = LINE('',#254,#255); -#254 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,-70.97315781796 - )); -#255 = VECTOR('',#256,1.); -#256 = DIRECTION('',(1.,0.,0.)); -#257 = ORIENTED_EDGE('',*,*,#39,.T.); -#258 = ORIENTED_EDGE('',*,*,#236,.T.); -#259 = ORIENTED_EDGE('',*,*,#260,.F.); -#260 = EDGE_CURVE('',#251,#229,#261,.T.); -#261 = LINE('',#262,#263); -#262 = CARTESIAN_POINT('',(46.298608189024,20.8572529153,-70.97315781796 - )); -#263 = VECTOR('',#264,1.); -#264 = DIRECTION('',(0.,0.,1.)); -#265 = PLANE('',#266); -#266 = AXIS2_PLACEMENT_3D('',#267,#268,#269); -#267 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,-70.97315781796 - )); -#268 = DIRECTION('',(0.,1.,0.)); -#269 = DIRECTION('',(0.,0.,1.)); -#270 = ADVANCED_FACE('',(#271),#282,.F.); -#271 = FACE_BOUND('',#272,.F.); -#272 = EDGE_LOOP('',(#273,#274,#275,#281)); -#273 = ORIENTED_EDGE('',*,*,#31,.F.); -#274 = ORIENTED_EDGE('',*,*,#61,.T.); -#275 = ORIENTED_EDGE('',*,*,#276,.T.); -#276 = EDGE_CURVE('',#62,#251,#277,.T.); -#277 = LINE('',#278,#279); -#278 = CARTESIAN_POINT('',(46.298608189024,0.,-70.97315781796)); -#279 = VECTOR('',#280,1.); -#280 = DIRECTION('',(0.,1.,0.)); -#281 = ORIENTED_EDGE('',*,*,#250,.F.); -#282 = PLANE('',#283); -#283 = AXIS2_PLACEMENT_3D('',#284,#285,#286); -#284 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#285 = DIRECTION('',(0.,0.,1.)); -#286 = DIRECTION('',(1.,0.,0.)); -#287 = ADVANCED_FACE('',(#288),#294,.T.); -#288 = FACE_BOUND('',#289,.T.); -#289 = EDGE_LOOP('',(#290,#291,#292,#293)); -#290 = ORIENTED_EDGE('',*,*,#78,.F.); -#291 = ORIENTED_EDGE('',*,*,#276,.T.); -#292 = ORIENTED_EDGE('',*,*,#260,.T.); -#293 = ORIENTED_EDGE('',*,*,#228,.F.); -#294 = PLANE('',#295); -#295 = AXIS2_PLACEMENT_3D('',#296,#297,#298); -#296 = CARTESIAN_POINT('',(46.298608189024,0.,-70.97315781796)); -#297 = DIRECTION('',(1.,0.,0.)); -#298 = DIRECTION('',(0.,0.,1.)); -#299 = ADVANCED_FACE('',(#300),#325,.T.); -#300 = FACE_BOUND('',#301,.T.); -#301 = EDGE_LOOP('',(#302,#310,#311,#319)); -#302 = ORIENTED_EDGE('',*,*,#303,.F.); -#303 = EDGE_CURVE('',#87,#304,#306,.T.); -#304 = VERTEX_POINT('',#305); -#305 = CARTESIAN_POINT('',(32.703857168398,3.2,-60.78483712899)); -#306 = LINE('',#307,#308); -#307 = CARTESIAN_POINT('',(32.703857168398,3.,-60.78483712899)); -#308 = VECTOR('',#309,1.); -#309 = DIRECTION('',(0.,1.,0.)); -#310 = ORIENTED_EDGE('',*,*,#86,.T.); -#311 = ORIENTED_EDGE('',*,*,#312,.F.); -#312 = EDGE_CURVE('',#313,#89,#315,.T.); -#313 = VERTEX_POINT('',#314); -#314 = CARTESIAN_POINT('',(26.938753236523,3.2,-55.20570756731)); -#315 = LINE('',#316,#317); -#316 = CARTESIAN_POINT('',(26.938753236523,3.,-55.20570756731)); -#317 = VECTOR('',#318,1.); -#318 = DIRECTION('',(0.,-1.,0.)); -#319 = ORIENTED_EDGE('',*,*,#320,.F.); -#320 = EDGE_CURVE('',#304,#313,#321,.T.); -#321 = LINE('',#322,#323); -#322 = CARTESIAN_POINT('',(16.008242280412,3.2,-44.62779994931)); -#323 = VECTOR('',#324,1.); -#324 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#325 = PLANE('',#326); -#326 = AXIS2_PLACEMENT_3D('',#327,#328,#329); -#327 = CARTESIAN_POINT('',(29.704873980143,3.,-57.88259703786)); -#328 = DIRECTION('',(-0.695421216677,0.,-0.718602345805)); -#329 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#330 = ADVANCED_FACE('',(#331),#349,.T.); -#331 = FACE_BOUND('',#332,.T.); -#332 = EDGE_LOOP('',(#333,#341,#342,#343)); -#333 = ORIENTED_EDGE('',*,*,#334,.F.); -#334 = EDGE_CURVE('',#205,#335,#337,.T.); -#335 = VERTEX_POINT('',#336); -#336 = CARTESIAN_POINT('',(26.715163243538,3.2,-66.97315781796)); -#337 = LINE('',#338,#339); -#338 = CARTESIAN_POINT('',(26.715163243538,3.,-66.97315781796)); -#339 = VECTOR('',#340,1.); -#340 = DIRECTION('',(0.,1.,0.)); -#341 = ORIENTED_EDGE('',*,*,#212,.T.); -#342 = ORIENTED_EDGE('',*,*,#303,.T.); -#343 = ORIENTED_EDGE('',*,*,#344,.F.); -#344 = EDGE_CURVE('',#335,#304,#345,.T.); -#345 = LINE('',#346,#347); -#346 = CARTESIAN_POINT('',(30.25240665457,3.2,-63.31800414721)); -#347 = VECTOR('',#348,1.); -#348 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#349 = PLANE('',#350); -#350 = AXIS2_PLACEMENT_3D('',#351,#352,#353); -#351 = CARTESIAN_POINT('',(29.709510205968,3.,-63.87899747347)); -#352 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#353 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#354 = ADVANCED_FACE('',(#355),#373,.T.); -#355 = FACE_BOUND('',#356,.T.); -#356 = EDGE_LOOP('',(#357,#365,#371,#372)); -#357 = ORIENTED_EDGE('',*,*,#358,.F.); -#358 = EDGE_CURVE('',#359,#335,#361,.T.); -#359 = VERTEX_POINT('',#360); -#360 = CARTESIAN_POINT('',(20.532019001374,3.2,-60.98947335481)); -#361 = LINE('',#362,#363); -#362 = CARTESIAN_POINT('',(9.922784884512,3.2,-50.72247862452)); -#363 = VECTOR('',#364,1.); -#364 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#365 = ORIENTED_EDGE('',*,*,#366,.T.); -#366 = EDGE_CURVE('',#359,#197,#367,.T.); -#367 = LINE('',#368,#369); -#368 = CARTESIAN_POINT('',(20.532019001374,3.,-60.98947335481)); -#369 = VECTOR('',#370,1.); -#370 = DIRECTION('',(0.,-1.,0.)); -#371 = ORIENTED_EDGE('',*,*,#204,.T.); -#372 = ORIENTED_EDGE('',*,*,#334,.T.); -#373 = PLANE('',#374); -#374 = AXIS2_PLACEMENT_3D('',#375,#376,#377); -#375 = CARTESIAN_POINT('',(23.522653113203,3.,-63.8836336993)); -#376 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#377 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#378 = ADVANCED_FACE('',(#379),#397,.T.); -#379 = FACE_BOUND('',#380,.T.); -#380 = EDGE_LOOP('',(#381,#389,#390,#391)); -#381 = ORIENTED_EDGE('',*,*,#382,.F.); -#382 = EDGE_CURVE('',#189,#383,#385,.T.); -#383 = VERTEX_POINT('',#384); -#384 = CARTESIAN_POINT('',(-20.53201900137,3.2,-60.98947335481)); -#385 = LINE('',#386,#387); -#386 = CARTESIAN_POINT('',(-20.53201900137,3.,-60.98947335481)); -#387 = VECTOR('',#388,1.); -#388 = DIRECTION('',(0.,1.,0.)); -#389 = ORIENTED_EDGE('',*,*,#196,.T.); -#390 = ORIENTED_EDGE('',*,*,#366,.F.); -#391 = ORIENTED_EDGE('',*,*,#392,.F.); -#392 = EDGE_CURVE('',#383,#359,#393,.T.); -#393 = LINE('',#394,#395); -#394 = CARTESIAN_POINT('',(5.354765181569,3.2,-60.98947335481)); -#395 = VECTOR('',#396,1.); -#396 = DIRECTION('',(1.,0.,0.)); -#397 = PLANE('',#398); -#398 = AXIS2_PLACEMENT_3D('',#399,#400,#401); -#399 = CARTESIAN_POINT('',(10.306473847682,3.,-60.98947335481)); -#400 = DIRECTION('',(0.,0.,1.)); -#401 = DIRECTION('',(0.,-1.,0.)); -#402 = ADVANCED_FACE('',(#403),#421,.F.); -#403 = FACE_BOUND('',#404,.F.); -#404 = EDGE_LOOP('',(#405,#413,#419,#420)); -#405 = ORIENTED_EDGE('',*,*,#406,.F.); -#406 = EDGE_CURVE('',#407,#180,#409,.T.); -#407 = VERTEX_POINT('',#408); -#408 = CARTESIAN_POINT('',(-25.53052705686,3.2,-65.82673637491)); -#409 = LINE('',#410,#411); -#410 = CARTESIAN_POINT('',(-25.53052705686,3.,-65.82673637491)); -#411 = VECTOR('',#412,1.); -#412 = DIRECTION('',(0.,-1.,0.)); -#413 = ORIENTED_EDGE('',*,*,#414,.F.); -#414 = EDGE_CURVE('',#383,#407,#415,.T.); -#415 = LINE('',#416,#417); -#416 = CARTESIAN_POINT('',(-9.454421870603,3.2,-50.26922436104)); -#417 = VECTOR('',#418,1.); -#418 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#419 = ORIENTED_EDGE('',*,*,#382,.F.); -#420 = ORIENTED_EDGE('',*,*,#188,.T.); -#421 = PLANE('',#422); -#422 = AXIS2_PLACEMENT_3D('',#423,#424,#425); -#423 = CARTESIAN_POINT('',(-23.00219525444,3.,-63.37996509944)); -#424 = DIRECTION('',(0.695421216677,0.,-0.718602345805)); -#425 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#426 = ADVANCED_FACE('',(#427),#446,.F.); -#427 = FACE_BOUND('',#428,.F.); -#428 = EDGE_LOOP('',(#429,#437,#444,#445)); -#429 = ORIENTED_EDGE('',*,*,#430,.F.); -#430 = EDGE_CURVE('',#431,#172,#433,.T.); -#431 = VERTEX_POINT('',#432); -#432 = CARTESIAN_POINT('',(-27.86158468659,3.2,-65.78852163128)); -#433 = LINE('',#434,#435); -#434 = CARTESIAN_POINT('',(-27.86158468659,3.,-65.78852163128)); -#435 = VECTOR('',#436,1.); -#436 = DIRECTION('',(0.,-1.,0.)); -#437 = ORIENTED_EDGE('',*,*,#438,.T.); -#438 = EDGE_CURVE('',#431,#407,#439,.T.); -#439 = CIRCLE('',#440,1.648528137424); -#440 = AXIS2_PLACEMENT_3D('',#441,#442,#443); -#441 = CARTESIAN_POINT('',(-26.67694849991,3.2,-64.64210018823)); -#442 = DIRECTION('',(0.,-1.,0.)); -#443 = DIRECTION('',(-1.,0.,0.)); -#444 = ORIENTED_EDGE('',*,*,#406,.T.); -#445 = ORIENTED_EDGE('',*,*,#179,.F.); -#446 = CYLINDRICAL_SURFACE('',#447,1.648528137424); -#447 = AXIS2_PLACEMENT_3D('',#448,#449,#450); -#448 = CARTESIAN_POINT('',(-26.67694849991,3.,-64.64210018823)); -#449 = DIRECTION('',(0.,-1.,0.)); -#450 = DIRECTION('',(-1.,0.,0.)); -#451 = ADVANCED_FACE('',(#452),#470,.F.); -#452 = FACE_BOUND('',#453,.F.); -#453 = EDGE_LOOP('',(#454,#462,#468,#469)); -#454 = ORIENTED_EDGE('',*,*,#455,.F.); -#455 = EDGE_CURVE('',#456,#163,#458,.T.); -#456 = VERTEX_POINT('',#457); -#457 = CARTESIAN_POINT('',(-29.08766684168,3.2,-64.52156932717)); -#458 = LINE('',#459,#460); -#459 = CARTESIAN_POINT('',(-29.08766684168,3.,-64.52156932717)); -#460 = VECTOR('',#461,1.); -#461 = DIRECTION('',(0.,-1.,0.)); -#462 = ORIENTED_EDGE('',*,*,#463,.F.); -#463 = EDGE_CURVE('',#431,#456,#464,.T.); -#464 = LINE('',#465,#466); -#465 = CARTESIAN_POINT('',(-29.44004200272,3.2,-64.15744811364)); -#466 = VECTOR('',#467,1.); -#467 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#468 = ORIENTED_EDGE('',*,*,#430,.T.); -#469 = ORIENTED_EDGE('',*,*,#171,.T.); -#470 = PLANE('',#471); -#471 = AXIS2_PLACEMENT_3D('',#472,#473,#474); -#472 = CARTESIAN_POINT('',(-28.47462576413,3.,-65.15504547923)); -#473 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#474 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#475 = ADVANCED_FACE('',(#476),#495,.F.); -#476 = FACE_BOUND('',#477,.F.); -#477 = EDGE_LOOP('',(#478,#486,#493,#494)); -#478 = ORIENTED_EDGE('',*,*,#479,.F.); -#479 = EDGE_CURVE('',#480,#155,#482,.T.); -#480 = VERTEX_POINT('',#481); -#481 = CARTESIAN_POINT('',(-28.96712497021,3.2,-57.16864680227)); -#482 = LINE('',#483,#484); -#483 = CARTESIAN_POINT('',(-28.96712497021,3.,-57.16864680227)); -#484 = VECTOR('',#485,1.); -#485 = DIRECTION('',(0.,-1.,0.)); -#486 = ORIENTED_EDGE('',*,*,#487,.T.); -#487 = EDGE_CURVE('',#480,#456,#488,.T.); -#488 = CIRCLE('',#489,5.2); -#489 = AXIS2_PLACEMENT_3D('',#490,#491,#492); -#490 = CARTESIAN_POINT('',(-25.35093464349,3.2,-60.90537900045)); -#491 = DIRECTION('',(0.,-1.,0.)); -#492 = DIRECTION('',(-1.,0.,0.)); -#493 = ORIENTED_EDGE('',*,*,#455,.T.); -#494 = ORIENTED_EDGE('',*,*,#162,.F.); -#495 = CYLINDRICAL_SURFACE('',#496,5.2); -#496 = AXIS2_PLACEMENT_3D('',#497,#498,#499); -#497 = CARTESIAN_POINT('',(-25.35093464349,3.,-60.90537900045)); -#498 = DIRECTION('',(0.,-1.,0.)); -#499 = DIRECTION('',(-1.,0.,0.)); -#500 = ADVANCED_FACE('',(#501),#519,.F.); -#501 = FACE_BOUND('',#502,.F.); -#502 = EDGE_LOOP('',(#503,#511,#517,#518)); -#503 = ORIENTED_EDGE('',*,*,#504,.F.); -#504 = EDGE_CURVE('',#505,#147,#507,.T.); -#505 = VERTEX_POINT('',#506); -#506 = CARTESIAN_POINT('',(-26.93875323652,3.2,-55.20570756731)); -#507 = LINE('',#508,#509); -#508 = CARTESIAN_POINT('',(-26.93875323652,3.,-55.20570756731)); -#509 = VECTOR('',#510,1.); -#510 = DIRECTION('',(0.,-1.,0.)); -#511 = ORIENTED_EDGE('',*,*,#512,.F.); -#512 = EDGE_CURVE('',#480,#505,#513,.T.); -#513 = LINE('',#514,#515); -#514 = CARTESIAN_POINT('',(-14.90185526362,3.2,-43.55710346492)); -#515 = VECTOR('',#516,1.); -#516 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#517 = ORIENTED_EDGE('',*,*,#479,.T.); -#518 = ORIENTED_EDGE('',*,*,#154,.T.); -#519 = PLANE('',#520); -#520 = AXIS2_PLACEMENT_3D('',#521,#522,#523); -#521 = CARTESIAN_POINT('',(-27.90836811563,3.,-56.14404399617)); -#522 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#523 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#524 = ADVANCED_FACE('',(#525),#543,.F.); -#525 = FACE_BOUND('',#526,.F.); -#526 = EDGE_LOOP('',(#527,#535,#541,#542)); -#527 = ORIENTED_EDGE('',*,*,#528,.F.); -#528 = EDGE_CURVE('',#529,#138,#531,.T.); -#529 = VERTEX_POINT('',#530); -#530 = CARTESIAN_POINT('',(-41.17904151244,3.2,-10.52828909594)); -#531 = LINE('',#532,#533); -#532 = CARTESIAN_POINT('',(-41.17904151244,3.,-10.52828909594)); -#533 = VECTOR('',#534,1.); -#534 = DIRECTION('',(0.,-1.,0.)); -#535 = ORIENTED_EDGE('',*,*,#536,.F.); -#536 = EDGE_CURVE('',#505,#529,#537,.T.); -#537 = LINE('',#538,#539); -#538 = CARTESIAN_POINT('',(-32.39120436163,3.2,-38.09921113329)); -#539 = VECTOR('',#540,1.); -#540 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#541 = ORIENTED_EDGE('',*,*,#504,.T.); -#542 = ORIENTED_EDGE('',*,*,#146,.T.); -#543 = PLANE('',#544); -#544 = AXIS2_PLACEMENT_3D('',#545,#546,#547); -#545 = CARTESIAN_POINT('',(-34.04006158346,3.,-32.92609366041)); -#546 = DIRECTION('',(-0.952773183837,0.,-0.30368282823)); -#547 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#548 = ADVANCED_FACE('',(#549),#568,.F.); -#549 = FACE_BOUND('',#550,.F.); -#550 = EDGE_LOOP('',(#551,#559,#566,#567)); -#551 = ORIENTED_EDGE('',*,*,#552,.F.); -#552 = EDGE_CURVE('',#553,#130,#555,.T.); -#553 = VERTEX_POINT('',#554); -#554 = CARTESIAN_POINT('',(-39.69176606491,3.2,-4.408923352436)); -#555 = LINE('',#556,#557); -#556 = CARTESIAN_POINT('',(-39.69176606491,3.,-4.408923352436)); -#557 = VECTOR('',#558,1.); -#558 = DIRECTION('',(0.,-1.,0.)); -#559 = ORIENTED_EDGE('',*,*,#560,.T.); -#560 = EDGE_CURVE('',#553,#529,#561,.T.); -#561 = CIRCLE('',#562,6.054044962965); -#562 = AXIS2_PLACEMENT_3D('',#563,#564,#565); -#563 = CARTESIAN_POINT('',(-35.41090981799,3.2,-8.689779599357)); -#564 = DIRECTION('',(0.,-1.,0.)); -#565 = DIRECTION('',(-1.,0.,0.)); -#566 = ORIENTED_EDGE('',*,*,#528,.T.); -#567 = ORIENTED_EDGE('',*,*,#137,.F.); -#568 = CYLINDRICAL_SURFACE('',#569,6.054044962965); -#569 = AXIS2_PLACEMENT_3D('',#570,#571,#572); -#570 = CARTESIAN_POINT('',(-35.41090981799,3.,-8.689779599357)); -#571 = DIRECTION('',(0.,-1.,0.)); -#572 = DIRECTION('',(-1.,0.,0.)); -#573 = ADVANCED_FACE('',(#574),#592,.F.); -#574 = FACE_BOUND('',#575,.F.); -#575 = EDGE_LOOP('',(#576,#584,#590,#591)); -#576 = ORIENTED_EDGE('',*,*,#577,.F.); -#577 = EDGE_CURVE('',#578,#121,#580,.T.); -#578 = VERTEX_POINT('',#579); -#579 = CARTESIAN_POINT('',(-36.85603142851,3.2,-1.573188716044)); -#580 = LINE('',#581,#582); -#581 = CARTESIAN_POINT('',(-36.85603142851,3.,-1.573188716044)); -#582 = VECTOR('',#583,1.); -#583 = DIRECTION('',(0.,-1.,0.)); -#584 = ORIENTED_EDGE('',*,*,#585,.F.); -#585 = EDGE_CURVE('',#553,#578,#586,.T.); -#586 = LINE('',#587,#588); -#587 = CARTESIAN_POINT('',(-36.19319006079,3.2,-0.910347348321)); -#588 = VECTOR('',#589,1.); -#589 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#590 = ORIENTED_EDGE('',*,*,#552,.T.); -#591 = ORIENTED_EDGE('',*,*,#129,.T.); -#592 = PLANE('',#593); -#593 = AXIS2_PLACEMENT_3D('',#594,#595,#596); -#594 = CARTESIAN_POINT('',(-38.27389874671,3.,-2.99105603424)); -#595 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#596 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#597 = ADVANCED_FACE('',(#598),#617,.F.); -#598 = FACE_BOUND('',#599,.F.); -#599 = EDGE_LOOP('',(#600,#608,#615,#616)); -#600 = ORIENTED_EDGE('',*,*,#601,.F.); -#601 = EDGE_CURVE('',#602,#113,#604,.T.); -#602 = VERTEX_POINT('',#603); -#603 = CARTESIAN_POINT('',(-32.57517518159,3.2,0.2)); -#604 = LINE('',#605,#606); -#605 = CARTESIAN_POINT('',(-32.57517518159,3.,0.2)); -#606 = VECTOR('',#607,1.); -#607 = DIRECTION('',(0.,-1.,0.)); -#608 = ORIENTED_EDGE('',*,*,#609,.T.); -#609 = EDGE_CURVE('',#602,#578,#610,.T.); -#610 = CIRCLE('',#611,6.054044962965); -#611 = AXIS2_PLACEMENT_3D('',#612,#613,#614); -#612 = CARTESIAN_POINT('',(-32.57517518159,3.2,-5.854044962965)); -#613 = DIRECTION('',(0.,-1.,0.)); -#614 = DIRECTION('',(-1.,0.,0.)); -#615 = ORIENTED_EDGE('',*,*,#577,.T.); -#616 = ORIENTED_EDGE('',*,*,#120,.F.); -#617 = CYLINDRICAL_SURFACE('',#618,6.054044962965); -#618 = AXIS2_PLACEMENT_3D('',#619,#620,#621); -#619 = CARTESIAN_POINT('',(-32.57517518159,3.,-5.854044962965)); -#620 = DIRECTION('',(0.,-1.,0.)); -#621 = DIRECTION('',(-1.,0.,0.)); -#622 = ADVANCED_FACE('',(#623),#641,.F.); -#623 = FACE_BOUND('',#624,.F.); -#624 = EDGE_LOOP('',(#625,#633,#639,#640)); -#625 = ORIENTED_EDGE('',*,*,#626,.F.); -#626 = EDGE_CURVE('',#627,#105,#629,.T.); -#627 = VERTEX_POINT('',#628); -#628 = CARTESIAN_POINT('',(35.082842712475,3.2,0.2)); -#629 = LINE('',#630,#631); -#630 = CARTESIAN_POINT('',(35.082842712475,3.,0.2)); -#631 = VECTOR('',#632,1.); -#632 = DIRECTION('',(0.,-1.,0.)); -#633 = ORIENTED_EDGE('',*,*,#634,.F.); -#634 = EDGE_CURVE('',#602,#627,#635,.T.); -#635 = LINE('',#636,#637); -#636 = CARTESIAN_POINT('',(-7.942265537672,3.2,0.2)); -#637 = VECTOR('',#638,1.); -#638 = DIRECTION('',(1.,0.,0.)); -#639 = ORIENTED_EDGE('',*,*,#601,.T.); -#640 = ORIENTED_EDGE('',*,*,#112,.T.); -#641 = PLANE('',#642); -#642 = AXIS2_PLACEMENT_3D('',#643,#644,#645); -#643 = CARTESIAN_POINT('',(-16.28758759079,3.,0.2)); -#644 = DIRECTION('',(0.,0.,1.)); -#645 = DIRECTION('',(0.,-1.,0.)); -#646 = ADVANCED_FACE('',(#647),#665,.T.); -#647 = FACE_BOUND('',#648,.T.); -#648 = EDGE_LOOP('',(#649,#657,#658,#659)); -#649 = ORIENTED_EDGE('',*,*,#650,.T.); -#650 = EDGE_CURVE('',#651,#97,#653,.T.); -#651 = VERTEX_POINT('',#652); -#652 = CARTESIAN_POINT('',(42.298608189024,3.2,-7.015765476549)); -#653 = LINE('',#654,#655); -#654 = CARTESIAN_POINT('',(42.298608189024,3.,-7.015765476549)); -#655 = VECTOR('',#656,1.); -#656 = DIRECTION('',(0.,-1.,0.)); -#657 = ORIENTED_EDGE('',*,*,#104,.T.); -#658 = ORIENTED_EDGE('',*,*,#626,.F.); -#659 = ORIENTED_EDGE('',*,*,#660,.F.); -#660 = EDGE_CURVE('',#651,#627,#661,.T.); -#661 = LINE('',#662,#663); -#662 = CARTESIAN_POINT('',(36.596246576251,3.2,-1.313403863776)); -#663 = VECTOR('',#664,1.); -#664 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#665 = PLANE('',#666); -#666 = AXIS2_PLACEMENT_3D('',#667,#668,#669); -#667 = CARTESIAN_POINT('',(38.67695526217,3.,-3.394112549695)); -#668 = DIRECTION('',(-0.707106781187,0.,-0.707106781187)); -#669 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#670 = ADVANCED_FACE('',(#671),#682,.T.); -#671 = FACE_BOUND('',#672,.T.); -#672 = EDGE_LOOP('',(#673,#679,#680,#681)); -#673 = ORIENTED_EDGE('',*,*,#674,.F.); -#674 = EDGE_CURVE('',#313,#651,#675,.T.); -#675 = LINE('',#676,#677); -#676 = CARTESIAN_POINT('',(32.699020781567,3.2,-37.13346923684)); -#677 = VECTOR('',#678,1.); -#678 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#679 = ORIENTED_EDGE('',*,*,#312,.T.); -#680 = ORIENTED_EDGE('',*,*,#96,.T.); -#681 = ORIENTED_EDGE('',*,*,#650,.F.); -#682 = PLANE('',#683); -#683 = AXIS2_PLACEMENT_3D('',#684,#685,#686); -#684 = CARTESIAN_POINT('',(34.581352051556,3.,-31.22785130119)); -#685 = DIRECTION('',(-0.952773183837,0.,0.30368282823)); -#686 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#687 = ADVANCED_FACE('',(#688,#738,#749,#767,#801),#812,.F.); -#688 = FACE_BOUND('',#689,.F.); -#689 = EDGE_LOOP('',(#690,#700,#708,#716,#724,#732)); -#690 = ORIENTED_EDGE('',*,*,#691,.F.); -#691 = EDGE_CURVE('',#692,#694,#696,.T.); -#692 = VERTEX_POINT('',#693); -#693 = CARTESIAN_POINT('',(16.626582997737,3.2,-8.940188245231)); -#694 = VERTEX_POINT('',#695); -#695 = CARTESIAN_POINT('',(4.383041634064E-04,3.2,-8.903751615529)); -#696 = LINE('',#697,#698); -#697 = CARTESIAN_POINT('',(4.347099726942,3.2,-8.913277437397)); -#698 = VECTOR('',#699,1.); -#699 = DIRECTION('',(-0.999997598615,0.,2.191520817069E-03)); -#700 = ORIENTED_EDGE('',*,*,#701,.F.); -#701 = EDGE_CURVE('',#702,#692,#704,.T.); -#702 = VERTEX_POINT('',#703); -#703 = CARTESIAN_POINT('',(19.029295926024,3.2,-17.63009960955)); -#704 = LINE('',#705,#706); -#705 = CARTESIAN_POINT('',(19.849519003668,3.2,-20.59660707692)); -#706 = VECTOR('',#707,1.); -#707 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#708 = ORIENTED_EDGE('',*,*,#709,.F.); -#709 = EDGE_CURVE('',#710,#702,#712,.T.); -#710 = VERTEX_POINT('',#711); -#711 = CARTESIAN_POINT('',(22.059435554995,3.2,-3.734519760785)); -#712 = LINE('',#713,#714); -#713 = CARTESIAN_POINT('',(17.698510515043,3.2,-23.73280021221)); -#714 = VECTOR('',#715,1.); -#715 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#716 = ORIENTED_EDGE('',*,*,#717,.F.); -#717 = EDGE_CURVE('',#718,#710,#720,.T.); -#718 = VERTEX_POINT('',#719); -#719 = CARTESIAN_POINT('',(-21.72552223146,3.2,-3.734519760785)); -#720 = LINE('',#721,#722); -#721 = CARTESIAN_POINT('',(0.297084840953,3.2,-3.734519760785)); -#722 = VECTOR('',#723,1.); -#723 = DIRECTION('',(1.,0.,0.)); -#724 = ORIENTED_EDGE('',*,*,#725,.F.); -#725 = EDGE_CURVE('',#726,#718,#728,.T.); -#726 = VERTEX_POINT('',#727); -#727 = CARTESIAN_POINT('',(-21.72552223146,3.2,-8.903751135252)); -#728 = LINE('',#729,#730); -#729 = CARTESIAN_POINT('',(-21.72552223146,3.2,-19.83215600037)); -#730 = VECTOR('',#731,1.); -#731 = DIRECTION('',(0.,0.,1.)); -#732 = ORIENTED_EDGE('',*,*,#733,.F.); -#733 = EDGE_CURVE('',#694,#726,#734,.T.); -#734 = LINE('',#735,#736); -#735 = CARTESIAN_POINT('',(-5.279852300138,3.2,-8.903751135252)); -#736 = VECTOR('',#737,1.); -#737 = DIRECTION('',(-1.,0.,0.)); -#738 = FACE_BOUND('',#739,.F.); -#739 = EDGE_LOOP('',(#740)); -#740 = ORIENTED_EDGE('',*,*,#741,.F.); -#741 = EDGE_CURVE('',#742,#742,#744,.T.); -#742 = VERTEX_POINT('',#743); -#743 = CARTESIAN_POINT('',(21.163799345768,3.2,-48.00951684793)); -#744 = CIRCLE('',#745,4.735522705283); -#745 = AXIS2_PLACEMENT_3D('',#746,#747,#748); -#746 = CARTESIAN_POINT('',(16.428276640485,3.2,-48.00951684793)); -#747 = DIRECTION('',(-0.,1.,0.)); -#748 = DIRECTION('',(1.,0.,0.)); -#749 = FACE_BOUND('',#750,.F.); -#750 = EDGE_LOOP('',(#751,#752,#753,#754,#755,#756,#757,#758,#759,#760, - #761,#762,#763,#764,#765,#766)); -#751 = ORIENTED_EDGE('',*,*,#536,.T.); -#752 = ORIENTED_EDGE('',*,*,#560,.F.); -#753 = ORIENTED_EDGE('',*,*,#585,.T.); -#754 = ORIENTED_EDGE('',*,*,#609,.F.); -#755 = ORIENTED_EDGE('',*,*,#634,.T.); -#756 = ORIENTED_EDGE('',*,*,#660,.F.); -#757 = ORIENTED_EDGE('',*,*,#674,.F.); -#758 = ORIENTED_EDGE('',*,*,#320,.F.); -#759 = ORIENTED_EDGE('',*,*,#344,.F.); -#760 = ORIENTED_EDGE('',*,*,#358,.F.); -#761 = ORIENTED_EDGE('',*,*,#392,.F.); -#762 = ORIENTED_EDGE('',*,*,#414,.T.); -#763 = ORIENTED_EDGE('',*,*,#438,.F.); -#764 = ORIENTED_EDGE('',*,*,#463,.T.); -#765 = ORIENTED_EDGE('',*,*,#487,.F.); -#766 = ORIENTED_EDGE('',*,*,#512,.T.); -#767 = FACE_BOUND('',#768,.F.); -#768 = EDGE_LOOP('',(#769,#779,#787,#795)); -#769 = ORIENTED_EDGE('',*,*,#770,.F.); -#770 = EDGE_CURVE('',#771,#773,#775,.T.); -#771 = VERTEX_POINT('',#772); -#772 = CARTESIAN_POINT('',(5.809375885494,3.2,-13.06417917474)); -#773 = VERTEX_POINT('',#774); -#774 = CARTESIAN_POINT('',(2.688069798796,3.2,-49.64588621989)); -#775 = LINE('',#776,#777); -#776 = CARTESIAN_POINT('',(4.914157977861,3.2,-23.55613296875)); -#777 = VECTOR('',#778,1.); -#778 = DIRECTION('',(-8.501532861635E-02,0.,-0.996379643459)); -#779 = ORIENTED_EDGE('',*,*,#780,.F.); -#780 = EDGE_CURVE('',#781,#771,#783,.T.); -#781 = VERTEX_POINT('',#782); -#782 = CARTESIAN_POINT('',(-5.809375885494,3.2,-13.06417917474)); -#783 = LINE('',#784,#785); -#784 = CARTESIAN_POINT('',(1.615747408047,3.2,-13.06417917474)); -#785 = VECTOR('',#786,1.); -#786 = DIRECTION('',(1.,0.,-3.066574716487E-16)); -#787 = ORIENTED_EDGE('',*,*,#788,.F.); -#788 = EDGE_CURVE('',#789,#781,#791,.T.); -#789 = VERTEX_POINT('',#790); -#790 = CARTESIAN_POINT('',(-2.688069798796,3.2,-49.64588621989)); -#791 = LINE('',#792,#793); -#792 = CARTESIAN_POINT('',(-4.890802006217,3.2,-23.82986495424)); -#793 = VECTOR('',#794,1.); -#794 = DIRECTION('',(-8.501532861635E-02,0.,0.996379643459)); -#795 = ORIENTED_EDGE('',*,*,#796,.F.); -#796 = EDGE_CURVE('',#773,#789,#797,.T.); -#797 = LINE('',#798,#799); -#798 = CARTESIAN_POINT('',(1.615747408047,3.2,-49.64588621989)); -#799 = VECTOR('',#800,1.); -#800 = DIRECTION('',(-1.,0.,0.)); -#801 = FACE_BOUND('',#802,.F.); -#802 = EDGE_LOOP('',(#803)); -#803 = ORIENTED_EDGE('',*,*,#804,.F.); -#804 = EDGE_CURVE('',#805,#805,#807,.T.); -#805 = VERTEX_POINT('',#806); -#806 = CARTESIAN_POINT('',(-11.6927539352,3.2,-48.00951684793)); -#807 = CIRCLE('',#808,4.735522705283); -#808 = AXIS2_PLACEMENT_3D('',#809,#810,#811); -#809 = CARTESIAN_POINT('',(-16.42827664048,3.2,-48.00951684793)); -#810 = DIRECTION('',(-0.,1.,0.)); -#811 = DIRECTION('',(1.,0.,0.)); -#812 = PLANE('',#813); -#813 = AXIS2_PLACEMENT_3D('',#814,#815,#816); -#814 = CARTESIAN_POINT('',(0.403056515455,3.2,-33.34517655273)); -#815 = DIRECTION('',(0.,1.,0.)); -#816 = DIRECTION('',(1.,0.,0.)); -#817 = ADVANCED_FACE('',(#818),#843,.T.); -#818 = FACE_BOUND('',#819,.T.); -#819 = EDGE_LOOP('',(#820,#828,#836,#842)); -#820 = ORIENTED_EDGE('',*,*,#821,.F.); -#821 = EDGE_CURVE('',#822,#692,#824,.T.); -#822 = VERTEX_POINT('',#823); -#823 = CARTESIAN_POINT('',(16.626582997737,0.,-8.940188245231)); -#824 = LINE('',#825,#826); -#825 = CARTESIAN_POINT('',(16.626582997737,-22.,-8.940188245231)); -#826 = VECTOR('',#827,1.); -#827 = DIRECTION('',(0.,1.,0.)); -#828 = ORIENTED_EDGE('',*,*,#829,.T.); -#829 = EDGE_CURVE('',#822,#830,#832,.T.); -#830 = VERTEX_POINT('',#831); -#831 = CARTESIAN_POINT('',(-5.329070518201E-15,0.,-8.903751135252)); -#832 = LINE('',#833,#834); -#833 = CARTESIAN_POINT('',(-18.54556462154,1.7763568394E-15, - -8.863107566442)); -#834 = VECTOR('',#835,1.); -#835 = DIRECTION('',(-0.999997598615,0.,2.191520817069E-03)); -#836 = ORIENTED_EDGE('',*,*,#837,.T.); -#837 = EDGE_CURVE('',#830,#694,#838,.T.); -#838 = LINE('',#839,#840); -#839 = CARTESIAN_POINT('',(-5.329070518201E-15,-22.,-8.903751135252)); -#840 = VECTOR('',#841,1.); -#841 = DIRECTION('',(0.,1.,0.)); -#842 = ORIENTED_EDGE('',*,*,#691,.F.); -#843 = PLANE('',#844); -#844 = AXIS2_PLACEMENT_3D('',#845,#846,#847); -#845 = CARTESIAN_POINT('',(8.237581109188,-22.,-8.921803528809)); -#846 = DIRECTION('',(-2.191520817069E-03,0.,-0.999997598615)); -#847 = DIRECTION('',(-0.999997598615,0.,2.191520817069E-03)); -#848 = ADVANCED_FACE('',(#849),#867,.T.); -#849 = FACE_BOUND('',#850,.T.); -#850 = EDGE_LOOP('',(#851,#859,#865,#866)); -#851 = ORIENTED_EDGE('',*,*,#852,.F.); -#852 = EDGE_CURVE('',#853,#702,#855,.T.); -#853 = VERTEX_POINT('',#854); -#854 = CARTESIAN_POINT('',(19.029295926024,0.,-17.63009960955)); -#855 = LINE('',#856,#857); -#856 = CARTESIAN_POINT('',(19.029295926024,-22.,-17.63009960955)); -#857 = VECTOR('',#858,1.); -#858 = DIRECTION('',(0.,1.,0.)); -#859 = ORIENTED_EDGE('',*,*,#860,.T.); -#860 = EDGE_CURVE('',#853,#822,#861,.T.); -#861 = LINE('',#862,#863); -#862 = CARTESIAN_POINT('',(23.053273013696,0.,-32.18365022821)); -#863 = VECTOR('',#864,1.); -#864 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#865 = ORIENTED_EDGE('',*,*,#821,.T.); -#866 = ORIENTED_EDGE('',*,*,#701,.F.); -#867 = PLANE('',#868); -#868 = AXIS2_PLACEMENT_3D('',#869,#870,#871); -#869 = CARTESIAN_POINT('',(17.956031892536,-22.,-13.7484168618)); -#870 = DIRECTION('',(-0.963836182336,0.,-0.26649542889)); -#871 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#872 = ADVANCED_FACE('',(#873),#891,.T.); -#873 = FACE_BOUND('',#874,.T.); -#874 = EDGE_LOOP('',(#875,#883,#884,#885)); -#875 = ORIENTED_EDGE('',*,*,#876,.T.); -#876 = EDGE_CURVE('',#877,#853,#879,.T.); -#877 = VERTEX_POINT('',#878); -#878 = CARTESIAN_POINT('',(22.059435554995,0.,-3.734519760785)); -#879 = LINE('',#880,#881); -#880 = CARTESIAN_POINT('',(12.741012548745,1.7763568394E-15, - -46.46683800444)); -#881 = VECTOR('',#882,1.); -#882 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#883 = ORIENTED_EDGE('',*,*,#852,.T.); -#884 = ORIENTED_EDGE('',*,*,#709,.F.); -#885 = ORIENTED_EDGE('',*,*,#886,.F.); -#886 = EDGE_CURVE('',#877,#710,#887,.T.); -#887 = LINE('',#888,#889); -#888 = CARTESIAN_POINT('',(22.059435554995,-22.,-3.734519760785)); -#889 = VECTOR('',#890,1.); -#890 = DIRECTION('',(0.,1.,0.)); -#891 = PLANE('',#892); -#892 = AXIS2_PLACEMENT_3D('',#893,#894,#895); -#893 = CARTESIAN_POINT('',(20.484588228021,-22.,-10.95643672209)); -#894 = DIRECTION('',(0.977039526026,0.,-0.213058124893)); -#895 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#896 = ADVANCED_FACE('',(#897),#915,.T.); -#897 = FACE_BOUND('',#898,.T.); -#898 = EDGE_LOOP('',(#899,#907,#908,#909)); -#899 = ORIENTED_EDGE('',*,*,#900,.T.); -#900 = EDGE_CURVE('',#901,#877,#903,.T.); -#901 = VERTEX_POINT('',#902); -#902 = CARTESIAN_POINT('',(-21.72552223146,0.,-3.734519760785)); -#903 = LINE('',#904,#905); -#904 = CARTESIAN_POINT('',(-22.63692080725,0.,-3.734519760785)); -#905 = VECTOR('',#906,1.); -#906 = DIRECTION('',(1.,0.,0.)); -#907 = ORIENTED_EDGE('',*,*,#886,.T.); -#908 = ORIENTED_EDGE('',*,*,#717,.F.); -#909 = ORIENTED_EDGE('',*,*,#910,.F.); -#910 = EDGE_CURVE('',#901,#718,#911,.T.); -#911 = LINE('',#912,#913); -#912 = CARTESIAN_POINT('',(-21.72552223146,-22.,-3.734519760785)); -#913 = VECTOR('',#914,1.); -#914 = DIRECTION('',(0.,1.,0.)); -#915 = PLANE('',#916); -#916 = AXIS2_PLACEMENT_3D('',#917,#918,#919); -#917 = CARTESIAN_POINT('',(0.19111316645,-22.,-3.734519760785)); -#918 = DIRECTION('',(0.,0.,1.)); -#919 = DIRECTION('',(0.,-1.,0.)); -#920 = ADVANCED_FACE('',(#921),#939,.T.); -#921 = FACE_BOUND('',#922,.T.); -#922 = EDGE_LOOP('',(#923,#931,#932,#933)); -#923 = ORIENTED_EDGE('',*,*,#924,.T.); -#924 = EDGE_CURVE('',#925,#901,#927,.T.); -#925 = VERTEX_POINT('',#926); -#926 = CARTESIAN_POINT('',(-21.72552223146,0.,-8.903751135252)); -#927 = LINE('',#928,#929); -#928 = CARTESIAN_POINT('',(-21.72552223146,0.,-38.64614663299)); -#929 = VECTOR('',#930,1.); -#930 = DIRECTION('',(0.,0.,1.)); -#931 = ORIENTED_EDGE('',*,*,#910,.T.); -#932 = ORIENTED_EDGE('',*,*,#725,.F.); -#933 = ORIENTED_EDGE('',*,*,#934,.F.); -#934 = EDGE_CURVE('',#925,#726,#935,.T.); -#935 = LINE('',#936,#937); -#936 = CARTESIAN_POINT('',(-21.72552223146,-22.,-8.903751135252)); -#937 = VECTOR('',#938,1.); -#938 = DIRECTION('',(0.,1.,0.)); -#939 = PLANE('',#940); -#940 = AXIS2_PLACEMENT_3D('',#941,#942,#943); -#941 = CARTESIAN_POINT('',(-21.72552223146,-22.,-6.319135448019)); -#942 = DIRECTION('',(-1.,0.,0.)); -#943 = DIRECTION('',(0.,1.,0.)); -#944 = ADVANCED_FACE('',(#945),#956,.T.); -#945 = FACE_BOUND('',#946,.T.); -#946 = EDGE_LOOP('',(#947,#953,#954,#955)); -#947 = ORIENTED_EDGE('',*,*,#948,.T.); -#948 = EDGE_CURVE('',#830,#925,#949,.T.); -#949 = LINE('',#950,#951); -#950 = CARTESIAN_POINT('',(-28.21385794834,0.,-8.903751135252)); -#951 = VECTOR('',#952,1.); -#952 = DIRECTION('',(-1.,0.,0.)); -#953 = ORIENTED_EDGE('',*,*,#934,.T.); -#954 = ORIENTED_EDGE('',*,*,#733,.F.); -#955 = ORIENTED_EDGE('',*,*,#837,.F.); -#956 = PLANE('',#957); -#957 = AXIS2_PLACEMENT_3D('',#958,#959,#960); -#958 = CARTESIAN_POINT('',(-10.96276111573,-22.,-8.903751135252)); -#959 = DIRECTION('',(0.,0.,-1.)); -#960 = DIRECTION('',(0.,1.,0.)); -#961 = ADVANCED_FACE('',(#962),#981,.T.); -#962 = FACE_BOUND('',#963,.T.); -#963 = EDGE_LOOP('',(#964,#973,#979,#980)); -#964 = ORIENTED_EDGE('',*,*,#965,.T.); -#965 = EDGE_CURVE('',#966,#966,#968,.T.); -#966 = VERTEX_POINT('',#967); -#967 = CARTESIAN_POINT('',(21.163799345768,0.,-48.00951684793)); -#968 = CIRCLE('',#969,4.735522705283); -#969 = AXIS2_PLACEMENT_3D('',#970,#971,#972); -#970 = CARTESIAN_POINT('',(16.428276640485,0.,-48.00951684793)); -#971 = DIRECTION('',(-0.,1.,0.)); -#972 = DIRECTION('',(1.,0.,0.)); -#973 = ORIENTED_EDGE('',*,*,#974,.T.); -#974 = EDGE_CURVE('',#966,#742,#975,.T.); -#975 = LINE('',#976,#977); -#976 = CARTESIAN_POINT('',(21.163799345768,-22.,-48.00951684793)); -#977 = VECTOR('',#978,1.); -#978 = DIRECTION('',(0.,1.,0.)); -#979 = ORIENTED_EDGE('',*,*,#741,.F.); -#980 = ORIENTED_EDGE('',*,*,#974,.F.); -#981 = CYLINDRICAL_SURFACE('',#982,4.735522705283); -#982 = AXIS2_PLACEMENT_3D('',#983,#984,#985); -#983 = CARTESIAN_POINT('',(16.428276640485,-22.,-48.00951684793)); -#984 = DIRECTION('',(0.,1.,0.)); -#985 = DIRECTION('',(1.,0.,0.)); -#986 = ADVANCED_FACE('',(#987),#1012,.F.); -#987 = FACE_BOUND('',#988,.F.); -#988 = EDGE_LOOP('',(#989,#999,#1005,#1006)); -#989 = ORIENTED_EDGE('',*,*,#990,.T.); -#990 = EDGE_CURVE('',#991,#993,#995,.T.); -#991 = VERTEX_POINT('',#992); -#992 = CARTESIAN_POINT('',(1.480297366167E-15,11.242400581089, - -46.71869179633)); -#993 = VERTEX_POINT('',#994); -#994 = CARTESIAN_POINT('',(3.256654205567E-15,17.8572529153, - -18.39898295202)); -#995 = LINE('',#996,#997); -#996 = CARTESIAN_POINT('',(2.6645352591E-15,18.133069549222, - -17.21814831317)); -#997 = VECTOR('',#998,1.); -#998 = DIRECTION('',(5.275122655166E-17,0.227455280238,0.97378852709)); -#999 = ORIENTED_EDGE('',*,*,#1000,.F.); -#1000 = EDGE_CURVE('',#771,#993,#1001,.T.); -#1001 = LINE('',#1002,#1003); -#1002 = CARTESIAN_POINT('',(5.649679875255,3.602918464526, - -13.21082950266)); -#1003 = VECTOR('',#1004,1.); -#1004 = DIRECTION('',(-0.349023821871,0.880598971639,-0.320511814002)); -#1005 = ORIENTED_EDGE('',*,*,#770,.T.); -#1006 = ORIENTED_EDGE('',*,*,#1007,.T.); -#1007 = EDGE_CURVE('',#773,#991,#1008,.T.); -#1008 = LINE('',#1009,#1010); -#1009 = CARTESIAN_POINT('',(1.103762571829,7.940067898719, - -47.92064259636)); -#1010 = VECTOR('',#1011,1.); -#1011 = DIRECTION('',(-0.299648208284,0.896513522642,0.326304236859)); -#1012 = PLANE('',#1013); -#1013 = AXIS2_PLACEMENT_3D('',#1014,#1015,#1016); -#1014 = CARTESIAN_POINT('',(5.823691883056,3.068404028665, - -13.45978839622)); -#1015 = DIRECTION('',(0.93629059846,0.342020143326,-7.988827695448E-02) - ); -#1016 = DIRECTION('',(-0.340781908463,0.939692620786,2.907695487824E-02) - ); -#1017 = ADVANCED_FACE('',(#1018),#1028,.F.); -#1018 = FACE_BOUND('',#1019,.F.); -#1019 = EDGE_LOOP('',(#1020,#1021,#1027)); -#1020 = ORIENTED_EDGE('',*,*,#1000,.T.); -#1021 = ORIENTED_EDGE('',*,*,#1022,.T.); -#1022 = EDGE_CURVE('',#993,#781,#1023,.T.); -#1023 = LINE('',#1024,#1025); -#1024 = CARTESIAN_POINT('',(-5.275833888477,4.546144602338, - -13.55413574101)); -#1025 = VECTOR('',#1026,1.); -#1026 = DIRECTION('',(-0.349023821871,-0.880598971639,0.320511814002)); -#1027 = ORIENTED_EDGE('',*,*,#780,.T.); -#1028 = PLANE('',#1029); -#1029 = AXIS2_PLACEMENT_3D('',#1030,#1031,#1032); -#1030 = CARTESIAN_POINT('',(2.828438300639,3.068404028665, - -13.01628215822)); -#1031 = DIRECTION('',(2.881637632171E-16,0.342020143326,0.939692620786) - ); -#1032 = DIRECTION('',(-1.048830324052E-16,0.939692620786,-0.342020143326 - )); -#1033 = ADVANCED_FACE('',(#1034),#1045,.F.); -#1034 = FACE_BOUND('',#1035,.F.); -#1035 = EDGE_LOOP('',(#1036,#1037,#1038,#1039)); -#1036 = ORIENTED_EDGE('',*,*,#788,.T.); -#1037 = ORIENTED_EDGE('',*,*,#1022,.F.); -#1038 = ORIENTED_EDGE('',*,*,#990,.F.); -#1039 = ORIENTED_EDGE('',*,*,#1040,.F.); -#1040 = EDGE_CURVE('',#789,#991,#1041,.T.); -#1041 = LINE('',#1042,#1043); -#1042 = CARTESIAN_POINT('',(-0.871390517001,8.635298785064, - -47.66759924779)); -#1043 = VECTOR('',#1044,1.); -#1044 = DIRECTION('',(0.299648208284,0.896513522642,0.326304236859)); -#1045 = PLANE('',#1046); -#1046 = AXIS2_PLACEMENT_3D('',#1047,#1048,#1049); -#1047 = CARTESIAN_POINT('',(-5.782806207227,3.068404028665, - -13.93896851312)); -#1048 = DIRECTION('',(-0.93629059846,0.342020143326,-7.988827695448E-02) - ); -#1049 = DIRECTION('',(0.340781908463,0.939692620786,2.907695487824E-02) - ); -#1050 = ADVANCED_FACE('',(#1051),#1056,.F.); -#1051 = FACE_BOUND('',#1052,.F.); -#1052 = EDGE_LOOP('',(#1053,#1054,#1055)); -#1053 = ORIENTED_EDGE('',*,*,#1040,.T.); -#1054 = ORIENTED_EDGE('',*,*,#1007,.F.); -#1055 = ORIENTED_EDGE('',*,*,#796,.T.); -#1056 = PLANE('',#1057); -#1057 = AXIS2_PLACEMENT_3D('',#1058,#1059,#1060); -#1058 = CARTESIAN_POINT('',(2.828438300639,3.068404028665, - -49.69378323641)); -#1059 = DIRECTION('',(0.,0.342020143326,-0.939692620786)); -#1060 = DIRECTION('',(0.,0.939692620786,0.342020143326)); -#1061 = ADVANCED_FACE('',(#1062),#1081,.T.); -#1062 = FACE_BOUND('',#1063,.T.); -#1063 = EDGE_LOOP('',(#1064,#1073,#1079,#1080)); -#1064 = ORIENTED_EDGE('',*,*,#1065,.T.); -#1065 = EDGE_CURVE('',#1066,#1066,#1068,.T.); -#1066 = VERTEX_POINT('',#1067); -#1067 = CARTESIAN_POINT('',(-11.6927539352,0.,-48.00951684793)); -#1068 = CIRCLE('',#1069,4.735522705283); -#1069 = AXIS2_PLACEMENT_3D('',#1070,#1071,#1072); -#1070 = CARTESIAN_POINT('',(-16.42827664048,0.,-48.00951684793)); -#1071 = DIRECTION('',(-0.,1.,0.)); -#1072 = DIRECTION('',(1.,0.,0.)); -#1073 = ORIENTED_EDGE('',*,*,#1074,.T.); -#1074 = EDGE_CURVE('',#1066,#805,#1075,.T.); -#1075 = LINE('',#1076,#1077); -#1076 = CARTESIAN_POINT('',(-11.6927539352,-22.,-48.00951684793)); -#1077 = VECTOR('',#1078,1.); -#1078 = DIRECTION('',(0.,1.,0.)); -#1079 = ORIENTED_EDGE('',*,*,#804,.F.); -#1080 = ORIENTED_EDGE('',*,*,#1074,.F.); -#1081 = CYLINDRICAL_SURFACE('',#1082,4.735522705283); -#1082 = AXIS2_PLACEMENT_3D('',#1083,#1084,#1085); -#1083 = CARTESIAN_POINT('',(-16.42827664048,-22.,-48.00951684793)); -#1084 = DIRECTION('',(0.,1.,0.)); -#1085 = DIRECTION('',(1.,0.,0.)); -#1086 = ADVANCED_FACE('',(#1087),#1095,.F.); -#1087 = FACE_BOUND('',#1088,.F.); -#1088 = EDGE_LOOP('',(#1089,#1090,#1091,#1092,#1093,#1094)); -#1089 = ORIENTED_EDGE('',*,*,#860,.T.); -#1090 = ORIENTED_EDGE('',*,*,#829,.T.); -#1091 = ORIENTED_EDGE('',*,*,#948,.T.); -#1092 = ORIENTED_EDGE('',*,*,#924,.T.); -#1093 = ORIENTED_EDGE('',*,*,#900,.T.); -#1094 = ORIENTED_EDGE('',*,*,#876,.T.); -#1095 = PLANE('',#1096); -#1096 = AXIS2_PLACEMENT_3D('',#1097,#1098,#1099); -#1097 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#1098 = DIRECTION('',(0.,1.,0.)); -#1099 = DIRECTION('',(0.,0.,1.)); -#1100 = ADVANCED_FACE('',(#1101),#1104,.F.); -#1101 = FACE_BOUND('',#1102,.F.); -#1102 = EDGE_LOOP('',(#1103)); -#1103 = ORIENTED_EDGE('',*,*,#965,.T.); -#1104 = PLANE('',#1105); -#1105 = AXIS2_PLACEMENT_3D('',#1106,#1107,#1108); -#1106 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#1107 = DIRECTION('',(0.,1.,0.)); -#1108 = DIRECTION('',(0.,0.,1.)); -#1109 = ADVANCED_FACE('',(#1110),#1113,.F.); -#1110 = FACE_BOUND('',#1111,.F.); -#1111 = EDGE_LOOP('',(#1112)); -#1112 = ORIENTED_EDGE('',*,*,#1065,.T.); -#1113 = PLANE('',#1114); -#1114 = AXIS2_PLACEMENT_3D('',#1115,#1116,#1117); -#1115 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#1116 = DIRECTION('',(0.,1.,0.)); -#1117 = DIRECTION('',(0.,0.,1.)); -#1118 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) -GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#1122)) -GLOBAL_UNIT_ASSIGNED_CONTEXT((#1119,#1120,#1121)) REPRESENTATION_CONTEXT -('Context #1','3D Context with UNIT and UNCERTAINTY') ); -#1119 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) ); -#1120 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) ); -#1121 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() ); -#1122 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-05),#1119, - 'distance_accuracy_value','confusion accuracy'); -#1123 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7)); -ENDSEC; -END-ISO-10303-21; diff --git a/docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.step b/docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.step deleted file mode 100644 index e6575701e4..0000000000 --- a/docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.step +++ /dev/null @@ -1,1048 +0,0 @@ -ISO-10303-21; -HEADER; -FILE_DESCRIPTION(('FreeCAD Model'),'2;1'); -FILE_NAME('Open CASCADE Shape Model','2026-08-05T11:06:23',('FreeCAD'),( - 'FreeCAD'),'Open CASCADE STEP processor 7.8','FreeCAD','Unknown'); -FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }')); -ENDSEC; -DATA; -#1 = APPLICATION_PROTOCOL_DEFINITION('international standard', - 'automotive_design',2000,#2); -#2 = APPLICATION_CONTEXT( - 'core data for automotive mechanical design processes'); -#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10); -#4 = PRODUCT_DEFINITION_SHAPE('','',#5); -#5 = PRODUCT_DEFINITION('design','',#6,#9); -#6 = PRODUCT_DEFINITION_FORMATION('','',#7); -#7 = PRODUCT('Open CASCADE STEP translator 7.8 1', - 'Open CASCADE STEP translator 7.8 1','',(#8)); -#8 = PRODUCT_CONTEXT('',#2,'mechanical'); -#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design'); -#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#976); -#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14); -#12 = CARTESIAN_POINT('',(0.,0.,0.)); -#13 = DIRECTION('',(0.,0.,1.)); -#14 = DIRECTION('',(1.,0.,-0.)); -#15 = MANIFOLD_SOLID_BREP('',#16); -#16 = CLOSED_SHELL('',(#17,#157,#188,#212,#236,#261,#285,#310,#334,#358, - #383,#407,#432,#456,#480,#504,#528,#545,#675,#706,#730,#754,#778, - #802,#819,#850,#866,#882,#894,#919,#944,#958,#967)); -#17 = ADVANCED_FACE('',(#18),#152,.T.); -#18 = FACE_BOUND('',#19,.T.); -#19 = EDGE_LOOP('',(#20,#30,#38,#46,#54,#62,#70,#79,#87,#96,#104,#112, - #121,#129,#138,#146)); -#20 = ORIENTED_EDGE('',*,*,#21,.F.); -#21 = EDGE_CURVE('',#22,#24,#26,.T.); -#22 = VERTEX_POINT('',#23); -#23 = CARTESIAN_POINT('',(26.824640127118,20.8572529153,-55.23443461327) - ); -#24 = VERTEX_POINT('',#25); -#25 = CARTESIAN_POINT('',(42.184838000445,20.8572529153,-7.043416644207) - ); -#26 = LINE('',#27,#28); -#27 = CARTESIAN_POINT('',(28.89442018934,20.8572529153,-48.74071565254) - ); -#28 = VECTOR('',#29,1.); -#29 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#30 = ORIENTED_EDGE('',*,*,#31,.F.); -#31 = EDGE_CURVE('',#32,#22,#34,.T.); -#32 = VERTEX_POINT('',#33); -#33 = CARTESIAN_POINT('',(32.56245481215,20.8572529153,-60.7871552419)); -#34 = LINE('',#35,#36); -#35 = CARTESIAN_POINT('',(12.092418778801,20.8572529153,-40.97745458223) - ); -#36 = VECTOR('',#37,1.); -#37 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#38 = ORIENTED_EDGE('',*,*,#39,.F.); -#39 = EDGE_CURVE('',#40,#32,#42,.T.); -#40 = VERTEX_POINT('',#41); -#41 = CARTESIAN_POINT('',(26.712845130625,20.8572529153,-66.83175546171) - ); -#42 = LINE('',#43,#44); -#43 = CARTESIAN_POINT('',(11.149878706433,20.8572529153,-82.91349687528) - ); -#44 = VECTOR('',#45,1.); -#45 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#46 = ORIENTED_EDGE('',*,*,#47,.F.); -#47 = EDGE_CURVE('',#48,#40,#50,.T.); -#48 = VERTEX_POINT('',#49); -#49 = CARTESIAN_POINT('',(20.57248334837,20.8572529153,-60.88947335481) - ); -#50 = LINE('',#51,#52); -#51 = CARTESIAN_POINT('',(9.111716439792,20.8572529153,-49.79841511636) - ); -#52 = VECTOR('',#53,1.); -#53 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#54 = ORIENTED_EDGE('',*,*,#55,.F.); -#55 = EDGE_CURVE('',#56,#48,#58,.T.); -#56 = VERTEX_POINT('',#57); -#57 = CARTESIAN_POINT('',(-20.57248334837,20.8572529153,-60.88947335481) - ); -#58 = LINE('',#59,#60); -#59 = CARTESIAN_POINT('',(-12.44623571629,20.8572529153,-60.88947335481) - ); -#60 = VECTOR('',#61,1.); -#61 = DIRECTION('',(1.,0.,0.)); -#62 = ORIENTED_EDGE('',*,*,#63,.T.); -#63 = EDGE_CURVE('',#56,#64,#66,.T.); -#64 = VERTEX_POINT('',#65); -#65 = CARTESIAN_POINT('',(-25.60006917852,20.8572529153,-65.75487614033) - ); -#66 = LINE('',#67,#68); -#67 = CARTESIAN_POINT('',(-32.03294606502,20.8572529153,-71.98023721416) - ); -#68 = VECTOR('',#69,1.); -#69 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#70 = ORIENTED_EDGE('',*,*,#71,.F.); -#71 = EDGE_CURVE('',#72,#64,#74,.T.); -#72 = VERTEX_POINT('',#73); -#73 = CARTESIAN_POINT('',(-27.78972445201,20.8572529153,-65.71897950961) - ); -#74 = CIRCLE('',#75,1.548528137424); -#75 = AXIS2_PLACEMENT_3D('',#76,#77,#78); -#76 = CARTESIAN_POINT('',(-26.67694849991,20.8572529153,-64.64210018823) - ); -#77 = DIRECTION('',(0.,-1.,0.)); -#78 = DIRECTION('',(1.,0.,0.)); -#79 = ORIENTED_EDGE('',*,*,#80,.T.); -#80 = EDGE_CURVE('',#72,#81,#83,.T.); -#81 = VERTEX_POINT('',#82); -#82 = CARTESIAN_POINT('',(-29.01580660709,20.8572529153,-64.4520272055) - ); -#83 = LINE('',#84,#85); -#84 = CARTESIAN_POINT('',(-31.36389178357,20.8572529153,-62.02567109858) - ); -#85 = VECTOR('',#86,1.); -#86 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#87 = ORIENTED_EDGE('',*,*,#88,.F.); -#88 = EDGE_CURVE('',#89,#81,#91,.T.); -#89 = VERTEX_POINT('',#90); -#90 = CARTESIAN_POINT('',(-28.89758284854,20.8572529153,-57.24050703685) - ); -#91 = CIRCLE('',#92,5.1); -#92 = AXIS2_PLACEMENT_3D('',#93,#94,#95); -#93 = CARTESIAN_POINT('',(-25.35093464349,20.8572529153,-60.90537900045) - ); -#94 = DIRECTION('',(0.,-1.,0.)); -#95 = DIRECTION('',(1.,0.,0.)); -#96 = ORIENTED_EDGE('',*,*,#97,.T.); -#97 = EDGE_CURVE('',#89,#98,#100,.T.); -#98 = VERTEX_POINT('',#99); -#99 = CARTESIAN_POINT('',(-26.82464012711,20.8572529153,-55.23443461327) - ); -#100 = LINE('',#101,#102); -#101 = CARTESIAN_POINT('',(-35.57003638008,20.8572529153,-63.69771634073 - )); -#102 = VECTOR('',#103,1.); -#103 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#104 = ORIENTED_EDGE('',*,*,#105,.T.); -#105 = EDGE_CURVE('',#98,#106,#108,.T.); -#106 = VERTEX_POINT('',#107); -#107 = CARTESIAN_POINT('',(-41.08376419406,20.8572529153,-10.49792081311 - )); -#108 = LINE('',#109,#110); -#109 = CARTESIAN_POINT('',(-32.53680963915,20.8572529153,-37.31309884227 - )); -#110 = VECTOR('',#111,1.); -#111 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#112 = ORIENTED_EDGE('',*,*,#113,.F.); -#113 = EDGE_CURVE('',#114,#106,#116,.T.); -#114 = VERTEX_POINT('',#115); -#115 = CARTESIAN_POINT('',(-39.62105538679,20.8572529153,-4.479634030555 - )); -#116 = CIRCLE('',#117,5.954044962965); -#117 = AXIS2_PLACEMENT_3D('',#118,#119,#120); -#118 = CARTESIAN_POINT('',(-35.41090981799,20.8572529153,-8.689779599357 - )); -#119 = DIRECTION('',(0.,-1.,0.)); -#120 = DIRECTION('',(1.,0.,0.)); -#121 = ORIENTED_EDGE('',*,*,#122,.T.); -#122 = EDGE_CURVE('',#114,#123,#125,.T.); -#123 = VERTEX_POINT('',#124); -#124 = CARTESIAN_POINT('',(-36.7853207504,20.8572529153,-1.643899394163) - ); -#125 = LINE('',#126,#127); -#126 = CARTESIAN_POINT('',(-56.28754386398,20.8572529153,-21.14612250775 - )); -#127 = VECTOR('',#128,1.); -#128 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#129 = ORIENTED_EDGE('',*,*,#130,.F.); -#130 = EDGE_CURVE('',#131,#123,#133,.T.); -#131 = VERTEX_POINT('',#132); -#132 = CARTESIAN_POINT('',(-32.57517518159,20.8572529153, - 9.999999999998E-02)); -#133 = CIRCLE('',#134,5.954044962965); -#134 = AXIS2_PLACEMENT_3D('',#135,#136,#137); -#135 = CARTESIAN_POINT('',(-32.57517518159,20.8572529153,-5.854044962965 - )); -#136 = DIRECTION('',(0.,-1.,0.)); -#137 = DIRECTION('',(1.,0.,0.)); -#138 = ORIENTED_EDGE('',*,*,#139,.T.); -#139 = EDGE_CURVE('',#131,#140,#142,.T.); -#140 = VERTEX_POINT('',#141); -#141 = CARTESIAN_POINT('',(35.041421356237,20.8572529153,0.1)); -#142 = LINE('',#143,#144); -#143 = CARTESIAN_POINT('',(-5.211766712356,20.8572529153,0.1)); -#144 = VECTOR('',#145,1.); -#145 = DIRECTION('',(1.,0.,0.)); -#146 = ORIENTED_EDGE('',*,*,#147,.F.); -#147 = EDGE_CURVE('',#24,#140,#148,.T.); -#148 = LINE('',#149,#150); -#149 = CARTESIAN_POINT('',(32.68311677643,20.8572529153,2.458304579807) - ); -#150 = VECTOR('',#151,1.); -#151 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#152 = PLANE('',#153); -#153 = AXIS2_PLACEMENT_3D('',#154,#155,#156); -#154 = CARTESIAN_POINT('',(-45.46495478095,20.8572529153,-70.97315781796 - )); -#155 = DIRECTION('',(0.,1.,0.)); -#156 = DIRECTION('',(0.,0.,1.)); -#157 = ADVANCED_FACE('',(#158),#183,.F.); -#158 = FACE_BOUND('',#159,.F.); -#159 = EDGE_LOOP('',(#160,#170,#176,#177)); -#160 = ORIENTED_EDGE('',*,*,#161,.T.); -#161 = EDGE_CURVE('',#162,#164,#166,.T.); -#162 = VERTEX_POINT('',#163); -#163 = CARTESIAN_POINT('',(26.824640127118,3.2,-55.23443461327)); -#164 = VERTEX_POINT('',#165); -#165 = CARTESIAN_POINT('',(42.184838000445,3.2,-7.043416644207)); -#166 = LINE('',#167,#168); -#167 = CARTESIAN_POINT('',(36.45312509682,3.2,-25.02606776693)); -#168 = VECTOR('',#169,1.); -#169 = DIRECTION('',(0.30368282823,0.,0.952773183837)); -#170 = ORIENTED_EDGE('',*,*,#171,.T.); -#171 = EDGE_CURVE('',#164,#24,#172,.T.); -#172 = LINE('',#173,#174); -#173 = CARTESIAN_POINT('',(42.184838000445,-5.,-7.043416644207)); -#174 = VECTOR('',#175,1.); -#175 = DIRECTION('',(0.,1.,0.)); -#176 = ORIENTED_EDGE('',*,*,#21,.F.); -#177 = ORIENTED_EDGE('',*,*,#178,.F.); -#178 = EDGE_CURVE('',#162,#22,#179,.T.); -#179 = LINE('',#180,#181); -#180 = CARTESIAN_POINT('',(26.824640127118,-5.,-55.23443461327)); -#181 = VECTOR('',#182,1.); -#182 = DIRECTION('',(0.,1.,0.)); -#183 = PLANE('',#184); -#184 = AXIS2_PLACEMENT_3D('',#185,#186,#187); -#185 = CARTESIAN_POINT('',(42.184838000445,-5.,-7.043416644207)); -#186 = DIRECTION('',(-0.952773183837,0.,0.30368282823)); -#187 = DIRECTION('',(-0.30368282823,0.,-0.952773183837)); -#188 = ADVANCED_FACE('',(#189),#207,.F.); -#189 = FACE_BOUND('',#190,.F.); -#190 = EDGE_LOOP('',(#191,#199,#205,#206)); -#191 = ORIENTED_EDGE('',*,*,#192,.T.); -#192 = EDGE_CURVE('',#164,#193,#195,.T.); -#193 = VERTEX_POINT('',#194); -#194 = CARTESIAN_POINT('',(35.041421356237,3.2,0.1)); -#195 = LINE('',#196,#197); -#196 = CARTESIAN_POINT('',(34.743124284224,3.2,0.398297072013)); -#197 = VECTOR('',#198,1.); -#198 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#199 = ORIENTED_EDGE('',*,*,#200,.T.); -#200 = EDGE_CURVE('',#193,#140,#201,.T.); -#201 = LINE('',#202,#203); -#202 = CARTESIAN_POINT('',(35.041421356237,-5.,0.1)); -#203 = VECTOR('',#204,1.); -#204 = DIRECTION('',(0.,1.,0.)); -#205 = ORIENTED_EDGE('',*,*,#147,.F.); -#206 = ORIENTED_EDGE('',*,*,#171,.F.); -#207 = PLANE('',#208); -#208 = AXIS2_PLACEMENT_3D('',#209,#210,#211); -#209 = CARTESIAN_POINT('',(35.041421356237,-5.,9.999999999998E-02)); -#210 = DIRECTION('',(-0.707106781187,0.,-0.707106781187)); -#211 = DIRECTION('',(0.707106781187,0.,-0.707106781187)); -#212 = ADVANCED_FACE('',(#213),#231,.T.); -#213 = FACE_BOUND('',#214,.T.); -#214 = EDGE_LOOP('',(#215,#223,#224,#225)); -#215 = ORIENTED_EDGE('',*,*,#216,.T.); -#216 = EDGE_CURVE('',#217,#193,#219,.T.); -#217 = VERTEX_POINT('',#218); -#218 = CARTESIAN_POINT('',(-32.57517518159,3.2,9.999999999998E-02)); -#219 = LINE('',#220,#221); -#220 = CARTESIAN_POINT('',(17.722238935846,3.2,0.1)); -#221 = VECTOR('',#222,1.); -#222 = DIRECTION('',(1.,0.,0.)); -#223 = ORIENTED_EDGE('',*,*,#200,.T.); -#224 = ORIENTED_EDGE('',*,*,#139,.F.); -#225 = ORIENTED_EDGE('',*,*,#226,.F.); -#226 = EDGE_CURVE('',#217,#131,#227,.T.); -#227 = LINE('',#228,#229); -#228 = CARTESIAN_POINT('',(-32.57517518159,-5.,9.999999999998E-02)); -#229 = VECTOR('',#230,1.); -#230 = DIRECTION('',(0.,1.,0.)); -#231 = PLANE('',#232); -#232 = AXIS2_PLACEMENT_3D('',#233,#234,#235); -#233 = CARTESIAN_POINT('',(35.041421356237,-5.,0.1)); -#234 = DIRECTION('',(0.,0.,1.)); -#235 = DIRECTION('',(-1.,0.,0.)); -#236 = ADVANCED_FACE('',(#237),#256,.T.); -#237 = FACE_BOUND('',#238,.F.); -#238 = EDGE_LOOP('',(#239,#248,#254,#255)); -#239 = ORIENTED_EDGE('',*,*,#240,.T.); -#240 = EDGE_CURVE('',#217,#241,#243,.T.); -#241 = VERTEX_POINT('',#242); -#242 = CARTESIAN_POINT('',(-36.7853207504,3.2,-1.643899394163)); -#243 = CIRCLE('',#244,5.954044962965); -#244 = AXIS2_PLACEMENT_3D('',#245,#246,#247); -#245 = CARTESIAN_POINT('',(-32.57517518159,3.2,-5.854044962965)); -#246 = DIRECTION('',(0.,-1.,0.)); -#247 = DIRECTION('',(1.,0.,0.)); -#248 = ORIENTED_EDGE('',*,*,#249,.T.); -#249 = EDGE_CURVE('',#241,#123,#250,.T.); -#250 = LINE('',#251,#252); -#251 = CARTESIAN_POINT('',(-36.7853207504,-5.,-1.643899394163)); -#252 = VECTOR('',#253,1.); -#253 = DIRECTION('',(0.,1.,0.)); -#254 = ORIENTED_EDGE('',*,*,#130,.F.); -#255 = ORIENTED_EDGE('',*,*,#226,.F.); -#256 = CYLINDRICAL_SURFACE('',#257,5.954044962965); -#257 = AXIS2_PLACEMENT_3D('',#258,#259,#260); -#258 = CARTESIAN_POINT('',(-32.57517518159,-5.,-5.854044962965)); -#259 = DIRECTION('',(0.,-1.,0.)); -#260 = DIRECTION('',(1.,0.,0.)); -#261 = ADVANCED_FACE('',(#262),#280,.T.); -#262 = FACE_BOUND('',#263,.T.); -#263 = EDGE_LOOP('',(#264,#272,#273,#274)); -#264 = ORIENTED_EDGE('',*,*,#265,.T.); -#265 = EDGE_CURVE('',#266,#241,#268,.T.); -#266 = VERTEX_POINT('',#267); -#267 = CARTESIAN_POINT('',(-39.62105538679,3.2,-4.479634030555)); -#268 = LINE('',#269,#270); -#269 = CARTESIAN_POINT('',(-35.41354572357,3.2,-0.272124367341)); -#270 = VECTOR('',#271,1.); -#271 = DIRECTION('',(0.707106781187,0.,0.707106781187)); -#272 = ORIENTED_EDGE('',*,*,#249,.T.); -#273 = ORIENTED_EDGE('',*,*,#122,.F.); -#274 = ORIENTED_EDGE('',*,*,#275,.F.); -#275 = EDGE_CURVE('',#266,#114,#276,.T.); -#276 = LINE('',#277,#278); -#277 = CARTESIAN_POINT('',(-39.62105538679,-5.,-4.479634030555)); -#278 = VECTOR('',#279,1.); -#279 = DIRECTION('',(0.,1.,0.)); -#280 = PLANE('',#281); -#281 = AXIS2_PLACEMENT_3D('',#282,#283,#284); -#282 = CARTESIAN_POINT('',(-36.7853207504,-5.,-1.643899394163)); -#283 = DIRECTION('',(-0.707106781187,0.,0.707106781187)); -#284 = DIRECTION('',(-0.707106781187,0.,-0.707106781187)); -#285 = ADVANCED_FACE('',(#286),#305,.T.); -#286 = FACE_BOUND('',#287,.F.); -#287 = EDGE_LOOP('',(#288,#297,#303,#304)); -#288 = ORIENTED_EDGE('',*,*,#289,.T.); -#289 = EDGE_CURVE('',#266,#290,#292,.T.); -#290 = VERTEX_POINT('',#291); -#291 = CARTESIAN_POINT('',(-41.08376419406,3.2,-10.49792081311)); -#292 = CIRCLE('',#293,5.954044962965); -#293 = AXIS2_PLACEMENT_3D('',#294,#295,#296); -#294 = CARTESIAN_POINT('',(-35.41090981799,3.2,-8.689779599357)); -#295 = DIRECTION('',(0.,-1.,0.)); -#296 = DIRECTION('',(1.,0.,0.)); -#297 = ORIENTED_EDGE('',*,*,#298,.T.); -#298 = EDGE_CURVE('',#290,#106,#299,.T.); -#299 = LINE('',#300,#301); -#300 = CARTESIAN_POINT('',(-41.08376419406,-5.,-10.49792081311)); -#301 = VECTOR('',#302,1.); -#302 = DIRECTION('',(0.,1.,0.)); -#303 = ORIENTED_EDGE('',*,*,#113,.F.); -#304 = ORIENTED_EDGE('',*,*,#275,.F.); -#305 = CYLINDRICAL_SURFACE('',#306,5.954044962965); -#306 = AXIS2_PLACEMENT_3D('',#307,#308,#309); -#307 = CARTESIAN_POINT('',(-35.41090981799,-5.,-8.689779599357)); -#308 = DIRECTION('',(0.,-1.,0.)); -#309 = DIRECTION('',(1.,0.,0.)); -#310 = ADVANCED_FACE('',(#311),#329,.T.); -#311 = FACE_BOUND('',#312,.T.); -#312 = EDGE_LOOP('',(#313,#321,#322,#323)); -#313 = ORIENTED_EDGE('',*,*,#314,.T.); -#314 = EDGE_CURVE('',#315,#290,#317,.T.); -#315 = VERTEX_POINT('',#316); -#316 = CARTESIAN_POINT('',(-26.82464012711,3.2,-55.23443461327)); -#317 = LINE('',#318,#319); -#318 = CARTESIAN_POINT('',(-35.86541700774,3.2,-26.86994056823)); -#319 = VECTOR('',#320,1.); -#320 = DIRECTION('',(-0.30368282823,0.,0.952773183837)); -#321 = ORIENTED_EDGE('',*,*,#298,.T.); -#322 = ORIENTED_EDGE('',*,*,#105,.F.); -#323 = ORIENTED_EDGE('',*,*,#324,.F.); -#324 = EDGE_CURVE('',#315,#98,#325,.T.); -#325 = LINE('',#326,#327); -#326 = CARTESIAN_POINT('',(-26.82464012711,-5.,-55.23443461327)); -#327 = VECTOR('',#328,1.); -#328 = DIRECTION('',(0.,1.,0.)); -#329 = PLANE('',#330); -#330 = AXIS2_PLACEMENT_3D('',#331,#332,#333); -#331 = CARTESIAN_POINT('',(-41.08376419406,-5.,-10.49792081311)); -#332 = DIRECTION('',(-0.952773183837,0.,-0.30368282823)); -#333 = DIRECTION('',(0.30368282823,0.,-0.952773183837)); -#334 = ADVANCED_FACE('',(#335),#353,.T.); -#335 = FACE_BOUND('',#336,.T.); -#336 = EDGE_LOOP('',(#337,#345,#346,#347)); -#337 = ORIENTED_EDGE('',*,*,#338,.T.); -#338 = EDGE_CURVE('',#339,#315,#341,.T.); -#339 = VERTEX_POINT('',#340); -#340 = CARTESIAN_POINT('',(-28.89758284854,3.2,-57.24050703685)); -#341 = LINE('',#342,#343); -#342 = CARTESIAN_POINT('',(-14.32522020853,3.2,-43.13822889076)); -#343 = VECTOR('',#344,1.); -#344 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#345 = ORIENTED_EDGE('',*,*,#324,.T.); -#346 = ORIENTED_EDGE('',*,*,#97,.F.); -#347 = ORIENTED_EDGE('',*,*,#348,.F.); -#348 = EDGE_CURVE('',#339,#89,#349,.T.); -#349 = LINE('',#350,#351); -#350 = CARTESIAN_POINT('',(-28.89758284854,-5.,-57.24050703685)); -#351 = VECTOR('',#352,1.); -#352 = DIRECTION('',(0.,1.,0.)); -#353 = PLANE('',#354); -#354 = AXIS2_PLACEMENT_3D('',#355,#356,#357); -#355 = CARTESIAN_POINT('',(-26.82464012711,-5.,-55.23443461327)); -#356 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#357 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#358 = ADVANCED_FACE('',(#359),#378,.T.); -#359 = FACE_BOUND('',#360,.F.); -#360 = EDGE_LOOP('',(#361,#370,#376,#377)); -#361 = ORIENTED_EDGE('',*,*,#362,.T.); -#362 = EDGE_CURVE('',#339,#363,#365,.T.); -#363 = VERTEX_POINT('',#364); -#364 = CARTESIAN_POINT('',(-29.01580660709,3.2,-64.4520272055)); -#365 = CIRCLE('',#366,5.1); -#366 = AXIS2_PLACEMENT_3D('',#367,#368,#369); -#367 = CARTESIAN_POINT('',(-25.35093464349,3.2,-60.90537900045)); -#368 = DIRECTION('',(0.,-1.,0.)); -#369 = DIRECTION('',(1.,0.,0.)); -#370 = ORIENTED_EDGE('',*,*,#371,.T.); -#371 = EDGE_CURVE('',#363,#81,#372,.T.); -#372 = LINE('',#373,#374); -#373 = CARTESIAN_POINT('',(-29.01580660709,-5.,-64.4520272055)); -#374 = VECTOR('',#375,1.); -#375 = DIRECTION('',(0.,1.,0.)); -#376 = ORIENTED_EDGE('',*,*,#88,.F.); -#377 = ORIENTED_EDGE('',*,*,#348,.F.); -#378 = CYLINDRICAL_SURFACE('',#379,5.1); -#379 = AXIS2_PLACEMENT_3D('',#380,#381,#382); -#380 = CARTESIAN_POINT('',(-25.35093464349,-5.,-60.90537900045)); -#381 = DIRECTION('',(0.,-1.,0.)); -#382 = DIRECTION('',(1.,0.,0.)); -#383 = ADVANCED_FACE('',(#384),#402,.T.); -#384 = FACE_BOUND('',#385,.T.); -#385 = EDGE_LOOP('',(#386,#394,#395,#396)); -#386 = ORIENTED_EDGE('',*,*,#387,.T.); -#387 = EDGE_CURVE('',#388,#363,#390,.T.); -#388 = VERTEX_POINT('',#389); -#389 = CARTESIAN_POINT('',(-27.78972445201,3.2,-65.71897950961)); -#390 = LINE('',#391,#392); -#391 = CARTESIAN_POINT('',(-29.67470230692,3.2,-63.77116791594)); -#392 = VECTOR('',#393,1.); -#393 = DIRECTION('',(-0.695421216677,0.,0.718602345805)); -#394 = ORIENTED_EDGE('',*,*,#371,.T.); -#395 = ORIENTED_EDGE('',*,*,#80,.F.); -#396 = ORIENTED_EDGE('',*,*,#397,.F.); -#397 = EDGE_CURVE('',#388,#72,#398,.T.); -#398 = LINE('',#399,#400); -#399 = CARTESIAN_POINT('',(-27.78972445201,-5.,-65.71897950961)); -#400 = VECTOR('',#401,1.); -#401 = DIRECTION('',(0.,1.,0.)); -#402 = PLANE('',#403); -#403 = AXIS2_PLACEMENT_3D('',#404,#405,#406); -#404 = CARTESIAN_POINT('',(-29.01580660709,-5.,-64.4520272055)); -#405 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#406 = DIRECTION('',(0.695421216677,0.,-0.718602345805)); -#407 = ADVANCED_FACE('',(#408),#427,.T.); -#408 = FACE_BOUND('',#409,.F.); -#409 = EDGE_LOOP('',(#410,#419,#425,#426)); -#410 = ORIENTED_EDGE('',*,*,#411,.T.); -#411 = EDGE_CURVE('',#388,#412,#414,.T.); -#412 = VERTEX_POINT('',#413); -#413 = CARTESIAN_POINT('',(-25.60006917852,3.2,-65.75487614033)); -#414 = CIRCLE('',#415,1.548528137424); -#415 = AXIS2_PLACEMENT_3D('',#416,#417,#418); -#416 = CARTESIAN_POINT('',(-26.67694849991,3.2,-64.64210018823)); -#417 = DIRECTION('',(0.,-1.,0.)); -#418 = DIRECTION('',(1.,0.,0.)); -#419 = ORIENTED_EDGE('',*,*,#420,.T.); -#420 = EDGE_CURVE('',#412,#64,#421,.T.); -#421 = LINE('',#422,#423); -#422 = CARTESIAN_POINT('',(-25.60006917852,-5.,-65.75487614033)); -#423 = VECTOR('',#424,1.); -#424 = DIRECTION('',(0.,1.,0.)); -#425 = ORIENTED_EDGE('',*,*,#71,.F.); -#426 = ORIENTED_EDGE('',*,*,#397,.F.); -#427 = CYLINDRICAL_SURFACE('',#428,1.548528137424); -#428 = AXIS2_PLACEMENT_3D('',#429,#430,#431); -#429 = CARTESIAN_POINT('',(-26.67694849991,-5.,-64.64210018823)); -#430 = DIRECTION('',(0.,-1.,0.)); -#431 = DIRECTION('',(1.,0.,0.)); -#432 = ADVANCED_FACE('',(#433),#451,.T.); -#433 = FACE_BOUND('',#434,.T.); -#434 = EDGE_LOOP('',(#435,#443,#444,#445)); -#435 = ORIENTED_EDGE('',*,*,#436,.T.); -#436 = EDGE_CURVE('',#437,#412,#439,.T.); -#437 = VERTEX_POINT('',#438); -#438 = CARTESIAN_POINT('',(-20.57248334837,3.2,-60.88947335481)); -#439 = LINE('',#440,#441); -#440 = CARTESIAN_POINT('',(-10.78812989347,3.2,-51.42074976419)); -#441 = VECTOR('',#442,1.); -#442 = DIRECTION('',(-0.718602345805,0.,-0.695421216677)); -#443 = ORIENTED_EDGE('',*,*,#420,.T.); -#444 = ORIENTED_EDGE('',*,*,#63,.F.); -#445 = ORIENTED_EDGE('',*,*,#446,.F.); -#446 = EDGE_CURVE('',#437,#56,#447,.T.); -#447 = LINE('',#448,#449); -#448 = CARTESIAN_POINT('',(-20.57248334837,-5.,-60.88947335481)); -#449 = VECTOR('',#450,1.); -#450 = DIRECTION('',(0.,1.,0.)); -#451 = PLANE('',#452); -#452 = AXIS2_PLACEMENT_3D('',#453,#454,#455); -#453 = CARTESIAN_POINT('',(-25.60006917852,-5.,-65.75487614033)); -#454 = DIRECTION('',(0.695421216677,0.,-0.718602345805)); -#455 = DIRECTION('',(0.718602345805,0.,0.695421216677)); -#456 = ADVANCED_FACE('',(#457),#475,.F.); -#457 = FACE_BOUND('',#458,.F.); -#458 = EDGE_LOOP('',(#459,#467,#473,#474)); -#459 = ORIENTED_EDGE('',*,*,#460,.T.); -#460 = EDGE_CURVE('',#437,#461,#463,.T.); -#461 = VERTEX_POINT('',#462); -#462 = CARTESIAN_POINT('',(20.57248334837,3.2,-60.88947335481)); -#463 = LINE('',#464,#465); -#464 = CARTESIAN_POINT('',(10.487769931912,3.2,-60.88947335481)); -#465 = VECTOR('',#466,1.); -#466 = DIRECTION('',(1.,0.,0.)); -#467 = ORIENTED_EDGE('',*,*,#468,.T.); -#468 = EDGE_CURVE('',#461,#48,#469,.T.); -#469 = LINE('',#470,#471); -#470 = CARTESIAN_POINT('',(20.57248334837,-5.,-60.88947335481)); -#471 = VECTOR('',#472,1.); -#472 = DIRECTION('',(0.,1.,0.)); -#473 = ORIENTED_EDGE('',*,*,#55,.F.); -#474 = ORIENTED_EDGE('',*,*,#446,.F.); -#475 = PLANE('',#476); -#476 = AXIS2_PLACEMENT_3D('',#477,#478,#479); -#477 = CARTESIAN_POINT('',(20.57248334837,-5.,-60.88947335481)); -#478 = DIRECTION('',(0.,0.,1.)); -#479 = DIRECTION('',(-1.,0.,0.)); -#480 = ADVANCED_FACE('',(#481),#499,.F.); -#481 = FACE_BOUND('',#482,.F.); -#482 = EDGE_LOOP('',(#483,#491,#497,#498)); -#483 = ORIENTED_EDGE('',*,*,#484,.T.); -#484 = EDGE_CURVE('',#461,#485,#487,.T.); -#485 = VERTEX_POINT('',#486); -#486 = CARTESIAN_POINT('',(26.712845130625,3.2,-66.83175546171)); -#487 = LINE('',#488,#489); -#488 = CARTESIAN_POINT('',(11.552651954056,3.2,-52.16060938843)); -#489 = VECTOR('',#490,1.); -#490 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#491 = ORIENTED_EDGE('',*,*,#492,.T.); -#492 = EDGE_CURVE('',#485,#40,#493,.T.); -#493 = LINE('',#494,#495); -#494 = CARTESIAN_POINT('',(26.712845130625,-5.,-66.83175546171)); -#495 = VECTOR('',#496,1.); -#496 = DIRECTION('',(0.,1.,0.)); -#497 = ORIENTED_EDGE('',*,*,#47,.F.); -#498 = ORIENTED_EDGE('',*,*,#468,.F.); -#499 = PLANE('',#500); -#500 = AXIS2_PLACEMENT_3D('',#501,#502,#503); -#501 = CARTESIAN_POINT('',(26.712845130625,-5.,-66.83175546171)); -#502 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#503 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#504 = ADVANCED_FACE('',(#505),#523,.F.); -#505 = FACE_BOUND('',#506,.F.); -#506 = EDGE_LOOP('',(#507,#515,#521,#522)); -#507 = ORIENTED_EDGE('',*,*,#508,.T.); -#508 = EDGE_CURVE('',#485,#509,#511,.T.); -#509 = VERTEX_POINT('',#510); -#510 = CARTESIAN_POINT('',(32.56245481215,3.2,-60.7871552419)); -#511 = LINE('',#512,#513); -#512 = CARTESIAN_POINT('',(31.642948840372,3.2,-61.73731197059)); -#513 = VECTOR('',#514,1.); -#514 = DIRECTION('',(0.695421216677,0.,0.718602345805)); -#515 = ORIENTED_EDGE('',*,*,#516,.T.); -#516 = EDGE_CURVE('',#509,#32,#517,.T.); -#517 = LINE('',#518,#519); -#518 = CARTESIAN_POINT('',(32.56245481215,-5.,-60.7871552419)); -#519 = VECTOR('',#520,1.); -#520 = DIRECTION('',(0.,1.,0.)); -#521 = ORIENTED_EDGE('',*,*,#39,.F.); -#522 = ORIENTED_EDGE('',*,*,#492,.F.); -#523 = PLANE('',#524); -#524 = AXIS2_PLACEMENT_3D('',#525,#526,#527); -#525 = CARTESIAN_POINT('',(32.56245481215,-5.,-60.7871552419)); -#526 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#527 = DIRECTION('',(-0.695421216677,0.,-0.718602345805)); -#528 = ADVANCED_FACE('',(#529),#540,.F.); -#529 = FACE_BOUND('',#530,.F.); -#530 = EDGE_LOOP('',(#531,#537,#538,#539)); -#531 = ORIENTED_EDGE('',*,*,#532,.T.); -#532 = EDGE_CURVE('',#509,#162,#533,.T.); -#533 = LINE('',#534,#535); -#534 = CARTESIAN_POINT('',(14.533354293065,3.2,-43.3396488543)); -#535 = VECTOR('',#536,1.); -#536 = DIRECTION('',(-0.718602345805,0.,0.695421216677)); -#537 = ORIENTED_EDGE('',*,*,#178,.T.); -#538 = ORIENTED_EDGE('',*,*,#31,.F.); -#539 = ORIENTED_EDGE('',*,*,#516,.F.); -#540 = PLANE('',#541); -#541 = AXIS2_PLACEMENT_3D('',#542,#543,#544); -#542 = CARTESIAN_POINT('',(26.824640127118,-5.,-55.23443461327)); -#543 = DIRECTION('',(-0.695421216677,0.,-0.718602345805)); -#544 = DIRECTION('',(0.718602345805,0.,-0.695421216677)); -#545 = ADVANCED_FACE('',(#546,#564,#614,#648,#659),#670,.F.); -#546 = FACE_BOUND('',#547,.F.); -#547 = EDGE_LOOP('',(#548,#549,#550,#551,#552,#553,#554,#555,#556,#557, - #558,#559,#560,#561,#562,#563)); -#548 = ORIENTED_EDGE('',*,*,#161,.F.); -#549 = ORIENTED_EDGE('',*,*,#532,.F.); -#550 = ORIENTED_EDGE('',*,*,#508,.F.); -#551 = ORIENTED_EDGE('',*,*,#484,.F.); -#552 = ORIENTED_EDGE('',*,*,#460,.F.); -#553 = ORIENTED_EDGE('',*,*,#436,.T.); -#554 = ORIENTED_EDGE('',*,*,#411,.F.); -#555 = ORIENTED_EDGE('',*,*,#387,.T.); -#556 = ORIENTED_EDGE('',*,*,#362,.F.); -#557 = ORIENTED_EDGE('',*,*,#338,.T.); -#558 = ORIENTED_EDGE('',*,*,#314,.T.); -#559 = ORIENTED_EDGE('',*,*,#289,.F.); -#560 = ORIENTED_EDGE('',*,*,#265,.T.); -#561 = ORIENTED_EDGE('',*,*,#240,.F.); -#562 = ORIENTED_EDGE('',*,*,#216,.T.); -#563 = ORIENTED_EDGE('',*,*,#192,.F.); -#564 = FACE_BOUND('',#565,.F.); -#565 = EDGE_LOOP('',(#566,#576,#584,#592,#600,#608)); -#566 = ORIENTED_EDGE('',*,*,#567,.F.); -#567 = EDGE_CURVE('',#568,#570,#572,.T.); -#568 = VERTEX_POINT('',#569); -#569 = CARTESIAN_POINT('',(16.626582997737,3.2,-8.940188245231)); -#570 = VERTEX_POINT('',#571); -#571 = CARTESIAN_POINT('',(4.383041634064E-04,3.2,-8.903751615529)); -#572 = LINE('',#573,#574); -#573 = CARTESIAN_POINT('',(4.347099726942,3.2,-8.913277437397)); -#574 = VECTOR('',#575,1.); -#575 = DIRECTION('',(-0.999997598615,0.,2.19152081707E-03)); -#576 = ORIENTED_EDGE('',*,*,#577,.F.); -#577 = EDGE_CURVE('',#578,#568,#580,.T.); -#578 = VERTEX_POINT('',#579); -#579 = CARTESIAN_POINT('',(19.029295926024,3.2,-17.63009960955)); -#580 = LINE('',#581,#582); -#581 = CARTESIAN_POINT('',(19.849519003668,3.2,-20.59660707692)); -#582 = VECTOR('',#583,1.); -#583 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#584 = ORIENTED_EDGE('',*,*,#585,.F.); -#585 = EDGE_CURVE('',#586,#578,#588,.T.); -#586 = VERTEX_POINT('',#587); -#587 = CARTESIAN_POINT('',(22.059435554995,3.2,-3.734519760785)); -#588 = LINE('',#589,#590); -#589 = CARTESIAN_POINT('',(17.698510515043,3.2,-23.73280021221)); -#590 = VECTOR('',#591,1.); -#591 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#592 = ORIENTED_EDGE('',*,*,#593,.F.); -#593 = EDGE_CURVE('',#594,#586,#596,.T.); -#594 = VERTEX_POINT('',#595); -#595 = CARTESIAN_POINT('',(-21.72552223146,3.2,-3.734519760785)); -#596 = LINE('',#597,#598); -#597 = CARTESIAN_POINT('',(0.297084840953,3.2,-3.734519760785)); -#598 = VECTOR('',#599,1.); -#599 = DIRECTION('',(1.,0.,0.)); -#600 = ORIENTED_EDGE('',*,*,#601,.F.); -#601 = EDGE_CURVE('',#602,#594,#604,.T.); -#602 = VERTEX_POINT('',#603); -#603 = CARTESIAN_POINT('',(-21.72552223146,3.2,-8.903751135252)); -#604 = LINE('',#605,#606); -#605 = CARTESIAN_POINT('',(-21.72552223146,3.2,-19.83215600037)); -#606 = VECTOR('',#607,1.); -#607 = DIRECTION('',(0.,0.,1.)); -#608 = ORIENTED_EDGE('',*,*,#609,.F.); -#609 = EDGE_CURVE('',#570,#602,#610,.T.); -#610 = LINE('',#611,#612); -#611 = CARTESIAN_POINT('',(-5.279852300138,3.2,-8.903751135252)); -#612 = VECTOR('',#613,1.); -#613 = DIRECTION('',(-1.,0.,0.)); -#614 = FACE_BOUND('',#615,.F.); -#615 = EDGE_LOOP('',(#616,#626,#634,#642)); -#616 = ORIENTED_EDGE('',*,*,#617,.F.); -#617 = EDGE_CURVE('',#618,#620,#622,.T.); -#618 = VERTEX_POINT('',#619); -#619 = CARTESIAN_POINT('',(5.809375885494,3.2,-13.06417917474)); -#620 = VERTEX_POINT('',#621); -#621 = CARTESIAN_POINT('',(2.688069798796,3.2,-49.64588621989)); -#622 = LINE('',#623,#624); -#623 = CARTESIAN_POINT('',(4.914157977861,3.2,-23.55613296875)); -#624 = VECTOR('',#625,1.); -#625 = DIRECTION('',(-8.501532861638E-02,0.,-0.996379643459)); -#626 = ORIENTED_EDGE('',*,*,#627,.F.); -#627 = EDGE_CURVE('',#628,#618,#630,.T.); -#628 = VERTEX_POINT('',#629); -#629 = CARTESIAN_POINT('',(-5.809375885494,3.2,-13.06417917474)); -#630 = LINE('',#631,#632); -#631 = CARTESIAN_POINT('',(1.615747408047,3.2,-13.06417917474)); -#632 = VECTOR('',#633,1.); -#633 = DIRECTION('',(1.,0.,-3.066574716487E-16)); -#634 = ORIENTED_EDGE('',*,*,#635,.F.); -#635 = EDGE_CURVE('',#636,#628,#638,.T.); -#636 = VERTEX_POINT('',#637); -#637 = CARTESIAN_POINT('',(-2.688069798796,3.2,-49.64588621989)); -#638 = LINE('',#639,#640); -#639 = CARTESIAN_POINT('',(-4.890802006217,3.2,-23.82986495424)); -#640 = VECTOR('',#641,1.); -#641 = DIRECTION('',(-8.501532861638E-02,0.,0.996379643459)); -#642 = ORIENTED_EDGE('',*,*,#643,.F.); -#643 = EDGE_CURVE('',#620,#636,#644,.T.); -#644 = LINE('',#645,#646); -#645 = CARTESIAN_POINT('',(1.615747408047,3.2,-49.64588621989)); -#646 = VECTOR('',#647,1.); -#647 = DIRECTION('',(-1.,0.,0.)); -#648 = FACE_BOUND('',#649,.F.); -#649 = EDGE_LOOP('',(#650)); -#650 = ORIENTED_EDGE('',*,*,#651,.F.); -#651 = EDGE_CURVE('',#652,#652,#654,.T.); -#652 = VERTEX_POINT('',#653); -#653 = CARTESIAN_POINT('',(-11.6927539352,3.2,-48.00951684793)); -#654 = CIRCLE('',#655,4.735522705283); -#655 = AXIS2_PLACEMENT_3D('',#656,#657,#658); -#656 = CARTESIAN_POINT('',(-16.42827664048,3.2,-48.00951684793)); -#657 = DIRECTION('',(-0.,1.,0.)); -#658 = DIRECTION('',(1.,0.,0.)); -#659 = FACE_BOUND('',#660,.F.); -#660 = EDGE_LOOP('',(#661)); -#661 = ORIENTED_EDGE('',*,*,#662,.F.); -#662 = EDGE_CURVE('',#663,#663,#665,.T.); -#663 = VERTEX_POINT('',#664); -#664 = CARTESIAN_POINT('',(21.163799345768,3.2,-48.00951684793)); -#665 = CIRCLE('',#666,4.735522705283); -#666 = AXIS2_PLACEMENT_3D('',#667,#668,#669); -#667 = CARTESIAN_POINT('',(16.428276640485,3.2,-48.00951684793)); -#668 = DIRECTION('',(-0.,1.,0.)); -#669 = DIRECTION('',(1.,0.,0.)); -#670 = PLANE('',#671); -#671 = AXIS2_PLACEMENT_3D('',#672,#673,#674); -#672 = CARTESIAN_POINT('',(0.403056515455,3.2,-33.34517655273)); -#673 = DIRECTION('',(0.,1.,0.)); -#674 = DIRECTION('',(1.,0.,0.)); -#675 = ADVANCED_FACE('',(#676),#701,.T.); -#676 = FACE_BOUND('',#677,.T.); -#677 = EDGE_LOOP('',(#678,#686,#694,#700)); -#678 = ORIENTED_EDGE('',*,*,#679,.F.); -#679 = EDGE_CURVE('',#680,#568,#682,.T.); -#680 = VERTEX_POINT('',#681); -#681 = CARTESIAN_POINT('',(16.626582997737,0.,-8.940188245231)); -#682 = LINE('',#683,#684); -#683 = CARTESIAN_POINT('',(16.626582997737,-22.,-8.940188245231)); -#684 = VECTOR('',#685,1.); -#685 = DIRECTION('',(0.,1.,0.)); -#686 = ORIENTED_EDGE('',*,*,#687,.T.); -#687 = EDGE_CURVE('',#680,#688,#690,.T.); -#688 = VERTEX_POINT('',#689); -#689 = CARTESIAN_POINT('',(-5.329070518201E-15,0.,-8.903751135252)); -#690 = LINE('',#691,#692); -#691 = CARTESIAN_POINT('',(-18.54556462154,1.7763568394E-15, - -8.863107566442)); -#692 = VECTOR('',#693,1.); -#693 = DIRECTION('',(-0.999997598615,0.,2.19152081707E-03)); -#694 = ORIENTED_EDGE('',*,*,#695,.T.); -#695 = EDGE_CURVE('',#688,#570,#696,.T.); -#696 = LINE('',#697,#698); -#697 = CARTESIAN_POINT('',(-5.329070518201E-15,-22.,-8.903751135252)); -#698 = VECTOR('',#699,1.); -#699 = DIRECTION('',(0.,1.,0.)); -#700 = ORIENTED_EDGE('',*,*,#567,.F.); -#701 = PLANE('',#702); -#702 = AXIS2_PLACEMENT_3D('',#703,#704,#705); -#703 = CARTESIAN_POINT('',(8.237581109188,-22.,-8.921803528809)); -#704 = DIRECTION('',(-2.19152081707E-03,0.,-0.999997598615)); -#705 = DIRECTION('',(-0.999997598615,0.,2.19152081707E-03)); -#706 = ADVANCED_FACE('',(#707),#725,.T.); -#707 = FACE_BOUND('',#708,.T.); -#708 = EDGE_LOOP('',(#709,#717,#723,#724)); -#709 = ORIENTED_EDGE('',*,*,#710,.F.); -#710 = EDGE_CURVE('',#711,#578,#713,.T.); -#711 = VERTEX_POINT('',#712); -#712 = CARTESIAN_POINT('',(19.029295926024,0.,-17.63009960955)); -#713 = LINE('',#714,#715); -#714 = CARTESIAN_POINT('',(19.029295926024,-22.,-17.63009960955)); -#715 = VECTOR('',#716,1.); -#716 = DIRECTION('',(0.,1.,0.)); -#717 = ORIENTED_EDGE('',*,*,#718,.T.); -#718 = EDGE_CURVE('',#711,#680,#719,.T.); -#719 = LINE('',#720,#721); -#720 = CARTESIAN_POINT('',(23.053273013696,0.,-32.18365022821)); -#721 = VECTOR('',#722,1.); -#722 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#723 = ORIENTED_EDGE('',*,*,#679,.T.); -#724 = ORIENTED_EDGE('',*,*,#577,.F.); -#725 = PLANE('',#726); -#726 = AXIS2_PLACEMENT_3D('',#727,#728,#729); -#727 = CARTESIAN_POINT('',(17.956031892536,-22.,-13.7484168618)); -#728 = DIRECTION('',(-0.963836182336,0.,-0.26649542889)); -#729 = DIRECTION('',(-0.26649542889,0.,0.963836182336)); -#730 = ADVANCED_FACE('',(#731),#749,.T.); -#731 = FACE_BOUND('',#732,.T.); -#732 = EDGE_LOOP('',(#733,#741,#747,#748)); -#733 = ORIENTED_EDGE('',*,*,#734,.T.); -#734 = EDGE_CURVE('',#688,#735,#737,.T.); -#735 = VERTEX_POINT('',#736); -#736 = CARTESIAN_POINT('',(-21.72552223146,0.,-8.903751135252)); -#737 = LINE('',#738,#739); -#738 = CARTESIAN_POINT('',(-28.21385794834,0.,-8.903751135252)); -#739 = VECTOR('',#740,1.); -#740 = DIRECTION('',(-1.,0.,0.)); -#741 = ORIENTED_EDGE('',*,*,#742,.T.); -#742 = EDGE_CURVE('',#735,#602,#743,.T.); -#743 = LINE('',#744,#745); -#744 = CARTESIAN_POINT('',(-21.72552223146,-22.,-8.903751135252)); -#745 = VECTOR('',#746,1.); -#746 = DIRECTION('',(0.,1.,0.)); -#747 = ORIENTED_EDGE('',*,*,#609,.F.); -#748 = ORIENTED_EDGE('',*,*,#695,.F.); -#749 = PLANE('',#750); -#750 = AXIS2_PLACEMENT_3D('',#751,#752,#753); -#751 = CARTESIAN_POINT('',(-10.96276111573,-22.,-8.903751135252)); -#752 = DIRECTION('',(0.,0.,-1.)); -#753 = DIRECTION('',(0.,1.,0.)); -#754 = ADVANCED_FACE('',(#755),#773,.T.); -#755 = FACE_BOUND('',#756,.T.); -#756 = EDGE_LOOP('',(#757,#765,#766,#767)); -#757 = ORIENTED_EDGE('',*,*,#758,.T.); -#758 = EDGE_CURVE('',#759,#711,#761,.T.); -#759 = VERTEX_POINT('',#760); -#760 = CARTESIAN_POINT('',(22.059435554995,0.,-3.734519760785)); -#761 = LINE('',#762,#763); -#762 = CARTESIAN_POINT('',(12.741012548745,1.7763568394E-15, - -46.46683800444)); -#763 = VECTOR('',#764,1.); -#764 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#765 = ORIENTED_EDGE('',*,*,#710,.T.); -#766 = ORIENTED_EDGE('',*,*,#585,.F.); -#767 = ORIENTED_EDGE('',*,*,#768,.F.); -#768 = EDGE_CURVE('',#759,#586,#769,.T.); -#769 = LINE('',#770,#771); -#770 = CARTESIAN_POINT('',(22.059435554995,-22.,-3.734519760785)); -#771 = VECTOR('',#772,1.); -#772 = DIRECTION('',(0.,1.,0.)); -#773 = PLANE('',#774); -#774 = AXIS2_PLACEMENT_3D('',#775,#776,#777); -#775 = CARTESIAN_POINT('',(20.484588228021,-22.,-10.95643672209)); -#776 = DIRECTION('',(0.977039526026,0.,-0.213058124893)); -#777 = DIRECTION('',(-0.213058124893,0.,-0.977039526026)); -#778 = ADVANCED_FACE('',(#779),#797,.T.); -#779 = FACE_BOUND('',#780,.T.); -#780 = EDGE_LOOP('',(#781,#789,#795,#796)); -#781 = ORIENTED_EDGE('',*,*,#782,.T.); -#782 = EDGE_CURVE('',#735,#783,#785,.T.); -#783 = VERTEX_POINT('',#784); -#784 = CARTESIAN_POINT('',(-21.72552223146,0.,-3.734519760785)); -#785 = LINE('',#786,#787); -#786 = CARTESIAN_POINT('',(-21.72552223146,0.,-38.64614663299)); -#787 = VECTOR('',#788,1.); -#788 = DIRECTION('',(0.,0.,1.)); -#789 = ORIENTED_EDGE('',*,*,#790,.T.); -#790 = EDGE_CURVE('',#783,#594,#791,.T.); -#791 = LINE('',#792,#793); -#792 = CARTESIAN_POINT('',(-21.72552223146,-22.,-3.734519760785)); -#793 = VECTOR('',#794,1.); -#794 = DIRECTION('',(0.,1.,0.)); -#795 = ORIENTED_EDGE('',*,*,#601,.F.); -#796 = ORIENTED_EDGE('',*,*,#742,.F.); -#797 = PLANE('',#798); -#798 = AXIS2_PLACEMENT_3D('',#799,#800,#801); -#799 = CARTESIAN_POINT('',(-21.72552223146,-22.,-6.319135448019)); -#800 = DIRECTION('',(-1.,0.,0.)); -#801 = DIRECTION('',(0.,1.,0.)); -#802 = ADVANCED_FACE('',(#803),#814,.T.); -#803 = FACE_BOUND('',#804,.T.); -#804 = EDGE_LOOP('',(#805,#811,#812,#813)); -#805 = ORIENTED_EDGE('',*,*,#806,.T.); -#806 = EDGE_CURVE('',#783,#759,#807,.T.); -#807 = LINE('',#808,#809); -#808 = CARTESIAN_POINT('',(-22.63692080725,0.,-3.734519760785)); -#809 = VECTOR('',#810,1.); -#810 = DIRECTION('',(1.,0.,0.)); -#811 = ORIENTED_EDGE('',*,*,#768,.T.); -#812 = ORIENTED_EDGE('',*,*,#593,.F.); -#813 = ORIENTED_EDGE('',*,*,#790,.F.); -#814 = PLANE('',#815); -#815 = AXIS2_PLACEMENT_3D('',#816,#817,#818); -#816 = CARTESIAN_POINT('',(0.19111316645,-22.,-3.734519760785)); -#817 = DIRECTION('',(0.,0.,1.)); -#818 = DIRECTION('',(0.,-1.,0.)); -#819 = ADVANCED_FACE('',(#820),#845,.F.); -#820 = FACE_BOUND('',#821,.F.); -#821 = EDGE_LOOP('',(#822,#832,#838,#839)); -#822 = ORIENTED_EDGE('',*,*,#823,.T.); -#823 = EDGE_CURVE('',#824,#826,#828,.T.); -#824 = VERTEX_POINT('',#825); -#825 = CARTESIAN_POINT('',(1.480297366167E-15,11.242400581089, - -46.71869179633)); -#826 = VERTEX_POINT('',#827); -#827 = CARTESIAN_POINT('',(3.256654205567E-15,17.8572529153, - -18.39898295202)); -#828 = LINE('',#829,#830); -#829 = CARTESIAN_POINT('',(2.6645352591E-15,18.133069549222, - -17.21814831317)); -#830 = VECTOR('',#831,1.); -#831 = DIRECTION('',(5.275122655165E-17,0.227455280238,0.97378852709)); -#832 = ORIENTED_EDGE('',*,*,#833,.F.); -#833 = EDGE_CURVE('',#618,#826,#834,.T.); -#834 = LINE('',#835,#836); -#835 = CARTESIAN_POINT('',(5.649679875255,3.602918464526,-13.21082950266 - )); -#836 = VECTOR('',#837,1.); -#837 = DIRECTION('',(-0.349023821871,0.880598971639,-0.320511814002)); -#838 = ORIENTED_EDGE('',*,*,#617,.T.); -#839 = ORIENTED_EDGE('',*,*,#840,.T.); -#840 = EDGE_CURVE('',#620,#824,#841,.T.); -#841 = LINE('',#842,#843); -#842 = CARTESIAN_POINT('',(1.103762571829,7.940067898719,-47.92064259636 - )); -#843 = VECTOR('',#844,1.); -#844 = DIRECTION('',(-0.299648208284,0.896513522642,0.326304236859)); -#845 = PLANE('',#846); -#846 = AXIS2_PLACEMENT_3D('',#847,#848,#849); -#847 = CARTESIAN_POINT('',(5.823691883056,3.068404028665,-13.45978839622 - )); -#848 = DIRECTION('',(0.93629059846,0.342020143326,-7.988827695448E-02)); -#849 = DIRECTION('',(-0.340781908463,0.939692620786,2.907695487825E-02) - ); -#850 = ADVANCED_FACE('',(#851),#861,.F.); -#851 = FACE_BOUND('',#852,.F.); -#852 = EDGE_LOOP('',(#853,#854,#860)); -#853 = ORIENTED_EDGE('',*,*,#833,.T.); -#854 = ORIENTED_EDGE('',*,*,#855,.T.); -#855 = EDGE_CURVE('',#826,#628,#856,.T.); -#856 = LINE('',#857,#858); -#857 = CARTESIAN_POINT('',(-5.275833888477,4.546144602338, - -13.55413574101)); -#858 = VECTOR('',#859,1.); -#859 = DIRECTION('',(-0.349023821871,-0.880598971639,0.320511814002)); -#860 = ORIENTED_EDGE('',*,*,#627,.T.); -#861 = PLANE('',#862); -#862 = AXIS2_PLACEMENT_3D('',#863,#864,#865); -#863 = CARTESIAN_POINT('',(2.828438300639,3.068404028665,-13.01628215822 - )); -#864 = DIRECTION('',(2.88163763217E-16,0.342020143326,0.939692620786)); -#865 = DIRECTION('',(-1.048830324052E-16,0.939692620786,-0.342020143326) - ); -#866 = ADVANCED_FACE('',(#867),#877,.F.); -#867 = FACE_BOUND('',#868,.F.); -#868 = EDGE_LOOP('',(#869,#875,#876)); -#869 = ORIENTED_EDGE('',*,*,#870,.T.); -#870 = EDGE_CURVE('',#636,#824,#871,.T.); -#871 = LINE('',#872,#873); -#872 = CARTESIAN_POINT('',(-0.871390517001,8.635298785064, - -47.66759924779)); -#873 = VECTOR('',#874,1.); -#874 = DIRECTION('',(0.299648208284,0.896513522642,0.326304236859)); -#875 = ORIENTED_EDGE('',*,*,#840,.F.); -#876 = ORIENTED_EDGE('',*,*,#643,.T.); -#877 = PLANE('',#878); -#878 = AXIS2_PLACEMENT_3D('',#879,#880,#881); -#879 = CARTESIAN_POINT('',(2.828438300639,3.068404028665,-49.69378323641 - )); -#880 = DIRECTION('',(0.,0.342020143326,-0.939692620786)); -#881 = DIRECTION('',(0.,0.939692620786,0.342020143326)); -#882 = ADVANCED_FACE('',(#883),#889,.F.); -#883 = FACE_BOUND('',#884,.F.); -#884 = EDGE_LOOP('',(#885,#886,#887,#888)); -#885 = ORIENTED_EDGE('',*,*,#635,.T.); -#886 = ORIENTED_EDGE('',*,*,#855,.F.); -#887 = ORIENTED_EDGE('',*,*,#823,.F.); -#888 = ORIENTED_EDGE('',*,*,#870,.F.); -#889 = PLANE('',#890); -#890 = AXIS2_PLACEMENT_3D('',#891,#892,#893); -#891 = CARTESIAN_POINT('',(-5.782806207227,3.068404028665, - -13.93896851312)); -#892 = DIRECTION('',(-0.93629059846,0.342020143326,-7.988827695448E-02) - ); -#893 = DIRECTION('',(0.340781908463,0.939692620786,2.907695487825E-02)); -#894 = ADVANCED_FACE('',(#895),#914,.T.); -#895 = FACE_BOUND('',#896,.T.); -#896 = EDGE_LOOP('',(#897,#906,#912,#913)); -#897 = ORIENTED_EDGE('',*,*,#898,.T.); -#898 = EDGE_CURVE('',#899,#899,#901,.T.); -#899 = VERTEX_POINT('',#900); -#900 = CARTESIAN_POINT('',(-11.6927539352,0.,-48.00951684793)); -#901 = CIRCLE('',#902,4.735522705283); -#902 = AXIS2_PLACEMENT_3D('',#903,#904,#905); -#903 = CARTESIAN_POINT('',(-16.42827664048,0.,-48.00951684793)); -#904 = DIRECTION('',(-0.,1.,0.)); -#905 = DIRECTION('',(1.,0.,0.)); -#906 = ORIENTED_EDGE('',*,*,#907,.T.); -#907 = EDGE_CURVE('',#899,#652,#908,.T.); -#908 = LINE('',#909,#910); -#909 = CARTESIAN_POINT('',(-11.6927539352,-22.,-48.00951684793)); -#910 = VECTOR('',#911,1.); -#911 = DIRECTION('',(0.,1.,0.)); -#912 = ORIENTED_EDGE('',*,*,#651,.F.); -#913 = ORIENTED_EDGE('',*,*,#907,.F.); -#914 = CYLINDRICAL_SURFACE('',#915,4.735522705283); -#915 = AXIS2_PLACEMENT_3D('',#916,#917,#918); -#916 = CARTESIAN_POINT('',(-16.42827664048,-22.,-48.00951684793)); -#917 = DIRECTION('',(0.,1.,0.)); -#918 = DIRECTION('',(1.,0.,0.)); -#919 = ADVANCED_FACE('',(#920),#939,.T.); -#920 = FACE_BOUND('',#921,.T.); -#921 = EDGE_LOOP('',(#922,#931,#937,#938)); -#922 = ORIENTED_EDGE('',*,*,#923,.T.); -#923 = EDGE_CURVE('',#924,#924,#926,.T.); -#924 = VERTEX_POINT('',#925); -#925 = CARTESIAN_POINT('',(21.163799345768,0.,-48.00951684793)); -#926 = CIRCLE('',#927,4.735522705283); -#927 = AXIS2_PLACEMENT_3D('',#928,#929,#930); -#928 = CARTESIAN_POINT('',(16.428276640485,0.,-48.00951684793)); -#929 = DIRECTION('',(-0.,1.,0.)); -#930 = DIRECTION('',(1.,0.,0.)); -#931 = ORIENTED_EDGE('',*,*,#932,.T.); -#932 = EDGE_CURVE('',#924,#663,#933,.T.); -#933 = LINE('',#934,#935); -#934 = CARTESIAN_POINT('',(21.163799345768,-22.,-48.00951684793)); -#935 = VECTOR('',#936,1.); -#936 = DIRECTION('',(0.,1.,0.)); -#937 = ORIENTED_EDGE('',*,*,#662,.F.); -#938 = ORIENTED_EDGE('',*,*,#932,.F.); -#939 = CYLINDRICAL_SURFACE('',#940,4.735522705283); -#940 = AXIS2_PLACEMENT_3D('',#941,#942,#943); -#941 = CARTESIAN_POINT('',(16.428276640485,-22.,-48.00951684793)); -#942 = DIRECTION('',(0.,1.,0.)); -#943 = DIRECTION('',(1.,0.,0.)); -#944 = ADVANCED_FACE('',(#945),#953,.F.); -#945 = FACE_BOUND('',#946,.F.); -#946 = EDGE_LOOP('',(#947,#948,#949,#950,#951,#952)); -#947 = ORIENTED_EDGE('',*,*,#718,.T.); -#948 = ORIENTED_EDGE('',*,*,#687,.T.); -#949 = ORIENTED_EDGE('',*,*,#734,.T.); -#950 = ORIENTED_EDGE('',*,*,#782,.T.); -#951 = ORIENTED_EDGE('',*,*,#806,.T.); -#952 = ORIENTED_EDGE('',*,*,#758,.T.); -#953 = PLANE('',#954); -#954 = AXIS2_PLACEMENT_3D('',#955,#956,#957); -#955 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#956 = DIRECTION('',(0.,1.,0.)); -#957 = DIRECTION('',(0.,0.,1.)); -#958 = ADVANCED_FACE('',(#959),#962,.F.); -#959 = FACE_BOUND('',#960,.F.); -#960 = EDGE_LOOP('',(#961)); -#961 = ORIENTED_EDGE('',*,*,#898,.T.); -#962 = PLANE('',#963); -#963 = AXIS2_PLACEMENT_3D('',#964,#965,#966); -#964 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#965 = DIRECTION('',(0.,1.,0.)); -#966 = DIRECTION('',(0.,0.,1.)); -#967 = ADVANCED_FACE('',(#968),#971,.F.); -#968 = FACE_BOUND('',#969,.F.); -#969 = EDGE_LOOP('',(#970)); -#970 = ORIENTED_EDGE('',*,*,#923,.T.); -#971 = PLANE('',#972); -#972 = AXIS2_PLACEMENT_3D('',#973,#974,#975); -#973 = CARTESIAN_POINT('',(-45.46495478095,0.,-70.97315781796)); -#974 = DIRECTION('',(0.,1.,0.)); -#975 = DIRECTION('',(0.,0.,1.)); -#976 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) -GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#980)) GLOBAL_UNIT_ASSIGNED_CONTEXT -((#977,#978,#979)) REPRESENTATION_CONTEXT('Context #1', - '3D Context with UNIT and UNCERTAINTY') ); -#977 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) ); -#978 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) ); -#979 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() ); -#980 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(5.E-06),#977, - 'distance_accuracy_value','confusion accuracy'); -#981 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7)); -ENDSEC; -END-ISO-10303-21; diff --git a/docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.stl b/docs/CAD/design/mate-connectors/BearConnector_Female_Trimmed.stl deleted file mode 100644 index 940dab90f7e3431fd6281ba9c866a27a04922029..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74884 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 N!BXDSQ=cjl{|^|eER6sF diff --git a/docs/CAD/design/mate-connectors/DESIGN_MATE_CONNECTORS.md b/docs/CAD/design/mate-connectors/DESIGN_MATE_CONNECTORS.md deleted file mode 100644 index fe1a7a5fee..0000000000 --- a/docs/CAD/design/mate-connectors/DESIGN_MATE_CONNECTORS.md +++ /dev/null @@ -1,922 +0,0 @@ -# Mate connectors: aligning with the mainstream CAD systems - -Research date: 2026-08-05. Written against `orca_cad` / `Snapmaker` at the M8 state -(`CadDocument.{hpp,cpp}`, `apply_mate`, `datum_frame`, the `Mate` card in `DesignPanel.cpp`). - -**Brief:** align with the mate-connector concept as the main CAD programs actually implement it, -and be simple, unequivocal, unconfusing. Alignment is the organising principle of this document: -every recommendation is labelled either **[INDUSTRY]** — do what they all do — or **[DEVIATION]** — -we would be departing, here is why and what it costs. - ---- - -## 0. The answer in ten lines - -1. Seven systems surveyed. **Five of the seven use the same model**; two are the old world. -2. The model: a joint is defined between **two local coordinate frames**, one rigidly attached to - each part, plus **one type** naming which DOF stay free. -3. The frame is called a mate connector (Onshape), a **joint origin** (Fusion, Inventor), a joint - connector (FreeCAD 1.0). Same object, three names. -4. **Every one of them expresses every DOF about the frame's Z axis.** One axis, one convention. -5. **Five types appear in every frame-based system with identical names and identical DOF**: - Fastened/Rigid, Revolute, Slider, Cylindrical, Planar. Ball is in four of five. -6. That is not fashion — those are the classical **lower kinematic pairs**. The vocabulary converged - because the mechanics converged. -7. Our kernel is already on the right side of the line: frame-based, five types, Z-relative, - superimpose-then-relax. **The architecture needs no revisiting.** -8. Where we are out of step: connectors that are not attached to a body; an origin that can only be - a face centroid; no live preview of the two Z arrows; a mate card of abstract dropdowns. -9. Where we would knowingly deviate: refusing a second mate per body (no vendor does this — it is - forced on us by having no solver) and possibly inverting the default mate direction. -10. Biggest single win for the stated goal, and it costs no kernel work: **draw both frames and - ghost the result before Confirm.** The convention stops needing to be remembered. - ---- - -## 1. The two families - -**Constraint-based ("old CAD").** The user states pairwise *geometric relations* between raw -topology — this face coincident with that face, this axis concentric with that axis, this plane -parallel at 12 mm. Each relation removes some DOF; a numerical solver satisfies all of them at once. -Fully positioning one part typically takes **three or more mates**, and the set can be -over-constrained, under-constrained, or satisfiable in several configurations. - -**Frame-based ("mate connectors").** The user places a *local coordinate system* on each part and -states **one** relation between the two frames. The relation is not "these surfaces touch" but -"these frames coincide, except for the following DOF, which stay free." - -Onshape's help page opens by drawing exactly this line: - -> *"Mates in Onshape are different than mates in old CAD systems. Many assemblies require only one -> Onshape Mate between any two instances, as the movement (degrees of freedom) between those two -> instances is embedded in the Mate."* - -The frame-based model won for three reasons, all of which matter here: - -- **One mate per pair.** No mental arithmetic about which three constraints add up to a hinge. -- **The DOF are declared, not deduced.** A revolute mate *is* one rotation. You do not discover the - remaining freedom by dragging. -- **It needs no simultaneous solver for the common case.** Frame-to-frame alignment is a matrix - composition — precisely what `apply_mate` already does. - -> **Caveat — several vendors ship both, and "align with X" is therefore ambiguous.** **Inventor** -> kept its legacy constraints *and* added frame-based Joints in 2012; many Inventor users still build -> assemblies entirely with the old constraint stack. **Creo** has placement constraints *and* -> Mechanism connections. **FreeCAD** had constraint-based Assembly2/3 add-ons before the frame-based -> Assembly workbench shipped in 1.0. So copying "what Inventor does" means copying **one of two -> coexisting workflows**. **Onshape and Fusion 360 are the only pure frame-based examples**, and they -> are the ones to weight most heavily when the evidence conflicts. - ---- - -## 2. Field survey — seven systems - -| | Onshape | Fusion 360 | Inventor | FreeCAD 1.0 | Creo | Siemens NX | SOLIDWORKS | -|---|---|---|---|---|---|---|---| -| **Family** | Frame | Frame | Frame (+ legacy constraints) | Frame (+ legacy add-ons) | Both | Constraint | Constraint | -| **Frame object** | Mate connector | Joint origin | Joint origin | Joint connector (`Placement1/2`) | CSYS on `Weld`/`6DOF` | — | — (nearest: **mate reference**) | -| **Where it lives** | Part Studio **and** Assembly; in the feature list | Component, inside the joint | Component / inside the joint | Inside the Joint object | Part | — | Part (up to 3 named entities) | -| **Origin placement** | Inferred family on hover; `Shift` locks | Discrete **snap points**; `Ctrl` cycles | Snap points + explicit origins | Inferred, previewed on hover | Picked CSYS | Picked entities | Picked entities | -| **Orientation control** | Primary axis (Z) + secondary axis; flip + 90° reorient | Flip, angle, offsets | Flip, angle, offsets | `Placement1/2` + `Offset1/2` | CSYS + offset | — | — | -| **Type inference** | No — explicit | No — explicit | **Yes — "Automatic"** from picked geometry | No | No | No | Partial (mate reference type) | -| **Solver** | Yes, simultaneous — *"order won't affect a Mate"* | Yes | Yes | Yes (Ondsel) | Yes | Yes | Yes | -| **Reuse across instances** | **Yes** — a Part Studio connector exists on every instance | Weak | Partial | Per-joint | Interfaces | Product Interface | Mate references auto-mate on insert | - -Three observations that shape everything below. - -- **Every frame-based system reduced the type list by an order of magnitude** relative to SOLIDWORKS - (7–13 vs ~25) and lost nothing. That is not simplification-by-omission; it is what happens when the - DOF live in the mate instead of being assembled from constraints. -- **Every one of them defines its types relative to a single axis.** Slider translates along Z, - Revolute rotates about Z, Cylindrical does both, Planar translates in X/Y and rotates about Z. - One axis carries the whole vocabulary. -- **Onshape alone treats the connector as a first-class, reusable, named object** — and that is also - where its worst usability complaints come from (§4). - ---- - -## 3. The type vocabulary — cross-system table - -DOF = degrees of freedom left **free**, stated about/along the connector Z. - -| DOF | Onshape | Fusion 360 | Inventor | FreeCAD 1.0 | Creo | **Ours today** | -|---|---|---|---|---|---|---| -| 0 | Fastened | Rigid | Rigid | Fixed | Rigid / Weld | **Fastened** ✅ | -| 1 — rot Z | Revolute | Revolute | Rotational | Revolute | Pin | **Revolute** ✅ | -| 1 — trans Z | Slider | Slider | Slider | Slider | Slider | **Slider** ✅ | -| 2 — rot + trans Z | Cylindrical | Cylindrical | Cylindrical | Cylindrical | Cylinder | **Cylindrical** ✅ | -| 3 — trans XY + rot Z | Planar | Planar | Planar | *(Parallel+Distance)* | Planar | **Planar** ✅ | -| 3 — rot XYZ | Ball | Ball | Ball | Ball | Ball | — | -| 2 — different axes | Pin slot | Pin-Slot | — | — | Slot / Bearing | — | -| 1 — coupled | Screw | — | — | Screw | — | — | -| 4 | Parallel | — | — | Parallel | — | — | -| other | Tangent, Width, Group | As-built | Automatic | Perpendicular, Angle, Distance, Gears, Belt, RackPinion | General, 6DOF | — | - -**Five types appear in every frame-based system, with the same name and the same DOF.** Those five -are the industry's common denominator, and they are exactly `mate_kind` 0–4 as already implemented. -Ball is in four of five. Everything past that is a long tail no two vendors agree on. - -### Why the convergence is a fact, not a fashion - -A rigid-body placement is an element of SE(3). A mate leaves some set of relative motions free. For -the mate to behave the same throughout its range — for a hinge to be a hinge at every angle — that -free set must be **closed under composition**: two allowed motions must compose to an allowed motion. -A closed set of motions is a **subgroup** of SE(3). - -The subgroups corresponding to physical surface-on-surface contact are the classical **six lower -pairs** (Reuleaux): - -| Pair | Free motion relative to Z | DOF | -|---|---|---| -| Revolute (R) | rotation about Z | 1 | -| Prismatic / slider (P) | translation along Z | 1 | -| Helical / screw (H) | coupled rotation + translation | 1 | -| Cylindrical (C) | rotation about **and** translation along Z | 2 | -| Planar (E/G) | translation in X,Y + rotation about Z | 3 | -| Spherical / ball (S) | rotation about X, Y, Z | 3 | - -Plus the two trivial ends: identity (0 DOF — **fastened**) and all of SE(3) (6 DOF — floating, i.e. -no mate). Hervé's Lie-subgroup analysis of the displacement group is the standard reference for -treating these as the algebraic building blocks of mechanism synthesis. - -**Consequence.** Anything outside this table is either (a) a *composition* needing a solver, or -(b) not a joint at all but a *measurement*: - -- Onshape's **Parallel** (4 DOF), **Tangent**, **Width**, **Pin slot**, and FreeCAD's **Distance / - Angle / Perpendicular** are constraints, not pairs — their free set is not a subgroup, so they only - make sense alongside a simultaneous solver. -- **Gear, Belt, Rack-and-pinion** are *relations between two mates*, a different object entirely. -- **Screw (H)** is a legitimate lower pair but needs a pitch parameter and is rare in printed parts. - -So the vendors' shared five, the lower pairs, and our `mate_kind` 0–4 are the same list arrived at -three ways. **[INDUSTRY] Stop looking for missing types and spend the budget on the connector.** - ---- - -## 4. What they all agree on — adopt verbatim - -Deviating from any of these makes an experienced user's intuition *wrong*, which is the operational -definition of "confusing". - -**A1 [INDUSTRY] — The connector is a full right-handed frame.** -Origin + Z (primary) + X (secondary). Onshape and Fusion expose exactly these two axis controls and -nothing else. A point cannot express spin; an axis cannot express clocking. -*Status: we comply* — `DatumCoordSys` carries origin/x/y and derives Z. - -**A2 [INDUSTRY] — Z is the joint axis; every DOF is about or along Z.** -Revolute rotates about Z. Slider translates along Z. Planar's free plane is normal to Z. Offsets run -along Z. This single rule is what makes the system learnable: **one axis to look at, and its meaning -never changes.** -*Status: we comply* — `mate_offset` along A's z, `mate_angle` about A's z. - -**A3 [INDUSTRY] — Mating superimposes the two frames; the type then relaxes specific DOF.** -FreeCAD states it most plainly: *"the second connector is superimposed on the first connector by -default and may change its position according to the joint type."* Fastened is not a special case — -it is the base case with nothing relaxed. -*Status: we comply* — `T = M_A · Rz · Tz · F · M_B⁻¹`, looser kinds relaxing from there. - -**A4 [INDUSTRY] — The connector belongs to a part and moves with it.** -Onshape: a connector defined in a Part Studio *"is available for reuse on every instance of that part -in every assembly in which it is instanced."* It is part geometry, not assembly geometry. -*Status: **violated**.* `CoordSysType::PointWorld` is a bare world XYZ with `X = world X` and no -`coordsys_body`. Such a connector does not follow its part. See §6 G1. - -**A5 [INDUSTRY] — Selection order is meaningful and must be visible.** -One connector is the reference; the other is driven onto it. Onshape spells out that offsets are -measured *"from the second Mate connector selected to the first"*, and that reversing the order -flips the sign. -*Status: complied with in the data model* (`mate_cs_a` fixed, `mate_cs_b` moves) *but not in the UI* — -two dropdowns labelled A and B do not tell the user which part is about to jump. - -**A6 [INDUSTRY] — Flip and re-clock live in the mate dialog, always.** -Onshape: *"Click the arrow icon to flip the direction of the primary axis. Click the Reorient -secondary axis icon to rotate the secondary axis in 90-degree increments."* -*Status: partial.* We have `mate_flip` (Z reversal). We have `mate_angle` as a free number — strictly -more powerful than 90° steps, and much worse to *use*: the common case is "it came in a quarter turn -out", and typing 90 is a worse gesture than pressing a button. - -**A7 [INDUSTRY] — DOF are shown, not inferred by the user.** -Onshape animates each mate's remaining DOF on demand; Fusion and Inventor name the DOF in the type -list. Our dropdown text already does this in words ("free spin + axial slide"). Keep it. - -**A8 [INDUSTRY] — Free DOF are preserved from the current placement, not zeroed.** -Onshape: a Planar mate aligns the frames *"but they are not restricted to this location with respect -to their degrees of freedom."* -*Status: we comply* — and it must be *said*, because a Planar mate that leaves the part where it was -looks like a mate that did nothing. - ---- - -## 5. Where they diverge — who to copy, and why - -### D1 — Where the connector's origin comes from - -| | Behaviour | -|---|---| -| **Fusion 360** | Discrete **snap points** only: vertex, edge midpoint, face centre, arc centre. `Ctrl` cycles the candidates under the cursor. A circle icon denotes a vertex, a triangle a midpoint. "Between two faces" is a separate explicit option. | -| **Onshape** | Infers a *family* on hover — centroid, every vertex, every edge midpoint, every arc centre, the centroids of interior regions (holes, slots), and the virtual sharps of conical faces. `Shift` locks the current candidate. | -| **Inventor** | Snap points, plus explicit joint origins for awkward cases. | -| **FreeCAD 1.0** | Hovering previews where the connector will land before you commit. | -| **Ours** | Always the **face centroid**. No alternative exists. | - -Onshape's richness has a cost its own documentation admits: *"The suggested locations are based on -the underlying geometry of the part and changing the geometry will change the location of the Mate. -This can be undesirable in certain situations."* On the forum this shows up as connectors that move -or break on edit — the classic topological-naming failure. Fusion's discrete set is poorer and far -more predictable. - -> **[INDUSTRY] Copy Fusion's candidate *set*.** A small, closed, enumerable set — **face centroid, -> vertex, edge midpoint, arc/circle centre** — each drawn before commit, with the card naming which is -> in use ("Origin: edge midpoint"). This is our largest expressiveness gap: a face centroid alone -> cannot place a hinge pin on a corner boss. It is also the one place where copying the *simpler* -> vendor is clearly right. -> -> **Open sub-choice — how the candidate is chosen.** Three options, in increasing order of magic: -> (1) **explicit dropdown** in the card after picking the face — no hover behaviour at all; -> (2) **Fusion's `Ctrl` cycling** through candidates under the cursor; (3) **Onshape's hover -> inference**. Kimi's independent review argued for (1) on the grounds that hover is exactly where -> both vendors' instability complaints originate, and that a dropdown gets ~90% of the expressiveness -> with none of the hover-guess debugging. That is a fair reading and (1) is the cheapest to build and -> the easiest to make unequivocal. **Recommendation: build (1) first; if hover is added later, let it -> *pre-fill the dropdown* rather than silently create an implicit connector** — which also keeps R2 -> (one kind of connector) intact. - -### D2 — Explicit type, or inferred from the geometry? - -Inventor is the only surveyed system that infers: *"Rotational is selected if the two selected -origins are circular. Cylindrical if the two selected origins are points on a cylinder. Ball if -points on a sphere. Rigid for all other origin selections."* Onshape and Fusion require an explicit -choice. - -> **[INDUSTRY, Inventor] Do both, in Inventor's order.** Infer a *default* type from what was picked, -> then show it in an editable control. Inference is what makes the tool feel like it understands the -> geometry; the visible, editable result is what keeps it unequivocal. Pure inference with no visible -> type is the confusing option; a pure dropdown with no default is the tedious one. This also fits -> the Design tab's geometry-first charter exactly: point at a bore, get Revolute offered. - -### D3 — How the Z-direction ambiguity is resolved - -This is the specific failure the brief is aimed at. A former IT trainer stated it precisely on the -Onshape forum: - -> *"There is always the risk that users will build their own conceptual models of how software works -> which may not match the designer's concept. The result is usually a poor user experience and many -> mistakes… for a good (say) Fixed mate to occur do the Z axes of the two mates have to be pointing -> in the same direction… Alternatively, should they be facing each other?"* - -He is asking the right question and **no vendor's documentation answers it.** Onshape's own advice — -*"if the behavior is not what you expected, try flipping the primary and/or secondary axis"* — is -trial and error. This is a gap in the industry, not a convention to copy. - -> **[INDUSTRY, method] Resolve it with live preview, not documentation.** FreeCAD previews the -> connector on hover; Onshape and Fusion both draw the frames. Draw **both** Z arrows the moment the -> second connector is picked, and ghost the resulting placement *before* Confirm. The convention then -> never has to be remembered because it is on screen. -> -> **[DEVIATION, optional] Name the two cases in the user's words** rather than in axis-speak: -> "the two faces come together" vs "the axes run the same way". No surveyed vendor does this — they -> all ship a flip arrow. It is a small, low-risk improvement on the state of the art, and it is -> separable from the default-direction question in §8 D1. - -### D4 — Named, reusable connectors on the part - -Onshape: connectors created in the Part Studio are reused on every instance in every assembly. -SOLIDWORKS' **mate reference** reaches the same end by another route: up to three named entities -(primary/secondary/tertiary) baked into the part so it auto-mates on drag-and-drop — and a *named* -mate reference seeks out a matching name on insertion. That naming trick is how a library of -fasteners assembles itself. - -> **[INDUSTRY] Out of scope now, but do not preclude it.** Give connectors a stable, user-visible -> name at creation. One string today; expensive to add once documents exist in the wild. - ---- - -## 6. Confusion catalogue - -Documented ways real implementations confuse people. Each is a requirement in disguise. - -**C1 — Which way does Z point?** See D3. If a user has to ask once, they will mis-predict a hundred -times. - -**C2 — The roll is unspecified.** Aligning Z leaves one rotation about Z undetermined. Something must -pin it, and if that something is world-derived, the frame does not rotate with its part. **This -codebase shipped exactly this bug** (`en4`): a face-only connector took Z from the face -normal but X from `coordsys_x_hint`, a world constant, so Fastened and Slider claimed to lock an -orientation the frame could not see. Fixed 2026-07-26 by deriving X from the face's own first usable -edge — but note the fix's own caveat: *"replaying an older document whose face-only connector fed a -mate can now place that body differently."* Roll conventions are load-bearing, and changing one is a -document-format change. - -**C3 — The origin drifts.** See D1. - -**C4 — Implicit and explicit connectors are not the same thing.** On the Onshape forum, implicit -connectors are reported to change their query structure when a feature is edited and re-accepted, and -are unusable in places explicit ones work. Two things called by one name that behave differently is a -permanent tax. - -**C5 — Which part moves?** A frame alignment is asymmetric. If the UI does not say which frame is -driven, the user finds out by watching the wrong part jump. - -**C6 — Which direction is a positive offset?** Onshape measures *"from the second Mate connector -selected to the first"* — the sign depends on pick order, and swapping the picks flips it. Documented -behaviour, documented surprise. - -**C7 — One intent, several mates.** The SOLIDWORKS failure: expressing "this shaft is in this hole, -resting on this shoulder" as three constraints, then discovering the solver picked the mirror -configuration. Frame-based systems fix this by construction; the requirement is not to reintroduce it. - -**C8 — Degenerate frames.** A circular face has no usable in-plane edge direction; a cylinder seam -projects to nothing; a picked edge parallel to Z gives a zero cross product. `datum_frame` handles all -three with fallbacks — the requirement is that a fallback be *visible*, because a silent fallback is -C2 wearing a different hat. - -**C9 — Order dependence without a solver.** Onshape can say *"Onshape solves Mates simultaneously so -order won't affect a Mate."* A system that composes transforms in tree order cannot say that. Two -mates driving one body means the second wins and the first is a lie on screen. - -**C10 — Mirrors and patterns.** A mirrored instance has a left-handed frame. Blindly mirroring a -connector gives a frame whose Z still points "out" but whose handedness flipped, so every rotation -runs backwards. Cheap to handle now, miserable to retrofit. - ---- - -## 7. Requirements - -Labelled **[INDUSTRY]** (what the frame-based systems do) or **[DEVIATION]** (we would depart). - -### Definition - -**R1 [INDUSTRY] — A mate connector is a frame attached to exactly one body.** No body, no connector. -*Test:* creating a connector without a body is rejected at creation, not at mate time. -→ **`CoordSysType::PointWorld` violates this.** It is a datum wearing a connector's name. - -**R2 [INDUSTRY] — One kind of connector, not two.** No "implicit" connector that behaves differently -from an explicit one. If hover inference is offered, hovering *creates* an ordinary connector. -*Why:* C4. *Test:* everything that accepts a connector accepts any connector. - -**R3 [INDUSTRY] — A mate names exactly one subgroup of free motion.** Fastened (0), Revolute (1), -Slider (1), Cylindrical (2), Planar (3), optionally Ball (3). *Why:* §3. *Test:* every type's free -set is closed; no type is "A and also B". - -### Orientation - -**R4 [INDUSTRY] — Everything is about Z. Say so once, in the UI.** *Test:* no mate parameter refers -to any other axis. - -**R5 [DEVIATION] — Z is the outward material direction, and mates default to FACING.** -A mate would drive B's Z onto **−A's Z** by default, so picking two faces that should touch makes -them touch with no options changed. *Why:* it is the whole of C1. -**Cost and caveat:** this inverts today's default (`mate_flip=false` currently *aligns*), and I could -not establish from any vendor's documentation what their default actually is — the forum question in -D3 went unanswered precisely because it is undocumented. So this is marked a deviation on the honest -grounds that **I cannot prove the industry agrees with it.** If D3's live preview lands first, the -default matters much less, because the user sees the outcome before committing. See §9 D1. - -**R6 [DEVIATION] — Name the two directions; do not ship a boolean called "flip".** -`Direction: Facing | Aligned`. Every surveyed vendor ships a flip arrow instead. A boolean requires -remembering what unticked means; two named values do not. Low risk, small improvement on the state of -the art. - -**R7 [INDUSTRY] — Roll is picked, or a stored quarter turn. Never world-derived.** -X from a referenced edge or in-plane direction; failing that, a deterministic body-attached seed, with -**Rotate 90°** offered as a stored integer 0–3 on top (this is Onshape's "reorient secondary axis", -A6). *Why:* C2 and the world-constant bug this project already shipped. *Test:* rotate the parent -body by any angle; the connector's X rotates with it — *this test already exists* ("a face-only frame -rotates with its body"). - -**R8 [INDUSTRY] — A degenerate roll is reported, not absorbed.** *Test:* a connector on a full -cylindrical face reports "roll undefined — pick a direction" rather than silently taking a fallback. - -### Placement - -**R9 [INDUSTRY, Fusion] — Origin comes from a small closed set of named candidates.** -**Face centroid, arc/circle centre, edge midpoint, vertex.** Four. Each stored as -`(kind, topological reference)` and resolved at rebuild. *Why:* D1. *Test:* the stored kind is visible -in the card; a rebuild either resolves it or raises an error. - -**R10 [INDUSTRY] — An unresolvable reference is an error, never a silent relocation.** -*Test:* delete the referenced face; the mate reports "connector A: face not found" and the body stays -where it was. - -### Semantics without a solver - -**R11 [DEVIATION] — A body is driven by at most one mate. The second is refused.** -**No surveyed system does this** — they all have solvers and all accept many mates per body. It is -forced on us by tree-order composition: a second mate on the same body silently overrides the first -and the screen shows a configuration satisfying only one stated intent (C9). *Test:* creating a -second mate whose moving body already has one is rejected, naming the existing mate. -This is the single largest departure in this document. See §9 D4. - -> **A tempting misreading, checked and rejected.** It is easy to find the claim that Onshape mandates -> *"exactly one Mate between any two instances"*, which would make R11 an industry agreement rather -> than a deviation. **The Onshape page does not say that.** It says *"**Many assemblies require only** -> one Onshape Mate between any two instances"* and then lists, as an explicit remedy, *"**Use more -> than one Mate if necessary.**"* One mate per pair is Onshape's *typical case*, not its rule. R11 -> remains a deviation and must be justified on our own architecture, not on theirs. - -**R11a [DEVIATION] — The refusal list.** With no solver, these are unsupportable and must be refused -rather than half-done: a second mate on an already-driven body; cycles (A→B, B→A); closed loops -(A→B, A→C, B→C); relations *between* mates (gear, belt, rack-and-pinion, screw coupling); **joint -limits**, which nothing can enforce without a solver; and **dragging a body to exercise a free DOF**, -which requires keeping the body on the allowed manifold. Motion analysis and animation follow from the -same lack. *Requirement:* none of these may appear in the UI as something that half-works. - -**R12 [DEVIATION] — The mate graph is an acyclic forest rooted at fixed bodies.** A body reached by -no mate is fixed; cycles are refused. Same root cause as R11. *Test:* A→B, B→A rejected at creation. - -**R13 [INDUSTRY] — Free DOF are preserved from the current placement, and the user is told.** -Behaviour already matches Onshape (A8); the telling does not. *Test:* the card for any type with -DOF > 0 says which motions remain and that dragging exercises them. - -**R14 [INDUSTRY] — State what mirroring does to a connector.** -*Checked in the code:* `datum_frame` ends with a Gram-Schmidt forcing a right-handed frame -(`ds.x = Y.cross(Z)`), so a connector resolved on a mirrored body comes out **right-handed, not -mirror-imaged**. Z follows the mirrored face's outward normal, X follows a mirrored edge, handedness -is re-imposed. Defensible — a mate on the mirrored part still turns the way its type says — but it -means a mirrored sub-assembly is *not* the mirror image of the original in its rotation sense. -*Requirement:* document it and pin it with a test. *Why:* C10. - -### Feedback — the part that actually removes confusion - -**R15 [INDUSTRY] — Before Confirm, the card answers four questions in words.** Which body moves; -which way Z points on each connector; how many DOF remain; what the offset is measured from. - -**R16 [INDUSTRY] — Draw both frames live, with Z distinguishable, and ghost the result.** -Two triads with Z rendered differently from X/Y (length, arrowhead, colour). *Why:* D3 — the fastest -way to make a convention unequivocal is to show it. *Test:* both Z directions are readable in a -screenshot. - -**R17 [INDUSTRY] — Show the DOF budget per body.** "Body 2: 1 of 6 DOF free (rotation about Z)." -The most educational readout in any assembly system, and free to compute here — the type *is* the DOF -count. *Test:* the number changes when the type changes. - -**R18 [DEVIATION] — Refuse loudly and name the alternative.** Where something is out of scope (a -second mate, a tangency, a gear ratio), say what is unsupported and what to do instead. Vendors do not -need this because their solvers accept the input. *Test:* no refusal message ends without a suggested -next action. - ---- - -## 8. Minimal specification, and gap analysis - -### The connector - -``` -MateConnector - body int required, ≥ 0 (R1) - origin_kind enum FaceCentroid | ArcCentre | EdgeMidpoint | Vertex (R9) - origin_ref topo ref face / edge / vertex index on that body - z_source implied by origin_kind: face normal, arc axis, edge tangent - roll_ref topo ref optional in-plane edge; else deterministic seed (R7) - roll_quarters int 0..3 stored quarter turns on top of the seed (R7, A6) - flip_z bool reverse Z at the connector - name string stable, user-visible (D4) -``` - -`flip_z` is a property of the **connector**, chosen once when it is made — not a per-mate -afterthought. Keeping connector-flip and mate-direction separate is what stops the "which flip do I -tick?" question. - -### The mate - -``` -Mate - kind enum Fastened | Revolute | Slider | Cylindrical | Planar [| Ball] (R3) - fixed connector A — its body does not move - moving connector B — its body is driven (A5, C5) - direction enum Facing | Aligned (R5, R6) - offset mm along A's Z, measured A → B — state this in the label (C6) - angle deg about A's Z (R4) -``` - -Within one field of what exists. - -### Gaps against today - -Source of record: `CadDocument.hpp:26,247-252,298-310`; `CadDocument.cpp:1669` (`datum_frame`), -`:2961` (`apply_mate`), `:1302` (`add_mate`); `DesignPanel.cpp:2671-2709` (the Mate card). - -| # | Gap | Severity | Ref | -|---|---|---|---| -| G1 | `PointWorld` connectors are not attached to a body and their X is a world constant | **High — data model** | A4/R1 | -| G2 | Origin is always the face centroid; no vertex / edge-midpoint / arc-centre snap | **High — expressiveness** | D1/R9 | -| G3 | No live preview of the two Z arrows or of the resulting placement | **High — this is the brief** | D3/R16 | -| G4 | Mate card is two abstract dropdowns; nothing says which body moves | High — charter + A5 | R15 | -| G5 | No joint-type inference from the picked geometry | Medium — feel | D2 | -| G6 | `add_mate` validates nothing — no one-mate-per-body, no cycle check | Medium | R11/R12 | -| G7 | No `Ball` type | Low | §3 | -| G8 | Re-clocking needs a typed angle; no 90° step control | Low, cheap | A6/R7 | -| G9 | Degenerate roll falls back silently | Low | C8/R8 | -| G10 | Connectors have no stable user-facing name | Low now, expensive later | D4 | - -**Already aligned — do not "fix" these:** the five types and their DOF; the frame definition (A1); -Z as the joint axis (A2); superimpose-then-relax (A3); the fixed/moving asymmetry in the data model -(A5); DOF wording in the type list (A7); free-DOF preservation (A8); right-handed frames under mirror -(R14); and `en4`'s fix, which put roll derivation on the body where it belongs (C2). - -**The pattern worth naming: the kernel is in good shape and the concept is under-explained.** Half the -requirements here are wording and drawing, not geometry. The two real engineering items are R9 (origin -candidates) and R11/R12 (the mate-graph rules). - -### Expensive-to-retrofit decisions — get these right in the data model now - -Changing any of these after documents exist in the wild costs a migration, not an edit. - -1. **Topological reference stability.** Storing raw face/edge indices is brittle — editing a body - renumbers faces. Either persistent topology IDs, or store the named origin *kind* plus a - deterministic search that re-finds the same geometric intent on rebuild. The latter is cheaper and - probably sufficient here; it is also what makes R10's "error, never silent relocation" enforceable. -2. **Connector ownership** (R1). Remove `PointWorld` or bind it to a body. Do this first. -3. **Mate direction semantics** (R5/D1). Inverting the default rewrites the meaning of every saved - mate. -4. **Roll representation** (R7). "First usable edge" is better than world-X but still fragile. Store - an explicit roll reference plus quarter turns. -5. **Coordinate convention** — Z = joint axis, X = roll reference. Changing this after release - invalidates every mate. -6. **Units** — offset in mm, angle in degrees. Never change. -7. **Mirror handedness** (R14) — document the decision, do not let it stay an accident. -8. **Flat body index vs. a component tree.** Mates currently reference bodies in a flat vector. If - **sub-assemblies** are ever in scope, mates must reference nodes in a tree instead. Retrofitting - this is painful and it is the one item on this list not already implied elsewhere in the document — - **decide now whether nested assemblies are in scope.** -9. **Serialization field semantics.** Adding fields is easy; redefining `mate_flip` or - `coordsys_x_hint` is not. -10. **The one-mate-per-body rule** (R11). Enforce at creation. Relaxing it later by adding a solver is - straightforward; allowing many mates now and discovering later that they silently conflict is not. - ---- - -## 8b. The visual shape of the connector — polarity and verse - -Researched separately (2026-08-05) by downloading and **looking at** the vendors' own figures, not -by reading their prose. Files kept alongside this document in `doc/design/mate-connectors/`. - -### What the systems actually draw - -**Onshape** — verified from `planarfacemateconnectors.png`, `cylindricalmateconnectors.png`, -`linearedgemateconnectors.png`, `mateconnector-planarpoints.png`, `matepointiconLG.png`: - -> **A small circle with one quadrant filled, plus three short coloured axis arms (X red, Y green, -> Z blue).** - -Three parts, each doing one job: - -| Element | What it says | -|---|---| -| The **circle** | "I am a frame, and this is my XY plane." | -| The **filled quadrant** | **The roll.** The shaded sector is the +X/+Y quadrant. | -| The **coloured arms** | The three axis directions, Z distinguished by colour. | - -The quadrant is the cleverest part of the whole design and it is easy to miss. The figure -`matepointreorientsecondaryaxis.png` shows three connectors side by side with the quadrant in three -different rotations — **it is the live readout of "reorient secondary axis in 90° increments" (A6).** -One glyph element makes the otherwise-invisible clocking visible, and makes the 90° button's effect -legible before you commit. The toolbar icon `matepointiconLG.png` is that same circle-with-a-quadrant, -so the symbol is consistent from toolbar to viewport. - -Candidate snap points, before you choose one, are drawn as **plain small white dots** on the model -(clear in `mateconnector-planarpoints.png`: dots at every corner and edge midpoint). Candidate and -committed are deliberately different weights — dots propose, the circle-and-triad commits. - -**FreeCAD 1.0** — verbatim from the wiki: *"Connectors are local coordinate systems and are marked by -a symbol with three axes (X, Y, Z) and a circle representing the XY-plane."* Same core as Onshape — -circle plus triad — **without** the quadrant. - -**Fusion 360** — the joint origin glyph, plus a documented icon language for *candidates*: *"A circle -denotes a vertex, and a triangle denotes a midpoint."* Shape encodes what kind of point it is. - -**Convergent core:** *circle for the XY plane + coloured triad*. Onshape alone adds the roll quadrant. - -### What none of them draw — and it is exactly what was asked for - -**Nothing in any vendor's glyph says which connector is the reference and which one is about to -move.** Both ends of a mate are drawn identically. That is confusion C5 ("which part moves?") left -unsolved in the visual language, and it is why the honest recommendation earlier was a live ghost — -the ghost compensates for a glyph that does not carry the information. - -So the two things asked for split cleanly, and only one of them is solved upstream: - -- **Verse** (*verso* — which way it points): **solved**. Z has a colour and a direction. -- **Polarity** (which end receives, which end inserts; who is anchored, who travels): **unsolved - everywhere.** This is open ground, and getting it right is a genuine improvement rather than a - deviation to justify. - -### Our starting point - -**We draw nothing.** `resolve_datum_coordsys()` (`CadDocument.cpp:1749`) has exactly one consumer in -the entire tree — `McpControl.cpp:1310`, the agent socket. A mate connector is today visible only to -a program. The glyph is unbuilt, so there is no migration cost to designing it properly now. - -### Proposed glyph: the magnet - -Adopt Onshape's proven core, then add the missing polarity with a metaphor that carries its own -instructions. - -``` - ▲ solid cone on +Z ONLY ← verse - | - ────●──── ← the disc = XY plane, ● = exact origin - ▨ quadrant filled ← roll / clocking, steps 90° -``` - -**Rule 1 — verse: draw +Z and never −Z.** A single stem with a cone head, on the positive side only. -No stem below the disc. A double-headed axis is the one thing that guarantees the question gets asked; -an arrow that exists on one side only cannot be misread. Length is asymmetric on purpose. - -**Rule 2 — roll: keep Onshape's quadrant.** Filled sector = the +X/+Y quadrant. It rotates in 90° -steps with the reorient control (A6/R7). This is aligned *and* it is the only in-glyph answer to -"where is X?", which matters because Fastened and Slider lock the clocking. - -**Rule 3 — polarity: solid cone travels, open collar receives.** -- The **driven** connector (B, on the body that will move) draws a **solid filled cone** — the plug. -- The **fixed** connector (A) draws an **open ring / hollow cone outline** — the socket. - -Same silhouette, so they read as a matched pair; opposite fill, so which one is about to jump is -answerable at a glance and without a legend. Plug-into-socket is the one mechanical metaphor every -user of this tool already has in their hands. - -**Rule 4 — the pair reads as a magnet.** Draw a dashed line joining the two origins the moment both -are picked. Two poles, one field line. And because a magnet's north seeks a south, **"facing" becomes -the self-evident default** — which quietly settles open decision D1 (§9) on visual grounds rather than -on a convention nobody can look up. If the glyph looks like a magnet, nobody has to be told that two -faces which touch have opposed normals. - -**Rule 5 — three states, three weights.** - -| State | Drawing | -|---|---| -| **Candidate** (hover) | small dot only — Onshape's white dots; shape may encode kind, Fusion-style | -| **Picked** | full glyph: disc + quadrant + cone | -| **Degenerate roll** (C8/R8) | the quadrant is drawn **hollow/hatched** — "roll undefined, pick a direction" | - -That last row is worth the trouble: it turns R8 from a message nobody reads into a mark you cannot -miss, and it costs one branch in the renderer. - -**Rule 6 — do not reuse the existing triad.** The bed-centre world triad -(`DesignCanvas.cpp:65`, `set_axes_at_bed_center`) and the move gizmo are already three-coloured arrows. -The connector must not be a fourth set of RGB arrows or the viewport becomes unreadable. The disc and -the quadrant are what distinguish it; keep the arms short, and consider drawing only Z on the -committed glyph, with X/Y implied by the quadrant. - -### Built and judged in the viewport, not in a mock - -The browser mock that first accompanied this section was the wrong instrument and its proportions -were meaningless: **every gizmo in this codebase is sized in SCREEN PIXELS** via `upp = 1/zoom` -(`render_shell_gizmo` uses `15.0 * upp`, `render_hole_gizmo` `9.0 * upp` for its cube). A connector -is a symbol, not a part — it must not shrink with the model. Nothing about that is visible in SVG. - -The glyph was therefore implemented and driven on the rig. Screenshots: `g-0*.png`, left in the workspace `artifacts/shots/` and not moved into the repo. -Five findings, none of which a mock could have produced: - -**F1 — Three axis arms lose to one.** Rendered side by side (`ORCA_CAD_GLYPH=A` vs default), the -Onshape-style RGB trio crowds a 22 px disc: the arrowheads are as large as the disc, they bury the -gold quadrant, and at an oblique angle the three heads pile into a coloured smudge. Worse, **it is -indistinguishable from the move gizmo and the bed triad**, which are already RGB arrow trios in this -viewport. One-sided Z wins on evidence, not taste. (`g-01-zoom.png` vs `g-02-zoom.png`.) - -**F2 — Polarity works, and colour does more of the work than fill.** A filled blue head against an -open grey outline head is readable instantly at 22 px (`g-03-zoom.png`). But the fill difference is -the *second* cue; the colour split carries it. Keep both — fill survives greyscale and colour-blind -palettes, colour survives small size. - -**F3 — Depth off floats, depth on tears.** With `GL_DEPTH_TEST` off, connectors on faces pointing -*away* from the camera still drew their discs over the solid, so the part looked covered in frames -that were really on its back. Turning depth on fixed that and immediately caused **z-fighting**: the -disc is exactly coplanar with its face, and came out as a broken dotted arc. The fix is depth **on** -plus a sub-pixel lift along Z (`0.7 * upp`), scaled by `upp` so it never becomes a visible gap on -zoom-in. Both failure modes are in the images (`g-03` torn, `g-04` clean). - -**F4 — The quadrant is the first thing to die at a grazing angle.** On a face seen nearly edge-on the -disc foreshortens to a sliver and the fan collapses into a blob (`g-01-zoom.png`, lower-right glyph). -The roll is exactly the information that is hardest to read when you most need it. Not yet solved — -see the open item below. - -**F5 — Roll-undefined in red is too loud.** It works, but it makes the *least* important connector -the most eye-catching thing on screen. Amber, or the same grey with a hatched quadrant, is enough. - -Also surfaced while testing, and unrelated to the glyph: `add_mate` accepted a mate between two -connectors **on the same body**, which is meaningless, and duly transformed the body relative to -itself. Concrete instance of gap G6. - -**Still untested:** a true grazing view (the view-cube click missed), a connector on a curved face, -and behaviour when a connector overlaps the move gizmo. F4 is the open design question — the disc may -need to billboard its *quadrant* while keeping the disc in-plane, which is a compromise no surveyed -vendor makes and which should be tried before being adopted. - -### What this costs - -A renderer for `resolve_datum_coordsys()` — which does not exist and has to be written whatever glyph -is chosen — plus one dashed line and three fill states. No kernel work. It is the same piece of work -as G3 (live preview), and doing them together is what makes the mate card honest. - ---- - -## 8c. The "faceted ridge dome" proposal — built, rendered, judged - -A colleague proposed replacing the flat disc with an **asymmetric low-poly solid**: a faceted -prismatic wedge with a dominant longitudinal ridge that **slopes** from a tall steep back to a long -shallow front, plus a male protrusion / female pocket pair with a 0.2 mm clearance. - -It was built rather than discussed. `faceted_ridge_key.scad` (this folder) (6 vertices, 7 faces), -verified as a closed manifold, exported through OpenSCAD, and flat-shaded from five directions with -`render_key.py` / `render_stl.py`. Sheets: `rk-sheet.png`, `cmp-sheet.png`. - -### The verdict: the shape is right, the male/female polarity cue is not - -**It solves F4, decisively.** The grazing view — where the flat disc dies, its quadrant collapsing to -a blob — is the view where this shape is *most* legible: the tall back and long shallow front are -unmistakable in silhouette. At a grazing angle the silhouette IS the information, and this solid's -silhouette is maximally informative there. That is a real, evidence-backed win over what is currently -in the code. - -**Down the mating axis (+Z) it also reads well**, which matters because that is the natural viewing -direction when you are looking at a face you intend to mate. - -**One degenerate view, and it is not the one I predicted.** I expected the ±X views (along the ridge) -to be silhouette-ambiguous, resolved only by shading. Wrong: front and back are clearly *different* — -the front shows several facets, the back is a **single flat featureless triangle**. So they are not -confusable, but the view from directly behind the tall end tells you nothing about roll or slope. -A second blind spot remains untested: from below the base, where the protrusion is hidden behind its -own face. - -**The female half fails, and much harder than expected.** Rendered with flat shading and no outlines — -the honest test, since a viewport draws no black edges — a recessed pocket is *invisible*: iso and -grazing show a plain block with a hairline; straight down the axis shows a **completely blank -rectangle**. The interior faces are lit almost identically to the top face and are occluded by the rim -from most angles. As a polarity cue, male/female therefore works in exactly one direction and returns -nothing in the other. - -> **Conclusion: do not overload shape with all three jobs.** Let the solid carry **verse and roll**, -> where it is excellent, and carry **polarity on a second channel** — colour plus the filled/open head -> that already tested well at 22 px (F2). Drawing the fixed connector as an outline/wireframe of the -> same solid is the variant worth trying; drawing it as a pocket is not. - -### Two premises in the brief are wrong - -**"Avoid curved surfaces to optimise rendering computations / rapid mesh processing."** Not a reason -for a viewport glyph. There are 2–20 connectors on screen, the renderer pushes `GLModel` triangles -directly, and it performs no CSG or mesh processing at all. **The real argument for flat facets is -legibility**: hard normals give distinct value steps between adjacent facets, and the renders confirm -that is exactly what makes the shape readable from an arbitrary angle. Keep the constraint, fix the -justification. (For a *printed* part the original justification is sound for a different reason: flat -facets slice without the stair-stepping a tessellated curve produces.) - -**"0.2 mm clearance for smooth mechanical mating."** Meaningless for a glyph. A symbol mates with -nothing, and every gizmo here is sized in screen pixels via `upp`, so a millimetre tolerance has no -referent. This is the strongest signal that **the brief was written for a physical printed part**, -not for a viewport symbol — as are "scannable" and "mechanical mating". See the open question below. - -### Two defects the build caught that discussion would not have - -1. **The flank quads are not planar.** Written as `[0,3,5,4]` and `[1,4,5,2]` the base edge and the - ridge edge are skew, so the four corners do not share a plane — my own first draft asserted the - opposite in a comment. Left as quads, the tessellator picks the fold direction, the "flat facet" - promise is broken by an unspecified crease, and two exporters can disagree about the shape. Fixed - by triangulating explicitly (7 faces, Euler 6 − 11 + 7 = 2). -2. **The pocket punched through its own plate.** A 4.5 mm key against a 3 mm demo plate gives a - through-hole, not a pocket. Minimum stock = height + clearance + pocket depth + a wall. - -Also worth recording: the first female render was misleading because the debug renderer outlined -*every* triangle, so a flat top face triangulated by CGAL looked like a faceted dome. The instrument -lied before the geometry did. Conclusions were only drawn after outlines were removed. - -### Second opinion, and the one disagreement worth resolving - -Kimi reviewed the proposal independently and **rejected it for the viewport**. It agreed on the two -wrong premises, agreed the female pocket is unreadable, and added the useful framing that a -screen-constant symbol and a model-constant part feature are two different design spaces that cannot -be served by one geometry. It also noted correctly that there is **no single scalar** that removes -ambiguity from every view: you need one asymmetry in the base plane (for top-down roll) and one out -of plane (the ridge slope, for front/back). Our base is scalene, so it has both. - -Its central objection was numeric and testable: *"at 22 px with 6–8 facets each facet is 3–7 px wide, -that is at the aliasing limit … minimum useful size is roughly 32–48 px, which is not compatible with -a 22 px screen-constant symbol."* My own renders were ~300 px, so the claim was unaddressed by my -evidence and would have killed the concept if true. - -**Rendered at 22, 32 and 48 px (`size-test.png`), it is false for this shape.** At 22 px all three -views still read: the grazing view shows the tall back and shallow front unmistakably, and the -down-axis view keeps a strong dark/light split. The reason Kimi's arithmetic does not apply is that -this solid presents only **four or five large facets with high value contrast**, not eight small ones — -the silhouette does most of the work, and silhouettes survive downsampling far better than facet -detail does. - -*Honest limit on that result:* the test renderer has no anti-aliasing, no perspective, one directional -light, and no background. Readable at 22 px against white is not the same as readable at 22 px on top -of a shaded gold part next to the move gizmo. That case still needs the rig. - -**Where I do not follow Kimi:** its recommendation is to **billboard** the existing flat glyph so it -never turns edge-on. That kills F4 by construction, but a billboarded frame cannot show the frame's -orientation *in place* — which is the entire reason the disc is a disc and not a dot — and it is what -no surveyed CAD system does; Onshape, Fusion and FreeCAD all draw the frame in the geometry. Worth -prototyping as an option, not worth adopting on argument. - -### Open question for Tommaso - -**Is this a viewport glyph or a printable alignment feature?** The vertex logic is identical either -way; only the units and the clearance change, and the `.scad` file states both readings. But the -answer decides whether `clr`/`depth` are real millimetres or meaningless, and whether the geometry -scales with the model or stays screen-constant. The brief's own language points at "physical", the -conversation it arrived in points at "glyph". - ---- - -## 9. Decisions for you - -**D1 — Invert the default direction to Facing?** [DEVIATION, R5] -It changes the meaning of every stored document containing a mate. Options: (a) invert and migrate, -writing `direction=Aligned` where `mate_flip` was false; (b) invert only for new mates and store -`direction` explicitly from now on. (b) is safer and costs one field. Note this project has taken one -such semantic hit knowingly before — the `en4` fix — and the golden fixture survived, so the -migration path is a known quantity. **If G3 (live preview) lands first, this matters much less.** - -**D2 — How far to take origin candidates?** [R9] -Four kinds is the Fusion-aligned recommendation. Two (face centroid + arc centre) would cover "sit on -a face" and "go down a hole" — most printed-part assembly — at a third of the work. Where do you want -to stop? - -**D3 — Ball mate: in or out?** -In four of five frame-based systems, so including it is the aligned choice. Out is defensible for -printable mechanical parts. Cheap either way — align origins, leave orientation free. Kimi's review -argued **out**: a true ball joint is hard to print and hard to use without a roll reference, and a -Fastened connector at the ball centre approximates it. - -**D3a — Should Planar be dropped?** [dissent worth recording] -Kimi's independent review recommended **removing Planar** and shipping four types, on the grounds that -"slide on a flat surface" is rarely how printed mechanisms work — you usually want a rail or a hinge — -and that Planar is the type most likely to confuse a user who expected "put this flat on that" and got -a part free to slide. It further ranked the honest minimum as **three**: Fastened, Revolute, Slider, -with Cylindrical useful and decomposable. -**I do not agree, and the reason is alignment.** Planar appears in every frame-based system surveyed, -it is a genuine lower pair, it is already implemented and tested, and removing it is a document-format -change made in exchange for nothing. The confusion Kimi names is real but it is a *feedback* problem — -it is exactly what R17 (show the DOF budget) and R13 (say that free DOF are preserved) exist to fix. -Recorded here because it is a legitimate reading of the same evidence and the call is yours. - -**D4 — Is refusing a second mate per body acceptable?** [DEVIATION, R11 — the big one] -It is the honest consequence of having no solver, and it is what makes the tool predictable. But **no -mainstream system behaves this way**, so it is the point where an experienced user's intuition will -break. It means a part cannot be constrained by two independent relationships — "in this hole *and* -resting on this shoulder" must be expressed by placing one connector correctly rather than by two -mates. If that trade is unacceptable, the answer is a solver, and the scope of this document changes -entirely. - -There is a strong argument that the trade is not merely acceptable but *correct for this product*: -the Design tab lives inside a slicer, and most of its users are positioning parts for printing rather -than building working mechanisms. For layout-and-export, tree-order composition is genuinely enough, -and adding a solver to look like Onshape would buy complexity nobody asked for. The rule to publish is -then simple and defensible: **one mate per moving body, acyclic, no relations between mates** — with -R18's loud refusals carrying the honesty. - ---- - -## Sources - -**Onshape** — [Mate Connector](https://cad.onshape.com/help/Content/PartStudio/mate_connector.htm) · -[Mates](https://cad.onshape.com/help/Content/Assembly/mates.htm) · -[Fastened](https://cad.onshape.com/help/Content/Assembly/fastened_mate.htm) · -[Revolute](https://cad.onshape.com/help/Content/Assembly/revolute_mate.htm) · -[Slider](https://cad.onshape.com/help/Content/Assembly/slider_mate.htm) · -[Cylindrical](https://cad.onshape.com/help/Content/Assembly/cylindrical_mate.htm) · -[Planar](https://cad.onshape.com/help/Content/Assembly/planar_mate.htm) · -[Ball](https://cad.onshape.com/help/Content/Assembly/ball_mate.htm) · -[Parallel](https://cad.onshape.com/help/Content/Assembly/parallel_mate.htm) · -[Tangent](https://cad.onshape.com/help/Content/Assembly/tangent_mate.htm) · -[Pin Slot](https://cad.onshape.com/help/Content/Assembly/pin_slot_mate.htm) · -[5 things you can do with mate connectors in Part Studios](https://www.onshape.com/en/resource-center/tech-tips/tech-tip-5-things-you-can-do-with-mate-connectors-in-onshape-part-studios) - -**Onshape forum** — [The concept behind Mates Z Axes](https://forum.onshape.com/discussion/22828/the-concept-behind-mates-z-axes) (C1/D3) · -[Implicit mate connectors act differently than explicit ones](https://forum.onshape.com/discussion/15736/implicit-mate-connectors-act-differently-than-explicit-ones) (C4) · -[Efficiently set mate connectors](https://forum.onshape.com/discussion/13133/efficiently-set-mate-connectors) - -**Fusion 360** — [Joint types](https://help.autodesk.com/cloudhelp/ENU/Fusion-Assemble/files/GUID-8818AE31-958A-4A59-989B-9875A174C67A.htm) · -[Joint origins](https://help.autodesk.com/view/fusion360/ENU/?guid=ASM-JOINT-ORIGIN) · -[Joints vs. Mates in Fusion](https://www.autodesk.com/products/fusion-360/blog/joints-mates-moving-fusion/) · -[Joint tips — snap points and Ctrl cycling](https://mgfx.co.za/blog/engineering-manufacturing-design/fusion-360-joint-tips/) - -**Inventor** — [Create Joints Reference](https://help.autodesk.com/cloudhelp/2026/ENU/Inventor-Help/files/GUID-6AA68E8F-7C97-4806-8483-3941DE915E70.htm) · -[Use Joint to define and manage relationships](https://knowledge.autodesk.com/support/inventor-products/learn-explore/caas/CloudHelp/cloudhelp/2014/ENU/Inventor/files/GUID-21DC3336-5C51-42C1-90FB-4299CD66E0C6-htm.html) (type inference, D2) - -**FreeCAD 1.0** — [Assembly Workbench](https://wiki.freecad.org/Assembly_Workbench) · -[Fixed Joint properties](https://wiki.freecad.org/Assembly_CreateJointFixed) - -**Creo** — [About Predefined Constraint Sets](https://support.ptc.com/help/creo/creo_pma/r12/usascii/assembly/asm/About_Predefined_Constraint_Sets.html) - -**Siemens NX** — [Assembly constraints](https://learnnx.com/lesson/siemens-nx-assemblies-assembly-constraints/) - -**SOLIDWORKS** — [Mate References](https://help.solidworks.com/2025/English/SolidWorks/sldworks/c_Mate_References_Overview_SWassy.htm) · -[Creating and using mate references](https://blogs.solidworks.com/tech/2019/07/creating-and-using-mate-references.html) - -**Theory** — [Hervé, The Lie group of rigid body displacements, a fundamental tool for mechanism design](https://www.sciencedirect.com/science/article/abs/pii/S0094114X98000512) · -[Joint kinematics — the six lower pairs and their DOF](https://erc-bpgc.github.io/handbook/mechanical/Joint%20Kinematics/) · -[ISO 10303-105 — Kinematics (STEP integrated resource)](https://www.iso.org/standard/78589.html) - -**Internal** — `en4` (closed 2026-07-26, fixes C2 here) · `CadDocument.cpp:1669` -`datum_frame` · `CadDocument.cpp:2961` `apply_mate` · `CadDocument.cpp:1302` `add_mate` - -**Second opinion** — an independent review by Kimi Code (2026-08-05) contributed the -vendors-ship-both caveat (§1), the explicit-dropdown option for origin choice (D1), the expanded -refusal list (R11a), the retrofit list (§8), and the dissents recorded at D3/D3a. One of its claims — -that Onshape mandates *"exactly one Mate between any two instances"* — **was checked against the -source and is wrong**; the correction is recorded at R11 because it is a misreading that would -otherwise turn our largest deviation into a false agreement. diff --git a/docs/CAD/design/mate-connectors/bear-01-view.png b/docs/CAD/design/mate-connectors/bear-01-view.png deleted file mode 100644 index eb57aaeefeb05111f1dd73b2c4ac263889f67a79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100101 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%0AuQeqlYF3LNtKh+lZ@ zW(VMB85=Ts;Cs%dF0xZze5iC#$mo3IFb|$a^)vO0j1qsQtyUT`DOI#>@11x1cWk%% zkYxqkUP`So_o3HRq5muB{r|xA?TLaA?ovJ~!`)t&2c+z7_DJ5d3KwGOng)CaO+f+_ zxe!5_4uT&0w8lw^?NzxoKU_(RBB1-Ni4CC@ADIN1oX_aW@72-kqoXN{Fs=$a@=l|T z$&7}t%VDQ8KV|t7V28t2+iWRfmqqkB`&~FAGkTmhEK|Y6O27oeW{ig;_#bX6a0nmyH9zn82^_}Yf85K^S=y{kTQx$N+n(|9b_=bjM}p0GTRD?#1pw|U&_psS^==8gL&KqTl-Z5v+^p@bQIEKg?w&KIcph2p1UAYOH^Tw~0#aAC z5cIO;fUM3~KD1hvT`B4^85H7jQ|48J@VG;;4FXCP;jB@0wd#BHGgf?1SNl87#?oB) zK2WI45$TfysBR4VFBDI1I(_*m%~(sER+!CSL6X! zU4BJ@%+My&R~f6-qOLb0uQz&B5_<~`bH{JhLXZ!D!#29yMR$NzK%lTU)mBkafsiB| zNq<(*5To@+3Vj#B%%3t~)F|8fSXufUvWohbPvyP;_7IH;-@$)Q9m_Q1RxmLNiHcl1 zG*~AUG@I06B-0wUDR(N1ziM*syrsSJMh}h4O2FMddX{(u786vy{pb$yYUW^v%&VhB zn}-F&x?+z@YNEVOg>V`&9EcA-hNv62L43D#)|!ir z;QQ}tvRC!D>`GZ7Z_|u>U0I4)r{OaRnmi>9;y-8h8FJGJs!>D08qB`|M#KrIG6GKRRb}(fFm|)jR(O=u+Bs diff --git a/docs/CAD/design/mate-connectors/bear-02-view.png b/docs/CAD/design/mate-connectors/bear-02-view.png deleted file mode 100644 index 46b6d834fa6befe33a26c983e5c0b0b6d18c7b4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96449 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

    tH?fQ1=5)ch9`_(g`ZyOc@0!c#v}+} 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

    - -
    -
    Orca Design · assembly
    -

    Mate connector glyph — polarity and verse

    -

    - Onshape's core (disc + roll quadrant + Z arrow) is adopted unchanged because it is proven and - aligned. The addition is polarity — which connector is anchored and - which one travels — which no surveyed CAD system encodes in its glyph. -

    -
    - -

    The three jobs of the glyph

    -
    -
    -
    - - - - -
    -
    Disc — the XY plane
    -

    Says “I am a frame, and this is the plane I sit in.” The dot is the exact origin.

    -
    - -
    -
    - - - - - -
    -
    Quadrant — the roll
    -

    - The filled sector is the +X/+Y quadrant. It steps 90° with the reorient control, so the - clocking that Fastened and Slider lock is visible before you commit. -

    -
    - -
    -
    - - - - - - - -
    -
    Arrow — the verse
    -

    - Drawn on +Z only. Nothing below the disc. A double-headed axis is what - makes people ask which way it points; a one-sided arrow cannot be misread. -

    -
    -
    - -

    Polarity — the part nobody else draws

    -
    -
    -
    - - - - - - - - -
    -
    Fixed — the socket
    -

    - Hollow head, muted colour. This body does not move. It receives. -

    -
    - -
    -
    - - - - - - - -
    -
    Driven — the plug
    -

    - Solid head, active colour. This body is the one that jumps. It inserts. -

    -
    - -
    -
    - - - - - - - - - - - - -
    -
    Roll undefined
    -

    - Hatched quadrant, dashed disc: a circular face or a seam gave no usable direction. Says - “pick a direction” without a dialog. -

    -
    -
    - -

    The pair reads as a magnet

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed · receives - driven · inserts - -
    -

    - Two arrows nose to nose. Because a magnet's north seeks a south, “facing” is the - self-evident default — which settles open decision D1 on visual grounds instead of a - convention nobody can look up. Nothing has to be remembered: the picture is the rule. - The dashed line is what makes the two glyphs read as one object. -

    -
    - -

    States

    -
    - - - - - - - - - - - - - - - - - - - - - -
    StateDrawingWhy
    Candidate (hover)small dot onlyOnshape draws plain white dots at every corner and midpoint. Dots propose; the full glyph commits.
    Pickeddisc + quadrant + coneThe committed frame, with roll and verse both readable.
    Roll undefinedhatched quadrant, dashed discTurns requirement R8 from a message nobody reads into a mark you cannot miss.
    -
    - -

    Constraints on the drawing

    -
    -

    - Do not make it a fourth RGB triad. The bed-centre world triad - (DesignCanvas.cpp:65) and the move gizmo are already three coloured arrows. The disc - and the quadrant are what tell a connector apart from those — keep the arms short, and consider - drawing only Z on the committed glyph, with X and Y implied by the quadrant. -

    -
    - roll quadrant - Z / driven - fixed - roll undefined -
    -
    - -
    - - diff --git a/docs/CAD/design/mate-connectors/coplanar_test.py b/docs/CAD/design/mate-connectors/coplanar_test.py deleted file mode 100644 index ea02ed3c80..0000000000 --- a/docs/CAD/design/mate-connectors/coplanar_test.py +++ /dev/null @@ -1,68 +0,0 @@ -# Does the connector pair let two hosts sit COPLANAR, or does it hold them apart? -# -# The male's flat back is the plane Y=0 and all its relief rises to +Y. So Y=0 is the natural -# mating datum: everything the male adds lives on one side of it. The test below builds two dummy -# host plates that meet on that plane -- one with the male FUSED on, one with the cavity CUT in -- -# and measures whether they touch, interfere, or stand apart. -# -# It also emits the artifact that makes this work in practice: a CUTTER solid (the male grown by -# the clearance) that you subtract from any host. A standalone female block cannot keep two hosts -# coplanar, because its own floor material stands between them; a cavity can. -# -# Run: /snap/bin/freecad.cmd coplanar_test.py - -import os -import FreeCAD as App -import Part -from FreeCAD import Vector - -HERE = os.path.dirname(os.path.abspath(__file__)) -MALE = os.path.join(HERE, "bear.step") -CLEAR = 0.20 - -male = Part.Shape(); male.read(MALE); male = male.Solids[0] -bb = male.BoundBox -print(f"male relief: Y {bb.YMin:.3f} .. {bb.YMax:.3f} -> datum plane Y=0, all relief on +Y") - -# the flat back face, and proof it is the whole silhouette sitting on Y=0 -back = max((f for f in male.Faces - if abs(f.CenterOfMass.y) < 1e-6 and abs(abs(f.normalAt(0, 0).y) - 1) < 1e-6), - key=lambda f: f.Area) -print(f"back face : {back.Area:.1f} mm2 on Y=0 -- this is the contact surface") - -# ---- the cutter: the male grown by the clearance, poking 0.2 mm proud so the boolean is clean -cutter = male.makeOffsetShape(CLEAR, 1e-6, False, False, 0, 2, False).Solids[0] -cb = cutter.BoundBox -print(f"cutter : Y {cb.YMin:.3f} .. {cb.YMax:.3f}, {cutter.Volume/1000:.2f} cm3") - -# ---- two dummy hosts meeting on Y = 0 -W, H = 120.0, 100.0 -hostA = Part.makeBox(W, 10.0, H, Vector(-W/2, -10.0, -15.0)) # occupies Y -10..0 -hostB = Part.makeBox(W, 30.0, H, Vector(-W/2, 0.0, -15.0)) # occupies Y 0..30 - -partA = hostA.fuse(male) # male stands proud of A's face -partB = hostB.cut(cutter) # cavity sunk into B from its face - -print(f"\npart A (host + male) : {partA.Volume/1000:.2f} cm3") -print(f"part B (host - cutter) : {partB.Volume/1000:.2f} cm3") - -# ---- the question ------------------------------------------------------------------ -inter = partA.common(partB) -iv = inter.Volume if inter.Solids else 0.0 -gap = partA.distToShape(partB)[0] -print(f"\nRESULT interference A vs B : {iv:.6f} mm3 (0 = they do not collide)") -print(f"RESULT closest approach : {gap:.4f} mm (0 = the host faces are touching)") - -# are the two host faces actually on the same plane? -fa = [f for f in partA.Faces if abs(f.CenterOfMass.y) < 1e-9 and abs(abs(f.normalAt(0,0).y)-1) < 1e-6] -fb = [f for f in partB.Faces if abs(f.CenterOfMass.y) < 1e-9 and abs(abs(f.normalAt(0,0).y)-1) < 1e-6] -print(f"RESULT A has {len(fa)} face(s) lying exactly on Y=0, total {sum(f.Area for f in fa):.1f} mm2") -print(f"RESULT B has {len(fb)} face(s) lying exactly on Y=0, total {sum(f.Area for f in fb):.1f} mm2") -print("RESULT -> the hosts meet on Y=0: COPLANAR" if fa and fb and iv < 1e-3 - else "RESULT -> NOT coplanar") - -doc = App.newDocument("Cutter") -o = doc.addObject("Part::Feature", "BearConnector_Cutter"); o.Shape = cutter -doc.recompute() -Part.export([o], os.path.join(HERE, "BearConnector_Cutter.step")) -print(f"\nwrote BearConnector_Cutter.step -- subtract this from any host to get the socket") diff --git a/docs/CAD/design/mate-connectors/cylindricalmateconnectors.png b/docs/CAD/design/mate-connectors/cylindricalmateconnectors.png deleted file mode 100644 index e4c1cebcf9af5b7a037323cb17c05a94e81dd593..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17385 zcmZ@zRErW#4rDEAuhfx_vr-eVGynq<23SCqhKTDX>lHwg(t_rDiuzh|jE3CWWX zZS}_{{??nV)Nk0P(^qF@F7%EnGA-PiZ2UrP+CyeNr&^pE^72^vpxGT@?DBhNEiLss zkL+(KXwpVSJyN4dDR@NlTl~>7ccM19?`E3!Q?1ibr@V(H>n5|QZ`hdjX5BU&xC)w% z%r3TUu3E=bOhd`d(fJH9ba3;ZYjD!7_=h0L4lBcwZc4P+`WrNfpGFP?$9z@kpzUdAv*+xt zbFJ@P;sz5Z+q>RzrdaFR8{^XA8RA_NSZDj9-dBD`Hx7%EIveXzXz%~1b8SBNVy=At zeNVVZ8*?JHqH1T``$lXocSKYa#V})5q(Y~$w1ex7Vm;o3HJN_9l?vL=51g1IR`q{Z zOTB9J=)SdFB+*~>6Pi7XqiHxgTkOqbS{y*CT82&kMmKWGTF_I$ywIC`mgKDj2{i!$3iuB$G`3Td8C*%be~PEoQYx= zWO_Z-IJHL1OIET3(_c5_W_Z4HGtdrM5V}Qn?vp6I8m2fS@({x&$9TM?X3bbwRK)M- z!YZVDYcyU<4_PvHyILFh03E4Q&HuG4DN2I|9qq~mr9;!YGx*&UQ|S(ypw}>&M5dKRs|O{x*HiuVYi5R$#E$OsQ;{|% zsb4$gbR6^Fj9btQ=gjXSw$GNnkMdx18kmOCHTdt}wG`viMh+(3!{lmLwktek(oVPE zQi{MoC*Cuy(IHc8vacSDacjJxW6QzkIHOiETg_bNQijcY_>L_v@p(|<$)5R5$qt(g zk)Q$7v(8Qr`6gvDeBBgZdZ)6(x^vNi<}A|_))Npis zcvz_4ryOz4CfZ21gX3{JBh$=+P9L%^+Y3Qw-yvJ_V5})$T!%hs%_d=eH67hxsv5{f zkk@lJa@QwFwq6u@U_c>*A2D7ex13_Ra9Q^5^B}u~b%&sS6f3WvWvN|gxK+Q%&4&u`lkkQR+ z0ek(;w@Cb6_pDZA!bz>O^8awceH5-G)pta;R@Q``xe5ph+q&828>;IU@J%SGE~1&s zB&TB4jP5p&WSzm%T(gwHK2O=DOvSZD5tX71q$sK*Tjgn(Wi+dqKab9Ka*3*ps&!<- zW8h++{9rySSgN=Qi4&%Nx$tuo?mqh^42`DecGg2`FqPF`4r>UMp-8eG!z2WJeor;? zA0kHBvL0KdPEU1!=chhFs3GBjpHjSbH)_PBX)=s97EYu#+^)aom`C36fH@#$nazEV zy1x=LW0U9J&wnUWLyqk8pZ?6#M$#+hKZcQJaimh9IJZ(e(qZkjruZ;C1&Y$+`vI5v ze$j=pb^%Pi6ET^(CQz=o4)FjJ7wLfquNjE;0Va93(nW<_k+%WXF|ghVrc zf=_jkEu<4A;u{C0E0ut{!8JF^E|s_Xjg6~?9r$>*>@}Po#L(@0NmAtONM70{&dbRo zgV}jq=;^Z(1HQ_9DpRF*R~bc=q6U-Tn5mDD?IgGuU%VFb+ejxn9I_cpf}fePafz=C zhrPpJlmCr7z`3qQkA(K@dmHz}mE{8G!g%+O2z-iHDjC@wZIxCn?4(e8e^U0FvV|#Z zBO=iuB&rj*iJRX?7Sd;#Xi&2H&lRg{Sxxj{;8wTo)O1V#>a-| z$4jzasR$LIO{ps>j}f)FEN4|&6<;^-DT;k62OrcP@YVKAOEjT7iJZ_}{WD1FdH1IPZMryy9 z;3d~qF`*?=^dsL^g)|oj%f1q$OoPpPDgAxaJ3;fk4_HAS5ogq`A98XcE8`$F_AZ66 zjWMr{&#yGOSLzQK;?^6YUuWnbK?^7ZO1n{v6s0ROkJ~k`h$MWZnF1hUgLoiYpNp#{I1|9-^9;p zM{S^M_+U`vkR!Z)n3V>6sGcW*q$I-I>AN^VLFtCZ*tY~0iw6s2Er^JYwycDVk_#_4 z8^7gMLA!%5Y}_9Bsk}&6$TjUUTs%m6%!N)d|0VPIrT23fb@fn0R;Lqr1E_km-L6Q!*ruf9&8H&elZdXIQJEvNKjy#k@L>Y%7K!zPttoUkyhXg?p6s-qo zI;}e$!MEdK*1FeU6KhRMh|Xp!jL^c+SXP7nJVPb54PqB=dJU{L-|o_j`9B%*^>*~W z_LW_I=z}2MLf$oS+{cNTTMT%ncumY-t?Kw;lx|tdx>1ZJk zX!REKko}?&So+^?JF?a`(OIFS7J85RXH%;=HJKkSErchDgm@We@5BSezSr~n`Mimf z(tu~!rbX6=?9{GLUx=}&g^bS1v1DhBLSoV%goL9O@u9%mJK94;Zepq_q)V?AhDnlV zPazW^H8U+cG@|l*=E|G|QjG5@r0D5!{Z_y;_V=M+xWs(A&A@S#-hfs@KjYfS?jEfe zL5x$fW9WKQxE>Q+FjWf2J<}L3C1ae_P=EQCzWpc2aNbN&V88q0EHQmhwe?BL?N{^C1+|qhJ5N7J9#@H-RH1HT6APxh=PiW`v$-hnXoPLT@`S zl;JRdnR&@ewn{p8%cXSF1EG#4krIvP{P`y99VE^_V3~`6pXENQ>;CXDdzi!JI3n?- z`d%*TEAAHI*@gj*$wD20kT^#%pcwo&dr`>PSW-H3TtMb0t zZ+meaQ!XETD(k!T-Kp1BJP@|~lG**ckqLkUXI&Fc2~J?a2M_2~i$Wq6o!VpRjA{TJ zG$PDJt5F^1Tv&O%Ju?XjtVQc_op|_KsDD3U3O2)=Artu~ntG}v>o$4?B60RQ6xj9q zfolj@G^DGT)XDot21Kf9n#6Q4WX?P%SQmqGvbXM77z@P+BpxXrd`RT^D zd>o@Yz6OkXS<1B8^>p2-&~EK={$|JEn9QJa&5;5Q-4XIGr5G5>UR=pLgG5= zx?ymYQ1M#t*T;2kUHmKY^zFlL#!O`h@V1MaO4fGuD;0bp?&tJWf)=@ZL6~d@SQ3%G zXFHVON$pz}ZgaB0s@2z$$3HSAncV5ujXE7Ek>~l7pjzhy z{6WQ$VNlK?JRJ~Es?hZ1dO7pWCyieRzUwX^%JnW>Dpl<^K8x@*y5dE^N4VN2w%0Wk zhLhpd7Nlj}5>mB?24aM}*h4ti4~tjzCJVSpU5`ESV;!d{V4Y(nB9pLaz?mCV0M3~N~o>z{*?JCZgz0#IUEs#hW<{Nlbjz5AseOqZ~ld_ zC02zdU?%Pb+F2#i@5^)8NRsZVM&SvV9;YWxpSStr4pX$f%MfwO0v53FpwuC~3MYiF zyjNQvIW_Y}K9>v}0}%?85;K$NMZp5lzMHqGm0oQ)ZhDI#D$m@X(P95;OwUh^GOC&r zv&S0?A|CKNC~s|gvv5=e?cyoUO*mLVo-yUkK{fosJTsZvFIYMI*s>gvpjAzP{IsBc z9ooS+;w#HhVUV??&!+hG8d3UcOq*{UqO!x3`G3$kWw&cGZ`L_=-w0cxRbkP zgeEWMJ&u6{sWd%FXY&ZqU(U!IWs_>j%;AS<@}=ao(Pl8uJsR)b+de&^C)K9|izkP~ zt;XQ_lM=_uph4x_UHySW5X<{A?I>M#1C54uz#osl?k7(G!eS_SLr{P%eenx@CmQY zF`nf7H+cxl5JyF9OZs$_r`n5#kOzGRH@~+WYE>RM%{+80!A+z(L%HH%rm$I02tZN6 zk}V8i(cCD=BRf+V$ejzl`JWHCvVYNqx`7PEAQ#L;>%o97aCL*%jSVX`DpBuc4@k9B zHBdl|Ve1c>2rWr**M9FWHXI#2lC~Optv*YY?TF+Jjp_1!va|EZiAYHA;c%O(v~dd< zrVfC~5Z-PicUmn%Kav256(+GTMamKHKxS#|3G$#t$sl0ic!n2F#>z>#53wKnHt>-W zz!sRzrz1eEKd+-qk&SbxQZNUDZ<4k;AK&U7DHx@sKxjnQYY(^b<}eNdh2%(A!4n>I zeSt8_sp=K<+jv8gLj7gowAm@nHAY?PwuWC%ldzFF%r-hI!Tp@#Q`+O6%Q2rJa(QR__2KO~B zhK78sQMCLIGS~5;PaGMN^`uCCtYA3`XgLJ{jVXT1oxAR~!}{%87|d+5wgcQP(TOsJ zMPx<7O2M7OosO&7qso<{9|pwZza-~~)_JY=2(Yp#w4b$kkm|F9s>aNZUrrsXgx)>0 z*Yv{VD!Aq6<0@hcS8F%uz{^O?2_P>AQ(tVx2K%WakfcZoNY@3T)Ay}evv{=v!55k^ z_nf_YBp$Z)K_1HlA}ZX%5^p`9eKH-DTbrGyMAyE8R9|R~T#U#7`B3i+y=r;#+I3ou zI2PBna;!&Gr<94#tx#H+cD~3wQojpg_Ep1Jz!VkvI;ZBRBnz*h7#hgJNKw}s!n6Th zgeNOdt{*Krc!2`eAMRaU3D>FC34?_AvJi}DqHH&;A(W6Beu29`LyBy3ISz?UKe#XW z%$yxdU>$yIvxYl#%BC}o{uiX}x9GArk~NzFABymzu(C>U>wxDUlYdFq({ z8N=?YZVF2SNsFP7vGq76diV6jE*Fl8{3-F1XpY-ECY@8J&8%p)8=FuG1H`{d0~e9t z-8Dr7v9}1tTd`yfO4K+7>B!ChaKvl`q7uiXhvdX^dl+rHvQmJ#GaNl9Xm9Z&24oC}1h}qvC8sqF#okpC!xVm?}#u%TNyZ&#s#C1cy=- zdgmcl-+y!67`9)d`|WFlo<%XhHhNc=;6r2X;gFCMJp>@J5F_r^YrawQj);+uUnsQH z^vqJ(3C;|$RP%>IiCqV2ul!FY8go>>am@cbZEmNQVhl}82o%~4ybu%;_R`7cNKr$A zRy4VTfTEW8sMWEF_R(!T7W^Nx%AwQ>bk5P)0RH(7 z%~Phe|8Tld0t5lwzgZa-f|Z8iwqe`4+{xvcy#M|2Ee^~7>#s~w(p;yalGxR4vpA|^ z5R@VFam;m6zFg&?KVzl{RI}}}pC8=~bGfic(4v*2hIB|sB3cF>V#UP@ab`CyfO6M; z$XzZ_Ejknh3E^4V9~`q>bQOq!P~xA%BAUf9D{vW%kPctx3If2|ulaL5^*1#8wY0Di zlQWS!6N6dgn2?UOHk>eiVI@4|*sGsJg}23V^RCJXK%I9r*FQZ_we`3d*OoKM0!*9q zXN8fKos~BB)EGw>(m>kCVAB1BJ4|1d`15DR>QoaXNIevtbM!W9o<%`Ov%0OmxNbld z5hx1CK7P@X2`jFEMYysMnp_k30Q4$&VwCF2f~`-Qrs@9c*IK3;t2c5 zgXGxJ6b{SE+Fb;i@Gnqob~g9rpLIZ#0Gzkwpg(U&GG5CFmNpH>GvyNPy>+@eQRXo6 zEnw~6osPhzY{8B_%Y!>KH_65>N#PrEB-Z5b{=2$q#uzoCVM63%`QZEGVcNR%-+FRL z3?vLj*RUIK9X$dqhi4Sw-rf=2jIF{kyVEw*3s7M#kuwL&h>;4RM8hAtDn8wRzGyyu z=hb0jX~6OyaGfkWn*As6pU8_y_wD!=;zK5mGA7f&2Mf2qU|sq)&byCG+^_DWkpsV*0Z7Mzgg9S#uprc ze&nrqKipIDj^ceQEK_Zs?90c~x$l<#sL0yu88|ZZ>`yc7WE>p_Be!|}c89};K4dh~ z>e#Zc%~>lPXwJb~4>S>#c&4woCP6Pt$A+UBHKaz2dhYHGo1nUe`gGjB z&h>k6;aAN}vM8G4{M7I9kVnoB&sbsVAZ;|`)AxsJEOMR{Nm)IlNb;9@w0|eq#)-<3 zqMRO-?&Mp{9jI_fMCDe(Pk@|de8uUC5KXcyn2lh@eucW2sri-fumYUD=9-*bA~=~# zm4b#v*XIYZ+C*UwwA{EKW-e zd>t*S3il4Q0N;S(&)okpSmr8={ndPL-q5JAgFcjA$V~+txkPJo{@Dj6LTDi4t)ybB z!2C)Bj^QuAK3U01{dC(*3MmlYS^7u?c6dW%zRuakREaNs^%w2qQ10sj7t0&J*1+lh z702yiihMBs0qTYrnHR#KYWQ28bS(f}0x?~zmKq2v#w{Z=T_Ve$`zi!bdr3=VKc$ZjnpeXkiL6wrW7 z+5qP>t)@qRqT40`>yRb_ZIVNDtw*75VQr2@j6J|^M$J>!d75&`aTE%F_M(7^osFUW zYk^@q%>0mYXZgQ&Oohx_2HtM#$?J{RjtD11<85|#FMr!G%zIw0$Y1_lwYsVJN>3DU z;W7|7GLHa;>lkpyN?9rDUr(Swi3p7N_82198Y+SN)lq|UA@qFJ5#WEf^@8f~vvlMQ zR@o18oejR^)_<>ypraRg`{sKW*s-n3wA$L*SnhU<_UXLtbDA=+;$y%S_A)EjED=Nf z#TDM`O8r<27|bB|bY?1$d+s4Z@9y2ouEP159Xet(CL#D9^s}PUDdVe94-&C7kIyn8 zQ@TM@y^O77%4nF&&v6z~JE42>w6E!1Yx(Ioq%^KN02Au}nhW?d5 zTulla%2tu0&}%+$44uv?KUwvdy&)TXD_uNU%|!RQkgjzkd*#IL*i}nj`QhBftl{}? za^Q~{30j%e3h1_8Bp!h?LyuVF$r|K^1xG%7tim}UFN^ow`Zou-0XHAul}NzCRK)N| zm;^2lT;5d4=T)3yCF*mBXW`q_$6JHR5EBKCBvNljS2g29ETUr0@QW|nz@yK6qugoq zE;uXOQ`2W=Xaz8iuAQb39Fy8`fvB{VjmJHQvX#&=H$)lt>8er z5V+hM#+{xYaV)S-GjDx=kuN?Ql*zw5c6f>5;E;=Z?gX{tL){LWxxxOqs|&;P>8rQb z+s|en2VH7_-zB~nQ(0o(jW+HqW(CSkGXm(1!H$i$MB8`vxLx3GP;VVR69{!yIqHv_ zaiv`ZKm^y4;MJe_Qp>BRkzpez}yfPoyniPF?e@vv}+ou#_*rHREnZ?*ThIp z!%ajH5@}HSxX;N9t>pJ$&*a_^7Y6BuCRi=Sh+`smA4!7ejkp(s zutrD+CYM_N$K;<~so@wNaD~*n>zgB{N)O*MF%%{D*69pUyl~JwAPa##s1q>suP>q1 z-=fjznvw_-Ocjqg{mclhJAwAF1HP!!XcgM9J8X;lmv%x32)Lf zOKWm$^sR0B_Dh}}PT7cyV3}dp?{?x2p(WX3&-8Uc0gbUu1v~vOqx_EJ|txvu!nCH-O zw6_mUPb88p-y<_R zb$5L1V9+dpVPHlmN0g)5i*fJM9!I{loquF={T3ljH7O;x=wx2Y<{mA_f1g064F2N> zBmOPlM3Yu@J5e7gTb}HhPC7d8VW$0$TK#qo6+H_&H9ZSV_YHn@e`C4AJo!*QL|op` zh3ov24Agj9^ly!fyG~)S4lBUY4J&*@F{S0ohC1Ag{!iMvbhfu z>S810yy5Dp^_j@!>eZ-5D;3jmh3V}e>f|Q^vvUU2_-hS^0q%gOFc#@+O*Da!Lk@6F z0QAL!Qb5d;&jgyS5rT%z{Drud5}?WE;|OjE|Eg<-sSMYxnd*9kF$ug;hT=VC{F4+G z^6N#j?}OS@yIs5W)!!kvUSmivp{lvOo3R@+2Q|W80;y@Jjv+jUl~VAIUiqQ(uVV5x zml+0|;K^%wA-jSq?mxmhFBw$au{SCqA>TvU#^WaFwAUJDw1qL?Ak@UEokc3h@CQRaLAhUq!T3S`Fz{#8_lhvpvRfH4Uf_fcKQUx`CL*u%{7;^#TMp z0LiW5mJ!P1`M8$YpD#m=kTLf$JW7QeRMLuS2+)mr^3ba)Y`rMM+921?MRCLKvVG{L zkpAyj1nK#%i_$EfMtJh<_5(p$QBl!9_|O|TpF?p4#en~OsO4lYPa)?At&BUoq34XU zXH68MlQ6GIpm`gcHeG+=fV z;3yiZaOuGK=uVBX`~I^+qRWtjZ`yS66fZdhb?MJEXaaG&NSU6BA0Q>yLbgi(*Dggy zef;WR{nLQfOxWbcB^x8d8?Pn_=fr2^*8Wa%A;Zq$dRcNZ55CyaeXE-(E>NtyC$S4Q z{!2x_@l}&iE@V~oZkS02J-X;ECx?}UfN6E5Zx89#vj~Yav%7(b+zAXJJU7AL$!*>q zcC{7n%aYWnu$yLgNCkBV*Z=<-{eEqurxETMH zs$g?x^PmME;Q;XJxy5lRsJ4U*9Yr^y1TgE+g%|pr()e>q$i-q)m zny(Ny)rQhW1{eSwg`y!JM*&o`Or#z{LQ$Zblkm=U|C5##5(Eh)F9pcF@`tjco;ZFc zPR^wB3RCpwS%&1K!$;%MHEgwlt|r&u>z~g*n=o=MEn>ed@1m;BWbyZtf7J(Ywd`NU zI1|uJs!ZM%HFh1auubfxm<3V{cSlsBvj3|cW9~gpy{0!$=iz>`M~9u*Oc`w8j^mE^<5aq(;&RX~*2)oe`KXvtJX)fb;fDNL6 z6)XdH!XWC*SVx_F1PaA03JJB-*f5~XYC(}eSW{kiK9%6Z0GkA6OCh~zx}zjc5sul?mp>_2U67y9|)uO#_;xC!QS7t9PYW}>-C7-DA?b*;D_aO4+cL%dVXDTSyF@cPFSr1l(Gd%{~$ncqb6M$V=KU#e7;?%AB<2Oa0 zEpFLO9Gx;1y0fx@Y0-njY%8S-|}vwcx);sujuK z+AbsVpb?e`-!hfvYYQlzm=b!zUzKApO4RsAOBTmDb@SI&UNLcMdd+PnyZP}FF(bB) zfcpd9Q-1x6Css^6i_wLjRCb87oU_!)_2#f&(~=fQ2b<=?t=O^>kUN&iM<0R!js!sK z46?v?oUI`i)#Dj6fijqMjl!wZjY&KUFv;nd>QT=gJM3(YtO!#Ye~OQUL?bGnMw5p6 z*2?Kuooe>LaPExu4+JG{24m;#wuZEo2eMxNBP(RN|6A*#6L}@f^mA*-ResHpMtMr> zyX3A0Yb$h#$IJ*Q@6A^DVH=NDdYDYLLu#sL^2Vr4sIeaf!)C;kGW9JeAjg4PHT@?# zTYTeuds>D)tm8~fAubmwpgR~~7RCG50io=(F5l*ymY)00?TqZXKbm%brrkMN=UX4J zw#kh3PZy^~2>ICX7dbPvR>D8|2eM=rg;n02c##voGyvo0YuJx1JkYy5?3{hq1|HSh zg#<0CGs?Q-NfU8MO+I618G^|hSE%t;?0)C`g4K_S2G#tVKcu~d?4*tDBAVY|(m|;} z+y~D@igM7$|8YsSg#zq(9}vqx#Yxn0X$S;tsD&#Q-~-V-T>({(2>tN!|IHN`3=SR$VA+8(C-bO%ZX!HP zo0VV|axvaHO}`)6`T<$htc}!z%T&{Dwb1{9Z~soJ3h%zr-7y?^1QS4Bvey{*LGMI1 z3ShveI4S}KSkLPgD7Dr!i^r;}3>*NAxCSV>M%=h6+wZtCan6slpg7*q?>oikL=YPx6TYrZnibRS3Y$Fx|~jb-k=UkbAb|2GJe z1p)q~54sXy^*SAEbn?RD6q|wck5+SeUNrqDc7%Tj+y)s$$5Wv4PhY}txLw~XhaWfG zL-!4^XUSJ#!^sVo+*5SH=PTH|j zsVc3?$L{`)aU*7-`|J)sKU;r8%=}t-aYZbVoZ#h=Wnl^&)b_eMPNQs-oprM8RHc*V z?dkr8IoVEeGZ7}Z={3MP$;ii10VejxpEv@8rkd=&YB z14{?iMIPj$){!~R7rT75ToF$xZ5N|bDq;!oPXg$6E~=iJsiUZ>zOnuM8Dw&E2Y;fM z^6{fHUCx7Y7Wj8hNW&_fnqF4Os8q#2%rgEL@&RUycoN9SkJ#tO$=B3|wpxFT%z-*( znrvqmtF$a;=ri4#rGIb5_kQIW9z}Nt&fkcV;p(E(E}oLU{$JG8NXpgr)w`U~V`b)- zvfZ}DTVILZlTOne?CCvU4}=|&dQd>~`KcSiew}hv_-K8kwn{eYoq(7iPnykFU#@7-T~ZjJ)A2o}+< ze~*9}x<7EK5ps2r@9TZ97Bg`8=Rvb-yXPx6URCtLUYO@2jy8)fxej_zpns=)YRc;e#Tq4f0x@P88K{+sTg~ToI^CPAm z5tTk#!-&d;%YmfpjPrEhjj77Mn$3N$Jd9(ndA@a-Yx%zuym^^Udbu92+w4A-FQIZg z|7~a2Bc}AvJ;>)ui~HDT1`uj|Q*$5B7PB9=+VUpLwT3_X$0Y-Eol-ciuXL-n#J^>K zcxAtoBLmwXz2TC-8E)4PT_I#1R;s=|#k)K9Qyo5aEl93j^}7mVYs>utVoFyx-kj*1 z;&5lhtPBzDWHD3k3bc@%gvd-+G2>7Z^@+ZyPvz-$eoy5kf1fSQEF5-Vig0OLuG+wP zh&g-7Cq1r^V3ejUmV1zyZz$!f?H(oZ^P^BTXx^8i`92{0&G`9m&V$$+dR3oZk~eAk zVYY7drt*ov45*+JT9)#~4yw zLx@zbzbsYk;&A^70vFcU^5v2>D7uvEE#v zHr{n2zlS|vBD-UF+xpM_sTn>PLm~5EAgBq@Wqi2a(X9;lq*0*yU&}Y_VS!J59;<)+ z^I<^E$Q0(#`IOhm^eyf!lS>`6;IKY%D$iEa-v9!sHf%^@!hV^aazv7!obpsW88ll2d&f5KCbJ{1GNFssNV{I-781naJ`CyaiJ+&(zo<^x=a)T^a~d_V zFY)Z@B)~6&1Q>iD=$)H7nOxh^Y*y()jXXwH{V+*m4kJqGMuwgw@1yUfG3k`pl+>vb!W7C$=TFejoP*G`^zw zzS0g?f%2Lb`EpA4Q3L4Nr~a=o#ZJi~88Q^+M~sdt_zX5kOb;eRxxgrlgPncRb}!~d zlY1k%36c{2tm*1I^?~Zs5{5ZAvZ8dX_zg)`(O?n{dh=yyUw)#vfP}C50Ztbwsz3m1 z6b1+ar*38v{4%r%cKiteLdui5xVRkqY~N<(zNA8@6&)aL_DfmkS>^WHI#!PTbu(3l z*sE}t5~UF{mt2z3$T)o8F-xvn<6?HUtL^1ub5$xkZ*VBv z)rn4zeR0!)&a6`VFl%|T&*tl|=1^$$`^ipJG=ws%hywC!7`S{kkG5#tQlJl-AuAKW zFEA6XD&8z<%518Y!eaGp1ABg!LmEyQR zQMDYO&|40=C4MZ_`wY52#$FNc=w#sO?|)VkdWyZginpI!soX7*`_=K;EAqsw;l3%5 zFwjSyMA~x_91wtcxQJ{VD{cdmLkK++z8jylks1d0T4*;URL~#hdPWb0PCAQi#u3su z!~2_67m~I5MxtT4r1Wf8A)23%RY2OqNoz@F z1ONUG^*A~j%Hjn($1-fLHH>moSDRv{fLsrFuIlk2YRCvk6QHLhFB*wSh)VqnPG#7| zyWQ^*BLJ7>cS0hAQwni!yg}YQAqn<>TIN7#PD|#x2+3S`LIc-0hjlBt@yTTsPJfxV$A;P*wXS~ z8u#A^8&d^!F%6qz3ONLfX^CDxW z5&?4uagBAX?DKz*NOS~L+O3a#Mv~a;*ND-dKZ`6oiT5>v3U-d4bIh;a*bLAvB_xX5 z^q1c1uweZ)+WmW2Dy6}P{aq4GE%nf#`&=)J@)^}_VPOvQD$CCC0!>chiT}<#X%eq$ zXR?h2TS*FziZ_%@uECev45DbehueQIsSS*u;4IkFWgBg-5CwuRvh0Jb1Xe_&uoh~U zb2|C&IhwKqj#mFW=@JQ@Z7rtg)@e|*sxHNr{e8v&bIYPRYZK0S6L*!@P52W7eR9W`>eYLl49Q*2<;meR8#Fw;lg+|~tt9=9vQ@rUnnB|j5(o!)%m|LN z!?F!w^d3H$vp?_LQRM3Yr_Us|p@5UZo)Q!iRR$hOj?LOQPO#YgEtbwF6D$+*YvrA0 z3?VFm1#wYj`0a@aXkHw6QXx!7l=rsWE1^>g3S7i*_f$2%?HTG5MZblA!B$`5&6o?3 zv(@zn8}q+LrVm%Yr@a}9dq=;~IbA|djS~1)DbXN?(9^Kj1~eLGP!(?hB-S*LWfxAI zNvRqFb5W0sL%Wik8(eN6K%l{d0U-LIq=4noC|&bzb*Is{$Afn+5*U*0?)>;Zn;FMBG+1&ztl8q!`toN<4CXc}r|3 zt6)bvF^D*M3M?js=nWXJ^)kBS4!?WnUC&hElA^PutyzDr@vUtWSyi_QRvWO#fTz{d zc-R~5>pHe(?)_HaCfBrSA#|hb5MK7f7#Mv1h5a97jV0f-hOnow2*2+zq4LvTG0UC- zGmj^FM^%39S6^{M}C*ksL*CaPV)IqDXgb@~} z88!zEB&qcqF?w?u)#c>9+wtJzIVuvww9lU=oc!4NkGs$fQTcXn@m$dHEi~#Hy5ULoD|2}k43&Iu0{^C$ z_#g{}8D;zZZzc+kNk6aOojyLFJPOEol65{gpYGdZss4p65cj4rc(^tzrwr9Hoi_zC zWOwg*CeXnD{hS5{%90h0Ri$50*S`@m%T)?fgO~Y6=f5QFFJM78Y?2cpbJvgocK03X z(qX%OS)q>LuX0CtAn-_xU8yTmDV%Qp1C-U|!t!l}GM_p5urYcg_pR~`wS9_y@ zJ6ltaBk_-si793q=QQT94G-V8;i`lB-z*_|kp4HAt?!UfFE6f}SP0y<-%^P%$0L;- zG-RSkEfOL+S}1mx8N(poTi2Qh=erbJ=}(ik^TiCmH`kohvw#7ixzf%xCC z`)rbkSjxL;9(UFh2lwMm%p^#AP#c@e6nUnoit&hh8fmC8c9YQFx=@Npx<%S@+$%wt z9jBoNqoD13tNUhoAn3P;YlqDA8||H@GZnb}5+F8Q1LfZ?*W2e`0fF;l0S_&c_p9XK z%1Bm9CVO(Q;$oQHb@!&YA~A%lD+N40;!GHRr;dCgZTdc3?MH94dlZ+~rv4 zQl#F?jWGg9n%%w76|Z|y2I==V$SMO@s;9c{drjpAeM?s0BC*%Qt8sA!_8V>yG>PUzJ+)!T#Dawd4kZmB$8L-3s! z9PYy1K3B z)_nqDmQ#b{DY@!cQC)rxx4DVVPmy_6S)(&!eJ6Y2eyBAz;-!=6uks_Nm&oI&Gq@G< z;FH!9B(l;D$Se|RjA1c)G!?Pgybq~UQs}D1{3JrK4;YRZ-;#3(H##ESL|nPyO;6VL zCnMp~<)GTsjQtTh-~U<>?mzy>1nth)H1@A3&PdEMDU7{TG!$>6HZ z;?im8;yrK0u9C|NIi?Q;4U{Kc$|jyjbSM0prk4V8fNxlIaIy38w-U|CG)=Q>6xwZ!VILNkp@h4-pZMFH@zuRbX7r)b zM>+W}e>whHRl4Ty5VkM!_T5=?`{TtaiVBP7QFinf{q^Yft7+98GwV3x(EQkCU4T318&btEZO1#w-zJ0%OyvbhD$X!9E|~w?@Fafzs%4<_*cX7$IsFEn%a?&c$nw|%X*;;-hvx;^mZ9BC&hQ_|);~{iIdh zp1R06S4PLT)iyzQ&40ctWeYQA&E( zisJHGOl;0aJ>4YBUhI9)(sGZ~9EI%tDzOE0GBdk`#}c?VJ=<41yugGx&x3+Na7ELWSLQ!gN`u3U?+wsxZ+6d;tH>jN(-jIPE$-t@#nSPcJT?G zsApVEmb*ChN~&zl1>+X^2?n{^4Ss29CI~b04HWb%c?Fv`8@wrJPcig_zg=;|?C@bT zN^lSVmF?=@yVjWgM_%FnPpbTL@(;-+^N;<-2s2YRguxHgT=&@9WJ(iC42nF+>`Au5 z8M%Vq@9aiyM0&PN z@s*G>5o2cK-Q0QuIOBDr{zAaM%l6uqV)iN9To0D-ZIE}{AYPd3v_kviFGiqi|2f)N zo3om4Ge3H9%fA~_EyPZ0o|G`Gl5y*k>~A=*5V#`!(*NcEs;VAwz44Q3KYiOKph8Wc z;$OGLF6~!dd$u*4*4Q<=irgQmCD0Mw_8fRT2ZN`ppUXO@geCwhhd4k0 diff --git a/docs/CAD/design/mate-connectors/faceted_ridge_key.scad b/docs/CAD/design/mate-connectors/faceted_ridge_key.scad deleted file mode 100644 index 3eb60064fb..0000000000 --- a/docs/CAD/design/mate-connectors/faceted_ridge_key.scad +++ /dev/null @@ -1,140 +0,0 @@ -// Faceted ridge key — asymmetric male/female alignment feature, flat facets only. -// -// 6 vertices, 7 faces, one closed manifold. Euler check: V - E + F = 6 - 11 + 7 = 2. -// No spheres, no cylinders, no splines, no fillets. -// -// THE FLANKS ARE TRIANGULATED EXPLICITLY, and that is not cosmetic. Written as quads -// [0,3,5,4] and [1,4,5,2] they are NOT planar — the base edge and the ridge edge are -// skew, so the four corners do not share a plane. A checker caught this after the first -// draft claimed the opposite. Left as quads, the tessellator picks the fold direction for -// you, which means the "flat facet" promise is broken by an unspecified crease and two -// exporters can disagree about the shape. Splitting them here fixes the crease at -// back-bottom -> front-ridge, which keeps the rear peak's triangle large and clean. -// -// FRAME CONVENTION (matches the CAD mate connector it is derived from): -// +Z the mating axis — the feature protrudes along it -// +X the roll reference — the ridge runs along it, low end forward -// +Y completes the right-handed frame -// -// WHAT BREAKS WHICH SYMMETRY -// rotational about Z ....... the ridge (elongation along X) -// 180 deg about Z .......... the ridge SLOPE: tall steep back, long shallow front -// mirror across XZ ......... deliberately NOT broken. Handedness is fixed by convention, -// so +Y is implied once Z and X are known. Breaking it would -// add a facet and buy nothing. -// -// KNOWN AMBIGUITY, stated rather than hidden: viewed exactly ALONG the ridge (+/-X, -// orthographic), the silhouette is the same isoceles triangle from front and back. Front -// and back are then distinguished by SHADING only — the long shallow front face catches -// light differently from the steep back face. If the target renderer is flat-shaded with a -// single headlight, verify this case before committing to the shape. - -// ---------------------------------------------------------------- parameters -L = 12.0; // overall length along the ridge (X) -W = 4.0; // half-width at the BACK -tf = 0.45; // front taper: front half-width = W * tf -H = 4.5; // peak height at the rear <-- the single dimension controlling asymmetry -pr = 0.22; // rear ridge position, fraction of L from the back -pf = 0.62; // front ridge position, fraction of L from the back -hf = 0.35; // front ridge height, fraction of H - -// Clearance is a PHYSICAL quantity and only means anything if this is a printed part. -// See the note at the bottom: for a viewport glyph it is meaningless. -clr = 0.20; // per-face clearance, mm -depth = 0.40; // extra pocket depth so the male never bottoms out before it seats - -Wf = W * tf; -xr0 = -L/2 + L * pr; -xr1 = -L/2 + L * pf; -Hf = H * hf; - -// ---------------------------------------------------------------- geometry -// Vertex order is fixed and referenced by the face table; do not reorder. -// 0 back-left 1 back-right 2 front-right 3 front-left -// 4 REAR PEAK (tall) 5 front ridge (low) -function ridge_pts(l, w, wf, h, hfr, x0, x1) = [ - [-l/2, -w, 0 ], // 0 - [-l/2, w, 0 ], // 1 - [ l/2, wf, 0 ], // 2 - [ l/2, -wf, 0 ], // 3 - [ x0, 0, h ], // 4 rear peak - [ x1, 0, hfr] // 5 front ridge, low -]; - -// OpenSCAD wants each face wound CLOCKWISE seen from OUTSIDE. The right-hand-rule -// outward-normal (CCW) form is given in the comment for anyone porting to STL/OCC, -// where the opposite convention is the usual one. -RIDGE_FACES = [ - [3, 2, 1, 0], // base (CCW-outward: [0,1,2,3]) planar, all z=0 - [1, 4, 0], // back (CCW-outward: [0,4,1]) steep - [5, 3, 0], // flank -Y a (CCW-outward: [0,3,5]) - [4, 5, 0], // flank -Y b (CCW-outward: [0,5,4]) - [5, 4, 1], // flank +Y a (CCW-outward: [1,4,5]) - [2, 5, 1], // flank +Y b (CCW-outward: [1,5,2]) - [5, 2, 3] // front (CCW-outward: [3,2,5]) long, shallow -]; - -module ridge_key(l = L, w = W, wf = Wf, h = H, hfr = Hf, x0 = xr0, x1 = xr1) { - polyhedron(points = ridge_pts(l, w, wf, h, hfr, x0, x1), - faces = RIDGE_FACES, - convexity = 3); -} - -// MALE: the protrusion, nominal size. -module ridge_key_male() { ridge_key(); } - -// FEMALE: the pocket. Grown by `clr` on every side and sunk `depth` deeper. -// -// HONEST LIMITATION: this grows the key by scaling its defining dimensions, which is NOT a -// true uniform surface offset — on the shallow front face the normal clearance comes out -// smaller than `clr`, because that face is far from perpendicular to every axis it is -// scaled along. A true offset needs minkowski() with a small cube, which is exact and slow, -// or an explicit per-face plane push, which is exact and fiddly. For a keying feature whose -// job is angular registration rather than a press fit, the approximation is the right trade -// — but do not quote this pocket as holding 0.2 mm everywhere, because it does not. -module ridge_key_female() { - translate([0, 0, -depth]) - ridge_key(l = L + 2*clr, - w = W + clr, - wf = Wf + clr, - h = H + clr + depth, - hfr = Hf + clr + depth, - x0 = xr0, - x1 = xr1); -} - -// ---------------------------------------------------------------- demo -// Left: the male key on its plate. Right: the plate with the pocket cut. -PLATE = [30, 18, 3]; - -module plate_with_male() { - translate([-PLATE[0]/2, -PLATE[1]/2, -PLATE[2]]) cube(PLATE); - ridge_key_male(); -} - -module plate_with_female() { - difference() { - translate([-PLATE[0]/2, -PLATE[1]/2, -PLATE[2]]) cube(PLATE); - ridge_key_female(); - } -} - -translate([-20, 0, 0]) plate_with_male(); -translate([ 20, 0, 0]) plate_with_female(); - -// ---------------------------------------------------------------- note on the two readings -// This file is written for the PHYSICAL reading: a printable alignment key, where `clr` and -// `depth` are real millimetres and flat facets genuinely help — they slice without the -// stair-stepping a tessellated curve produces, and they print without support on the -// shallow front face. -// -// If the intent is instead the VIEWPORT GLYPH for a CAD mate connector, then: -// - `clr` and `depth` are meaningless: a symbol does not mate with anything; -// - all dimensions must become SCREEN PIXELS scaled by upp = 1/zoom, because every gizmo -// in that viewport is screen-constant and must not shrink with the model; -// - "low-poly for rendering performance" is not a real reason at ~2-20 glyphs per frame. -// The real reason to keep flat facets there is LEGIBILITY: hard normals give distinct -// value steps between facets, and that is what lets a 22-px solid read as an oriented -// object instead of a grey blob. -// The vertex logic above is identical under both readings. Only the units and the clearance -// change. diff --git a/docs/CAD/design/mate-connectors/fem-0.png b/docs/CAD/design/mate-connectors/fem-0.png deleted file mode 100644 index 391881e72a61a4577e4acea81ae044ee7314f4d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5985 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 jT`dg(s^3N{N} diff --git a/docs/CAD/design/mate-connectors/fem-1.png b/docs/CAD/design/mate-connectors/fem-1.png deleted file mode 100644 index c329d20bea00914d58df757d21ffd31ae33fcb42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4068 zcmeHK`&*J(9)GPZn;dl-EvGEmnRd&`%(AiazLu<0rbeaaC{m+ldBO1lqJq>^mNug` z=4BKkF|91M@`3_lrA2CL<`qyd6%bKLP*kpaoL_dIefk4-pNXGd&ikJ8p6}=OJ#YFk zZx6$@o7O@QWO(>{_u~+>A_{`^-mF;xMno$^^dQLi;bC{TlQ`1!aNspRK?!TN^h8HK z{ciTH^{ZBM4&QhByr{^nyUD@mc(kF7!?(A*f=v&HMZn=6N2W=q*kYfsy8&nQt)8Z5 zd}CtUv!?mWD_Cqo8HsHcDa~_>f68zAZBpLwgLJPn`L>-z*e>rZ_8Y{fcP<$@EDXh4-6IjTOZr<9jtKcGH&t9Efrz!i2B`uqD2ynVYH zOv6a}qifq}7|Cj53&xq|v(>E~NHS|0sPBl$U)TZTF`F9pMCZ?c< z6-EiRA{*vX;&;)U8W|U69~W8`+^Bt93u#x^saP9QyV3M%#hbld2`e(8bydB2`|h1>>}X3Nn)V>P>o&L2 z_Q#fM6wBm=nc-M`lSX0gg2(y#_I0`H7`WNdBPz!N`*`7aYc`eGmBA#a6w_R4_Wr-b zYuzfD%1PM>FGX5WQK6CBKqFNG5Mx=UEUY}|XcqW~i+N(_#Dx!%ss4vy#@7=R^D6uWBX zP6%2&cQu3#)yHa73;fyIOlQR)ps68VWMsbYQQW9zf$xg@FhZlzNjV>Qb8EGOCPmu4u&*#5O6WU+DjxsCZ0%ittI z&htz9{;yEr0_?rn$T12a1JGoTS2bys2#+nb5q;&U8l~@Q)x2b3mIbQ{YIr&}QbFa> zfW~uK3&V*h3}$|Dc8u8b15i$RWo0TDGd?hK0YK8UnXx1+G}{$~>vCRjS{lzjQFc_! zYgC!|I3Y~oo}TUbdqTDy&EFl~NwTV9#6ACc6(D8i0C!H@V{UFvCMei9w|ao}bedLb zZf-949}b?07j-s{W>^8%4urF+(~M!vB)O1cS>{!^@9dlWD+XqnfJxRju<{=vahrf?7^<}nNe3dJ89V#EpBO0ryNWo1Gjz(_QDmrXsOm!!UN z$cP;w^R#J9n49Qc7AvrTP2=3Vw#9d)p42F|ZRqP8-ZP&;rF>mD1&)}i9SBC!vDRb> zJ0#1w?KubturpzwKgv4eM=_X^yVD+Elo*wo^$F=h}GpTuVp_Q&v+ zEohNA6bnbxP^tUv>~d0`gDT8W_}QuIu2hhToeq*m-hS+Uu>L;m)Sy`~uGrQ#vj34# zC`_4Y5IwkeyTC5eFASfD#ELn!kqA=}DZcYQq684csJOkoJc`NXZ~)#`eE+551cWJ) zmcUl5sB?Z8hZbKp@vavZMDkKq({7hmgEZO+_Fq~W1}Ko=WdN-wgTnA<*4EbkD8l^K zET^Yi6IWkwo%9cyoSdwr&(MGw8*F6?ZVW7G{H7olNGhOb@b2Mkpf|)!<-TZ6LZ7cc z68HYf@}A}gP$)kUFhBbI`EU6_AV*miDQ`&j^4vgl0Ps_0l?*sBipNN5xwcNyUlG`p zNG#c1)8i0mr9o82}B#5oNF z^w6&&B0Aa{b{(+(_KsUmnHQILIdG#B?gMB7Y@j|Qa+t$$IP>DN?I1Z-d7ePfxTvP8 zrA$?BVMRu8V=@xc=WDXvJLqX>t5Ky4B=`IjTfXca@FqTCVVGj60PUg}XdJ{U5GsDI ztga5LUYyNxQJxb#s_t@8p>)cDn;sSoqT4BTb#*g!T(n@(ZXjsy`FD3!(k9}w3wpz# zv5r?_N!4-v&n~WP7m>o>6ltSsL8|Kyj6q`A zVMRxI-9TjyOpr^oWjT&_Qr& z_Pq6&I!GdHemE0nNA@m_H^ivrA&#!Ph%< N81C&}dGPel{{omNlnej> diff --git a/docs/CAD/design/mate-connectors/fem-2.png b/docs/CAD/design/mate-connectors/fem-2.png deleted file mode 100644 index a32f2ffbf1407d5aa775e2be7bf814bfe04f7313..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4274 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 Hu0;IMR-$c)p>C}k+BPqZbKisaOkdM2jBG_uKI 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®{*IHQ;a_`P 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 diff --git a/docs/CAD/design/mate-connectors/fem-sheet2.png b/docs/CAD/design/mate-connectors/fem-sheet2.png deleted file mode 100644 index 879b4295fb66582d7ad20bca17b69b4903f7ffd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26906 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

    ^jsJJutE!pK$OL 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 diff --git a/docs/CAD/design/mate-connectors/female.stl b/docs/CAD/design/mate-connectors/female.stl deleted file mode 100644 index 57b0ef6d20..0000000000 --- a/docs/CAD/design/mate-connectors/female.stl +++ /dev/null @@ -1,226 +0,0 @@ -solid OpenSCAD_Model - facet normal 1 -0 0 - outer loop - vertex 15 -9 0 - vertex 15 9 -8 - vertex 15 9 0 - endloop - endfacet - facet normal 1 0 0 - outer loop - vertex 15 9 -8 - vertex 15 -9 0 - vertex 15 -9 -8 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex 15 9 0 - vertex 5.3246 1.63218 0 - vertex 15 -9 0 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex 15 9 0 - vertex -4.79494 3.42759 0 - vertex 5.3246 1.63218 0 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex 15 9 0 - vertex -5.97725 3.87059 0 - vertex -4.79494 3.42759 0 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex -5.97725 3.87059 0 - vertex -15 9 0 - vertex -5.97725 -3.87059 0 - endloop - endfacet - facet normal -0 0 1 - outer loop - vertex -15 9 0 - vertex -5.97725 3.87059 0 - vertex 15 9 0 - endloop - endfacet - facet normal -0 0 1 - outer loop - vertex 5.3246 -1.63218 0 - vertex 15 -9 0 - vertex 5.3246 1.63218 0 - endloop - endfacet - facet normal -0 0 1 - outer loop - vertex -4.79494 -3.42759 0 - vertex 15 -9 0 - vertex 5.3246 -1.63218 0 - endloop - endfacet - facet normal -0 0 1 - outer loop - vertex -5.97725 -3.87059 0 - vertex 15 -9 0 - vertex -4.79494 -3.42759 0 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex -5.97725 -3.87059 0 - vertex -15 -9 0 - vertex 15 -9 0 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex -15 -9 0 - vertex -5.97725 -3.87059 0 - vertex -15 9 0 - endloop - endfacet - facet normal 0 0 -1 - outer loop - vertex -15 -9 -8 - vertex 15 9 -8 - vertex 15 -9 -8 - endloop - endfacet - facet normal -0 0 -1 - outer loop - vertex 15 9 -8 - vertex -15 -9 -8 - vertex -15 9 -8 - endloop - endfacet - facet normal -1 0 0 - outer loop - vertex -15 -9 -8 - vertex -15 9 0 - vertex -15 9 -8 - endloop - endfacet - facet normal -1 -0 0 - outer loop - vertex -15 9 0 - vertex -15 -9 -8 - vertex -15 -9 0 - endloop - endfacet - facet normal 0 1 -0 - outer loop - vertex 15 9 -8 - vertex -15 9 0 - vertex 15 9 0 - endloop - endfacet - facet normal 0 1 0 - outer loop - vertex -15 9 0 - vertex 15 9 -8 - vertex -15 9 -8 - endloop - endfacet - facet normal 0 -1 0 - outer loop - vertex -15 -9 -8 - vertex 15 -9 0 - vertex -15 -9 0 - endloop - endfacet - facet normal 0 -1 -0 - outer loop - vertex 15 -9 0 - vertex -15 -9 -8 - vertex 15 -9 -8 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex -6.2 4.2 -0.4 - vertex 6.2 -2 -0.4 - vertex 6.2 2 -0.4 - endloop - endfacet - facet normal 0 0 1 - outer loop - vertex 6.2 -2 -0.4 - vertex -6.2 4.2 -0.4 - vertex -6.2 -4.2 -0.4 - endloop - endfacet - facet normal 0.873667 0 -0.486524 - outer loop - vertex -5.97725 -3.87059 0 - vertex -6.2 4.2 -0.4 - vertex -5.97725 3.87059 0 - endloop - endfacet - facet normal 0.873667 0 -0.486524 - outer loop - vertex -6.2 4.2 -0.4 - vertex -5.97725 -3.87059 0 - vertex -6.2 -4.2 -0.4 - endloop - endfacet - facet normal -0.107146 0.603912 -0.789816 - outer loop - vertex 6.2 -2 -0.4 - vertex -4.79494 -3.42759 0 - vertex 5.3246 -1.63218 0 - endloop - endfacet - facet normal -0.107147 0.603918 -0.789812 - outer loop - vertex -4.79494 -3.42759 0 - vertex 6.2 -2 -0.4 - vertex -6.2 -4.2 -0.4 - endloop - endfacet - facet normal -0.304068 0.811519 -0.498978 - outer loop - vertex -4.79494 -3.42759 0 - vertex -6.2 -4.2 -0.4 - vertex -5.97725 -3.87059 0 - endloop - endfacet - facet normal -0.304068 -0.811519 -0.498978 - outer loop - vertex -5.97725 3.87059 0 - vertex -6.2 4.2 -0.4 - vertex -4.79494 3.42759 0 - endloop - endfacet - facet normal -0.107146 -0.603912 -0.789816 - outer loop - vertex -4.79494 3.42759 0 - vertex 6.2 2 -0.4 - vertex 5.3246 1.63218 0 - endloop - endfacet - facet normal -0.107147 -0.603918 -0.789812 - outer loop - vertex 6.2 2 -0.4 - vertex -4.79494 3.42759 0 - vertex -6.2 4.2 -0.4 - endloop - endfacet - facet normal -0.415603 0 -0.909546 - outer loop - vertex 5.3246 -1.63218 0 - vertex 6.2 2 -0.4 - vertex 6.2 -2 -0.4 - endloop - endfacet - facet normal -0.415603 0 -0.909546 - outer loop - vertex 6.2 2 -0.4 - vertex 5.3246 -1.63218 0 - vertex 5.3246 1.63218 0 - endloop - endfacet -endsolid OpenSCAD_Model diff --git a/docs/CAD/design/mate-connectors/female_only.scad b/docs/CAD/design/mate-connectors/female_only.scad deleted file mode 100644 index 89f83b4655..0000000000 --- a/docs/CAD/design/mate-connectors/female_only.scad +++ /dev/null @@ -1,12 +0,0 @@ -// Female half alone, for the legibility test: is a recessed faceted pocket readable in a -// shaded view, or does a concave feature just read as a dark hole with no orientation? -use - -// The plate must be THICKER than the key is tall, or the "pocket" is a through-hole. The -// first version used 3 mm against a 4.5 mm key and cut straight through — caught only by -// rendering it. Minimum stock = H + clearance + pocket depth + a wall to print against. -PLATE = [30, 18, 8]; -difference() { - translate([-PLATE[0]/2, -PLATE[1]/2, -PLATE[2]]) cube(PLATE); - ridge_key_female(); -} diff --git a/docs/CAD/design/mate-connectors/fit_check.py b/docs/CAD/design/mate-connectors/fit_check.py deleted file mode 100644 index 5a9ee01063..0000000000 --- a/docs/CAD/design/mate-connectors/fit_check.py +++ /dev/null @@ -1,20 +0,0 @@ -# Measure the assembled fit between the supplied male and the generated female. -# This is the number that matters: the minimum gap in the seated position. -# Run: /snap/bin/freecad.cmd fit_check.py -import os -import Part - -HERE = os.path.dirname(os.path.abspath(__file__)) -male = Part.Shape(); male.read(os.path.join(HERE, "bear.step")) -fem = Part.Shape(); fem.read(os.path.join(HERE, "BearConnector_Female.step")) -male, fem = male.Solids[0], fem.Solids[0] - -d = male.distToShape(fem) -print(f"RESULT minimum gap male<->female, seated: {d[0]:.4f} mm (design clearance 0.20)") - -c = male.common(fem) -print(f"RESULT interference volume: {(c.Volume if c.Solids else 0.0):.6f} mm3") - -p = d[1][0][0] -print(f"RESULT tightest point on the male: ({p.x:.2f}, {p.y:.2f}, {p.z:.2f})") -print(f"RESULT male {male.Volume/1000:.2f} cm3 / female {fem.Volume/1000:.2f} cm3") diff --git a/docs/CAD/design/mate-connectors/glyph-preview.png b/docs/CAD/design/mate-connectors/glyph-preview.png deleted file mode 100644 index 5e62c9a17f30ea871711d5e867b27cf48311bfbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12890 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 diff --git a/docs/CAD/design/mate-connectors/trim_female.py b/docs/CAD/design/mate-connectors/trim_female.py deleted file mode 100644 index 2753e4a3a8..0000000000 --- a/docs/CAD/design/mate-connectors/trim_female.py +++ /dev/null @@ -1,74 +0,0 @@ -# Trim the boxy frame off the female so its outer shape is the bear face itself. -# -# Method: take the male's flat back face (the plane Y=0 -- that face IS the bear silhouette), -# offset its OUTER wire outward in 2D, extrude the result along the insertion axis, and keep only -# the part of the female inside it. Everything outside is the block frame and goes away. -# -# NOTE ON THE NUMBER. The pocket's side walls stand at +0.20 mm from the male outline, because that -# is the clearance. A trim boundary at +0.10 mm therefore falls INSIDE them by 0.10 mm and removes -# the side wall entirely rather than leaving a thin one. The script runs the requested value and -# then measures what is actually left, so the outcome is a number rather than an opinion; it also -# emits a second variant at an offset that leaves a printable wall, for comparison. -# -# Run: /snap/bin/freecad.cmd trim_female.py - -import os, sys -import FreeCAD as App -import Part -from FreeCAD import Vector - -HERE = os.path.dirname(os.path.abspath(__file__)) -MALE = os.path.join(HERE, "bear.step") -FEMALE = os.path.join(HERE, "BearConnector_Female.step") - -REQUESTED = 0.10 # as asked -CLEARANCE = 0.20 # what the pocket was built with -SAFE_WALL = 1.60 # a wall that survives an FDM nozzle: clearance + ~1.4 mm - -male = Part.Shape(); male.read(MALE); male = male.Solids[0] -fem = Part.Shape(); fem.read(FEMALE); fem = fem.Solids[0] -print(f"female in : {fem.Volume/1000:.2f} cm3, {len(fem.Faces)} faces") - -# --- the bear silhouette: the male's flat back face at Y = 0 -back = None -for f in male.Faces: - n = f.normalAt(0, 0) - if abs(f.CenterOfMass.y) < 1e-6 and abs(abs(n.y) - 1.0) < 1e-6: - if back is None or f.Area > back.Area: - back = f -if back is None: - print("FAIL: could not find the flat back face at Y=0"); sys.exit(1) -print(f"silhouette: back face area {back.Area:.1f} mm2, {len(back.Wires)} wires " - f"(outer + {len(back.Wires)-1} holes: eyes and mouth)") - -fb = fem.BoundBox -y0, y1 = fb.YMin - 5.0, fb.YMax + 5.0 - -def trimmed(offset): - """keep only the part of the female inside the silhouette grown by `offset`""" - wire = back.OuterWire - grown = wire.makeOffset2D(offset, join=2, fill=False, openResult=False, intersection=True) - face = Part.Face(Part.Wire(grown.Edges)) - prism = face.extrude(Vector(0, y1 - y0, 0)) - prism.translate(Vector(0, y0 - face.CenterOfMass.y, 0)) - return fem.common(prism) - -for tag, off, out in (("requested", REQUESTED, "BearConnector_Female_Trimmed.step"), - ("safe wall", SAFE_WALL, "BearConnector_Female_Trimmed_wall.step")): - r = trimmed(off) - if not r.Solids: - print(f"\n{tag} (+{off:.2f} mm): NOTHING LEFT"); continue - wall = off - CLEARANCE - # is there any material left at the level of the pocket's side wall? - sec = r.section(Part.makePlane(400, 400, Vector(-200, 1.5, -200), Vector(0, 1, 0))) - perim = sum(e.Length for e in sec.Edges) - print(f"\n{tag} (+{off:.2f} mm) wall = {wall:+.2f} mm") - print(f" volume {r.Volume/1000:.2f} cm3, {len(r.Solids)} solid(s), {len(r.Faces)} faces") - print(f" section through the pocket wall at Y=1.5: {perim:.1f} mm of edge") - if wall <= 0: - print(f" -> the trim cuts {abs(wall):.2f} mm INSIDE the pocket wall: no side wall remains") - doc = App.newDocument(tag.replace(" ", "_")) - o = doc.addObject("Part::Feature", "Female") - o.Shape = r; doc.recompute() - Part.export([o], os.path.join(HERE, out)) - print(f" wrote {out}") diff --git a/docs/CAD/design/mate-connectors/verify_trimmed.py b/docs/CAD/design/mate-connectors/verify_trimmed.py deleted file mode 100644 index 81f6f3fea1..0000000000 --- a/docs/CAD/design/mate-connectors/verify_trimmed.py +++ /dev/null @@ -1,25 +0,0 @@ -# Check both trimmed females still fit the male, and export STLs for a visual comparison. -# Run: /snap/bin/freecad.cmd verify_trimmed.py -import os -import Mesh, Part - -HERE = os.path.dirname(os.path.abspath(__file__)) -male = Part.Shape(); male.read(os.path.join(HERE, "bear.step")); male = male.Solids[0] - -for name in ("BearConnector_Female_Trimmed", "BearConnector_Female_Trimmed_wall"): - p = os.path.join(HERE, name + ".step") - s = Part.Shape(); s.read(p); s = s.Solids[0] - d = male.distToShape(s)[0] - c = male.common(s) - cv = c.Volume if c.Solids else 0.0 - bb = s.BoundBox - print(f"{name}") - print(f" {bb.XLength:.2f} x {bb.YLength:.2f} x {bb.ZLength:.2f} mm, {s.Volume/1000:.2f} cm3, " - f"{len(s.Faces)} faces, valid={s.isValid()}") - print(f" gap to male {d:.4f} mm, interference {cv:.6f} mm3") - m = Mesh.Mesh(); m.addFacets([tuple(t) for t in s.tessellate(0.12)[1]] and - [(s.tessellate(0.12)[0][a], s.tessellate(0.12)[0][b], - s.tessellate(0.12)[0][c2]) - for a, b, c2 in s.tessellate(0.12)[1]]) - m.write(os.path.join(HERE, name + ".stl")) - print(f" wrote {name}.stl ({m.CountFacets} facets)") diff --git a/docs/CAD/design_tab.md b/docs/CAD/design_tab.md deleted file mode 100644 index 1be0a2ea33..0000000000 --- a/docs/CAD/design_tab.md +++ /dev/null @@ -1,333 +0,0 @@ -# The Design tab - -Object-driven parametric CAD inside the slicer. Point at geometry; the geometry offers the -verbs that apply to it. Selection comes first and the tool consumes it. Draw a sketch, -constrain it, turn it into a solid, refine it, and send it straight to Prepare — without -leaving for another application and coming back through an STL. - -The model is a **recipe**, not a mesh. Every action becomes a feature in a tree that is -replayed from the start whenever anything changes, so editing a dimension you set twenty -steps ago rebuilds everything downstream. The geometry kernel is OCCT, which the slicer -already ships for STEP import. - ---- - -## Getting started - -1. Open the **Design** tab. -2. Click a face or a reference plane in the viewport, then press `Shift+S` (Sketch). The offer - opens with the sketch tools on it. -3. Draw a closed profile, then press **✓ Confirm** in the floating action bar. -4. With the sketch selected, press `Shift+E` (Extrude). -5. Press **Commit to Plate** to hand the solid to Prepare. - -The status line under the toolbar is the thing to watch: it says what the current tool is -waiting for. When no plane is picked it reads *"Click a face or a reference plane in the -viewport, then a sketch tool"*; once one is picked it reads *"Sketching on — pick a -tool"*. It is also where a refusal explains itself. - ---- - -## Selecting - -- One left-click selects what is under the cursor. There is no click-cycling through - face → edge → body. -- A click near a corner takes the corner, not the face behind it. -- Left-drag sweeps a rubber band, and a rubber band takes the whole body. -- An open sketch line can be clicked, even where it bounds a region. -- Double-click a sketch stroke to edit it — the gesture belongs on the geometry. -- Editing a dimension's value **updates** that dimension instead of adding a second one next - to it. -- The floating chrome that belongs to a sketch leaves with the sketch when it ends. -- Sketching happens on the face you clicked, first click. -- A sketch whose entities form no wire **fails** instead of extruding a default box. A - subtraction that removes nothing is reported as an error instead of a silent success. - ---- - -## The offer - -Right-click on the geometry, released without moving the mouse (an 8 px budget — a -right-drag that orbits the camera does not open it). Left-click still only selects, so -pointing at things stays quiet. - -The offer also opens by itself the moment you press Sketch on a face or plane, showing the -sketch tools — the app hands you the tools directly. - -**Eight families, always in this fixed order:** Create, Add material, Remove, Dress-up, -Repeat, Transform, Reference, Modify. - -- A family with at least one applicable verb shows it. Several applicable verbs collapse - into a submenu under the family name. -- A family with nothing applicable is **shown greyed in place, with the reason** — e.g. - *"Create — Click a face or a reference plane in the viewport, then a sketch tool"*. It is - not hidden. A control that cannot be used still says what it is and what you would have to - do first. -- Inside a sketch the offer shows the sketch verbs; outside it shows the feature verbs. - -**Document-level actions never enter the offer**, because they act on the document and not -on a selection: Import STEP, Import mesh, Text, SVG, Export STEP, Commit to Plate, Undo, -Redo, Variables, Section view, Origin planes, World axes. They live in the toolbar. - ---- - -## Keyboard - -Single letters drive sketch tools **while a sketch is open**; Shift+letter drives feature -tools and single letters drive view toggles **when no sketch is open**. The two maps are -selected by the mode, not by whether a sketch session is running. - -### Sketch (while a sketch is open) - -| Key | Tool | -|---|---| -| `L` | Line — click start, then end | -| `R` | Rectangle — click two opposite corners | -| `C` | Circle — click centre, then radius | -| `A` | Arc — click start, end, then a point | -| `S` | Slot — two centreline ends, then width | -| `E` | Ellipse — centre, major end, minor point | -| `B` | Spline — click control points | -| `P` | Point — click to place | -| `G` | Polygon — click centre, then a vertex | -| `D` | Dimension — click 2 points or an entity | -| `T` | Trim — click a segment to trim it | -| `X` | Extend — click a line/arc to extend it | -| `O` | Offset — pick an entity, drag the distance | -| `M` | Mirror — pick axis, then entities | -| `F` | Fillet — pick two lines, set the radius | -| `H` | Chamfer — pick two lines, set the distance | -| `K` | Constrain — finish the live sketch and enter constrain | -| `Q` | Construction toggle — draw the next entity as construction geometry | -| `Del` | Delete the selected sketch entity | -| `Esc` | Cancel the live tool | - -### Feature (when no sketch is open) - -| Key | Tool | -|---|---| -| `Shift+S` | Sketch | -| `Shift+E` | Extrude — extrude a profile, or push/pull a picked face | -| `Shift+R` | Revolve | -| `Shift+W` | Sweep | -| `Shift+L` | Loft | -| `Shift+N` | Pattern | -| `Shift+G` | Surface Extrude | -| `Shift+J` | Surface Revolve | -| `Shift+O` | Surface Loft | -| `Shift+Q` | Surface Fill | -| `Shift+U` | Surface Offset | -| `Shift+V` | Thicken Surface | -| `Shift+P` | Plane | -| `Shift+A` | Axis | -| `Shift+C` | Coord Sys | -| `Shift+Y` | Transform | -| `Shift+Z` | Mirror | -| `Shift+B` | Boolean | -| `Shift+X` | Cut | -| `Shift+F` | Fillet / Chamfer | -| `Shift+D` | Draft | -| `Shift+K` | Shell | -| `Shift+H` | Hole | -| `Shift+T` | Thread | -| `Shift+I` | Import STEP | -| `Shift+M` | Import mesh | - -### View toggles (single letters, when no sketch is open) - -| Key | Action | -|---|---| -| `Home` | Axonometric view, fitted to the model | -| `P` | Origin planes on/off | -| `A` | World axes on/off | -| `X` | Section view on/off | - -While the section is on: `PageUp` / `PageDown` move the cut plane, `F` flips which half is -kept. With no section on, `F` is Place on Face — lay the picked face flat on the bed. - ---- - -## Sketching - -A sketch is a closed (or open) 2D profile on a plane or on a flat face of an existing body. -Press `Shift+S`, click the face or plane you want to sketch on, and draw. The toolbar and -the offer both carry the sketch tools. - -**Entities:** line, polyline, rectangle (corner / centre / oblique / rounded), circle -(centre-radius / 2-point / 3-point), arc (centre-point / 3-point / tangent), ellipse and -elliptical arc, polygon (inscribed / circumscribed), slot (straight / arc), spline, point, -and text. - -**Editing:** move, rotate, scale, trim, extend, offset, mirror, and linear or polar arrays. - -**Constraints:** coincident, horizontal, vertical, parallel, perpendicular, tangent, equal, -concentric, midpoint, symmetric, fix, plus dimensional radius, diameter, distance and angle. -The solver reports the remaining degrees of freedom and tells you when a sketch is fully -constrained — or when a constraint conflicts with one already there. - -Sketches stay editable. Selecting one in the feature tree reopens it with its dimensions -live. - ---- - -## Building solids - -Grouped in the toolbar by what they do, one concept per drawer. - -### Add material -| Tool | Shortcut | What it does | -|---|---|---| -| Extrude | `Shift+E` | Extrude a profile, or push/pull a face already on a body | -| Revolve | `Shift+R` | Revolve a profile about an axis | -| Sweep | `Shift+W` | Sweep a profile along a path — including a helix, for springs and augers | -| Loft | `Shift+L` | Skin between two or more profiles | -| Thicken | — | Offset a solid face into a thin plate as a new body | -| Rib | — | Grow a stiffening wall from an open sketch line, fused to a body | - -Extrude offers blind, symmetric, two-sided, through-all and up-to-face end conditions, plus -a draft angle on the side wall, and can add, subtract, intersect or start a new body. - -### Surface -Sheet bodies — surfaces with no thickness — for shapes that are easier to build as skins and -solidify afterwards. - -| Tool | Shortcut | -|---|---| -| Surface Extrude | `Shift+G` | -| Surface Revolve | `Shift+J` | -| Surface Loft | `Shift+O` | -| Surface Fill | `Shift+Q` | -| Surface Offset | `Shift+U` | -| Thicken Surface | `Shift+V` | - -Thicken Surface is how a sheet becomes a printable solid. - -### Dress-up -| Tool | Shortcut | -|---|---| -| Fillet / Chamfer | `Shift+F` | -| Draft (taper a face) | `Shift+D` | -| Shell | `Shift+K` | -| Delete Face | — | - -Delete Face removes faces and heals the solid — useful for stripping a feature off an -imported part. - -### Holes -**Hole** (`Shift+H`) drills simple, counterbored or countersunk holes, with an ISO/ANSI -standards table so you can ask for an M6 clearance hole instead of computing a diameter. -**Thread** (`Shift+T`) cuts a real helical thread into a bore or onto a shaft. - -### Placement -Operations that move a body without changing its shape: **Transform** (`Shift+Y`), -**Mirror** (`Shift+Z`), and **Mate** for assemblies. - -### Combining -**Boolean** (`Shift+B`) unions, subtracts or intersects two bodies. **Cut** (`Shift+X`) -splits a body with a plane. **Pattern** (`Shift+N`) repeats a body linearly, in a circle, or -along a curve. - ---- - -## Reference geometry - -Datum features carry no material; they exist to give later features something to attach to. - -- **Plane** (`Shift+P`) — offset, tilted, midplane, tangent, through two edges, or coincident -- **Axis** (`Shift+A`) — two points, a face normal, a cylinder centreline, the intersection of - two planes, or along an edge -- **Coord Sys** (`Shift+C`) — a full frame, from a world point or from a face plus a - direction edge -- **Helix** — a helical curve to sweep along -- **Project** — project a body's edges onto a plane as sketch geometry - -On the Coord Sys tool, picking a direction **edge** is worth the extra click: without one the -frame takes its X from the face's first edge, which is deterministic but not necessarily the -direction you meant. - ---- - -## Assemblies - -**Mate** aligns two coordinate systems and moves one body onto the other. Five kinds: - -| Kind | Leaves free | -|---|---| -| Fastened | nothing — 6 DOF locked | -| Planar | sliding in the plane | -| Revolute | rotation about the axis | -| Slider | sliding along the axis | -| Cylindrical | rotation *and* sliding | - -**Check interference** reports every overlapping pair of solids with the overlapping volume, -so a clash is a number rather than an impression. Bodies that merely touch enclose no volume -and are not reported. - ---- - -## Variables and expressions - -Define named variables and drive dimensions from them. Any numeric field accepts an -expression — `width/2`, `wall*3` — and everything re-evaluates on recompute. Change one -variable and the whole model follows. - ---- - -## Import and export - -**Import STEP** brings in a real B-rep solid, not a mesh: its faces and edges can be filleted, -shelled and cut like anything modelled here. - -**Import mesh** (STL/OBJ) converts triangles to a B-rep body and tells you honestly what it -got — whether the result is a closed solid or an open shell, with the boundary and -non-manifold edge counts. A large mesh becomes a large number of faces, which is slow to -edit; the importer warns before you commit to it. - -**Export STEP** writes the model out for another CAD tool. - -**Commit to Plate** sends the solid to Prepare for slicing. The whole feature recipe is saved -inside the 3MF, so reopening the project restores the editable model rather than a frozen -mesh. - ---- - -## View controls - -**Section view** (`X`) hides half the model so you can see inside — `PageUp`/`PageDown` move -the plane, `F` flips which half is kept. **Place on Face** (`F`, when section is off) lays a -picked face flat on the bed. Origin planes (`P`) and world axes (`A`) can be toggled on while -you orient yourself. - ---- - -## Known limitations - -Being straight about the edges, so nobody discovers them the hard way: - -- **Rib** needs a sketch containing an explicit open line. A parametric rectangle sketch - carries no individual entities, so Rib cannot use one. -- **Surface Loft** and **Surface Fill** have kernel tests but have not been exercised by hand. -- Card wiring for 9 of the 16 late-wired tools has never been click-tested. -- Mate resolves by composing transforms directly. There is no 3D assembly solver, so mates - are applied in order rather than solved simultaneously, and mate limits are not implemented. -- Move-face and replace-face are not implemented — OCCT offers no clean primitive for them. -- There is no automated GUI test in CI. Every behaviour above is traced to code and to a - hand pass, not to a synthetic click. - ---- - -## Where the code lives - -| Path | Role | -|---|---| -| `src/libslic3r/CAD/CadDocument.*` | the feature recipe and its replay | -| `src/libslic3r/CAD/GeometryEngine.*` | OCCT wrapper — faces, edges, booleans, healing | -| `src/libslic3r/CAD/SketchEngine.*` | profile → wire → solid | -| `src/libslic3r/CAD/SketchSolver.*` | constraint solving, over the vendored solver | -| `src/libslic3r/slvs/` | vendored 2D constraint solver (GPLv3) | -| `src/slic3r/GUI/CAD/DesignPanel.*` | the tab: toolbar, cards, feature tree | -| `src/slic3r/GUI/CAD/DesignCanvas.*` | viewport integration | -| `src/slic3r/GUI/CAD/DesignSketchTool.*` | in-canvas sketching | - -Build with `-DSLIC3R_CAD=ON` (the default). With it OFF the tab is not compiled and the deps -prefix matches upstream exactly — see [cad_dependency_weight.md](cad_dependency_weight.md). diff --git a/docs/CAD/design_tab_pr_description.md b/docs/CAD/design_tab_pr_description.md deleted file mode 100644 index a6d67f0e7c..0000000000 --- a/docs/CAD/design_tab_pr_description.md +++ /dev/null @@ -1,73 +0,0 @@ -# Design (CAD) tab — upstream pull request - -## What this adds - -A sketch-first parametric CAD tab inside the slicer. The workflow is direct: -sketch → constrain → solid features → commit to plate. The whole feature recipe is -persisted inside the 3MF, so reopening restores an editable model rather than a frozen mesh. - -- Kernel: OCCT, which upstream already links for STEP import — see - [cad_dependency_weight.md](docs/cad_dependency_weight.md) -- Constraint solver: vendored SolveSpace `libslvs` subset -- Interaction model: object-driven — point at geometry, the geometry offers the verbs that - apply to it; see [cad_ux_guidelines.md](docs/cad_ux_guidelines.md) -- Full user-facing documentation: [design_tab.md](docs/design_tab.md) - -## Why it belongs in the slicer - -Every round trip through an external CAD tool costs a file export, a re-import, and the -design intent that both steps discard. A part modified after slicing should return to its -feature history, not to a mesh. Keeping the CAD model inside the slicer preserves that -loop — the nozzle diameter, the build volume and the material are known at design time. - -For the integration case in full: [design_tab_upstream_portability.md](docs/design_tab_upstream_portability.md). - -## How it is built - -The `SLIC3R_CAD` CMake flag (default ON) gates the entire tab. With it OFF the tab is not -compiled and the deps prefix matches upstream exactly — the dependency diff is one line in -OCCT's CMake: `BUILD_MODULE_ModelingAlgorithms=OFF → ON`. - -Measured cost table: [cad_dependency_weight.md](docs/cad_dependency_weight.md). - -## Diff shape - - - -Against merge-base `d6cb667b894f`: - - 306 files changed, 83032 insertions(+), 777 deletions(-) - -350 commits, of which 284 are new files and 37 modify upstream files. 99.3 % of the diff -is new code. The negotiable surface is the 37 modified files. - -## Tests - -205 `TEST_CASE` blocks across 6 new test source files. This counts assertions written, not -assertions passed — a run needs a build. - -`scripts/CAD/run-kernel-tests.sh` is the headless verification contract: it builds only -`libslic3r_tests` (not the GUI app), needs no display, and exit 0 means the CAD suite -passed. It now runs with **no exclusions** — both cases that used to be quarantined (the -circle-line tangency solver abort and the internal-thread reference) are fixed. - -## Licensing - -The vendored solver in `src/libslic3r/slvs/` is **GPL-3.0** (see `src/libslic3r/slvs/LICENSE`), -not LGPL. The combined work is distributable under AGPL-3.0. See the Licensing section of -[design_tab_upstream_portability.md](docs/design_tab_upstream_portability.md) for the -AGPLv3/GPLv3 compatibility argument; this point should be confirmed with upstream explicitly. - -## Not verified - -- Card wiring for 9 of the 16 late-wired tools was never click-tested. -- There is no automated GUI test in CI. A green kernel run says nothing about the GUI — - synthetic clicks never drift, so the test suite and the viewport are two separate realities. -- The click-test defect rate has **not converged**: a second pass found no new defects, but - four further days of work found five more. The earlier pass is not evidence of stability. - -## Reviewer's map - -See the [Where the code lives](docs/design_tab.md#where-the-code-lives) table in the user -doc for the file-to-role mapping, and [docs/ux/tool_atlas.json](docs/ux/tool_atlas.json) as -the generated-from source of `src/slic3r/GUI/CAD/DesignOffer.hpp`. diff --git a/docs/CAD/design_tab_upstream_portability.md b/docs/CAD/design_tab_upstream_portability.md deleted file mode 100644 index a7e9cd84ed..0000000000 --- a/docs/CAD/design_tab_upstream_portability.md +++ /dev/null @@ -1,162 +0,0 @@ -# Design (CAD) tab — upstream integration brief - -**Question:** can the Design tab (sketch-first parametric CAD: sketch → constrain → -extrude/revolve/fillet/hole/thread/shell, multi-body, undo, 3MF persistence) land in -mainline OrcaSlicer? - -**Answer: yes, and the ask is far smaller than previously believed.** OCCT is *already* -an OrcaSlicer dependency. We are not asking upstream to adopt a new library; we are -asking it to widen one it already builds, at a measured cost of **3.77 MiB on Windows**. - -> ### Corrections to the 2026-06-21 assessment -> That revision was written before the persistence work landed and got two load-bearing -> facts wrong. Both are corrected here from direct measurement of the branch: -> -> 1. **"The real blocker: OCCT … a dependency mainline OrcaSlicer has never carried."** -> **False.** `deps/OCCT/` exists at the merge-base and upstream links it from -> `Format/STEP.cpp`, `Format/svg.cpp`, and `Shape/TextShape.cpp`. Our entire -> dependency diff is **one line**: `BUILD_MODULE_ModelingAlgorithms=OFF → ON`. -> 2. **"vendored SolveSpace solver … LGPL."** **False.** `src/libslic3r/slvs/LICENSE` is -> **GPL-3.0**, not LGPL. This is fine (see Licensing) but must not be misstated. -> -> It also claimed "no changes to Model" — no longer true; 3MF recipe persistence adds one -> `std::string` to `Model`. - -## Measured shape of the change - -Against merge-base `449a4cf9fc` (34 commits ahead): - -| | files | lines | -|---|---:|---:| -| **New files** | 138 | +61,720 | -| **Modified upstream files** | 23 | +457 / −75 | -| **Deleted upstream files** | 0 | — | - -The 62 kLOC headline is inflated by localization. The feature itself: - -| area | LOC | files | -|---|---:|---:| -| kernel (`src/libslic3r/`) | 15,828 | 37 | -| GUI (`src/slic3r/`) | 19,544 | 14 | -| tests (Catch2) | 2,567 | 6 | -| i18n (unrelated; strip from the CAD PR) | 23,438 | 77 | - -**99.3 % of the diff is new files.** The negotiable surface is 457 added lines across 23 -files, and nothing upstream is deleted. The largest single hook is `GLCanvas3D.cpp` -(+110/−2): an `m_design_sketch_tool` member plus render/mouse/key hooks, **every one -already null-guarded** — which is why the compile-time gate below is cheap. - -No changes to the slicing pipeline (Print/PrintObject/Layer/GCode), Tab, or the -printer-profile/config system. - -## The dependency ask, precisely - -Not "adopt OCCT" — **widen the existing OCCT build**: - -```diff -- -DBUILD_MODULE_ModelingAlgorithms=OFF -+ -DBUILD_MODULE_ModelingAlgorithms=ON -``` - -Cost, measured from the shipped Windows artifact (42 OCCT DLLs, 45.43 MiB total): - -| toolkit | size | note | -|---|---:|---| -| `TKFillet.dll` | 2.02 MiB | only exists with the flag ON | -| `TKOffset.dll` | 1.75 MiB | only exists with the flag ON | -| **delta** | **3.77 MiB** | Windows only (OCCT is Shared on Win, Static elsewhere) | - -`TKBool` is *not* part of the delta — upstream's `DataExchange` already pulls it in -transitively. On macOS/Linux OCCT links statically, so the cost is only the code actually -referenced, not a 3.77 MiB floor. - -**Unmeasured, and we should measure before the call:** clean-deps build-time delta with -the flag ON vs OFF, and the resulting CI runner-minute cost. Do not guess these at him. - -## Licensing - -- Vendored solver `src/libslic3r/slvs/` — **GPL-3.0**, 9,339 LOC, © Jonathan Westhues, - a self-contained subset of SolveSpace (`libslvs`). No external dependencies. -- OrcaSlicer — **AGPL-3.0** (`LICENSE.txt`). - -GPLv3 §13 expressly permits combining a GPLv3 work with an AGPLv3 work; AGPLv3 §13 grants -the converse. The combined work is distributable under AGPL-3.0 with the solver's GPLv3 -terms preserved. This is a favourable direction (GPLv3 → into an AGPLv3 project), but it -is a point to **confirm explicitly with upstream**, not to assert unilaterally. - -Open question for SoftFever: keep the solver **vendored** (current: pinned, no submodule, -no external build) or move it to `deps/` as a fetched external? Vendoring costs us -upstream-sync burden; `deps/` costs build complexity. - -## The one irreversible decision: the 3MF format - -Persistence adds an **optional** archive entry and one field: - -```cpp -// Model.hpp -std::string cad_recipe; // empty for non-CAD projects -``` - -``` -Metadata/orca_cad.bin // written only when cad_recipe is non-empty -``` - -Readers that do not know the entry ignore it; writers skip it entirely when empty. So -existing projects are bit-identical and old readers are unaffected. Good. - -**But the moment upstream ships this, it owns forward-compatibility forever.** Three -things should be settled *before* the first release, because none can be changed after: - -1. **Name.** Renamed to `Metadata/orca_cad.bin`. -2. **Encoding.** The recipe is an opaque **cereal `PortableBinaryArchive`** blob whose - layout is the field order of `CadFeature::serialize`. Portable across endianness and - word size — *not* across a field reorder. Append-only is currently a convention held by - discipline, not by any check. -3. **Embedded BRep.** `Import` features embed OCCT's ASCII BRep for the imported solid, - which couples saved project files to an OCCT BRep revision. Alternative: re-import from - the source STEP and store only a reference. Worth deciding deliberately. - -**Concrete gap we should close before the call.** `test_caddocument.cpp` covers the -in-memory round-trip and correctly refuses a version-999 blob — but there is **no -checked-in v1 fixture on disk**. A reordered field in `CadFeature::serialize` would pass -the entire suite while silently breaking every previously-saved project. Ship a golden -`.bin` fixture generated today plus a test that loads it; that is the only thing that will -hold the format still once real users have files. - -## Proposed PR decomposition - -35 kLOC in one PR is not reviewable. Behind the flag, slices 1–4 are behaviour-neutral for -existing users: - -1. **Build gate + OCCT flag + Windows packaging guard.** `-DSLIC3R_CAD=ON/OFF`, default - **OFF**. Flips `ModelingAlgorithms=ON`. Includes the guard that asserts every linked - OCCT toolkit has a shipped DLL (already on both forks: `546cef5f42`). ← *this is what - makes SoftFever's "parallel build" a one-line CI matrix entry.* -2. **Vendored `slvs` solver** + its Catch2 tests. No GUI, no OCCT. -3. **CAD kernel** (`CadDocument`, `SketchEngine`, `GeometryEngine`, `Sketch*`) + kernel - tests. Headless, no GUI. -4. **3MF recipe persistence** + golden-fixture regression test. -5. **GUI Design tab** (`DesignPanel`, `DesignCanvas`, `DesignSketchTool`, `GLGizmoSketch`) - + the 23 upstream hooks. - -## Agenda for the call - -Questions only SoftFever can answer: - -- Does OrcaSlicer *want* to be a CAD-integrated slicer? (Strategic; everything else is mechanical.) -- Default of `SLIC3R_CAD` at merge time, and when it flips ON. -- Vendored solver vs `deps/` external; and confirmation of the GPLv3/AGPLv3 combination. -- Project-file format: neutral name, encoding, embedded-BRep policy, and who owns v1 forward-compat. -- Undo/redo: the Design tab has its own stack; integrate with Orca's snapshot system or keep separate? -- Does he want the i18n work (Romanian, +23 kLOC) as a wholly separate PR? (Yes, almost certainly.) - -## Verdict - -Portability **high**. The prior "does upstream want OCCT" framing was wrong — OCCT is -already there. What remains is a 3.77 MiB dependency widening, a compile-time gate that -the existing null-guards make cheap, and one file-format decision that must be made before -the first release rather than after. - ---- -*Revised 2026-07-10 from direct measurement of `cad-mainline` @ `546cef5f42` vs upstream -merge-base `449a4cf9fc`. Supersedes the 2026-06-21 read-only assessment.* diff --git a/docs/CAD/ux/interaction-model.md b/docs/CAD/ux/interaction-model.md deleted file mode 100644 index 2041978ea0..0000000000 --- a/docs/CAD/ux/interaction-model.md +++ /dev/null @@ -1,144 +0,0 @@ -# Design tab — interaction model - -The contract for Esc, the right mouse button, and the states between them. Code that changes any -of the three changes this file in the same commit. - -## 1. The state machine - -`src/slic3r/GUI/CAD/DesignInteraction.hpp` — a four-level LIFO stack. The enum value *is* the -depth, so "which level does this press belong to" is a comparison rather than a chain of -special cases spread over three files. - -```cpp -enum class CadLevel : int { - Idle = 0, // nothing transient is up: Esc clears the selection - Tool = 1, // a feature card / armed sketch tool / constrain session: Esc exits it - Gesture = 2, // an uncommitted delta (entity being drawn, body being dragged): Esc reverts it - Transient = 3, // a value field or a popup menu: Esc closes just that -}; - -struct CadInteractionState { // the four bits routing actually needs - bool value_field_open{false}; - bool gesture_active{false}; - bool tool_armed{false}; - bool has_selection{false}; -}; - -constexpr CadLevel cad_escape_level(const CadInteractionState& s) -{ - if (s.value_field_open) return CadLevel::Transient; - if (s.gesture_active) return CadLevel::Gesture; - if (s.tool_armed) return CadLevel::Tool; - return CadLevel::Idle; -} -``` - -The rule is a `constexpr` free function over a POD, not a method on the panel, so the ordering -that is the entire contract is checkable without a window, a GL context or an event loop. Five -`static_assert`s in the header do exactly that, at compile time. - -**Strict invariant.** No level of Esc deletes a feature, discards a sketch that holds geometry, -or rolls history back. Destroying work needs a gesture that says so: - -| To destroy | Gesture | -|---|---| -| a feature | Delete / Backspace on an explicit selection | -| a drawn sketch | the ribbon's ✗ Cancel, which asks first | -| the last committed change | Ctrl+Z | - -## 2. Event routing - -**`OnKeyDown(WXK_ESCAPE)`** — `DesignPanel`'s `wxEVT_CHAR_HOOK`, one line: - -```cpp -if (key == WXK_ESCAPE) { escape(); return; } -``` - -Every Esc in the tab goes through it, whatever holds focus. `DesignPanel::escape_level()` answers -the four questions of `CadInteractionState` about this panel; `DesignPanel::escape()` acts on the -one level that answer names, and on no other: - -| Level | What one press does | What it must not touch | -|---|---|---| -| `Transient` | close the value field (`cancel_value` / `inline_cancel`) | the tool, which stays armed | -| `Gesture` | drop the clicks of the entity being drawn, or put a moved body back at the pose it had when the gizmo appeared | everything already committed | -| `Tool` | discard a feature card's *candidate*; drop an armed sketch tool to Select; end Constrain | committed features; entities already drawn | -| `Idle` | clear the selection (model and sketch); leave a sketch session **only if it is empty** | a sketch holding geometry — it is left through Finish or Cancel | - -A sketch *session* is deliberately not a `Tool`. It is the environment the Idle level lives in, -which is what makes the destructive path unrepresentable rather than merely unlikely. - -**`OnRightDown` / `OnRightUp`** — `DesignCanvas::set_on_context_menu`, bound after `GLCanvas3D`'s -own handlers so it can consume the event before them: - -```cpp -RIGHT_DOWN: remember the press position and the clock, then Skip() // the canvas still seeds the orbit - -RIGHT_UP: terminated = sketch_tool.take_right_consumed(); // read-and-clear, always - is_click = drift <= 3 px && dt <= 200 ms; // both budgets, or it was navigation - if (callback && !terminated && !inline_busy && is_click) { - select_at_screen(press.x, press.y); // raycast at the PRESS, not the release - on_context_menu(ClientToScreen(press)); - return; // consumed - } - Skip(); // orbit / pan / the handlers underneath -``` - -Two independent budgets because the two failure modes are independent: drift alone still popped a -menu at the end of a slow, careful orbit. `take_right_consumed()` is how a right-click that -already meant something to the armed sketch tool (terminate a chain, drop an edit-op) declines to -also mean "open a menu". - -## 3. Transition table - -`sel` = something is picked. Blank = the input does nothing at that state. - -| State | Left-click | Right-click | Esc | Enter | -|---|---|---|---|---| -| **Idle — model view** | pick / escalate the pick | offer menu for what is under the cursor | clear the selection | — | -| **Idle — sketch, empty** | pick | sketch offer menu | leave the session (nothing to lose) | Finish sketch | -| **Idle — sketch, drawn** | pick | sketch offer menu | clear the selection; status says the sketch is kept | Finish sketch | -| **Tool — feature card** | pick the card's next reference | offer menu | discard the candidate, close the card | commit the feature | -| **Tool — sketch tool armed** | place the first point | drop the tool to Select | drop the tool to Select | — | -| **Tool — constrain** | pick an entity | offer menu | end the session | apply | -| **Gesture — drawing** | place the next point | terminate the chain (keep what is drawn) | drop the in-progress entity, tool stays armed | commit the entity as drawn | -| **Gesture — moving a body** | drop the body here | end the move | revert to the pose at move-start | keep the placement | -| **Transient — value field** | — | — | close the field, tool stays armed | commit the value, advance the chain | -| **Transient — popup menu** | run the entry | — | close the menu | run the highlighted entry | -| **any** | — | — | *never* deletes, discards or rolls back | — | - -Right-hold-and-drag is not in the table on purpose: past 3 px or 200 ms it is navigation, and -navigation does not transition the state machine. - -## 4. Visual scaffolding - -Entering a sketch changes three things at once, so the state is legible from across the room: - -- **Banner.** A teal strip across the top of the viewport: `Editing: Sketch N · N = look normal to - the plane · Finish or Cancel in the toolbar`. Indicator only — Confirm and Cancel stay on the one - ribbon action bar, per the Design UX contract. It is a sibling above the canvas, not a floating - child over it: a child window over a `wxGLCanvas` is a native window on GTK and does not reliably - stack over GL, and this banner's job is to be unmissable rather than clever. -- **The printer bed is muted.** A plate grid and a sketch grid are the same visual language, and - reading one as the other is how a sketch gets drawn against the wrong reference. The view - checkbox remains the stored preference and is restored on the way out; ticking it mid-sketch - still shows the bed, because that is a deliberate act and this is only a default. -- **`N` looks normal to the plane**, keeping the current zoom, with the plane's own y axis as up. - Sketch key map only — in Feature mode the navigator orb owns orientation. - -## 5. Context menu content - -The offer is generated from `docs/CAD/ux/tool_atlas.json`; its 8-row shape and permanent row -indices are ratified and are not changed here. Checked against the per-context vocabularies asked -for in the 2026-09-05 interaction brief, the atlas already carries all of them except two, both on -a planar face: - -| Asked for | Status | -|---|---| -| Revolve on a planar face | **not offered, and should not be**: `revolve` accepts `sk_loop` only, because the kernel takes a sketch profile — a face is not one | -| Offset Face | offered as **Thicken** (`thicken`, accepts `face_planar`); `surf_offset` is the sheet-body verb and accepts `body_sheet` | - -View and document actions — Zoom to Fit, View Isometric, Clear Selection, Finish Sketch, Normal to -Sketch — stay in chrome by the atlas's own rule: the offer describes verbs that consume a -*selection*, and these act on the document or the camera. Esc covers Clear Selection, `N` covers -Normal to Sketch, and the ribbon covers Finish. diff --git a/docs/CAD/ux/mockups/cmp_12__edge_str.svg b/docs/CAD/ux/mockups/cmp_12__edge_str.svg deleted file mode 100644 index 3e641e90ec..0000000000 --- a/docs/CAD/ux/mockups/cmp_12__edge_str.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Straight edge selected — pick what to do with itWorking document · 12 slotsDress-up2Pattern on CurveReference3Straight edge \ No newline at end of file diff --git a/docs/CAD/ux/mockups/cmp_12__face_planar.svg b/docs/CAD/ux/mockups/cmp_12__face_planar.svg deleted file mode 100644 index 85809d5bf6..0000000000 --- a/docs/CAD/ux/mockups/cmp_12__face_planar.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 12 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/cmp_12__sk_line.svg b/docs/CAD/ux/mockups/cmp_12__sk_line.svg deleted file mode 100644 index 6535c9fe77..0000000000 --- a/docs/CAD/ux/mockups/cmp_12__sk_line.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 12 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line \ No newline at end of file diff --git a/docs/CAD/ux/mockups/cmp_8__edge_str.svg b/docs/CAD/ux/mockups/cmp_8__edge_str.svg deleted file mode 100644 index 2c41ce74fc..0000000000 --- a/docs/CAD/ux/mockups/cmp_8__edge_str.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Straight edge selected — pick what to do with itWorking document · 8 slotsDress-up2Pattern on CurveReference3Straight edge \ No newline at end of file diff --git a/docs/CAD/ux/mockups/cmp_8__face_planar.svg b/docs/CAD/ux/mockups/cmp_8__face_planar.svg deleted file mode 100644 index 5a58e2fb72..0000000000 --- a/docs/CAD/ux/mockups/cmp_8__face_planar.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 8 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/cmp_8__sk_line.svg b/docs/CAD/ux/mockups/cmp_8__sk_line.svg deleted file mode 100644 index 4ca4000d59..0000000000 --- a/docs/CAD/ux/mockups/cmp_8__sk_line.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__art.svg b/docs/CAD/ux/mockups/fresh__art.svg deleted file mode 100644 index a54dd49b43..0000000000 --- a/docs/CAD/ux/mockups/fresh__art.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetText / imported art selected — pick what to do with itFresh document · 8 slotsABCModify2Text / imported art \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__art__modify.svg b/docs/CAD/ux/mockups/fresh__art__modify.svg deleted file mode 100644 index 184611c682..0000000000 --- a/docs/CAD/ux/mockups/fresh__art__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsABCDeleteDelEditText / imported art · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__bodies_2.svg b/docs/CAD/ux/mockups/fresh__bodies_2.svg deleted file mode 100644 index f282bef03a..0000000000 --- a/docs/CAD/ux/mockups/fresh__bodies_2.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetTwo bodies selected — pick what to do with itFresh document · 8 slotsMeasureDeleteDelTwo bodies \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__body_sheet.svg b/docs/CAD/ux/mockups/fresh__body_sheet.svg deleted file mode 100644 index 635b2f6aa6..0000000000 --- a/docs/CAD/ux/mockups/fresh__body_sheet.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetSheet body selected — pick what to do with itFresh document · 8 slotsMeasureModify2Sheet body \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__body_sheet__modify.svg b/docs/CAD/ux/mockups/fresh__body_sheet__modify.svg deleted file mode 100644 index 0cd330046d..0000000000 --- a/docs/CAD/ux/mockups/fresh__body_sheet__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelEditSheet body · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__body_solid.svg b/docs/CAD/ux/mockups/fresh__body_solid.svg deleted file mode 100644 index ef21e2b8e2..0000000000 --- a/docs/CAD/ux/mockups/fresh__body_solid.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetSolid body selected — pick what to do with itFresh document · 8 slotsMeasureModify2Solid body \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__body_solid__modify.svg b/docs/CAD/ux/mockups/fresh__body_solid__modify.svg deleted file mode 100644 index 8b5d1485a0..0000000000 --- a/docs/CAD/ux/mockups/fresh__body_solid__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelEditSolid body · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__coordsys.svg b/docs/CAD/ux/mockups/fresh__coordsys.svg deleted file mode 100644 index e20d08d000..0000000000 --- a/docs/CAD/ux/mockups/fresh__coordsys.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCoordinate system selected — pick what to do with itFresh document · 8 slotsModify2Coordinate system \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__coordsys__modify.svg b/docs/CAD/ux/mockups/fresh__coordsys__modify.svg deleted file mode 100644 index b6b4871d16..0000000000 --- a/docs/CAD/ux/mockups/fresh__coordsys__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelEditCoordinate system · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__datum_axis.svg b/docs/CAD/ux/mockups/fresh__datum_axis.svg deleted file mode 100644 index 8174807ceb..0000000000 --- a/docs/CAD/ux/mockups/fresh__datum_axis.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetDatum axis selected — pick what to do with itFresh document · 8 slotsModify2Datum axis \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__datum_axis__modify.svg b/docs/CAD/ux/mockups/fresh__datum_axis__modify.svg deleted file mode 100644 index cba7005358..0000000000 --- a/docs/CAD/ux/mockups/fresh__datum_axis__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelEditDatum axis · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__datum_plane.svg b/docs/CAD/ux/mockups/fresh__datum_plane.svg deleted file mode 100644 index ffabd59969..0000000000 --- a/docs/CAD/ux/mockups/fresh__datum_plane.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetDatum plane selected — pick what to do with itFresh document · 8 slotsSketchShift+SReference2Modify2Datum plane \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__datum_plane__modify.svg b/docs/CAD/ux/mockups/fresh__datum_plane__modify.svg deleted file mode 100644 index 9ac26e4ce1..0000000000 --- a/docs/CAD/ux/mockups/fresh__datum_plane__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelEditDatum plane · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__datum_plane__reference.svg b/docs/CAD/ux/mockups/fresh__datum_plane__reference.svg deleted file mode 100644 index b42479441a..0000000000 --- a/docs/CAD/ux/mockups/fresh__datum_plane__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsPlaneShift+PHelixDatum plane · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__edge_circ.svg b/docs/CAD/ux/mockups/fresh__edge_circ.svg deleted file mode 100644 index 3e72544c04..0000000000 --- a/docs/CAD/ux/mockups/fresh__edge_circ.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCircular edge selected — pick what to do with itFresh document · 8 slotsMeasureCircular edge \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__edge_str.svg b/docs/CAD/ux/mockups/fresh__edge_str.svg deleted file mode 100644 index c4e54d5335..0000000000 --- a/docs/CAD/ux/mockups/fresh__edge_str.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetStraight edge selected — pick what to do with itFresh document · 8 slotsReference3Straight edge \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__edge_str__reference.svg b/docs/CAD/ux/mockups/fresh__edge_str__reference.svg deleted file mode 100644 index 80dd7a877a..0000000000 --- a/docs/CAD/ux/mockups/fresh__edge_str__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsMeasurePlaneShift+PAxisShift+AStraight edge · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__face_cyl.svg b/docs/CAD/ux/mockups/fresh__face_cyl.svg deleted file mode 100644 index d2335e4177..0000000000 --- a/docs/CAD/ux/mockups/fresh__face_cyl.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCylindrical face selected — pick what to do with itFresh document · 8 slotsReference3EditCylindrical face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__face_cyl__reference.svg b/docs/CAD/ux/mockups/fresh__face_cyl__reference.svg deleted file mode 100644 index 7cb83afb56..0000000000 --- a/docs/CAD/ux/mockups/fresh__face_cyl__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsMeasureAxisShift+AHelixCylindrical face · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__face_other.svg b/docs/CAD/ux/mockups/fresh__face_other.svg deleted file mode 100644 index d8dcfb96ab..0000000000 --- a/docs/CAD/ux/mockups/fresh__face_other.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCurved face selected — pick what to do with itFresh document · 8 slotsMeasureEditCurved face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__face_planar.svg b/docs/CAD/ux/mockups/fresh__face_planar.svg deleted file mode 100644 index a66268fc9a..0000000000 --- a/docs/CAD/ux/mockups/fresh__face_planar.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetPlanar face selected — pick what to do with itFresh document · 8 slotsSketchShift+SExtrudeShift+EReference4EditPlanar face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__face_planar__reference.svg b/docs/CAD/ux/mockups/fresh__face_planar__reference.svg deleted file mode 100644 index 9aa608a5e7..0000000000 --- a/docs/CAD/ux/mockups/fresh__face_planar__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsCoord SysShift+CMeasurePlaneShift+PAxisShift+APlanar face · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__none.svg b/docs/CAD/ux/mockups/fresh__none.svg deleted file mode 100644 index 56ac028b74..0000000000 --- a/docs/CAD/ux/mockups/fresh__none.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetClick a face or a reference plane, then a toolFresh document · 8 slotsSketchShift+SReference4Nothing selected \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__none__reference.svg b/docs/CAD/ux/mockups/fresh__none__reference.svg deleted file mode 100644 index 0492aa6de0..0000000000 --- a/docs/CAD/ux/mockups/fresh__none__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsCoord SysShift+CHelixPlaneShift+PAxisShift+ANothing selected · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_2ent.svg b/docs/CAD/ux/mockups/fresh__sk_2ent.svg deleted file mode 100644 index dd02122149..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_2ent.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetTwo sketch entities selected — pick what to do with itFresh document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Two sketch entities \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_2ent__create.svg b/docs/CAD/ux/mockups/fresh__sk_2ent__create.svg deleted file mode 100644 index 98e984bff2..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_2ent__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCreate — pick oneFresh document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Two sketch entities · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_2ent__dressup.svg b/docs/CAD/ux/mockups/fresh__sk_2ent__dressup.svg deleted file mode 100644 index 926594bb86..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_2ent__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetDress-up — pick oneFresh document · 8 slotsFilletFChamferHTwo sketch entities · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_2ent__modify.svg b/docs/CAD/ux/mockups/fresh__sk_2ent__modify.svg deleted file mode 100644 index 242332967e..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_2ent__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelExtendXTwo sketch entities · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_2ent__reference.svg b/docs/CAD/ux/mockups/fresh__sk_2ent__reference.svg deleted file mode 100644 index aa105c3a9e..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_2ent__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsConstructionQDimensionDConstrainKTwo sketch entities · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_arc.svg b/docs/CAD/ux/mockups/fresh__sk_arc.svg deleted file mode 100644 index 0712508411..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_arc.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetSketch arc or circle selected — pick what to do with itFresh document · 8 slotsCreate9OffsetOTrimTMirrorMMoveReference3Modify2Sketch arc or circle \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_arc__create.svg b/docs/CAD/ux/mockups/fresh__sk_arc__create.svg deleted file mode 100644 index d22d916e60..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_arc__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCreate — pick oneFresh document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch arc or circle · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_arc__modify.svg b/docs/CAD/ux/mockups/fresh__sk_arc__modify.svg deleted file mode 100644 index cf3fc2450f..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_arc__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelExtendXSketch arc or circle · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_arc__reference.svg b/docs/CAD/ux/mockups/fresh__sk_arc__reference.svg deleted file mode 100644 index cfbe0a1042..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_arc__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsConstructionQDimensionDConstrainKSketch arc or circle · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_line.svg b/docs/CAD/ux/mockups/fresh__sk_line.svg deleted file mode 100644 index e40d823e13..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_line.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetSketch line selected — pick what to do with itFresh document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_line__create.svg b/docs/CAD/ux/mockups/fresh__sk_line__create.svg deleted file mode 100644 index 1a6f7b561c..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_line__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCreate — pick oneFresh document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch line · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_line__dressup.svg b/docs/CAD/ux/mockups/fresh__sk_line__dressup.svg deleted file mode 100644 index 3fce662ba1..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_line__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetDress-up — pick oneFresh document · 8 slotsFilletFChamferHSketch line · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_line__modify.svg b/docs/CAD/ux/mockups/fresh__sk_line__modify.svg deleted file mode 100644 index 26318535ef..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_line__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelExtendXSketch line · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_line__reference.svg b/docs/CAD/ux/mockups/fresh__sk_line__reference.svg deleted file mode 100644 index 2abc91fc99..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_line__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsConstructionQDimensionDConstrainKSketch line · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_loop.svg b/docs/CAD/ux/mockups/fresh__sk_loop.svg deleted file mode 100644 index 58eaa9e4fb..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_loop.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetClosed sketch loop selected — pick what to do with itFresh document · 8 slotsAdd material5Modify2Closed sketch loop \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_loop__add.svg b/docs/CAD/ux/mockups/fresh__sk_loop__add.svg deleted file mode 100644 index c6e6c90701..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_loop__add.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetAdd material — pick oneFresh document · 8 slotsExtrudeShift+ERevolveShift+RSurface ExtrudeShift+GSurface RevolveSurface FillClosed sketch loop · Add material \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_loop__modify.svg b/docs/CAD/ux/mockups/fresh__sk_loop__modify.svg deleted file mode 100644 index 416e62d7cd..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_loop__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetModify — pick oneFresh document · 8 slotsDeleteDelEditClosed sketch loop · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_none.svg b/docs/CAD/ux/mockups/fresh__sk_none.svg deleted file mode 100644 index ce7604c47d..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_none.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetSketch, nothing picked selected — pick what to do with itFresh document · 8 slotsCreate9Reference2Sketch, nothing picked \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_none__create.svg b/docs/CAD/ux/mockups/fresh__sk_none__create.svg deleted file mode 100644 index 06994f9517..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_none__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCreate — pick oneFresh document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch, nothing picked · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_none__reference.svg b/docs/CAD/ux/mockups/fresh__sk_none__reference.svg deleted file mode 100644 index e4e558bbd9..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_none__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsDimensionDConstructionQSketch, nothing picked · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_point.svg b/docs/CAD/ux/mockups/fresh__sk_point.svg deleted file mode 100644 index 65acf94ac7..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_point.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetSketch point selected — pick what to do with itFresh document · 8 slotsCreate9MoveReference2DeleteDelSketch point \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_point__create.svg b/docs/CAD/ux/mockups/fresh__sk_point__create.svg deleted file mode 100644 index fbe094d147..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_point__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetCreate — pick oneFresh document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch point · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__sk_point__reference.svg b/docs/CAD/ux/mockups/fresh__sk_point__reference.svg deleted file mode 100644 index 8b02eba29f..0000000000 --- a/docs/CAD/ux/mockups/fresh__sk_point__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsDimensionDConstrainKSketch point · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__vertex.svg b/docs/CAD/ux/mockups/fresh__vertex.svg deleted file mode 100644 index 1e1661d6aa..0000000000 --- a/docs/CAD/ux/mockups/fresh__vertex.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetVertex selected — pick what to do with itFresh document · 8 slotsReference4Vertex \ No newline at end of file diff --git a/docs/CAD/ux/mockups/fresh__vertex__reference.svg b/docs/CAD/ux/mockups/fresh__vertex__reference.svg deleted file mode 100644 index aad3d46b6e..0000000000 --- a/docs/CAD/ux/mockups/fresh__vertex__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetReference — pick oneFresh document · 8 slotsCoord SysShift+CMeasurePlaneShift+PAxisShift+AVertex · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/gen_offer_mockups.py b/docs/CAD/ux/mockups/gen_offer_mockups.py deleted file mode 100644 index 6ddd4caae4..0000000000 --- a/docs/CAD/ux/mockups/gen_offer_mockups.py +++ /dev/null @@ -1,884 +0,0 @@ -#!/usr/bin/env python3 -"""Render the object-driven tool offer from docs/ux/tool_atlas.json. - -Fork-neutral on purpose: this file and everything it emits name no product, so the -two forks carry byte-identical copies (the charter itself is the only doc that -substitutes the product name). - -Every mockup in docs/ux/mockups/ and the review page docs/ux/offer_atlas.html are -generated by this script. Nothing is hand-drawn: the point of the offer is that a -tool's address never changes, and a human drawing 40-odd states by hand is exactly -how an address quietly changes. - - python3 docs/ux/mockups/gen_offer_mockups.py - -It fails loudly rather than rendering a broken map: an unknown slot, an unknown -selection id in `accepts`, or one address holding two different verbs for the same -selection is an error, not a warning. - -SVG on purpose. There is no rsvg/inkscape/cairosvg on the build box and -ImageMagick would rasterise through its own weak internal renderer; SVG renders -exactly in a browser at any zoom and stays diffable in git. Rasterise later if -slides need it. -""" - -import json -import re -import math -import os -import sys -from collections import defaultdict - -HERE = os.path.dirname(os.path.abspath(__file__)) -UX = os.path.dirname(HERE) -ATLAS = os.path.join(UX, "tool_atlas.json") - -# The reach target from the charter (L11 / 6.1): if the ring plus a real part do not -# fit here, the design has failed before it is built. -W, H = 1366, 768 - -C = { - "app": "#1a1d21", "chrome": "#23272d", "panel": "#20242a", "line": "#31363d", - "vp0": "#2f353d", "vp1": "#242930", "grid": "#333a43", - "text": "#e7ecf1", "muted": "#8e9aa7", "dim": "#5f6a76", - "top": "#93a1b1", "left": "#6f7b89", "right": "#5a6472", "edge": "#39414b", - "hi": "#f5a623", "hi_soft": "#f5a62333", "sheet": "#7fa8c9", - "ring": "#171a1e", "chip": "#2e343c", "chip_line": "#3c444e", - "key": "#454f5b", "accent": "#4f9bd9", "empty": "#2a2f36", -} - - -# ---------------------------------------------------------------- glyphs -# Each glyph draws inside a 24x24 box centred on (0,0) — i.e. -12..12. -def _g(body): - return body - - -GLYPHS = { - # model - "sketch": '', - "extrude": '', - "revolve": '', - "sweep": '', - "loft": '', - "thicken": '', - "rib": '', - "boolean": '', - "hole": '', - "thread": '', - "shell": '', - "cut": '', - "split": '', - "fillet": '', - "chamfer": '', - "draft": '', - "surf_off": '', - "pattern": '', - "mirror": '', - "pat_curve": '', - "move": '', - "mate": '', - "align": '', - "plane": '', - "axis": '', - "csys": '', - "helix": '', - "project": '', - "measure": '', - "mass": '', - "interfere": '', - "edit": '', - "del_face": '', - "colour": '', - "delete": '', - # sketch primitives - "sk_line": '', - "sk_rect": '', - "sk_circle": '', - "sk_arc": '', - "sk_slot": '', - "sk_ell": '', - "sk_spline": '', - "sk_poly": '', - "sk_point": '', - "sk_offset": '', - "sk_trim": '', - "sk_ext": '', - "sk_mir": '', - "sk_move": '', - "sk_dim": '', - "sk_lock": '', - "sk_constr": '', - # families (used when a slot holds more than one verb) - "fam_create": '', - "fam_add": '', - "fam_remove": '', - "fam_dressup": '', - "fam_repeat": '', - "fam_transform": '', - "fam_reference": '', - # Deliberately NOT the bare pencil: create/sketch already owns that shape and the two slots - # are adjacent (N and NW). A pencil over a solid reads as "change the thing that exists". - "fam_modify": '' - '', -} - -VERB_GLYPH = { - "sketch": "sketch", "extrude": "extrude", "revolve": "revolve", "sweep": "sweep", - "loft": "loft", "thicken": "thicken", "rib": "rib", "boolean": "boolean", - "surf_extrude": "extrude", "surf_revolve": "revolve", "surf_loft": "loft", - "surf_fill": "surf_off", "thicken_surf": "thicken", "hole": "hole", "thread": "thread", - "shell": "shell", "cut": "cut", "split": "split", "fillet": "fillet", - "chamfer": "chamfer", "draft": "draft", "surf_offset": "surf_off", - "pattern": "pattern", "mirror": "mirror", "pat_curve": "pat_curve", - "transform": "move", "mate": "mate", "align": "align", "plane": "plane", - "axis": "axis", "coordsys_v": "csys", "helix": "helix", "project": "project", - "measure": "measure", "mass_props": "mass", "interference": "interfere", - "edit_feature": "edit", "delete_face": "del_face", "colour": "colour", "delete": "delete", - "sk_line_t": "sk_line", "sk_rect": "sk_rect", "sk_circle": "sk_circle", - "sk_arc_t": "sk_arc", "sk_slot": "sk_slot", "sk_ellipse": "sk_ell", - "sk_spline": "sk_spline", "sk_polygon": "sk_poly", "sk_point_t": "sk_point", - "sk_offset": "sk_offset", "sk_trim": "sk_trim", "sk_fillet": "fillet", - "sk_chamfer": "chamfer", "sk_mirror": "sk_mir", "sk_move": "sk_move", - "sk_dimension": "sk_dim", "sk_constrain": "sk_lock", "sk_construct": "sk_constr", - "sk_extend": "sk_ext", "sk_delete": "delete", -} - - -def glyph(name, cx, cy, colour, scale=1.0, sw=1.6): - body = GLYPHS.get(name, '') - return (f'{body}') - - -# ---------------------------------------------------------------- iso scene -COS30, SIN30 = math.cos(math.radians(30)), math.sin(math.radians(30)) - - -def iso(x, y, z, ox, oy, s=1.0): - return (ox + (x - y) * COS30 * s, oy + (x + y) * SIN30 * s - z * s) - - -def poly(pts, fill, stroke=None, extra=""): - d = " ".join(f"{x:.1f},{y:.1f}" for x, y in pts) - # Only emit our own stroke-width when the caller has not supplied one: a duplicate - # attribute is last-one-wins in a browser but invalid markup, and it is exactly the - # kind of thing a stricter renderer refuses outright. - if stroke: - st = f' stroke="{stroke}"' + ("" if "stroke-width" in extra else ' stroke-width="1"') - else: - st = ' stroke="none"' - return f'' - - -def box(ox, oy, w=150, d=105, h=52, s=1.0, hi=None): - """Three visible faces of a box. `hi` names the face/edge to highlight.""" - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - top = [P(0, 0, h), P(w, 0, h), P(w, d, h), P(0, d, h)] - left = [P(0, d, h), P(w, d, h), P(w, d, 0), P(0, d, 0)] - right = [P(w, 0, h), P(w, d, h), P(w, d, 0), P(w, 0, 0)] - out = [ - poly(left, C["left"], C["edge"]), - poly(right, C["right"], C["edge"]), - poly(top, C["hi"] if hi == "top" else C["top"], C["edge"]), - ] - if hi == "top": - out.append(poly(top, "none", "#ffd07a", ' stroke-width="2.5"')) - if hi == "front_edge": - a, b = P(0, d, h), P(w, d, h) - out.append(f'') - if hi == "vertex": - v = P(w, d, h) - out.append(f'') - if hi == "whole": - out.append(poly(top, "none", "#ffd07a", ' stroke-width="2.5"')) - out.append(poly(left, "none", "#ffd07a", ' stroke-width="2.5"')) - out.append(poly(right, "none", "#ffd07a", ' stroke-width="2.5"')) - return "".join(out), P - - -def bore(P, cx, cy, h, r=22, s=1.0, hi=False): - """A cylindrical bore through the top face, drawn as ellipse + wall.""" - c = P(cx, cy, h) - rx, ry = r * COS30 * 2 * s, r * SIN30 * 2 * s - col = C["hi"] if hi else "#20252b" - wall = C["hi"] if hi == "wall" else "#3b434d" - return (f'' - f'' - + (f'' if hi else "")), c - - -def scene(kind, ox, oy): - """Return (svg, anchor) for a selection kind. Anchor = where the ring centres.""" - s = 1.0 - if kind == "origin": - # A document with nothing in it: the three origin planes are all there is to click. - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - g = poly([P(-70, -70, 0), P(70, -70, 0), P(70, 70, 0), P(-70, 70, 0)], - "#4f9bd91f", "#4f9bd977", ' stroke-width="1.5" stroke-dasharray="6 4"') - g += poly([P(-70, 0, -70), P(70, 0, -70), P(70, 0, 70), P(-70, 0, 70)], - "#e05c5c14", "#e05c5c66", ' stroke-width="1.5" stroke-dasharray="6 4"') - g += poly([P(0, -70, -70), P(0, 70, -70), P(0, 70, 70), P(0, -70, 70)], - "#5ce07a14", "#5ce07a66", ' stroke-width="1.5" stroke-dasharray="6 4"') - return g, P(0, 0, 0) - if kind == "empty": - g, P = box(ox, oy, s=s) - return g, P(75, 52, 26) - if kind == "box_top": - g, P = box(ox, oy, s=s, hi="top") - return g, P(75, 52, 52) - if kind == "box_whole": - g, P = box(ox, oy, s=s, hi="whole") - return g, P(75, 52, 26) - if kind == "box_edge": - g, P = box(ox, oy, s=s, hi="front_edge") - return g, P(75, 105, 52) - if kind == "box_vertex": - g, P = box(ox, oy, s=s, hi="vertex") - return g, P(150, 105, 52) - if kind in ("box_bore", "box_bore_rim"): - g, P = box(ox, oy, s=s) - b, c = bore(P, 75, 52, 52, hi=("wall" if kind == "box_bore" else True)) - return g + b, c - if kind == "box_fillet_face": - g, P = box(ox, oy, s=s) - a, b = P(0, 0, 52), P(0, 105, 52) - g += (f'') - return g, P(0, 52, 52) - if kind == "sheet": - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - pts = [P(0, 0, 40), P(150, 0, 55), P(150, 105, 30), P(0, 105, 18)] - return (poly(pts, C["sheet"], "#ffd07a", ' stroke-width="2.5" opacity="0.85"'), - P(75, 52, 36)) - if kind == "two_boxes": - g1, P1 = box(ox - 60, oy - 10, w=110, d=80, h=44, hi="whole") - g2, P2 = box(ox + 70, oy + 26, w=95, d=70, h=60, hi="whole") - return g1 + g2, (ox + 42, oy + 6) - if kind == "datum": - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - g, _ = box(ox, oy, s=s) - pts = [P(-25, -25, 70), P(175, -25, 70), P(175, 130, 70), P(-25, 130, 70)] - g += poly(pts, "#4f9bd933", C["hi"], ' stroke-width="2.5" stroke-dasharray="6 4"') - return g, P(75, 52, 70) - if kind == "axis": - g, P = box(ox, oy, s=s) - a, b = P(-30, 52, 52), P(180, 52, 52) - g += (f'') - return g, P(75, 52, 52) - if kind == "csys": - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - g, _ = box(ox, oy, s=s) - o = P(0, 0, 52) - for tgt, col in ((P(60, 0, 52), "#e05c5c"), (P(0, 60, 52), "#5ce07a"), (P(0, 0, 112), "#5c9ce0")): - g += (f'') - g += f'' - return g, o - if kind == "art": - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - g, _ = box(ox, oy, s=s) - c = P(75, 52, 52) - g += (f'' - f'ABC') - return g, c - if kind == "loop": - P = lambda x, y, z: iso(x, y, z, ox, oy, s) - g, _ = box(ox, oy, s=s) - pts = [P(28, 22, 52), P(122, 22, 52), P(122, 83, 52), P(28, 83, 52)] - g += poly(pts, C["hi_soft"], C["hi"], ' stroke-width="3"') - return g, P(75, 52, 52) - # --- sketch mode: flat, camera normal to the plane - gx, gy = ox - 30, oy - 10 - grid = "".join( - f'' - for i in range(11)) + "".join( - f'' - for i in range(16)) - if kind == "sk_empty": - return grid, (gx, gy) - if kind == "sk_line": - return (grid + f'' - f'' - f'', (gx, gy + 10)) - if kind == "sk_arc": - return (grid + f'', (gx, gy)) - if kind == "sk_point": - return (grid + f'', (gx, gy)) - if kind == "sk_two": - return (grid + f'' - f'', (gx, gy + 5)) - return grid, (gx, gy) - - -# ---------------------------------------------------------------- app chrome -def chrome(title, status, mode="model", empty_doc=False): - tabs = ["Prepare", "Preview", "Design"] - g = [f'', - f'' - f'' - f'', - f'', - f''] - x = 24 - for t in tabs: - on = (t == "Design") - g.append(f'{t}') - if on: - g.append(f'') - x += len(t) * 9 + 34 - # toolbar - g.append(f'' - f'') - fams = (["sketch", "extrude", "hole", "fillet", "pattern", "boolean", "plane", "move", "measure"] - if mode == "model" else - ["sk_line", "sk_rect", "sk_circle", "sk_arc", "sk_slot", "sk_spline", "sk_dim", "sk_trim", "sk_lock"]) - for i, gl in enumerate(fams): - cx = 40 + i * 46 - g.append(f'') - g.append(glyph(gl, cx, 68, C["muted"], 0.62, 1.7)) - # left rail - g.append(f'' - f'') - g.append(f'FEATURES') - # A "fresh document" mockup that shows six existing features is a lie, and it is the one - # state the group will look at hardest — it is the first-run picture (B5). - tree = ([] if empty_doc else - [("Sketch1", 0), ("Extrude1", 0), ("Hole1", 1), ("Fillet1", 1), ("Sketch2", 0), ("Extrude2", 0)]) - if empty_doc: - g.append(f'nothing yet') - for i, (n, ind) in enumerate(tree): - yy = 150 + i * 26 - g.append(glyph("sketch" if n.startswith("Sketch") else "extrude", 30 + ind * 14, yy - 4, C["dim"], 0.42, 1.8)) - g.append(f'{n}') - # status bar - g.append(f'' - f'{status}') - g.append(f'{title}') - return "".join(g) - - -# ---------------------------------------------------------------- the ring -def ring(cx, cy, items, capacity, sel_name, radius=134): - """items: dict angle_index -> (glyph, label, key, count) or None for an empty slot.""" - # The scrim is deliberately faint. §4.1: the offer "never blocks the view of what it acts - # on" — at 0.55 the disc swallowed the very face the ring was opened on, which is the rule - # failing in its own mockup. - g = [f'', - f'', - f''] - step = 360.0 / capacity - for i in range(capacity): - ang = math.radians(-90 + i * step) - px, py = cx + radius * math.cos(ang), cy + radius * math.sin(ang) - it = items.get(i) - if not it: - g.append(f'') - continue - gl, label, key, count = it - g.append(f'') - g.append(glyph(gl, px, py - 2, C["text"], 0.92, 1.7)) - g.append(f'{label}') - if key: - kw = 13 + len(key) * 6.6 - g.append(f'' - f'{key}') - if count and count > 1: - g.append(f'' - f'{count}') - # The selection name sits on the LOWER rim of the centre hole, not above it: above, it - # landed on top of the north slot's shortcut chip and hid it. Below, the pick stays - # visible through the hole and the pill is clear of the south chip at cy+104. - w = 13 + len(sel_name) * 6.9 - g.append(f'' - f'{sel_name}') - return "".join(g) - - -# ---------------------------------------------------------------- enumeration -def load(): - with open(ATLAS, encoding="utf-8") as f: - return json.load(f) - - -def validate(A): - slot_ids = {s["id"] for s in A["slots"]} - sel_ids = {s["id"] for s in A["selections"]} - errs = [] - seen = {} - for v in A["verbs"]: - if v["slot"] not in slot_ids: - errs.append(f'{v["id"]}: unknown slot {v["slot"]!r}') - for a in v["accepts"]: - if a not in sel_ids: - errs.append(f'{v["id"]}: accepts unknown selection {a!r}') - if v["id"] in seen: - errs.append(f'{v["id"]}: duplicate verb id') - seen[v["id"]] = v["slot"] - if errs: - sys.exit("tool_atlas.json is inconsistent:\n " + "\n ".join(errs)) - return slot_ids, sel_ids - - -def eligible(A, sel_id, doc): - sel = next(s for s in A["selections"] if s["id"] == sel_id) - out = [] - for v in A["verbs"]: - if v.get("mode", "model") != sel["mode"]: - continue - if sel_id not in v["accepts"]: - continue - n = v.get("needs") or {} - if n.get("bodies", 0) > doc["bodies"]: - continue - if n.get("sketches", 0) > doc["sketches"]: - continue - if n.get("sheet") and not doc["sheet"]: - continue - out.append(v) - return out - - -def by_slot(A, verbs): - order = [s["id"] for s in A["slots"]] - d = defaultdict(list) - for v in verbs: - d[v["slot"]].append(v) - return {k: d[k] for k in order if d[k]} - - -def ring_items(A, grouped): - order = [s["id"] for s in A["slots"]] - slot_label = {s["id"]: s["label"] for s in A["slots"]} - items = {} - for i, sid in enumerate(order): - vs = grouped.get(sid) - if not vs: - continue - if len(vs) == 1: - v = vs[0] - items[i] = (VERB_GLYPH.get(v["id"], "fam_" + sid), v["name"], v.get("key"), 1) - else: - items[i] = ("fam_" + sid, slot_label[sid], None, len(vs)) - return items - - -# ---------------------------------------------------------------- rendering -def menu(cx, cy, header, rows, submenu=None, sub_at=None): - """Vertical list form of the offer. rows: (glyph, name, key, count, enabled, reason). - - The invariant is unchanged — a verb has one permanent row index, and rows that do not - apply are DISABLED IN PLACE, never removed. What changes against the ring is what an - unavailable slot can say: an empty circle says nothing, a greyed row says its own name - and the reason it is grey, in the words the product already ships. - """ - RW, RH, HD = 324, 34, 38 - # The reason line is the whole point of a disabled row, so it must FIT: at 10px italic a - # glyph is ~4.9px, and anything past the box edge is a promise the layout does not keep. - fit = int((RW - 62) / 4.9) - x, y = cx + 26, cy - 30 - h = HD + len(rows) * RH + 10 - if y + h > H - 44: - y = max(100, H - 44 - h) - g = [f'', - f'', - f'{header.upper()}', - f''] - # a leader from the pick point to the menu, so the list is visibly ABOUT that geometry - g.insert(0, f'') - g.insert(0, f'') - for i, (gl, name, key, count, on, reason) in enumerate(rows): - ry = y + HD + i * RH - op = "1" if on else "0.34" - if on and i == 0: - g.append(f'') - g.append(f'') - g.append(glyph(gl, x + 26, ry + RH / 2, C["text"], 0.72, 1.7)) - g.append(f'{name}') - if key: - kw = 13 + len(key) * 6.6 - g.append(f'' - f'{key}') - elif count and count > 1: - g.append(f'') - g.append(f'{count}') - g.append('') - if not on and reason: - r = reason if len(reason) <= fit else reason[:fit - 1].rstrip(" ,—-") + "…" - g.append(f'{r}') - if submenu: - sy = y + HD + (sub_at or 0) * RH - 6 - sh = 12 + len(submenu) * RH - sx = x + RW + 8 - g.append(f'') - g.append(f'') - for i, (gl, name, key, _c, on, _r) in enumerate(submenu): - ry = sy + 6 + i * RH - g.append(f'') - g.append(glyph(gl, sx + 24, ry + RH / 2, C["text"], 0.72, 1.7)) - g.append(f'{name}') - if key: - kw = 13 + len(key) * 6.6 - g.append(f'' - f'{key}') - g.append('') - return "".join(g) - - -OVERFLOWS = [] # (selection, doc state, family, verbs that did not fit the sub-ring) - - -def svg_doc(inner): - return (f'{inner}') - - -def render_list_state(A, sel, doc, expand=None): - """The vertical-list form: every family row always present, in the same order, with the - ones that do not apply disabled and carrying their reason.""" - verbs = eligible(A, sel["id"], doc) - grouped = by_slot(A, verbs) - fresh = doc["bodies"] == 0 and doc["sketches"] == 0 - shape = "origin" if (fresh and sel["shape"] == "empty") else sel["shape"] - art, (ax, ay) = scene(shape, 620, 360) - rows, sub, sub_at = [], None, None - for idx, s in enumerate(A["slots"]): - vs = grouped.get(s["id"], []) - if len(vs) == 1: - v = vs[0] - rows.append((VERB_GLYPH.get(v["id"], "fam_" + s["id"]), v["name"], v.get("key"), 1, True, None)) - elif len(vs) > 1: - rows.append(("fam_" + s["id"], s["label"], None, len(vs), True, None)) - if expand == s["id"]: - sub_at = idx - sub = [(VERB_GLYPH.get(v["id"], "fam_" + s["id"]), v["name"], v.get("key"), 1, True, None) - for v in vs] - else: - # Disabled in place, with the product's own refusal text — the thing an empty - # slot in the ring could never say. - cands = [v for v in A["verbs"] - if v["slot"] == s["id"] and v.get("mode", "model") == sel["mode"]] - why = next((v["refusal"] for v in cands if v.get("refusal")), None) - rows.append(("fam_" + s["id"], s["label"], None, 0, False, why)) - status = ("Right-click the geometry to see what you can do with it" if not expand - else f'{sel["name"]} — pick one') - inner = chrome(f'{doc["name"]} · vertical list', status, sel["mode"], empty_doc=fresh) - inner += art - inner += menu(ax, ay, sel["name"], rows, sub, sub_at) - return svg_doc(inner) - - -def render_state(A, sel, doc, capacity=8, secondary=None): - verbs = eligible(A, sel["id"], doc) - grouped = by_slot(A, verbs) - anchor_x, anchor_y = 810, 400 - fresh = doc["bodies"] == 0 and doc["sketches"] == 0 - shape = "origin" if (fresh and sel["shape"] == "empty") else sel["shape"] - art, (ax, ay) = scene(shape, anchor_x - 90, anchor_y - 40) - if secondary: - vs = grouped[secondary] - # A sub-ring ANCHORS ON ITS PARENT'S DIRECTION. Fanning from north instead put Extrude - # at N — Create's address in the primary map — so the second level contradicted the - # first. Anchored, an address is two consistent strokes: Add material is NE, and the - # first verb of Add material is NE again. - base = [s["id"] for s in A["slots"]].index(secondary) - overflow = [] - if len(vs) > capacity: - # Do NOT wrap: (base+i) % capacity would quietly put verb 9 on top of verb 1, which - # is the invariant failing silently — the one outcome worse than an ugly ring. Show - # what fits, mark the rest, and report it. - overflow = vs[capacity - 1:] - vs = vs[:capacity - 1] - items = {(base + i) % capacity: - (VERB_GLYPH.get(v["id"], "fam_" + secondary), v["name"], v.get("key"), 1) - for i, v in enumerate(vs)} - if overflow: - items[(base + capacity - 1) % capacity] = ("fam_" + secondary, "More", None, len(overflow)) - OVERFLOWS.append((sel["name"], doc["id"], secondary, [v["name"] for v in overflow])) - label = next(s["label"] for s in A["slots"] if s["id"] == secondary) - sel_name = f'{sel["name"]} · {label}' - status = f'{label} — pick one' - else: - items = ring_items(A, grouped) - sel_name = sel["name"] - status = ("Click a face or a reference plane, then a tool" - if sel["id"] == "none" else f'{sel["name"]} selected — pick what to do with it') - inner = chrome(f'{doc["name"]} · {capacity} slots', status, sel["mode"], empty_doc=fresh) - inner += art - inner += ring(ax, ay, items, capacity, sel_name) - return svg_doc(inner), verbs, grouped - - -def main(): - A = load() - validate(A) - docs = {d["id"]: d for d in A["doc_states"]} - outdir = HERE - rows, files = [], [] - total_primary = total_secondary = 0 - fill_sum = fill_n = 0 - - for d in A["doc_states"]: - for sel in A["selections"]: - svg, verbs, grouped = render_state(A, sel, d) - name = f'{d["id"]}__{sel["id"]}.svg' - with open(os.path.join(outdir, name), "w", encoding="utf-8") as f: - f.write(svg) - files.append((name, f'{sel["name"]} — {d["name"]}')) - total_primary += 1 - fill_sum += len(grouped) - fill_n += 1 - secondaries = [] - for sid, vs in grouped.items(): - if len(vs) > 1: - s2, _, _ = render_state(A, sel, d, secondary=sid) - n2 = f'{d["id"]}__{sel["id"]}__{sid}.svg' - with open(os.path.join(outdir, n2), "w", encoding="utf-8") as f: - f.write(s2) - label = next(s["label"] for s in A["slots"] if s["id"] == sid) - files.append((n2, f'{sel["name"]} — {label} ring')) - secondaries.append(sid) - total_secondary += 1 - rows.append({ - "doc": d["name"], "sel": sel["name"], "mode": sel["mode"], - "verbs": len(verbs), "slots": len(grouped), - "second": len(secondaries), - "detail": {sid: [v["name"] for v in vs] for sid, vs in grouped.items()}, - }) - - # Form-factor comparison: the SAME state as a ring and as a vertical list. - for sid in ("face_planar", "edge_str", "body_solid", "sk_line", "none"): - sel = next(s for s in A["selections"] if s["id"] == sid) - d = docs["fresh"] if sid == "none" else docs["rich"] - with open(os.path.join(outdir, f"list__{sid}.svg"), "w", encoding="utf-8") as f: - f.write(render_list_state(A, sel, d)) - for sid, fam in (("face_planar", "add"), ("sk_none", "create")): - sel = next(s for s in A["selections"] if s["id"] == sid) - with open(os.path.join(outdir, f"list__{sid}__{fam}.svg"), "w", encoding="utf-8") as f: - f.write(render_list_state(A, sel, docs["rich"], expand=fam)) - - # comparison sheet: 8 vs 12 slots on the same three selections - for cap in (8, 12): - for sid in ("face_planar", "edge_str", "sk_line"): - sel = next(s for s in A["selections"] if s["id"] == sid) - svg, _, _ = render_state(A, sel, docs["rich"], capacity=cap) - n = f'cmp_{cap}__{sid}.svg' - with open(os.path.join(outdir, n), "w", encoding="utf-8") as f: - f.write(svg) - - write_atlas(A, rows, files, total_primary, total_secondary, - fill_sum / max(1, fill_n)) - print(f"{total_primary} primary + {total_secondary} secondary = " - f"{total_primary+total_secondary} offer states rendered") - print(f"mean populated slots per primary ring: {fill_sum/max(1,fill_n):.2f} of 8") - if OVERFLOWS: - seen = {(f, tuple(v)) for _, _, f, v in OVERFLOWS} - print(f"OVERFLOW: {len(OVERFLOWS)} sub-rings did not fit 8 slots — " - f"{len(seen)} distinct case(s):") - for f, v in sorted(seen): - print(f" {f}: {', '.join(v)} pushed behind 'More'") - - -def write_atlas(A, rows, files, n_prim, n_sec, mean_fill): - gui_missing = [v["name"] for v in A["verbs"] if not v.get("gui", True)] - esc = lambda s: (s.replace("&", "&").replace("<", "<").replace(">", ">")) - cards = "".join( - f'

    {esc(t)}
    {esc(t)}
    ' - for n, t in files) - cmp8 = "".join(f'
    ' - f'
    8 slots — {s}
    ' - for s in ("face_planar", "edge_str", "sk_line")) - cmp12 = "".join(f'
    ' - f'
    12 slots — {s}
    ' - for s in ("face_planar", "edge_str", "sk_line")) - if OVERFLOWS: - cases = sorted({(f, tuple(v)) for _, _, f, v in OVERFLOWS}) - over_html = ("Measured, not predicted: " + "; ".join( - f'the {esc(f)} sub-ring needs {8 + len(v)} addresses, so ' - f'{esc(", ".join(v))} {"is" if len(v) == 1 else "are"} pushed behind a “More” slot' - for f, v in cases) + - ". This is the decision the ring size actually turns on — either a verb moves to " - "another family, or the tail goes to a third level, or the ring is not eight. " - "It affects sketch mode only; every model-mode family fits.") - else: - over_html = "No sub-ring exceeds the ring capacity. Eight slots is sufficient everywhere." - trs = "".join( - f'{esc(r["sel"])}{esc(r["doc"])}{r["verbs"]}' - f'{r["slots"]}/8{r["second"]}' - f'{esc(" · ".join(k + ": " + ", ".join(v) for k, v in r["detail"].items()))}' - for r in rows) - html = f"""Design tab — offer atlas - -
    -

    The offer — atlas of every state

    -

    Every state of the object-driven tool offer, generated from - docs/ux/tool_atlas.json. The invariant under test: a verb has one address, that - address is the same in every selection where it appears, and slots that do not apply are drawn - empty rather than compacted.

    -
    -
    {len(A["verbs"])}verbs mapped
    -
    {len(A["selections"])}selection kinds
    -
    {n_prim}primary rings
    -
    {n_sec}secondary rings
    -
    {n_prim+n_sec}states total
    -
    {mean_fill:.1f}/8mean slots filled
    -
    {len(gui_missing)}verbs with no GUI yet
    -
    -
    -
    -

    Decision 0 — ring or vertical list

    -

    The live question. Both forms carry the SAME map and the same invariant — fixed order, never - re-sorted, nothing compacted; only the geometry differs. Left-click selects; right-click opens the - offer. What the list buys: a disabled row can state its own reason in the words the product - already ships, where an empty slot in a ring is mute; nine sketch primitives fit without an - overflow; shortcuts line up in a readable column; long translated names fit; and it is navigable - by arrow key and by screen reader, which a radial is not. What it costs: no equidistant flick - gesture, and travel to the last row is longer than to the nearest direction. Pairs below — - list first, the same state as a ring second.

    - -

    Decision 1 — ring capacity

    -

    The same three selections at eight and at twelve. Eight keeps 45° between neighbours, which is - the reliable eyes-free pointing threshold and maps 1:1 to the numpad; twelve buys direct addresses - for more verbs at 30° spacing and positions that stop being nameable.

    -
    {cmp8}
    -
    {cmp12}
    - -

    Decision 2 — one map or one per mode

    -

    These renders use ONE shared map: sketch verbs occupy the same eight families as model verbs, - so dress-up is south-east whether you picked a solid edge or a sketch line. Compare the sketch-mode - states below against the model-mode ones — if a verb that exists in both modes ever appears at two - addresses, the shared map has failed and the split map is the answer.

    - -

    The matrix — every selection, every document state

    -
    - - {trs} -
    SelectionDocumentVerbsSlots2nd ringsWhat lands where
    - -

    Overflow — the one place eight slots is not enough

    -

    {over_html}

    - -

    Not in the offer

    -

    {esc(", ".join(A["chrome_only"]["items"]))} — these act on the document, not on a selection, - so they stay in chrome. Verbs with kernel support but no GUI today, which still hold an address: - {esc(", ".join(gui_missing))}.

    - -

    All states

    -
    {cards}
    -
    -""" - with open(os.path.join(UX, "offer_atlas.html"), "w", encoding="utf-8") as f: - f.write(html) - - # A second, SELF-CONTAINED copy for review outside the repo: the artifact host blocks - # every external request, so relative would silently show nothing. Curated - # rather than all 113 states — the whole set inlined is 1.5 MB, which is a poor thing to - # send to the low-end machine this design is meant to serve (6.1). - def inline(name): - p = os.path.join(HERE, name) - if not os.path.exists(p): - return "" - s = open(p, encoding="utf-8").read() - return s.replace("{inline(n)}
    {esc(t)}
    ' - for n, t in picks if inline(n)) - head, _, tail = html.partition('

    All states

    ') - # The comparison grids in the head use , which resolves to nothing - # once the page is served on its own. Inline those too rather than shipping empty frames. - head = re.sub(r']*>', - lambda m: inline(m.group(1)), head) - inline_html = head + '

    The states

    \n
    ' + figs + '
    \n' - with open(os.path.join(UX, "offer_atlas_inline.html"), "w", encoding="utf-8") as f: - f.write(inline_html) - - -if __name__ == "__main__": - main() diff --git a/docs/CAD/ux/mockups/list__body_solid.svg b/docs/CAD/ux/mockups/list__body_solid.svg deleted file mode 100644 index ec3b3a84f4..0000000000 --- a/docs/CAD/ux/mockups/list__body_solid.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listSOLID BODYCreateClick a face or a reference plane in the viewport, t…Add materialCreate a sketch, or pick a solid face, firstRemove3Dress-up2Repeat3MoveShift+YReference3Modify3 \ No newline at end of file diff --git a/docs/CAD/ux/mockups/list__edge_str.svg b/docs/CAD/ux/mockups/list__edge_str.svg deleted file mode 100644 index 3942000d25..0000000000 --- a/docs/CAD/ux/mockups/list__edge_str.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listSTRAIGHT EDGECreateClick a face or a reference plane in the viewport, t…Add materialCreate a sketch, or pick a solid face, firstRemovePick a face or a plane to drill intoDress-up2Pattern on CurveTransformTransform needs a body — add or import one firstReference3ModifyDelete Face needs a body — add or import one first \ No newline at end of file diff --git a/docs/CAD/ux/mockups/list__face_planar.svg b/docs/CAD/ux/mockups/list__face_planar.svg deleted file mode 100644 index 5ff6892902..0000000000 --- a/docs/CAD/ux/mockups/list__face_planar.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listPLANAR FACESketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2 \ No newline at end of file diff --git a/docs/CAD/ux/mockups/list__face_planar__add.svg b/docs/CAD/ux/mockups/list__face_planar__add.svg deleted file mode 100644 index fb80f2a5c7..0000000000 --- a/docs/CAD/ux/mockups/list__face_planar__add.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face — pick oneWorking document · vertical listPLANAR FACESketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2ExtrudeShift+EThicken \ No newline at end of file diff --git a/docs/CAD/ux/mockups/list__none.svg b/docs/CAD/ux/mockups/list__none.svg deleted file mode 100644 index 1e68606383..0000000000 --- a/docs/CAD/ux/mockups/list__none.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESnothing yetRight-click the geometry to see what you can do with itFresh document · vertical listNOTHING SELECTEDSketchShift+SAdd materialCreate a sketch, or pick a solid face, firstRemovePick a face or a plane to drill intoDress-upPick an edge to roundRepeatCreate a solid body to pattern firstTransformTransform needs a body — add or import one firstReference4ModifyDelete Face needs a body — add or import one first \ No newline at end of file diff --git a/docs/CAD/ux/mockups/list__sk_line.svg b/docs/CAD/ux/mockups/list__sk_line.svg deleted file mode 100644 index 1b1c2e9cd2..0000000000 --- a/docs/CAD/ux/mockups/list__sk_line.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listSKETCH LINECreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2 \ No newline at end of file diff --git a/docs/CAD/ux/mockups/list__sk_none__create.svg b/docs/CAD/ux/mockups/list__sk_none__create.svg deleted file mode 100644 index 0b41268fa6..0000000000 --- a/docs/CAD/ux/mockups/list__sk_none__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch, nothing picked — pick oneWorking document · vertical listSKETCH, NOTHING PICKEDCreate9Add materialRemoveDress-upRepeatTransformReference2ModifyLineLRectangleRCircleCArcASlotSEllipseESplineBPolygonGPointP \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__art.svg b/docs/CAD/ux/mockups/rich__art.svg deleted file mode 100644 index e835ed1ce0..0000000000 --- a/docs/CAD/ux/mockups/rich__art.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Text / imported art selected — pick what to do with itWorking document · 8 slotsABCPatternShift+NMoveShift+YModify2Text / imported art \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__art__modify.svg b/docs/CAD/ux/mockups/rich__art__modify.svg deleted file mode 100644 index 4c8cdef2b5..0000000000 --- a/docs/CAD/ux/mockups/rich__art__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsABCDeleteDelEditText / imported art · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__bodies_2.svg b/docs/CAD/ux/mockups/rich__bodies_2.svg deleted file mode 100644 index a977b7f1de..0000000000 --- a/docs/CAD/ux/mockups/rich__bodies_2.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Two bodies selected — pick what to do with itWorking document · 8 slotsCombineShift+BMateReference2DeleteDelTwo bodies \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__bodies_2__reference.svg b/docs/CAD/ux/mockups/rich__bodies_2__reference.svg deleted file mode 100644 index 1bfbac0db0..0000000000 --- a/docs/CAD/ux/mockups/rich__bodies_2__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsMeasureInterferenceTwo bodies · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_sheet.svg b/docs/CAD/ux/mockups/rich__body_sheet.svg deleted file mode 100644 index a1cc27808d..0000000000 --- a/docs/CAD/ux/mockups/rich__body_sheet.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sheet body selected — pick what to do with itWorking document · 8 slotsThicken SurfaceSurface OffsetMoveShift+YMeasureModify3Sheet body \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_sheet__modify.svg b/docs/CAD/ux/mockups/rich__body_sheet__modify.svg deleted file mode 100644 index 7ce8dbee54..0000000000 --- a/docs/CAD/ux/mockups/rich__body_sheet__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsColourDeleteDelEditSheet body · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_solid.svg b/docs/CAD/ux/mockups/rich__body_solid.svg deleted file mode 100644 index e958532347..0000000000 --- a/docs/CAD/ux/mockups/rich__body_solid.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Solid body selected — pick what to do with itWorking document · 8 slotsRemove3Dress-up2Repeat3MoveShift+YReference3Modify3Solid body \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_solid__dressup.svg b/docs/CAD/ux/mockups/rich__body_solid__dressup.svg deleted file mode 100644 index 7a9ceed334..0000000000 --- a/docs/CAD/ux/mockups/rich__body_solid__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Dress-up — pick oneWorking document · 8 slotsFilletShift+FChamferSolid body · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_solid__modify.svg b/docs/CAD/ux/mockups/rich__body_solid__modify.svg deleted file mode 100644 index 7a6340d40f..0000000000 --- a/docs/CAD/ux/mockups/rich__body_solid__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsColourDeleteDelEditSolid body · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_solid__reference.svg b/docs/CAD/ux/mockups/rich__body_solid__reference.svg deleted file mode 100644 index 27639836c6..0000000000 --- a/docs/CAD/ux/mockups/rich__body_solid__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsMassProjectMeasureSolid body · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_solid__remove.svg b/docs/CAD/ux/mockups/rich__body_solid__remove.svg deleted file mode 100644 index edbddb7115..0000000000 --- a/docs/CAD/ux/mockups/rich__body_solid__remove.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Remove — pick oneWorking document · 8 slotsShellShift+KCutShift+XSplitSolid body · Remove \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__body_solid__repeat.svg b/docs/CAD/ux/mockups/rich__body_solid__repeat.svg deleted file mode 100644 index 74dd67c7de..0000000000 --- a/docs/CAD/ux/mockups/rich__body_solid__repeat.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Repeat — pick oneWorking document · 8 slotsPatternShift+NMirrorShift+ZPattern on CurveSolid body · Repeat \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__coordsys.svg b/docs/CAD/ux/mockups/rich__coordsys.svg deleted file mode 100644 index 8cef106106..0000000000 --- a/docs/CAD/ux/mockups/rich__coordsys.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Coordinate system selected — pick what to do with itWorking document · 8 slotsMateModify2Coordinate system \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__coordsys__modify.svg b/docs/CAD/ux/mockups/rich__coordsys__modify.svg deleted file mode 100644 index 92eb28d3dc..0000000000 --- a/docs/CAD/ux/mockups/rich__coordsys__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelEditCoordinate system · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__datum_axis.svg b/docs/CAD/ux/mockups/rich__datum_axis.svg deleted file mode 100644 index c9da648ab7..0000000000 --- a/docs/CAD/ux/mockups/rich__datum_axis.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Datum axis selected — pick what to do with itWorking document · 8 slotsModify2Datum axis \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__datum_axis__modify.svg b/docs/CAD/ux/mockups/rich__datum_axis__modify.svg deleted file mode 100644 index fd0ae80c52..0000000000 --- a/docs/CAD/ux/mockups/rich__datum_axis__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelEditDatum axis · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__datum_plane.svg b/docs/CAD/ux/mockups/rich__datum_plane.svg deleted file mode 100644 index 8ef88bd9a3..0000000000 --- a/docs/CAD/ux/mockups/rich__datum_plane.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Datum plane selected — pick what to do with itWorking document · 8 slotsSketchShift+SRemove2MirrorShift+ZReference3Modify2Datum plane \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__datum_plane__modify.svg b/docs/CAD/ux/mockups/rich__datum_plane__modify.svg deleted file mode 100644 index 39e5cad41e..0000000000 --- a/docs/CAD/ux/mockups/rich__datum_plane__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelEditDatum plane · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__datum_plane__reference.svg b/docs/CAD/ux/mockups/rich__datum_plane__reference.svg deleted file mode 100644 index bae3119b6b..0000000000 --- a/docs/CAD/ux/mockups/rich__datum_plane__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsProjectPlaneShift+PHelixDatum plane · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__datum_plane__remove.svg b/docs/CAD/ux/mockups/rich__datum_plane__remove.svg deleted file mode 100644 index c98b33ad31..0000000000 --- a/docs/CAD/ux/mockups/rich__datum_plane__remove.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Remove — pick oneWorking document · 8 slotsHoleShift+HCutShift+XDatum plane · Remove \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__edge_circ.svg b/docs/CAD/ux/mockups/rich__edge_circ.svg deleted file mode 100644 index 1d07291623..0000000000 --- a/docs/CAD/ux/mockups/rich__edge_circ.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Circular edge selected — pick what to do with itWorking document · 8 slotsThreadShift+TDress-up2MeasureCircular edge \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__edge_circ__dressup.svg b/docs/CAD/ux/mockups/rich__edge_circ__dressup.svg deleted file mode 100644 index b315fc2e22..0000000000 --- a/docs/CAD/ux/mockups/rich__edge_circ__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Dress-up — pick oneWorking document · 8 slotsFilletShift+FChamferCircular edge · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__edge_str.svg b/docs/CAD/ux/mockups/rich__edge_str.svg deleted file mode 100644 index 2c41ce74fc..0000000000 --- a/docs/CAD/ux/mockups/rich__edge_str.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Straight edge selected — pick what to do with itWorking document · 8 slotsDress-up2Pattern on CurveReference3Straight edge \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__edge_str__dressup.svg b/docs/CAD/ux/mockups/rich__edge_str__dressup.svg deleted file mode 100644 index 8e674c044f..0000000000 --- a/docs/CAD/ux/mockups/rich__edge_str__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Dress-up — pick oneWorking document · 8 slotsFilletShift+FChamferStraight edge · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__edge_str__reference.svg b/docs/CAD/ux/mockups/rich__edge_str__reference.svg deleted file mode 100644 index bc99ca6a13..0000000000 --- a/docs/CAD/ux/mockups/rich__edge_str__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsMeasurePlaneShift+PAxisShift+AStraight edge · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_cyl.svg b/docs/CAD/ux/mockups/rich__face_cyl.svg deleted file mode 100644 index 3c2860373f..0000000000 --- a/docs/CAD/ux/mockups/rich__face_cyl.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Cylindrical face selected — pick what to do with itWorking document · 8 slotsThreadShift+TReference3Modify2Cylindrical face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_cyl__modify.svg b/docs/CAD/ux/mockups/rich__face_cyl__modify.svg deleted file mode 100644 index 9f573682ad..0000000000 --- a/docs/CAD/ux/mockups/rich__face_cyl__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDelete FaceEditCylindrical face · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_cyl__reference.svg b/docs/CAD/ux/mockups/rich__face_cyl__reference.svg deleted file mode 100644 index a62be81de8..0000000000 --- a/docs/CAD/ux/mockups/rich__face_cyl__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsMeasureAxisShift+AHelixCylindrical face · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_other.svg b/docs/CAD/ux/mockups/rich__face_other.svg deleted file mode 100644 index e5cff4eee5..0000000000 --- a/docs/CAD/ux/mockups/rich__face_other.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Curved face selected — pick what to do with itWorking document · 8 slotsThickenDraftShift+DMeasureModify2Curved face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_other__modify.svg b/docs/CAD/ux/mockups/rich__face_other__modify.svg deleted file mode 100644 index 8b5fcfd63f..0000000000 --- a/docs/CAD/ux/mockups/rich__face_other__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDelete FaceEditCurved face · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar.svg b/docs/CAD/ux/mockups/rich__face_planar.svg deleted file mode 100644 index 5a58e2fb72..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 8 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar__add.svg b/docs/CAD/ux/mockups/rich__face_planar__add.svg deleted file mode 100644 index 82acc85038..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar__add.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Add material — pick oneWorking document · 8 slotsExtrudeShift+EThickenPlanar face · Add material \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar__dressup.svg b/docs/CAD/ux/mockups/rich__face_planar__dressup.svg deleted file mode 100644 index 8d32b9f212..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Dress-up — pick oneWorking document · 8 slotsFilletShift+FChamferDraftShift+DPlanar face · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar__modify.svg b/docs/CAD/ux/mockups/rich__face_planar__modify.svg deleted file mode 100644 index bffd218131..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDelete FaceEditPlanar face · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar__reference.svg b/docs/CAD/ux/mockups/rich__face_planar__reference.svg deleted file mode 100644 index 4682f1e7b1..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsCoord SysShift+CProjectMeasurePlaneShift+PAxisShift+APlanar face · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar__remove.svg b/docs/CAD/ux/mockups/rich__face_planar__remove.svg deleted file mode 100644 index b77a5a0c50..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar__remove.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Remove — pick oneWorking document · 8 slotsHoleShift+HShellShift+KPlanar face · Remove \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__face_planar__transform.svg b/docs/CAD/ux/mockups/rich__face_planar__transform.svg deleted file mode 100644 index 2e27983c8b..0000000000 --- a/docs/CAD/ux/mockups/rich__face_planar__transform.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Transform — pick oneWorking document · 8 slotsMateAlign toPlanar face · Transform \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__none.svg b/docs/CAD/ux/mockups/rich__none.svg deleted file mode 100644 index 4e8a9dc640..0000000000 --- a/docs/CAD/ux/mockups/rich__none.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Click a face or a reference plane, then a toolWorking document · 8 slotsSketchShift+SReference4Nothing selected \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__none__reference.svg b/docs/CAD/ux/mockups/rich__none__reference.svg deleted file mode 100644 index 14c47c4b27..0000000000 --- a/docs/CAD/ux/mockups/rich__none__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsCoord SysShift+CHelixPlaneShift+PAxisShift+ANothing selected · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_2ent.svg b/docs/CAD/ux/mockups/rich__sk_2ent.svg deleted file mode 100644 index 738b01ecf2..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_2ent.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Two sketch entities selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Two sketch entities \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_2ent__create.svg b/docs/CAD/ux/mockups/rich__sk_2ent__create.svg deleted file mode 100644 index 6d27ffa34a..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_2ent__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Two sketch entities · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_2ent__dressup.svg b/docs/CAD/ux/mockups/rich__sk_2ent__dressup.svg deleted file mode 100644 index 929520b8d2..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_2ent__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Dress-up — pick oneWorking document · 8 slotsFilletFChamferHTwo sketch entities · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_2ent__modify.svg b/docs/CAD/ux/mockups/rich__sk_2ent__modify.svg deleted file mode 100644 index d8be7c2d4f..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_2ent__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelExtendXTwo sketch entities · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_2ent__reference.svg b/docs/CAD/ux/mockups/rich__sk_2ent__reference.svg deleted file mode 100644 index 9ffc2592a8..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_2ent__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsConstructionQDimensionDConstrainKTwo sketch entities · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_arc.svg b/docs/CAD/ux/mockups/rich__sk_arc.svg deleted file mode 100644 index d470369f1a..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_arc.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch arc or circle selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTMirrorMMoveReference3Modify2Sketch arc or circle \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_arc__create.svg b/docs/CAD/ux/mockups/rich__sk_arc__create.svg deleted file mode 100644 index 1f901596ec..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_arc__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch arc or circle · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_arc__modify.svg b/docs/CAD/ux/mockups/rich__sk_arc__modify.svg deleted file mode 100644 index 52f9d7d3e5..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_arc__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelExtendXSketch arc or circle · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_arc__reference.svg b/docs/CAD/ux/mockups/rich__sk_arc__reference.svg deleted file mode 100644 index 53e949c184..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_arc__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsConstructionQDimensionDConstrainKSketch arc or circle · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_line.svg b/docs/CAD/ux/mockups/rich__sk_line.svg deleted file mode 100644 index 4ca4000d59..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_line.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_line__create.svg b/docs/CAD/ux/mockups/rich__sk_line__create.svg deleted file mode 100644 index c37f2d0e45..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_line__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch line · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_line__dressup.svg b/docs/CAD/ux/mockups/rich__sk_line__dressup.svg deleted file mode 100644 index 29e86762d2..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_line__dressup.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Dress-up — pick oneWorking document · 8 slotsFilletFChamferHSketch line · Dress-up \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_line__modify.svg b/docs/CAD/ux/mockups/rich__sk_line__modify.svg deleted file mode 100644 index b9775b7d94..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_line__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelExtendXSketch line · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_line__reference.svg b/docs/CAD/ux/mockups/rich__sk_line__reference.svg deleted file mode 100644 index 6eb1ef621f..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_line__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsConstructionQDimensionDConstrainKSketch line · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_loop.svg b/docs/CAD/ux/mockups/rich__sk_loop.svg deleted file mode 100644 index c07d1c09c3..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_loop.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Closed sketch loop selected — pick what to do with itWorking document · 8 slotsAdd material8PatternShift+NModify2Closed sketch loop \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_loop__add.svg b/docs/CAD/ux/mockups/rich__sk_loop__add.svg deleted file mode 100644 index bf9f429b81..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_loop__add.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Add material — pick oneWorking document · 8 slotsSurface FillExtrudeShift+ERevolveShift+RSweepShift+WLoftShift+LSurface ExtrudeShift+GSurface RevolveSurface LoftClosed sketch loop · Add material \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_loop__modify.svg b/docs/CAD/ux/mockups/rich__sk_loop__modify.svg deleted file mode 100644 index c466379c9e..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_loop__modify.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Modify — pick oneWorking document · 8 slotsDeleteDelEditClosed sketch loop · Modify \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_none.svg b/docs/CAD/ux/mockups/rich__sk_none.svg deleted file mode 100644 index ffe49ab9a0..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_none.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch, nothing picked selected — pick what to do with itWorking document · 8 slotsCreate9Reference2Sketch, nothing picked \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_none__create.svg b/docs/CAD/ux/mockups/rich__sk_none__create.svg deleted file mode 100644 index c9a2bc698d..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_none__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch, nothing picked · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_none__reference.svg b/docs/CAD/ux/mockups/rich__sk_none__reference.svg deleted file mode 100644 index cc9d465fa4..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_none__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsDimensionDConstructionQSketch, nothing picked · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_point.svg b/docs/CAD/ux/mockups/rich__sk_point.svg deleted file mode 100644 index f089787bca..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_point.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch point selected — pick what to do with itWorking document · 8 slotsCreate9MoveReference2DeleteDelSketch point \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_point__create.svg b/docs/CAD/ux/mockups/rich__sk_point__create.svg deleted file mode 100644 index 5d2070a6c5..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_point__create.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch point · Create \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__sk_point__reference.svg b/docs/CAD/ux/mockups/rich__sk_point__reference.svg deleted file mode 100644 index e090382a24..0000000000 --- a/docs/CAD/ux/mockups/rich__sk_point__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsDimensionDConstrainKSketch point · Reference \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__vertex.svg b/docs/CAD/ux/mockups/rich__vertex.svg deleted file mode 100644 index f37bbd7d47..0000000000 --- a/docs/CAD/ux/mockups/rich__vertex.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Vertex selected — pick what to do with itWorking document · 8 slotsReference4Vertex \ No newline at end of file diff --git a/docs/CAD/ux/mockups/rich__vertex__reference.svg b/docs/CAD/ux/mockups/rich__vertex__reference.svg deleted file mode 100644 index ceabba032e..0000000000 --- a/docs/CAD/ux/mockups/rich__vertex__reference.svg +++ /dev/null @@ -1 +0,0 @@ -PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsCoord SysShift+CMeasurePlaneShift+PAxisShift+AVertex · Reference \ No newline at end of file diff --git a/docs/CAD/ux/offer_atlas.html b/docs/CAD/ux/offer_atlas.html deleted file mode 100644 index 35c4f5860c..0000000000 --- a/docs/CAD/ux/offer_atlas.html +++ /dev/null @@ -1,78 +0,0 @@ -Design tab — offer atlas - -
    -

    The offer — atlas of every state

    -

    Every state of the object-driven tool offer, generated from - docs/ux/tool_atlas.json. The invariant under test: a verb has one address, that - address is the same in every selection where it appears, and slots that do not apply are drawn - empty rather than compacted.

    -
    -
    60verbs mapped
    -
    20selection kinds
    -
    40primary rings
    -
    73secondary rings
    -
    113states total
    -
    3.5/8mean slots filled
    -
    5verbs with no GUI yet
    -
    -
    -
    -

    Decision 0 — ring or vertical list

    -

    The live question. Both forms carry the SAME map and the same invariant — fixed order, never - re-sorted, nothing compacted; only the geometry differs. Left-click selects; right-click opens the - offer. What the list buys: a disabled row can state its own reason in the words the product - already ships, where an empty slot in a ring is mute; nine sketch primitives fit without an - overflow; shortcuts line up in a readable column; long translated names fit; and it is navigable - by arrow key and by screen reader, which a radial is not. What it costs: no equidistant flick - gesture, and travel to the last row is longer than to the nearest direction. Pairs below — - list first, the same state as a ring second.

    - -

    Decision 1 — ring capacity

    -

    The same three selections at eight and at twelve. Eight keeps 45° between neighbours, which is - the reliable eyes-free pointing threshold and maps 1:1 to the numpad; twelve buys direct addresses - for more verbs at 30° spacing and positions that stop being nameable.

    -
    8 slots — face_planar
    8 slots — edge_str
    8 slots — sk_line
    -
    12 slots — face_planar
    12 slots — edge_str
    12 slots — sk_line
    - -

    Decision 2 — one map or one per mode

    -

    These renders use ONE shared map: sketch verbs occupy the same eight families as model verbs, - so dress-up is south-east whether you picked a solid edge or a sketch line. Compare the sketch-mode - states below against the model-mode ones — if a verb that exists in both modes ever appears at two - addresses, the shared map has failed and the split map is the answer.

    - -

    The matrix — every selection, every document state

    -
    - - -
    SelectionDocumentVerbsSlots2nd ringsWhat lands where
    Nothing selectedWorking document52/81create: Sketch · reference: Plane, Axis, Coord Sys, Helix
    Planar faceWorking document188/86create: Sketch · add: Extrude, Thicken · remove: Hole, Shell · dressup: Fillet, Chamfer, Draft · repeat: Pattern · transform: Mate, Align to · reference: Plane, Axis, Coord Sys, Project, Measure · modify: Edit, Delete Face
    Cylindrical faceWorking document63/82remove: Thread · reference: Axis, Helix, Measure · modify: Edit, Delete Face
    Curved faceWorking document54/81add: Thicken · dressup: Draft · reference: Measure · modify: Edit, Delete Face
    Straight edgeWorking document63/82dressup: Fillet, Chamfer · repeat: Pattern on Curve · reference: Plane, Axis, Measure
    Circular edgeWorking document43/81remove: Thread · dressup: Fillet, Chamfer · reference: Measure
    VertexWorking document41/81reference: Plane, Axis, Coord Sys, Measure
    Solid bodyWorking document156/85remove: Shell, Cut, Split · dressup: Fillet, Chamfer · repeat: Pattern, Mirror, Pattern on Curve · transform: Move · reference: Project, Measure, Mass · modify: Edit, Colour, Delete
    Sheet bodyWorking document75/81add: Thicken Surface · dressup: Surface Offset · transform: Move · reference: Measure · modify: Edit, Colour, Delete
    Two bodiesWorking document54/81add: Combine · transform: Mate · reference: Measure, Interference · modify: Delete
    Datum planeWorking document95/83create: Sketch · remove: Hole, Cut · repeat: Mirror · reference: Plane, Helix, Project · modify: Edit, Delete
    Datum axisWorking document21/81modify: Edit, Delete
    Coordinate systemWorking document32/81transform: Mate · modify: Edit, Delete
    Text / imported artWorking document43/81repeat: Pattern · transform: Move · modify: Edit, Delete
    Closed sketch loopWorking document113/82add: Extrude, Revolve, Sweep, Loft, Surface Extrude, Surface Revolve, Surface Loft, Surface Fill · repeat: Pattern · modify: Edit, Delete
    Sketch, nothing pickedWorking document112/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · reference: Dimension, Construction
    Sketch lineWorking document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch arc or circleWorking document187/83create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch pointWorking document134/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · transform: Move · reference: Dimension, Constrain · modify: Delete
    Two sketch entitiesWorking document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Nothing selectedFresh document52/81create: Sketch · reference: Plane, Axis, Coord Sys, Helix
    Planar faceFresh document74/81create: Sketch · add: Extrude · reference: Plane, Axis, Coord Sys, Measure · modify: Edit
    Cylindrical faceFresh document42/81reference: Axis, Helix, Measure · modify: Edit
    Curved faceFresh document22/80reference: Measure · modify: Edit
    Straight edgeFresh document31/81reference: Plane, Axis, Measure
    Circular edgeFresh document11/80reference: Measure
    VertexFresh document41/81reference: Plane, Axis, Coord Sys, Measure
    Solid bodyFresh document32/81reference: Measure · modify: Edit, Delete
    Sheet bodyFresh document32/81reference: Measure · modify: Edit, Delete
    Two bodiesFresh document22/80reference: Measure · modify: Delete
    Datum planeFresh document53/82create: Sketch · reference: Plane, Helix · modify: Edit, Delete
    Datum axisFresh document21/81modify: Edit, Delete
    Coordinate systemFresh document21/81modify: Edit, Delete
    Text / imported artFresh document21/81modify: Edit, Delete
    Closed sketch loopFresh document72/82add: Extrude, Revolve, Surface Extrude, Surface Revolve, Surface Fill · modify: Edit, Delete
    Sketch, nothing pickedFresh document112/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · reference: Dimension, Construction
    Sketch lineFresh document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch arc or circleFresh document187/83create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch pointFresh document134/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · transform: Move · reference: Dimension, Constrain · modify: Delete
    Two sketch entitiesFresh document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    - -

    Overflow — the one place eight slots is not enough

    -

    Measured, not predicted: the create sub-ring needs 10 addresses, so Polygon, Point are pushed behind a “More” slot. This is the decision the ring size actually turns on — either a verb moves to another family, or the tail goes to a third level, or the ring is not eight. It affects sketch mode only; every model-mode family fits.

    - -

    Not in the offer

    -

    Import STEP, Import mesh, Text, SVG, Export STEP, Commit to Plate, Undo, Redo, Variables, Section view, Origin planes, World axes — these act on the document, not on a selection, - so they stay in chrome. Verbs with kernel support but no GUI today, which still hold an address: - Split, Pattern on Curve, Align to, Measure, Interference.

    - -

    All states

    -
    Nothing selected — Working document
    Nothing selected — Working document
    Nothing selected — Reference ring
    Nothing selected — Reference ring
    Planar face — Working document
    Planar face — Working document
    Planar face — Add material ring
    Planar face — Add material ring
    Planar face — Remove ring
    Planar face — Remove ring
    Planar face — Dress-up ring
    Planar face — Dress-up ring
    Planar face — Transform ring
    Planar face — Transform ring
    Planar face — Reference ring
    Planar face — Reference ring
    Planar face — Modify ring
    Planar face — Modify ring
    Cylindrical face — Working document
    Cylindrical face — Working document
    Cylindrical face — Reference ring
    Cylindrical face — Reference ring
    Cylindrical face — Modify ring
    Cylindrical face — Modify ring
    Curved face — Working document
    Curved face — Working document
    Curved face — Modify ring
    Curved face — Modify ring
    Straight edge — Working document
    Straight edge — Working document
    Straight edge — Dress-up ring
    Straight edge — Dress-up ring
    Straight edge — Reference ring
    Straight edge — Reference ring
    Circular edge — Working document
    Circular edge — Working document
    Circular edge — Dress-up ring
    Circular edge — Dress-up ring
    Vertex — Working document
    Vertex — Working document
    Vertex — Reference ring
    Vertex — Reference ring
    Solid body — Working document
    Solid body — Working document
    Solid body — Remove ring
    Solid body — Remove ring
    Solid body — Dress-up ring
    Solid body — Dress-up ring
    Solid body — Repeat ring
    Solid body — Repeat ring
    Solid body — Reference ring
    Solid body — Reference ring
    Solid body — Modify ring
    Solid body — Modify ring
    Sheet body — Working document
    Sheet body — Working document
    Sheet body — Modify ring
    Sheet body — Modify ring
    Two bodies — Working document
    Two bodies — Working document
    Two bodies — Reference ring
    Two bodies — Reference ring
    Datum plane — Working document
    Datum plane — Working document
    Datum plane — Remove ring
    Datum plane — Remove ring
    Datum plane — Reference ring
    Datum plane — Reference ring
    Datum plane — Modify ring
    Datum plane — Modify ring
    Datum axis — Working document
    Datum axis — Working document
    Datum axis — Modify ring
    Datum axis — Modify ring
    Coordinate system — Working document
    Coordinate system — Working document
    Coordinate system — Modify ring
    Coordinate system — Modify ring
    Text / imported art — Working document
    Text / imported art — Working document
    Text / imported art — Modify ring
    Text / imported art — Modify ring
    Closed sketch loop — Working document
    Closed sketch loop — Working document
    Closed sketch loop — Add material ring
    Closed sketch loop — Add material ring
    Closed sketch loop — Modify ring
    Closed sketch loop — Modify ring
    Sketch, nothing picked — Working document
    Sketch, nothing picked — Working document
    Sketch, nothing picked — Create ring
    Sketch, nothing picked — Create ring
    Sketch, nothing picked — Reference ring
    Sketch, nothing picked — Reference ring
    Sketch line — Working document
    Sketch line — Working document
    Sketch line — Create ring
    Sketch line — Create ring
    Sketch line — Dress-up ring
    Sketch line — Dress-up ring
    Sketch line — Reference ring
    Sketch line — Reference ring
    Sketch line — Modify ring
    Sketch line — Modify ring
    Sketch arc or circle — Working document
    Sketch arc or circle — Working document
    Sketch arc or circle — Create ring
    Sketch arc or circle — Create ring
    Sketch arc or circle — Reference ring
    Sketch arc or circle — Reference ring
    Sketch arc or circle — Modify ring
    Sketch arc or circle — Modify ring
    Sketch point — Working document
    Sketch point — Working document
    Sketch point — Create ring
    Sketch point — Create ring
    Sketch point — Reference ring
    Sketch point — Reference ring
    Two sketch entities — Working document
    Two sketch entities — Working document
    Two sketch entities — Create ring
    Two sketch entities — Create ring
    Two sketch entities — Dress-up ring
    Two sketch entities — Dress-up ring
    Two sketch entities — Reference ring
    Two sketch entities — Reference ring
    Two sketch entities — Modify ring
    Two sketch entities — Modify ring
    Nothing selected — Fresh document
    Nothing selected — Fresh document
    Nothing selected — Reference ring
    Nothing selected — Reference ring
    Planar face — Fresh document
    Planar face — Fresh document
    Planar face — Reference ring
    Planar face — Reference ring
    Cylindrical face — Fresh document
    Cylindrical face — Fresh document
    Cylindrical face — Reference ring
    Cylindrical face — Reference ring
    Curved face — Fresh document
    Curved face — Fresh document
    Straight edge — Fresh document
    Straight edge — Fresh document
    Straight edge — Reference ring
    Straight edge — Reference ring
    Circular edge — Fresh document
    Circular edge — Fresh document
    Vertex — Fresh document
    Vertex — Fresh document
    Vertex — Reference ring
    Vertex — Reference ring
    Solid body — Fresh document
    Solid body — Fresh document
    Solid body — Modify ring
    Solid body — Modify ring
    Sheet body — Fresh document
    Sheet body — Fresh document
    Sheet body — Modify ring
    Sheet body — Modify ring
    Two bodies — Fresh document
    Two bodies — Fresh document
    Datum plane — Fresh document
    Datum plane — Fresh document
    Datum plane — Reference ring
    Datum plane — Reference ring
    Datum plane — Modify ring
    Datum plane — Modify ring
    Datum axis — Fresh document
    Datum axis — Fresh document
    Datum axis — Modify ring
    Datum axis — Modify ring
    Coordinate system — Fresh document
    Coordinate system — Fresh document
    Coordinate system — Modify ring
    Coordinate system — Modify ring
    Text / imported art — Fresh document
    Text / imported art — Fresh document
    Text / imported art — Modify ring
    Text / imported art — Modify ring
    Closed sketch loop — Fresh document
    Closed sketch loop — Fresh document
    Closed sketch loop — Add material ring
    Closed sketch loop — Add material ring
    Closed sketch loop — Modify ring
    Closed sketch loop — Modify ring
    Sketch, nothing picked — Fresh document
    Sketch, nothing picked — Fresh document
    Sketch, nothing picked — Create ring
    Sketch, nothing picked — Create ring
    Sketch, nothing picked — Reference ring
    Sketch, nothing picked — Reference ring
    Sketch line — Fresh document
    Sketch line — Fresh document
    Sketch line — Create ring
    Sketch line — Create ring
    Sketch line — Dress-up ring
    Sketch line — Dress-up ring
    Sketch line — Reference ring
    Sketch line — Reference ring
    Sketch line — Modify ring
    Sketch line — Modify ring
    Sketch arc or circle — Fresh document
    Sketch arc or circle — Fresh document
    Sketch arc or circle — Create ring
    Sketch arc or circle — Create ring
    Sketch arc or circle — Reference ring
    Sketch arc or circle — Reference ring
    Sketch arc or circle — Modify ring
    Sketch arc or circle — Modify ring
    Sketch point — Fresh document
    Sketch point — Fresh document
    Sketch point — Create ring
    Sketch point — Create ring
    Sketch point — Reference ring
    Sketch point — Reference ring
    Two sketch entities — Fresh document
    Two sketch entities — Fresh document
    Two sketch entities — Create ring
    Two sketch entities — Create ring
    Two sketch entities — Dress-up ring
    Two sketch entities — Dress-up ring
    Two sketch entities — Reference ring
    Two sketch entities — Reference ring
    Two sketch entities — Modify ring
    Two sketch entities — Modify ring
    -
    diff --git a/docs/CAD/ux/offer_atlas_inline.html b/docs/CAD/ux/offer_atlas_inline.html deleted file mode 100644 index 3bbfb1b303..0000000000 --- a/docs/CAD/ux/offer_atlas_inline.html +++ /dev/null @@ -1,77 +0,0 @@ -Design tab — offer atlas - -
    -

    The offer — atlas of every state

    -

    Every state of the object-driven tool offer, generated from - docs/ux/tool_atlas.json. The invariant under test: a verb has one address, that - address is the same in every selection where it appears, and slots that do not apply are drawn - empty rather than compacted.

    -
    -
    60verbs mapped
    -
    20selection kinds
    -
    40primary rings
    -
    73secondary rings
    -
    113states total
    -
    3.5/8mean slots filled
    -
    5verbs with no GUI yet
    -
    -
    -
    -

    Decision 0 — ring or vertical list

    -

    The live question. Both forms carry the SAME map and the same invariant — fixed order, never - re-sorted, nothing compacted; only the geometry differs. Left-click selects; right-click opens the - offer. What the list buys: a disabled row can state its own reason in the words the product - already ships, where an empty slot in a ring is mute; nine sketch primitives fit without an - overflow; shortcuts line up in a readable column; long translated names fit; and it is navigable - by arrow key and by screen reader, which a radial is not. What it costs: no equidistant flick - gesture, and travel to the last row is longer than to the nearest direction. Pairs below — - list first, the same state as a ring second.

    - -

    Decision 1 — ring capacity

    -

    The same three selections at eight and at twelve. Eight keeps 45° between neighbours, which is - the reliable eyes-free pointing threshold and maps 1:1 to the numpad; twelve buys direct addresses - for more verbs at 30° spacing and positions that stop being nameable.

    -
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 8 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face
    8 slots — face_planar
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Straight edge selected — pick what to do with itWorking document · 8 slotsDress-up2Pattern on CurveReference3Straight edge
    8 slots — edge_str
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line
    8 slots — sk_line
    -
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 12 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face
    12 slots — face_planar
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Straight edge selected — pick what to do with itWorking document · 12 slotsDress-up2Pattern on CurveReference3Straight edge
    12 slots — edge_str
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 12 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line
    12 slots — sk_line
    - -

    Decision 2 — one map or one per mode

    -

    These renders use ONE shared map: sketch verbs occupy the same eight families as model verbs, - so dress-up is south-east whether you picked a solid edge or a sketch line. Compare the sketch-mode - states below against the model-mode ones — if a verb that exists in both modes ever appears at two - addresses, the shared map has failed and the split map is the answer.

    - -

    The matrix — every selection, every document state

    -
    - - -
    SelectionDocumentVerbsSlots2nd ringsWhat lands where
    Nothing selectedWorking document52/81create: Sketch · reference: Plane, Axis, Coord Sys, Helix
    Planar faceWorking document188/86create: Sketch · add: Extrude, Thicken · remove: Hole, Shell · dressup: Fillet, Chamfer, Draft · repeat: Pattern · transform: Mate, Align to · reference: Plane, Axis, Coord Sys, Project, Measure · modify: Edit, Delete Face
    Cylindrical faceWorking document63/82remove: Thread · reference: Axis, Helix, Measure · modify: Edit, Delete Face
    Curved faceWorking document54/81add: Thicken · dressup: Draft · reference: Measure · modify: Edit, Delete Face
    Straight edgeWorking document63/82dressup: Fillet, Chamfer · repeat: Pattern on Curve · reference: Plane, Axis, Measure
    Circular edgeWorking document43/81remove: Thread · dressup: Fillet, Chamfer · reference: Measure
    VertexWorking document41/81reference: Plane, Axis, Coord Sys, Measure
    Solid bodyWorking document156/85remove: Shell, Cut, Split · dressup: Fillet, Chamfer · repeat: Pattern, Mirror, Pattern on Curve · transform: Move · reference: Project, Measure, Mass · modify: Edit, Colour, Delete
    Sheet bodyWorking document75/81add: Thicken Surface · dressup: Surface Offset · transform: Move · reference: Measure · modify: Edit, Colour, Delete
    Two bodiesWorking document54/81add: Combine · transform: Mate · reference: Measure, Interference · modify: Delete
    Datum planeWorking document95/83create: Sketch · remove: Hole, Cut · repeat: Mirror · reference: Plane, Helix, Project · modify: Edit, Delete
    Datum axisWorking document21/81modify: Edit, Delete
    Coordinate systemWorking document32/81transform: Mate · modify: Edit, Delete
    Text / imported artWorking document43/81repeat: Pattern · transform: Move · modify: Edit, Delete
    Closed sketch loopWorking document113/82add: Extrude, Revolve, Sweep, Loft, Surface Extrude, Surface Revolve, Surface Loft, Surface Fill · repeat: Pattern · modify: Edit, Delete
    Sketch, nothing pickedWorking document112/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · reference: Dimension, Construction
    Sketch lineWorking document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch arc or circleWorking document187/83create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch pointWorking document134/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · transform: Move · reference: Dimension, Constrain · modify: Delete
    Two sketch entitiesWorking document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Nothing selectedFresh document52/81create: Sketch · reference: Plane, Axis, Coord Sys, Helix
    Planar faceFresh document74/81create: Sketch · add: Extrude · reference: Plane, Axis, Coord Sys, Measure · modify: Edit
    Cylindrical faceFresh document42/81reference: Axis, Helix, Measure · modify: Edit
    Curved faceFresh document22/80reference: Measure · modify: Edit
    Straight edgeFresh document31/81reference: Plane, Axis, Measure
    Circular edgeFresh document11/80reference: Measure
    VertexFresh document41/81reference: Plane, Axis, Coord Sys, Measure
    Solid bodyFresh document32/81reference: Measure · modify: Edit, Delete
    Sheet bodyFresh document32/81reference: Measure · modify: Edit, Delete
    Two bodiesFresh document22/80reference: Measure · modify: Delete
    Datum planeFresh document53/82create: Sketch · reference: Plane, Helix · modify: Edit, Delete
    Datum axisFresh document21/81modify: Edit, Delete
    Coordinate systemFresh document21/81modify: Edit, Delete
    Text / imported artFresh document21/81modify: Edit, Delete
    Closed sketch loopFresh document72/82add: Extrude, Revolve, Surface Extrude, Surface Revolve, Surface Fill · modify: Edit, Delete
    Sketch, nothing pickedFresh document112/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · reference: Dimension, Construction
    Sketch lineFresh document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch arc or circleFresh document187/83create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    Sketch pointFresh document134/82create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · transform: Move · reference: Dimension, Constrain · modify: Delete
    Two sketch entitiesFresh document208/84create: Line, Rectangle, Circle, Arc, Slot, Ellipse, Spline, Polygon, Point · add: Offset · remove: Trim · dressup: Fillet, Chamfer · repeat: Mirror · transform: Move · reference: Dimension, Constrain, Construction · modify: Extend, Delete
    - -

    Overflow — the one place eight slots is not enough

    -

    Measured, not predicted: the create sub-ring needs 10 addresses, so Polygon, Point are pushed behind a “More” slot. This is the decision the ring size actually turns on — either a verb moves to another family, or the tail goes to a third level, or the ring is not eight. It affects sketch mode only; every model-mode family fits.

    - -

    Not in the offer

    -

    Import STEP, Import mesh, Text, SVG, Export STEP, Commit to Plate, Undo, Redo, Variables, Section view, Origin planes, World axes — these act on the document, not on a selection, - so they stay in chrome. Verbs with kernel support but no GUI today, which still hold an address: - Split, Pattern on Curve, Align to, Measure, Interference.

    - -

    The states

    -
    PreparePreviewDesignFEATURESnothing yetClick a face or a reference plane, then a toolFresh document · 8 slotsSketchShift+SReference4Nothing selected
    Fresh document, nothing selected — the first-run picture
    PreparePreviewDesignFEATURESnothing yetRight-click the geometry to see what you can do with itFresh document · vertical listNOTHING SELECTEDSketchShift+SAdd materialCreate a sketch, or pick a solid face, firstRemovePick a face or a plane to drill intoDress-upPick an edge to roundRepeatCreate a solid body to pattern firstTransformTransform needs a body — add or import one firstReference4ModifyDelete Face needs a body — add or import one first
    LIST · fresh document — every family present, the unavailable ones say why
    PreparePreviewDesignFEATURESnothing yetClick a face or a reference plane, then a toolFresh document · 8 slotsSketchShift+SReference4Nothing selected
    RING · the same state — an empty slot cannot say anything
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listPLANAR FACESketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2
    LIST · planar face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 8 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face
    RING · planar face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch, nothing picked — pick oneWorking document · vertical listSKETCH, NOTHING PICKEDCreate9Add materialRemoveDress-upRepeatTransformReference2ModifyLineLRectangleRCircleCArcASlotSEllipseESplineBPolygonGPointP
    LIST · sketch Create submenu — all 9 primitives fit, no overflow
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch, nothing picked · Create
    RING · the same submenu — 2 verbs pushed behind “More”
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face — pick oneWorking document · vertical listPLANAR FACESketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2ExtrudeShift+EThicken
    LIST · planar face, Add material submenu
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Add material — pick oneWorking document · 8 slotsExtrudeShift+EThickenPlanar face · Add material
    RING · planar face, Add material sub-ring
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listSOLID BODYCreateClick a face or a reference plane in the viewport, t…Add materialCreate a sketch, or pick a solid face, firstRemove3Dress-up2Repeat3MoveShift+YReference3Modify3
    LIST · solid body
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listSTRAIGHT EDGECreateClick a face or a reference plane in the viewport, t…Add materialCreate a sketch, or pick a solid face, firstRemovePick a face or a plane to drill intoDress-up2Pattern on CurveTransformTransform needs a body — add or import one firstReference3ModifyDelete Face needs a body — add or import one first
    LIST · straight edge
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Right-click the geometry to see what you can do with itWorking document · vertical listSKETCH LINECreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2
    LIST · sketch line
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Click a face or a reference plane, then a toolWorking document · 8 slotsSketchShift+SReference4Nothing selected
    RING · Nothing selected
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 8 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face
    RING · Planar face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Cylindrical face selected — pick what to do with itWorking document · 8 slotsThreadShift+TReference3Modify2Cylindrical face
    RING · Cylindrical face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Curved face selected — pick what to do with itWorking document · 8 slotsThickenDraftShift+DMeasureModify2Curved face
    RING · Curved face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Straight edge selected — pick what to do with itWorking document · 8 slotsDress-up2Pattern on CurveReference3Straight edge
    RING · Straight edge
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Circular edge selected — pick what to do with itWorking document · 8 slotsThreadShift+TDress-up2MeasureCircular edge
    RING · Circular edge
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Vertex selected — pick what to do with itWorking document · 8 slotsReference4Vertex
    RING · Vertex
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Solid body selected — pick what to do with itWorking document · 8 slotsRemove3Dress-up2Repeat3MoveShift+YReference3Modify3Solid body
    RING · Solid body
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sheet body selected — pick what to do with itWorking document · 8 slotsThicken SurfaceSurface OffsetMoveShift+YMeasureModify3Sheet body
    RING · Sheet body
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Two bodies selected — pick what to do with itWorking document · 8 slotsCombineShift+BMateReference2DeleteDelTwo bodies
    RING · Two bodies
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Datum plane selected — pick what to do with itWorking document · 8 slotsSketchShift+SRemove2MirrorShift+ZReference3Modify2Datum plane
    RING · Datum plane
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Datum axis selected — pick what to do with itWorking document · 8 slotsModify2Datum axis
    RING · Datum axis
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Coordinate system selected — pick what to do with itWorking document · 8 slotsMateModify2Coordinate system
    RING · Coordinate system
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Text / imported art selected — pick what to do with itWorking document · 8 slotsABCPatternShift+NMoveShift+YModify2Text / imported art
    RING · Text / imported art
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Closed sketch loop selected — pick what to do with itWorking document · 8 slotsAdd material8PatternShift+NModify2Closed sketch loop
    RING · Closed sketch loop
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch, nothing picked selected — pick what to do with itWorking document · 8 slotsCreate9Reference2Sketch, nothing picked
    RING · Sketch, nothing picked
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line
    RING · Sketch line
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch arc or circle selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTMirrorMMoveReference3Modify2Sketch arc or circle
    RING · Sketch arc or circle
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch point selected — pick what to do with itWorking document · 8 slotsCreate9MoveReference2DeleteDelSketch point
    RING · Sketch point
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Two sketch entities selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Two sketch entities
    RING · Two sketch entities
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Add material — pick oneWorking document · 8 slotsExtrudeShift+EThickenPlanar face · Add material
    Planar face · Add material sub-ring
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Reference — pick oneWorking document · 8 slotsCoord SysShift+CProjectMeasurePlaneShift+PAxisShift+APlanar face · Reference
    Planar face · Reference sub-ring
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Create — pick oneWorking document · 8 slotsLineLRectangleRCircleCArcASlotSEllipseESplineBMore2Sketch, nothing picked · Create
    Sketch · Create sub-ring (the overflow case)
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Remove — pick oneWorking document · 8 slotsShellShift+KCutShift+XSplitSolid body · Remove
    Solid body · Remove sub-ring
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 8 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face
    8 slots — planar face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Planar face selected — pick what to do with itWorking document · 12 slotsSketchShift+SAdd material2Remove2Dress-up3PatternShift+NTransform2Reference5Modify2Planar face
    12 slots — planar face
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 8 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line
    8 slots — sketch line
    PreparePreviewDesignFEATURESSketch1Extrude1Hole1Fillet1Sketch2Extrude2Sketch line selected — pick what to do with itWorking document · 12 slotsCreate9OffsetOTrimTDress-up2MirrorMMoveReference3Modify2Sketch line
    12 slots — sketch line
    diff --git a/docs/HLSD/design-tab.md b/docs/HLSD/design-tab.md new file mode 100644 index 0000000000..67589be328 --- /dev/null +++ b/docs/HLSD/design-tab.md @@ -0,0 +1,213 @@ +# Design tab — High Level Design + +## Purpose and scope + +The Design tab is a parametric CAD environment inside the slicer: sketch, constrain, build +solid features, commit the result to the plate. It exists because the alternative is a round +trip through an external CAD application, and that round trip discards design intent at both +ends — a part edited after slicing comes back as a mesh rather than as the feature history +that produced it. Keeping the model in the project means a dimension can be changed after the +part has been sliced, with the nozzle diameter, the build volume and the material already +known. + +Its coupling to the rest of the application is deliberately narrow. It adds no stage to the +slicing pipeline and touches neither the preset system nor `Tab`. It reaches the rest of Orca +in two places: **Commit to Plate**, which hands finished solids to Prepare as ordinary model +objects, and one optional 3MF archive entry that carries the recipe. Everything else is +contained in `src/libslic3r/CAD/` and `src/slic3r/GUI/CAD/`. + +The user-facing manual lives in the wiki +([Design Tab](https://www.orcaslicer.com/wiki/design_tab)), not here. This document covers the +parts of the design that the code does not make evident. + +## The model is a recipe + +`CadDocument` holds an ordered list of `CadFeature` and nothing else that matters. Bodies, +meshes and display geometry are **derived**: `recompute()` replays the feature list from the +start and rebuilds them. Editing a dimension set twenty features ago is therefore an ordinary +edit — everything downstream is rebuilt by the same replay that built it the first time. + +Two consequences follow from deriving rather than storing: + +- **Undo is a snapshot of `features` alone.** The caller calls `checkpoint()` before the + mutations that make up one user action; undo restores that snapshot and recomputes. Because + everything else is derived, one checkpoint is exactly one `Ctrl+Z` step and the restored + state is exact rather than approximately reconstructed. The tab keeps this stack itself; it + is not Orca's project snapshot system, which operates on `Model` objects the Design tab does + not own until Commit. +- **Face and edge ids are session-scoped.** They are indices into `TopExp::MapShapes`, so a + rebuild invalidates every one of them. `CadDocument::topo_generation` is bumped on every + rebuild so a holder of an id can discover that it is stale instead of silently addressing a + different edge. The counter is deliberately not serialized: an id means nothing outside the + run that produced it. + +## Geometry kernel and dependency surface + +The kernel is OCCT, which OrcaSlicer **already** links — `Format/STEP.cpp`, `Format/svg.cpp` +and `Shape/TextShape.cpp` use it upstream. The Design tab adds no third-party dependency; it +widens the existing OCCT build by one module flag in `deps/OCCT/OCCT.cmake`: + +```cmake +-DBUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD} +``` + +Most of that module's twelve toolkits were already being built, because `DataExchange` — the +STEP path upstream ships — depends on them. The delta is `TKFillet` (used through +`BRepFilletAPI`), `TKOffset` (`BRepOffsetAPI`) and `TKFeat`, which nothing here references but +which the module flag builds anyway, because OCCT's module flags are all-or-nothing. On macOS +and Linux OCCT links statically, so an unreferenced toolkit costs build time and no shipped +bytes; on Windows OCCT builds shared, so the cost there is real DLL bytes. That Windows figure +has not been measured, and `OCCT.cmake` says so rather than carrying a number that was derived +from an incomplete toolkit list. + +On Windows the packaging step asserts that every linked OCCT toolkit has a shipped DLL and +fails the configure with the name of any that is missing, because the alternative failure — a +deps prefix built with a different `SLIC3R_CAD` setting than the app — otherwise surfaces as a +missing DLL at first launch. + +## The sketch constraint solver + +`src/libslic3r/slvs/` is a vendored subset of SolveSpace's `libslvs`: self-contained, no +external dependencies, **GPL-3.0**, with its `LICENSE` preserved verbatim in the directory. +`SketchSolver.cpp` is its only consumer and drives every sketch constraint in the tab. + +OrcaSlicer is AGPL-3.0. GPLv3 §13 permits combining a GPLv3 work with an AGPLv3 work and +AGPLv3 §13 grants the converse, so the combined work is distributable under AGPL-3.0 with the +solver's GPLv3 terms preserved. The solver is vendored rather than fetched as a dependency +because it is a pinned subset with no build system of its own; the cost of that choice is +upstream-sync burden, paid deliberately to keep `deps/` unchanged. + +## The SLIC3R_CAD gate + +`SLIC3R_CAD` (default ON) compiles the tab and selects the OCCT module flag above. With it OFF +the tab is not built and the deps prefix matches upstream exactly. The gate is cheap because +the hooks the Design tab adds to shared GUI code — chiefly the `m_design_sketch_tool` member +and the render, mouse and key hooks in `GLCanvas3D` — are null-guarded on the path they extend, +so removing the tab removes behaviour rather than requiring the host code to be rewritten. + +The flag has to agree between the dependencies and the application; that is what the DLL +assertion above is checking. + +## Project persistence + +A project stores the recipe as one optional archive entry, `Metadata/orca_cad.bin`, backed by +a single `std::string cad_recipe` on `Model`. The entry is written only when the string is +non-empty, and readers that do not know it ignore it, so projects that contain no CAD model are +byte-identical to what upstream would have written and older readers are unaffected. + +The blob is a cereal binary archive whose layout is the field order of `CadFeature`'s +save/load. That makes the format the one irreversible decision in the subsystem, and the rules +that keep it survivable are: + +- **Append only, never reorder.** Enums serialize positionally as their underlying integer, so + inserting a value in the middle of `SketchConstraintType` or `CadFeatureType` reinterprets + every constraint in every saved project. New fields go at the end. +- **Features are length-framed.** Since v5 each feature is a length-prefixed, self-contained + cereal stream, so a reader can skip a feature written by a newer build and stop cleanly on an + older one. This is what makes appending a field a non-breaking change from here on. v4 and + earlier still open through the pre-framing flat path; v1 is deliberately not loadable and has + no migration path. +- **A newer stamp is refused, not guessed at.** `deserialize_recipe` rejects a blob whose + version exceeds `ORCA_CAD_RECIPE_VERSION` with a message naming both versions. +- **The rules are held by fixtures, not by discipline.** `tests/data/cad_recipe_v{3,4,5}.bin` + are checked-in blobs from the builds that wrote them, and the tests that load them fail if a + field is reordered — which the in-memory round-trip test cannot detect. A regeneration test + (`[.regen]`, not run by default) produces a fresh fixture when a new version is stamped. + +`Import` features embed the imported solid as an OCCT BRep string inside the recipe rather than +referencing the source file, so a project opens without the STEP or mesh it was built from. +The cost is that saved projects are coupled to an OCCT BRep revision. + +## The interaction contract + +Three inputs carry the whole modelling loop — left click, right click and `Esc` — and the +contract between them is stated in code rather than spread across handlers. + +`DesignInteraction.hpp` defines a four-level LIFO stack whose enum value *is* the depth, so +"which level does this press belong to" is a comparison: + +| Level | Holds | One `Esc` press | +| --- | --- | --- | +| `Transient` | a value field or a popup menu | closes it; the tool stays armed | +| `Gesture` | an uncommitted delta — an entity being drawn, a body being dragged | reverts it; committed work is untouched | +| `Tool` | a feature card, an armed sketch tool, a constrain session | exits it; drawn entities survive | +| `Idle` | nothing transient | clears the selection; leaves a sketch session only if it is empty | + +`cad_escape_level()` is a `constexpr` free function over a POD of four booleans rather than a +method on the panel, so the ordering that is the entire contract is checkable without a window, +a GL context or an event loop — five `static_assert`s in the header do exactly that at compile +time. + +**The strict invariant: no level of `Esc` deletes a feature, discards a sketch that holds +geometry, or rolls history back.** Destroying work needs a gesture that says so — `Del` on an +explicit selection, the sketch ribbon's Cancel, which asks first, or `Ctrl+Z`. A sketch +*session* is deliberately not a `Tool` level; it is the environment the `Idle` level lives in, +which makes the destructive path unrepresentable rather than merely unlikely. + +Right-click is read at button-up against two independent budgets — 3 px of drift and 200 ms — +because drift alone still popped a menu at the end of a slow, careful orbit. The raycast uses +the press position, not the release. An armed sketch tool that already consumed the right +button (to terminate a chain, say) declines to also open a menu, through a read-and-clear flag. +Past either budget the event is navigation, and navigation does not transition the state +machine. + +Entering a sketch changes three things at once so the mode is legible: a banner above the +canvas (a sibling of the canvas, not a child over it — on GTK a child window over a +`wxGLCanvas` is a native window and does not reliably stack over GL), the printer bed muted so +a plate grid is never read as a sketch grid, and `N` to look normal to the plane. Code that +changes any of the three belongs with a change to this section. + +## The offer is generated, not hand-written + +Right-clicking geometry opens the *offer*: eight families in a fixed order, each verb at a +permanent row index, verbs that do not apply shown disabled **in place with their reason** +rather than removed. The invariant is that a verb's row index is identical in every selection +where it appears and that adding a verb never moves an existing one — the hand learns the +position, so the menu is never re-sorted, compacted or adaptively ordered. + +An invariant across 92 verbs and 20 selection kinds does not survive by review, so the map +exists once, as data: `scripts/CAD/tool_atlas.json` carries every verb with its row, key, icon, +accepted selections, preconditions and refusal string, and `scripts/CAD/gen_offer_table.py` +emits `src/slic3r/GUI/CAD/DesignOffer.hpp` from it. The header is checked in and never +hand-edited; `scripts/CAD/run-all-checks.sh` runs the generator with `--check` as its first +rung, which is what makes "GENERATED — DO NOT EDIT" a fact rather than a request. The generator +also refuses an atlas with a duplicate verb id, since `mcp_run_verb` resolves a verb by id and +would make the second one unreachable. + +The atlas and its generator sit in `scripts/CAD/` rather than in `docs/`: they are build inputs +for a checked-in header, not documentation. + +## Automation surface + +`McpControl` exposes the document over JSON-RPC when `ORCA_CAD_MCP` is set in the environment, +with `tools/orca_cad_mcp_bridge.py` as the client side. It describes the scene, queries +topology, measures, and runs the same verbs the offer does — it re-implements nothing, so a +scripted action and a clicked one cannot diverge. It is off unless the variable is set. + +## Where the code lives + +| Path | Role | +| --- | --- | +| `src/libslic3r/CAD/CadDocument.*` | the feature recipe, its replay, undo and serialization | +| `src/libslic3r/CAD/GeometryEngine.*` | OCCT wrapper — faces, edges, booleans, healing | +| `src/libslic3r/CAD/SketchEngine.*` | profile → wire → solid | +| `src/libslic3r/CAD/SketchSolver.*` | constraint solving, over the vendored solver | +| `src/libslic3r/slvs/` | vendored 2D constraint solver (GPLv3) | +| `src/slic3r/GUI/CAD/DesignPanel.*` | the tab: toolbar, feature cards, tree, key maps | +| `src/slic3r/GUI/CAD/DesignCanvas.*` | viewport integration | +| `src/slic3r/GUI/CAD/DesignSketchTool.*` | in-canvas sketching | +| `src/slic3r/GUI/CAD/DesignInteraction.hpp` | the Esc level contract | +| `src/slic3r/GUI/CAD/DesignOffer.hpp` | generated offer table | +| `scripts/CAD/tool_atlas.json` | source of truth for the offer | + +## Verification + +The kernel is covered by Catch2 suites in `tests/libslic3r/` (`test_caddocument`, +`test_sketchconstraints`, `test_sketchedit`, `test_sketchimport`, `test_sketchinference`, +`test_sketchprofile`, `test_slvs_constraints`), which need no display; +`scripts/CAD/run-kernel-tests.sh` builds only `libslic3r_tests` and runs them headless. + +The GUI half is not covered by CI, which has no OpenGL canvas or synthetic input: the ladders +in `scripts/CAD/` drive a running application in a local rig instead, and +`scripts/CAD/run-all-checks.sh` is the gate that runs all of them. A green kernel run says +nothing about the viewport, so the two are reported separately rather than as one number. diff --git a/scripts/CAD/README.md b/scripts/CAD/README.md index 861b77a976..a5c6836e29 100644 --- a/scripts/CAD/README.md +++ b/scripts/CAD/README.md @@ -33,6 +33,21 @@ part of `libslic3r_tests` and run under `ctest` on every platform like any other unit test. This script exists for the local loop, where it is a two-minute round trip instead of a full application build. +## Generated sources + +Two checked-in files are emitted from data that lives here, so the source and the thing +compiled against it cannot drift apart: + +| Generator | Source | Output | +|---|---|---| +| `gen_offer_table.py` | `tool_atlas.json` — every offer verb with its row, key, icon, accepted selections and refusal string | `src/slic3r/GUI/CAD/DesignOffer.hpp` | +| `mate-glyph/emit_glyph_table.py` | `mate-glyph/bear_outline.json`, measured off `mate-glyph/bear.step` by `extract_outline.py` (needs FreeCAD) | the `kBear…` tables in `DesignSketchTool.cpp` | + +`python3 scripts/CAD/gen_offer_table.py` rewrites the header and `--check` proves the +checked-in one matches the atlas — `run-all-checks.sh` runs the check as its first rung, +which is what makes the header's "GENERATED — DO NOT EDIT" enforceable. Edit the atlas, +never the header. + ## Build and run | Script | Purpose | @@ -48,6 +63,6 @@ Two constraints that are not obvious and have each cost a session: - **A window manager is required.** Without one, windows are never focused, and an unfocused GTK app ignores synthetic keys — which looks exactly like a code bug. -`docs/rig_build_traps.md` documents these and three more, with symptoms and exact +`scripts/CAD/rig_build_traps.md` documents these and three more, with symptoms and exact recovery commands. Read it before debugging a configure or link failure one of these scripts reports. diff --git a/scripts/CAD/build-gui-incremental.sh b/scripts/CAD/build-gui-incremental.sh index 539a21c973..f87113fdaf 100755 --- a/scripts/CAD/build-gui-incremental.sh +++ b/scripts/CAD/build-gui-incremental.sh @@ -15,7 +15,7 @@ # On success the binary is inside the persistent volume at # /OrcaSlicer/build/package/bin/orca-slicer (copy it out with a follow-up # `docker run --rm -v orcacad_buildcache:/b alpine cp ...` or via this script's tail). -# Rig build traps already paid for once each (stale project, NLopt cache, pybind11, OCCT_LIBS, SLIC3R_CAD gate): docs/rig_build_traps.md +# Rig build traps already paid for once each (stale project, NLopt cache, pybind11, OCCT_LIBS, SLIC3R_CAD gate): scripts/CAD/rig_build_traps.md set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" diff --git a/scripts/CAD/build-gui.sh b/scripts/CAD/build-gui.sh index 0a1a86dd5c..edf93a5ca0 100755 --- a/scripts/CAD/build-gui.sh +++ b/scripts/CAD/build-gui.sh @@ -5,7 +5,7 @@ # NEVER build inside the GUI container (snapmaker-gui / orcacad-gui). Its baked /OrcaSlicer tree # is the Jun-13 Snapmaker-derived source, so a `cmake .` in there silently reconfigures the # shared build dir as project(Snapmaker_Orca) and this fork's targets vanish. That is Trap 1 of -# five; all of them, with symptoms and exact recovery commands, are in docs/rig_build_traps.md. +# five; all of them, with symptoms and exact recovery commands, are in scripts/CAD/rig_build_traps.md. # Read that file before debugging a configure or link failure this script reports. # # Usage: @@ -92,7 +92,7 @@ docker run --rm \ # A target-only build writes src/Release/, but this fork's start-headless-gui.sh may default BIN to the # PACKAGED path that only build_linux.sh refreshes — launching with the default would then run a -# stale binary. Pass BIN explicitly. See docs/rig_build_traps.md. +# stale binary. Pass BIN explicitly. See scripts/CAD/rig_build_traps.md. echo "=== launch the rig on the binary just built ===" echo " docker exec -e BIN=/OrcaSlicer/build/src/Release/$BIN ${PREFIX}-gui /OrcaSlicer/scripts/CAD/start-headless-gui.sh" exit "$rc" diff --git a/docs/CAD/ux/mockups/gen_offer_table.py b/scripts/CAD/gen_offer_table.py similarity index 80% rename from docs/CAD/ux/mockups/gen_offer_table.py rename to scripts/CAD/gen_offer_table.py index 9744f9288b..442be27697 100644 --- a/docs/CAD/ux/mockups/gen_offer_table.py +++ b/scripts/CAD/gen_offer_table.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 -"""Emit the C++ offer table from docs/ux/tool_atlas.json. +"""Emit the C++ offer table from scripts/CAD/tool_atlas.json. - python3 docs/ux/mockups/gen_offer_table.py + python3 scripts/CAD/gen_offer_table.py # rewrite the header + python3 scripts/CAD/gen_offer_table.py --check # verify the checked-in header -The map exists ONCE. The mockups and the shipping menu read the same rows in the same -order from the same file, so a drawing and the product cannot drift apart — which is the -only way row constancy (charter 4.1) survives contact with a codebase. +The map exists ONCE: the atlas is the only place a verb's row, key, icon and refusal +string are written down, so the menu the user sees cannot drift from the table the code +is compiled against. Output: src/slic3r/GUI/CAD/DesignOffer.hpp, checked in and never hand-edited. """ @@ -14,15 +15,9 @@ import json import os import sys -HERE = os.path.dirname(os.path.abspath(__file__)) -UX = os.path.dirname(HERE) -# One dirname more than you would expect: this generator lives at docs/CAD/ux/mockups/, -# not docs/ux/mockups/, since the design docs moved into the CAD subfolder (bbd1989e1e). -# With the old count REPO resolved to docs/, so OUT pointed at docs/src/.../DesignOffer.hpp, -# which does not exist -- and --check then diffed the real generated table against an empty -# file and reported the whole 189-line header as a difference. -REPO = os.path.dirname(os.path.dirname(os.path.dirname(UX))) -ATLAS = os.path.join(UX, "tool_atlas.json") +HERE = os.path.dirname(os.path.abspath(__file__)) # scripts/CAD +REPO = os.path.dirname(os.path.dirname(HERE)) +ATLAS = os.path.join(HERE, "tool_atlas.json") OUT = os.path.join(REPO, "src", "slic3r", "GUI", "CAD", "DesignOffer.hpp") # selection id -> C++ enumerator @@ -42,15 +37,40 @@ def cstr(s): return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"' +def validate(A): + """Refuse an atlas the header cannot represent, naming every fault at once. + + An unknown slot or selection would surface as a bare ValueError from .index() below, and a + duplicate verb id would not surface at all: DesignPanel::mcp_run_verb looks a verb up by id + and takes the first match, so the second row would be unreachable from the offer. + """ + slot_ids = {s["id"] for s in A["slots"]} + sel_ids = {s["id"] for s in A["selections"]} + errs, seen = [], set() + for v in A["verbs"]: + if v["slot"] not in slot_ids: + errs.append(f'{v["id"]}: unknown slot {v["slot"]!r}') + for a in v["accepts"]: + if a not in sel_ids: + errs.append(f'{v["id"]}: accepts unknown selection {a!r}') + if v["id"] in seen: + errs.append(f'{v["id"]}: duplicate verb id') + seen.add(v["id"]) + for s in sel_ids - set(ENUM): + errs.append(f"selection {s!r} has no OfferSel enumerator") + if errs: + sys.exit("tool_atlas.json is inconsistent:\n " + "\n ".join(errs)) + + def main(): A = json.load(open(ATLAS, encoding="utf-8")) + validate(A) sels = [s["id"] for s in A["selections"]] - assert all(s in ENUM for s in sels), [s for s in sels if s not in ENUM] slots = [s["id"] for s in A["slots"]] lines = [ "// GENERATED FILE — DO NOT EDIT.", - "// Source: docs/ux/tool_atlas.json Generator: docs/ux/mockups/gen_offer_table.py", + "// Source: scripts/CAD/tool_atlas.json Generator: scripts/CAD/gen_offer_table.py", "//", "// The object-driven tool offer (charter 4.1): every verb has ONE row index, that index", "// is the same in every selection it appears in, and verbs that do not apply are shown", diff --git a/docs/CAD/design/mate-connectors/bear.step b/scripts/CAD/mate-glyph/bear.step similarity index 100% rename from docs/CAD/design/mate-connectors/bear.step rename to scripts/CAD/mate-glyph/bear.step diff --git a/docs/CAD/design/mate-connectors/bear_outline.json b/scripts/CAD/mate-glyph/bear_outline.json similarity index 100% rename from docs/CAD/design/mate-connectors/bear_outline.json rename to scripts/CAD/mate-glyph/bear_outline.json diff --git a/docs/CAD/design/mate-connectors/emit_glyph_table.py b/scripts/CAD/mate-glyph/emit_glyph_table.py similarity index 100% rename from docs/CAD/design/mate-connectors/emit_glyph_table.py rename to scripts/CAD/mate-glyph/emit_glyph_table.py diff --git a/docs/CAD/design/mate-connectors/extract_outline.py b/scripts/CAD/mate-glyph/extract_outline.py similarity index 100% rename from docs/CAD/design/mate-connectors/extract_outline.py rename to scripts/CAD/mate-glyph/extract_outline.py diff --git a/docs/CAD/rig_build_traps.md b/scripts/CAD/rig_build_traps.md similarity index 100% rename from docs/CAD/rig_build_traps.md rename to scripts/CAD/rig_build_traps.md diff --git a/scripts/CAD/run-all-checks.sh b/scripts/CAD/run-all-checks.sh index 1c0388ac89..8a99a3b6cb 100755 --- a/scripts/CAD/run-all-checks.sh +++ b/scripts/CAD/run-all-checks.sh @@ -54,10 +54,9 @@ run_in_rig() { # copy the script in fresh, then run it ther # says. The header calls itself GENERATED and had been hand-edited anyway — which cost four rows # that existed only in the header, one row wired to the wrong action, and a count of 91 for a # 92-row array, so the last verb was unreachable (z8rs, ziam). -# docs/CAD/, not docs/: SoftFever moved the design docs into the CAD subfolder -# (bbd1989e1e) and this line kept the old path, so the rung failed on a missing file -# rather than on anything about the table. The other fork still has docs/ux/. -step "offer table matches the atlas" python3 docs/CAD/ux/mockups/gen_offer_table.py --check +# The atlas and its generator live beside this script, in scripts/CAD/: they are build inputs +# for a checked-in header, not documentation, so they do not belong under docs/. +step "offer table matches the atlas" python3 scripts/CAD/gen_offer_table.py --check step "kernel suite" scripts/CAD/run-kernel-tests.sh --vol "${KVOL:-orcacad_kerneltest}" diff --git a/scripts/CAD/run-kernel-tests.sh b/scripts/CAD/run-kernel-tests.sh index ef4356cb65..cc52a2a00b 100755 --- a/scripts/CAD/run-kernel-tests.sh +++ b/scripts/CAD/run-kernel-tests.sh @@ -19,7 +19,7 @@ # working tree to a per-volume staging dir on that host and re-runs this same script # there, so the verification contract is identical either way. Drop --host once the image # is present locally. -# Rig build traps already paid for once each (stale project, NLopt cache, pybind11, OCCT_LIBS, SLIC3R_CAD gate): docs/rig_build_traps.md +# Rig build traps already paid for once each (stale project, NLopt cache, pybind11, OCCT_LIBS, SLIC3R_CAD gate): scripts/CAD/rig_build_traps.md set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" diff --git a/docs/CAD/ux/tool_atlas.json b/scripts/CAD/tool_atlas.json similarity index 99% rename from docs/CAD/ux/tool_atlas.json rename to scripts/CAD/tool_atlas.json index 94709f2057..224b118c9e 100644 --- a/docs/CAD/ux/tool_atlas.json +++ b/scripts/CAD/tool_atlas.json @@ -1,6 +1,6 @@ { "_comment": [ - "Source of truth for the object-driven tool offer (see docs/cad_ux_guidelines.md 4.1).", + "Source of truth for the object-driven tool offer (see docs/HLSD/design-tab.md).", "Every verb, the selections it accepts, the document state it needs, and its fixed", "compass address. Extracted from the code, not from memory:", " verbs + shortcuts + refusal messages -> src/slic3r/GUI/CAD/DesignPanel.cpp (feat_dropdown", @@ -8,8 +8,8 @@ " kernel coverage -> src/libslic3r/CAD/CadDocument.hpp (enum CadFeatureType)", " headless coverage -> src/slic3r/GUI/CAD/McpControl.cpp (method == \"...\")", " selection kinds -> src/slic3r/GUI/CAD/DesignCanvas.hpp (the on_*_selected callbacks)", - "gen_offer_mockups.py reads this file; the eventual C++ offer table is generated from it", - "too, so the map exists once." + "gen_offer_table.py emits src/slic3r/GUI/CAD/DesignOffer.hpp from this file, so the map", + "exists once. Edit the atlas and regenerate; never hand-edit the header." ], "slots": [ { diff --git a/src/slic3r/GUI/CAD/DesignOffer.hpp b/src/slic3r/GUI/CAD/DesignOffer.hpp index ad1998d5c5..fd15f537d3 100644 --- a/src/slic3r/GUI/CAD/DesignOffer.hpp +++ b/src/slic3r/GUI/CAD/DesignOffer.hpp @@ -1,5 +1,5 @@ // GENERATED FILE — DO NOT EDIT. -// Source: docs/ux/tool_atlas.json Generator: docs/ux/mockups/gen_offer_table.py +// Source: scripts/CAD/tool_atlas.json Generator: scripts/CAD/gen_offer_table.py // // The object-driven tool offer (charter 4.1): every verb has ONE row index, that index // is the same in every selection it appears in, and verbs that do not apply are shown diff --git a/src/slic3r/GUI/CAD/DesignPanel.cpp b/src/slic3r/GUI/CAD/DesignPanel.cpp index d247ddfd26..5f64025f2b 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.cpp +++ b/src/slic3r/GUI/CAD/DesignPanel.cpp @@ -1,7 +1,7 @@ #include "slic3r/GUI/CAD/DesignPanel.hpp" #include "slic3r/GUI/CAD/DesignCanvas.hpp" #include "slic3r/GUI/CAD/DesignSketchTool.hpp" -#include "slic3r/GUI/CAD/DesignOffer.hpp" // generated offer table — see docs/ux/tool_atlas.json +#include "slic3r/GUI/CAD/DesignOffer.hpp" // generated offer table — see scripts/CAD/tool_atlas.json #include "libslic3r/CAD/GeometryEngine.hpp" // face_by_index for face-extrude gizmo anchor #include "libslic3r/TriangleMesh.hpp" // mesh import: STL/OBJ -> indexed_triangle_set #include "libslic3r/Format/OBJ.hpp" @@ -6067,7 +6067,7 @@ bool DesignPanel::sketch_plane_target(wxString& what) const // --------------------------------------------------------------------------------------------- // The object-driven offer (charter §4.1). Right-click the geometry and get a vertical list in the // ratified row order, with the verbs that do not apply DISABLED IN PLACE carrying their reason. -// The rows come from DesignOffer.hpp, generated from docs/ux/tool_atlas.json — the same file the +// The rows come from DesignOffer.hpp, generated from scripts/CAD/tool_atlas.json — the same file the // mockups are drawn from, so a drawing and the product cannot drift apart. // --------------------------------------------------------------------------------------------- diff --git a/src/slic3r/GUI/CAD/DesignSketchTool.cpp b/src/slic3r/GUI/CAD/DesignSketchTool.cpp index 716df658ed..c195d6a877 100644 --- a/src/slic3r/GUI/CAD/DesignSketchTool.cpp +++ b/src/slic3r/GUI/CAD/DesignSketchTool.cpp @@ -4034,7 +4034,7 @@ void DesignSketchTool::render_mate_connectors() // the information. The same bear rendered flat vs in relief gives 164 vs 210 lit pixels at 16 // degrees and 66 vs 120 at 6. So the glyph is a small shaded solid, not an outline. // -// The geometry is EMITTED from the real part by doc/design/mate-connectors/emit_glyph_table.py, +// The geometry is EMITTED from the real part by scripts/CAD/mate-glyph/emit_glyph_table.py, // not hand-drawn, so the glyph and the printed connector cannot drift apart. Two vertices stand // above the 3 mm plate in the actual B-rep, which is why the snout here is a tent with one crest // edge and four flanks drafted at 20 degrees rather than anything more elaborate. @@ -4042,7 +4042,7 @@ void DesignSketchTool::render_mate_connectors() // The cheek dot is the handedness mark. Without it the glyph differs from its own mirror by only // 5-9 % of its lit pixels, which is not enough to read; the dot roughly doubles that and, unlike // making the eyes uneven, identifies the side from that cheek alone instead of by comparison. -// Emitted by doc/design/mate-connectors/emit_glyph_table.py from bear.step — do not hand-edit. +// Emitted by scripts/CAD/mate-glyph/emit_glyph_table.py from bear.step — do not hand-edit. // Normalised to the part's bounding span and centred: the renderer scales by one radius. static const Vec2d kBearOutline[] = { // 12 verts, RDP eps 0.030, CCW {+0.3842, +0.3294}, {+0.3156, +0.4002}, {+0.2424, +0.3294}, From c03f4925a9a71a301efade7af21f8de7aced334a Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 1 Oct 2026 02:54:18 +0800 Subject: [PATCH 82/89] Apply per-variant cooling, ramming and temperature range on nozzle-rack slices Fan speeds, multi-tool ramming, the tower interface and flush temperature fallbacks and the custom G-code placeholders now use the extruder variant a filament prints with on each layer, instead of reading by filament id. --- .../OrcaSlicer_profile_validator.cpp | 5 +- src/libslic3r/GCode.cpp | 35 ++++++++--- src/libslic3r/GCode.hpp | 3 + src/libslic3r/GCode/CoolingBuffer.cpp | 10 +-- src/libslic3r/GCode/CoolingBuffer.hpp | 2 + src/libslic3r/GCode/ToolOrdering.cpp | 22 ++++--- src/libslic3r/Print.cpp | 19 +++--- src/libslic3r/Print.hpp | 6 +- src/libslic3r/PrintConfig.cpp | 21 +++---- tests/fff_print/test_multifilament.cpp | 62 +++++++++++++++++++ .../test_config_variant_expansion.cpp | 41 +++--------- .../test_toolordering_nozzle_group.cpp | 18 ++++++ 12 files changed, 166 insertions(+), 78 deletions(-) diff --git a/src/dev-utils/OrcaSlicer_profile_validator.cpp b/src/dev-utils/OrcaSlicer_profile_validator.cpp index e41ce737ca..c328c565c8 100644 --- a/src/dev-utils/OrcaSlicer_profile_validator.cpp +++ b/src/dev-utils/OrcaSlicer_profile_validator.cpp @@ -308,8 +308,8 @@ DynamicPrintConfig slice_config(PresetBundle &bundle) // type (Direct Drive + Bowden) the mismatched lookup spams [error] lines. Single-nozzle and non-BBL // printers keep the default map (their toolchange rides the AMS/tool-changer path unchanged). const bool pin_filament_map = bundle.is_bbl_vendor() && nozzles > 1; + auto &fmap = bundle.project_config.option("filament_map", true)->values; if (pin_filament_map) { - auto &fmap = bundle.project_config.option("filament_map", true)->values; for (size_t i = 0; i < fmap.size(); ++i) fmap[i] = int(i % nozzles) + 1; } @@ -317,9 +317,8 @@ DynamicPrintConfig slice_config(PresetBundle &bundle) // A fresh printer selection uses its declared nozzle volumes, just like the // app. Otherwise a high-flow preset is silently sliced with Standard tuning. bundle.reset_default_nozzle_volume_type(); - auto &filament_map = bundle.project_config.option("filament_map", true)->values; bundle.project_config.option("filament_volume_map", true)->values = - bundle.get_default_nozzle_volume_types_for_filaments(filament_map); + bundle.get_default_nozzle_volume_types_for_filaments(fmap); DynamicPrintConfig cfg = bundle.full_config(); cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 90cca3f084..dedd024f2b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1258,7 +1258,7 @@ static std::vector get_path_of_change_filament(const Print& print) config.set_key_value("old_filament_temp", new ConfigOptionInt(old_filament_temp)); int interface_temp = full_config.filament_tower_interface_print_temp.get_at(new_filament_id); if (interface_temp == -1) - interface_temp = full_config.nozzle_temperature_range_high.get_at(new_filament_id); + interface_temp = full_config.nozzle_temperature_range_high.get_at(new_fi); if (full_config.enable_tower_interface_features && tcr.is_contact) new_filament_temp = interface_temp; config.set_key_value("new_filament_temp", new ConfigOptionInt(new_filament_temp)); @@ -1309,7 +1309,7 @@ static std::vector get_path_of_change_filament(const Print& print) flush_temps[idx] = use_fast_flush ? m_print_config->filament_flush_temp_fast.get_at(fi) : m_print_config->filament_flush_temp.get_at(fi); if (flush_temps[idx] == 0) - flush_temps[idx] = m_print_config->nozzle_temperature_range_high.get_at(idx); + flush_temps[idx] = m_print_config->nozzle_temperature_range_high.get_at(fi); filament_cooling_before_tower[idx] = m_print_config->filament_cooling_before_tower.get_at(fi); } if (tcr.is_contact || gcodegen.m_layer_index == 0) @@ -1563,7 +1563,7 @@ static std::vector get_path_of_change_filament(const Print& print) const bool will_go_down = !is_approx(z, current_z); const bool is_ramming = (gcodegen.config().single_extruder_multi_material) || (!gcodegen.config().single_extruder_multi_material && - gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool)); + gcodegen.config().filament_multitool_ramming.get_at(gcodegen.get_filament_config_index(tcr.initial_tool))); // Orca: user-facing override (Printer Settings > Wipe tower > "Tool change on wipe tower"). // Forces the toolhead to travel over the wipe tower before issuing Tx even on multi-toolhead // printers without ramming, where Orca would otherwise emit Tx in place (potentially over the part). @@ -1620,7 +1620,7 @@ static std::vector get_path_of_change_filament(const Print& print) if (gcodegen.config().enable_tower_interface_features && tcr.is_contact) { interface_temp = gcodegen.config().filament_tower_interface_print_temp.get_at(new_extruder_id); if (interface_temp == -1) - interface_temp = gcodegen.config().nozzle_temperature_range_high.get_at(new_extruder_id); + interface_temp = gcodegen.config().nozzle_temperature_range_high.get_at(gcodegen.get_filament_config_index(new_extruder_id)); toolchange_temp_override = interface_temp; } toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override, @@ -4342,6 +4342,12 @@ size_t GCode::get_filament_config_index(int filament_id) const return filament_id; } +size_t GCode::get_filament_config_index(int filament_id, size_t layer_id) const +{ + // Orca: uncached, as the stages after the generator run concurrently with it. + return m_print ? m_print->get_filament_config_indx(filament_id, (int) layer_id, false) : filament_id; +} + size_t GCode::get_nozzle_config_index(int filament_id) const { if (m_print) { @@ -9409,10 +9415,13 @@ void GCode::update_placeholder_parser_with_variant_params() // Helper: remap config arrays from variant index space to filament_id index space. // After remapping, gcode templates can use param[filament_id] directly. + std::vector config_index(num_filaments); + for (size_t i = 0; i < num_filaments; ++i) + config_index[i] = get_filament_config_index(i); auto remap_by_filament = [&](const auto &src) { std::decay_t dst(num_filaments); for (size_t i = 0; i < num_filaments; ++i) - dst[i] = src.get_at(get_filament_config_index(i)); + dst[i] = src.get_at(config_index[i]); return dst; }; @@ -9427,6 +9436,16 @@ void GCode::update_placeholder_parser_with_variant_params() this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer))); this->placeholder_parser().set("pressure_advance", new ConfigOptionFloats(remap_by_filament(m_config.pressure_advance))); this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(remap_by_filament(m_config.enable_pressure_advance))); + this->placeholder_parser().set("fan_min_speed", new ConfigOptionFloats(remap_by_filament(m_config.fan_min_speed))); + this->placeholder_parser().set("fan_max_speed", new ConfigOptionFloats(remap_by_filament(m_config.fan_max_speed))); + this->placeholder_parser().set("additional_cooling_fan_speed", new ConfigOptionInts(remap_by_filament(m_config.additional_cooling_fan_speed))); + this->placeholder_parser().set("filament_minimal_purge_on_wipe_tower", new ConfigOptionFloats(remap_by_filament(m_config.filament_minimal_purge_on_wipe_tower))); + this->placeholder_parser().set("filament_multitool_ramming", new ConfigOptionBools(remap_by_filament(m_config.filament_multitool_ramming))); + this->placeholder_parser().set("filament_multitool_ramming_volume", new ConfigOptionFloats(remap_by_filament(m_config.filament_multitool_ramming_volume))); + this->placeholder_parser().set("filament_multitool_ramming_flow", new ConfigOptionFloats(remap_by_filament(m_config.filament_multitool_ramming_flow))); + this->placeholder_parser().set("nozzle_temperature_range_low", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_range_low))); + const auto nozzle_temperature_range_high = remap_by_filament(m_config.nozzle_temperature_range_high); + this->placeholder_parser().set("nozzle_temperature_range_high", new ConfigOptionInts(nozzle_temperature_range_high)); // --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed --- this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_by_filament(m_config.retraction_distances_when_cut))); @@ -9447,7 +9466,7 @@ void GCode::update_placeholder_parser_with_variant_params() if (flush_v_speed[i] == 0) flush_v_speed[i] = filament_max_v[i]; if (flush_temps[i] == 0) - flush_temps[i] = m_config.nozzle_temperature_range_high.get_at(i); + flush_temps[i] = nozzle_temperature_range_high[i]; } this->placeholder_parser().set("flush_volumetric_speeds", new ConfigOptionFloats(flush_v_speed)); this->placeholder_parser().set("flush_temperatures", new ConfigOptionInts(flush_temps)); @@ -9706,7 +9725,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo { int interface_temp = m_config.filament_tower_interface_print_temp.get_at(new_filament_id); if (interface_temp == -1) - interface_temp = m_config.nozzle_temperature_range_high.get_at(new_filament_id); + interface_temp = m_config.nozzle_temperature_range_high.get_at(new_fi); dyn_config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp)); } if (toolchange_temp_override > 0) { @@ -9745,7 +9764,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo flush_temps[idx] = use_fast_flush ? m_print->config().filament_flush_temp_fast.get_at(fi) : m_print->config().filament_flush_temp.get_at(fi); if (flush_temps[idx] == 0) - flush_temps[idx] = m_print->config().nozzle_temperature_range_high.get_at(idx); + flush_temps[idx] = m_print->config().nozzle_temperature_range_high.get_at(fi); filament_cooling_before_tower[idx] = m_print->config().filament_cooling_before_tower.get_at(fi); } std::fill(filament_cooling_before_tower.begin(), filament_cooling_before_tower.end(), 0); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index bded77d9d1..6516a3f6fd 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -296,6 +296,9 @@ public: // resolver keys filament-indexed arrays, the nozzle resolver keys (extruder x volume-type) // slot arrays. Both degenerate to filament_id / extruder index on single-volume printers. size_t get_filament_config_index(int filament_id) const; + // The filament resolver for a given layer, for the export pipeline stages after the generator, + // which run behind the current layer and concurrently with the generator. + size_t get_filament_config_index(int filament_id, size_t layer_id) const; size_t get_nozzle_config_index(int filament_id) const; // Object and support extrusions of the same PrintObject at the same print_z. diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index e55b89212e..7f6e9c67d5 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -18,7 +18,7 @@ namespace Slic3r { -CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0), m_current_nozzle(0) +CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_gcodegen(gcodegen), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0), m_current_nozzle(0) { this->reset(gcodegen.writer().get_position()); @@ -737,10 +737,12 @@ std::string CoolingBuffer::apply_layer_cooldown( &ironing_fan_control, &ironing_fan_speed ](bool immediately_apply) { #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) - float fan_min_speed = EXTRUDER_CONFIG(fan_min_speed); + // The per-variant options take the extruder variant the filament prints with on this layer + const size_t config_index = m_gcodegen.get_filament_config_index(m_current_extruder, layer_id); + float fan_min_speed = m_config.fan_min_speed.get_at(config_index); float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0; //BBS - int additional_fan_speed_new = EXTRUDER_CONFIG(additional_cooling_fan_speed); + int additional_fan_speed_new = m_config.additional_cooling_fan_speed.get_at(config_index); int close_fan_the_first_x_layers = EXTRUDER_CONFIG(close_fan_the_first_x_layers); // Is the fan speed ramp enabled? int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer); @@ -776,7 +778,7 @@ std::string CoolingBuffer::apply_layer_cooldown( // additional_fan_speed_new is left at its configured value (auxiliary fan is independent of the // part-cooling override). } else if (int(layer_id) >= close_fan_the_first_x_layers) { - float fan_max_speed = EXTRUDER_CONFIG(fan_max_speed); + float fan_max_speed = m_config.fan_max_speed.get_at(config_index); float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time)); float fan_cooling_layer_time = float(EXTRUDER_CONFIG(fan_cooling_layer_time)); //BBS: always enable the fan speed interpolation according to layer time diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 9b0a5ab9df..742b8f78b7 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -54,6 +54,8 @@ private: // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. const PrintConfig &m_config; + // Resolves the filament config index of the per-variant options. + const GCode &m_gcodegen; unsigned int m_current_extruder; unsigned int m_current_nozzle; //BBS: current fan speed diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 7ea46cf8a7..5026323d54 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -1230,15 +1230,21 @@ void ToolOrdering::cal_most_used_extruder(const PrintConfig &config) float ToolOrdering::cal_max_additional_fan(const PrintConfig &config) { - // record + std::set used_filaments; + for (const LayerTools &layer_tools : m_layer_tools) + used_filaments.insert(layer_tools.extruders.begin(), layer_tools.extruders.end()); + if (used_filaments.empty()) + return 0; + + // Orca: additional_cooling_fan_speed can hold one value per extruder variant a filament prints with; + // filament_self_index maps such a column to its filament. + const std::vector &self_index = config.filament_self_index.values; + const size_t columns = std::max(config.additional_cooling_fan_speed.size(), size_t(*used_filaments.rbegin()) + 1); float max_fan = 0; - for (LayerTools &layer_tools : m_layer_tools) { - std::vector filaments = layer_tools.extruders; - std::set layer_extruder_count; - // count once only - for (unsigned int &filament : filaments) - if (max_fan < config.additional_cooling_fan_speed.get_at(filament)) - max_fan = config.additional_cooling_fan_speed.get_at(filament); + for (size_t column = 0; column < columns; ++column) { + const unsigned int filament_id = self_index.size() == columns ? self_index[column] - 1 : column; + if (used_filaments.count(filament_id) && max_fan < config.additional_cooling_fan_speed.get_at(column)) + max_fan = config.additional_cooling_fan_speed.get_at(column); } return max_fan; } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index be4e217d5c..5f5592cfce 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -4329,9 +4329,9 @@ bool Print::is_dynamic_group_reorder() const return true; } -int Print::get_filament_config_indx(int filament_id, int layer_id) +int Print::get_filament_config_indx(int filament_id, int layer_id, bool use_cache) { - return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, m_filament_index_map); + return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, use_cache ? &m_filament_index_map : nullptr); } void Print::update_filament_self_index_cache() @@ -4374,7 +4374,7 @@ int Print::get_nozzle_config_index(int filament_id, int layer_id) return get_config_index(filament_id, layer_id, m_default_region_config.print_extruder_variant.values, m_default_region_config.print_extruder_id.values, m_nozzle_index_map); } -int Print::get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, FilamentIndexMap &index_map) +int Print::get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, FilamentIndexMap *index_map) { auto group_result = get_layered_nozzle_group_result(); // Orca: defensive — when no grouping producer has published a result yet, fall back to the @@ -4385,7 +4385,8 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vectorextruder_id)); NozzleVolumeType nozzle_volume_type = nozzle_info->volume_type; + if (!index_map) + return get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list); FilamentIndexKey key{filament_id, extruder_type, nozzle_volume_type}; - auto iter = index_map.find(key); - if (iter == index_map.end()) { + auto iter = index_map->find(key); + if (iter == index_map->end()) { int index = get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list); - index_map[key] = index; + (*index_map)[key] = index; return index; } else { - return index_map[key]; + return iter->second; } } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index dc4323e537..f185962b1e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -1222,7 +1222,9 @@ public: // Post-slicing config-slot resolvers: map a (filament, layer) pair to the index of its // per-(extruder x volume type) column in the expanded variant arrays, cached by grouping context. - int get_filament_config_indx(int filament_id, int layer_id); + // Orca: without use_cache, the filament resolver leaves the cache alone, for the G-code export + // pipeline's cooling stage, which runs concurrently with the generator stage filling it. + int get_filament_config_indx(int filament_id, int layer_id, bool use_cache = true); int get_nozzle_config_index(int filament_id, int layer_id); // Orca: Implement prusa's filament shrink compensation approach @@ -1282,7 +1284,7 @@ protected: }; using FilamentIndexMap = std::unordered_map; using PrintIndexMap = std::unordered_map; - int get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, FilamentIndexMap &index_map); + int get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, FilamentIndexMap *index_map); int get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, PrintIndexMap &index_map); // Invalidates the step, and its depending steps in Print. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 9eb1e95e5d..58bfc60852 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -9463,17 +9463,6 @@ std::set print_options_with_variant = { std::set filament_options_with_variant = { "filament_flow_ratio", "filament_max_volumetric_speed", - "enable_pressure_advance", - "pressure_advance", - "fan_min_speed", - "fan_max_speed", - "additional_cooling_fan_speed", - "filament_minimal_purge_on_wipe_tower", - "filament_multitool_ramming", - "filament_multitool_ramming_volume", - "filament_multitool_ramming_flow", - "nozzle_temperature_range_low", - "nozzle_temperature_range_high", // Per-variant ramming / pre-cooling / nozzle-change filament overrides "filament_ramming_volumetric_speed", "filament_pre_cooling_temperature", @@ -9527,6 +9516,16 @@ std::set filament_options_with_variant = { "adaptive_pressure_advance_model", "adaptive_pressure_advance_overhangs", "adaptive_pressure_advance_bridges", + // Orca: cooling fans, multi-tool ramming and recommended nozzle temperature range + "fan_min_speed", + "fan_max_speed", + "additional_cooling_fan_speed", + "filament_minimal_purge_on_wipe_tower", + "filament_multitool_ramming", + "filament_multitool_ramming_volume", + "filament_multitool_ramming_flow", + "nozzle_temperature_range_low", + "nozzle_temperature_range_high", "activate_air_filtration", "activate_air_filtration_during_print", "activate_air_filtration_on_completion", diff --git a/tests/fff_print/test_multifilament.cpp b/tests/fff_print/test_multifilament.cpp index c39efaccf5..445b1c0694 100644 --- a/tests/fff_print/test_multifilament.cpp +++ b/tests/fff_print/test_multifilament.cpp @@ -893,3 +893,65 @@ TEST_CASE("Each filament sets the pressure advance of its extruder variant on a CHECK(gcode.find("; start pressure advance " + pressure_advance + "\n") != std::string::npos); } } + +// The speeds, in percent, a G-code turns a fan on at: the part cooling fan for `M106 S`, the auxiliary +// fan for `M106 P2 S`. +static std::set fan_speeds(const std::string &gcode, const std::string &command) +{ + std::set speeds; + std::istringstream stream(gcode); + for (std::string line; std::getline(stream, line);) + if (line.rfind(command, 0) == 0) + if (const int pwm = std::stoi(line.substr(command.size())); pwm > 0) + speeds.insert(int(std::lround(pwm * 100. / 255.))); + return speeds; +} + +// The fan speeds and the recommended nozzle temperature range are tuned per extruder variant like the +// other filament variant settings. +TEST_CASE("Each filament cools with the fan speeds of its extruder variant", "[MultiFilament]") +{ + auto [nozzle_volume_type, filament, fan_min_speed, fan_max_speed, additional_fan_speed, range_high] = GENERATE(table({ + { nvtStandard, 1, 15, 25, 10, 240 }, + { nvtHighFlow, 1, 35, 45, 20, 260 }, + { nvtHighFlow, 2, 55, 65, 40, 280 }, // filament 2 defines no High Flow variant + })); + // Layers printed faster than slow_down_layer_time run the fan at its maximum speed, layers slower than + // fan_cooling_layer_time at its minimum. + const bool fast_layers = GENERATE(false, true); + DYNAMIC_SECTION(get_nozzle_volume_type_string(nozzle_volume_type) << " nozzle, filament " << filament << (fast_layers ? ", fast layers" : ", slow layers")) { + DynamicPrintConfig config = multifilament_config(2, { + { "extruder_variant_list", "Direct Drive Standard,Direct Drive High Flow" }, + // filament 1 defines Standard and High Flow, filament 2 Standard + { "filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow;Direct Drive Standard" }, + { "filament_self_index", "1,1,2" }, + { "fan_min_speed", "15,35,55" }, + { "fan_max_speed", "25,45,65" }, + { "additional_cooling_fan_speed", "10,20,40" }, + { "nozzle_temperature_range_high", "240,260,280" }, + { "auxiliary_fan", 1 }, + { "reduce_fan_stop_start_freq", "1,1" }, + { "slow_down_layer_time", fast_layers ? "1000,1000" : "0,0" }, + { "fan_cooling_layer_time", fast_layers ? "1000,1000" : "0,0" }, + { "slow_down_for_layer_cooling", "0,0" }, + { "enable_overhang_bridge_fan", "0,0" }, + { "sparse_infill_filament_id", filament }, + { "internal_solid_filament_id", filament }, + { "top_surface_filament_id", filament }, + { "bottom_surface_filament_id", filament }, + { "outer_wall_filament_id", filament }, + { "inner_wall_filament_id", filament }, + { "enable_prime_tower", 0 }, + { "skirt_loops", 0 }, + { "brim_type", "no_brim" }, + // custom G-code indexes the per-filament arrays by filament + { "machine_start_gcode", "; start range high {nozzle_temperature_range_high[initial_extruder]}" }, + }); + config.option("nozzle_volume_type", true)->values = { nozzle_volume_type }; + const std::string gcode = slice({ cube(20) }, config); + + CHECK(fan_speeds(gcode, "M106 S") == std::set{ fast_layers ? fan_max_speed : fan_min_speed }); + CHECK(fan_speeds(gcode, "M106 P2 S") == std::set{ additional_fan_speed }); + CHECK(gcode.find("; start range high " + std::to_string(range_high) + "\n") != std::string::npos); + } +} diff --git a/tests/libslic3r/test_config_variant_expansion.cpp b/tests/libslic3r/test_config_variant_expansion.cpp index 17cf7fc305..f30ad985e3 100644 --- a/tests/libslic3r/test_config_variant_expansion.cpp +++ b/tests/libslic3r/test_config_variant_expansion.cpp @@ -52,20 +52,18 @@ TEST_CASE("Filament cooling and pressure settings follow the selected nozzle var {"nozzle_temperature_range_high", "255", "280"}, }; const int variant_index = GENERATE(0, 1, 2, 3); - const int value_count = 4; const bool load_preset = GENERATE(false, true); const bool high_flow = variant_index % 2 != 0; for (const Setting &setting : settings) { - DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " values=" << value_count << " loaded=" << load_preset) { + DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " loaded=" << load_preset) { DynamicPrintConfig printer = make_hybrid_printer_config(); if (variant_index >= 2) printer.option("extruder_type")->values[1] = etBowden; DynamicPrintConfig filament; filament.option("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"}; - std::string values = setting.standard; - for (int i = 1; i < value_count; ++i) - values += std::string(",") + (i % 2 ? setting.high_flow : setting.standard); + // one value per variant of filament_extruder_variant + const std::string values = std::string(setting.standard) + "," + setting.high_flow + "," + setting.standard + "," + setting.high_flow; REQUIRE(filament.option(setting.key, true)->deserialize(values)); if (load_preset) { @@ -88,35 +86,6 @@ TEST_CASE("Filament cooling and pressure settings follow the selected nozzle var } } -TEST_CASE("Filaments on the same hybrid tool keep their own cooling and pressure variants", "[Config][FilamentVariants]") -{ - DynamicPrintConfig config = make_hybrid_printer_config(); - config.option("filament_self_index", true)->values = {1, 1, 2, 2}; - config.option("filament_extruder_variant", true)->values = - {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}; - config.option("filament_map", true)->values = {2, 2}; - config.option("filament_volume_map", true)->values = {nvtHighFlow, nvtStandard}; - config.option("fan_max_speed", true)->values = {15., 60., 20., 80.}; - config.option("pressure_advance", true)->values = {0.04, 0.02, 0.05, 0.03}; - config.option("filament_multitool_ramming", true)->values = {false, true, false, true}; - std::vector> nozzle_types; - const int count = config.get_extruder_nozzle_volume_count(2, nozzle_types); - std::set keys = filament_options_with_variant; - keys.insert("filament_self_index"); - config.update_values_to_printer_extruders_for_multiple_filaments(config, 2, count, keys, - "filament_self_index", "filament_extruder_variant"); - const auto &fan = config.option("fan_max_speed")->values; - REQUIRE(fan.size() == 2); - REQUIRE_THAT(fan[0], Catch::Matchers::WithinAbs(60., 1e-9)); - REQUIRE_THAT(fan[1], Catch::Matchers::WithinAbs(20., 1e-9)); - const auto &pa = config.option("pressure_advance")->values; - REQUIRE(pa.size() == 2); - REQUIRE_THAT(pa[0], Catch::Matchers::WithinAbs(0.02, 1e-9)); - REQUIRE_THAT(pa[1], Catch::Matchers::WithinAbs(0.05, 1e-9)); - REQUIRE(config.option("filament_multitool_ramming")->get_at(0)); - REQUIRE_FALSE(config.option("filament_multitool_ramming")->get_at(1)); -} - TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]") { ConfigOptionFloats machine({10., 20., 30.}); @@ -495,6 +464,8 @@ TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves pe config.option("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}; config.option("filament_max_volumetric_speed", true)->values = {12., 20., 13., 21.}; + config.option("fan_max_speed", true)->values = {15., 60., 20., 80.}; + config.option("filament_multitool_ramming", true)->values = {false, true, false, true}; }; std::set filament_keys = filament_options_with_variant; @@ -514,6 +485,8 @@ TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves pe "filament_self_index", "filament_extruder_variant"); REQUIRE(config.option("filament_max_volumetric_speed")->values == std::vector({12., 21.})); + REQUIRE(config.option("fan_max_speed")->values == std::vector({15., 80.})); + REQUIRE(config.option("filament_multitool_ramming")->values == std::vector({0, 1})); REQUIRE(config.option("filament_extruder_variant")->values == std::vector({"Direct Drive Standard", "Direct Drive High Flow"})); REQUIRE(config.option("filament_self_index")->values == std::vector({1, 2})); diff --git a/tests/libslic3r/test_toolordering_nozzle_group.cpp b/tests/libslic3r/test_toolordering_nozzle_group.cpp index d9a5d70406..4074a80995 100644 --- a/tests/libslic3r/test_toolordering_nozzle_group.cpp +++ b/tests/libslic3r/test_toolordering_nozzle_group.cpp @@ -948,6 +948,9 @@ TEST_CASE("Filaments ordered after a migrator shift columns and the resolver tra config.option("nozzle_temperature", true)->values = {200, 210, 220, 230, 240, 250}; config.option("filament_retraction_length", true)->values = {0.5, 0.5, 0.7, 0.9, 1.4, 1.4}; config.option("retraction_length", true)->values = {0.8, 0.9, 1.0, 1.1}; + config.option("fan_max_speed", true)->values = {10, 10, 20, 60, 30, 30}; + config.option("additional_cooling_fan_speed", true)->values = {1, 1, 2, 6, 3, 3}; + config.option("nozzle_temperature_range_high", true)->values = {230, 230, 240, 280, 250, 250}; Model model; model.add_object("cube", "", make_cube(20, 20, 20))->add_instance(); @@ -988,6 +991,21 @@ TEST_CASE("Filaments ordered after a migrator shift columns and the resolver tra REQUIRE(merged.size() == 4); REQUIRE_THAT(merged[3], Catch::Matchers::WithinAbs(1.4, 1e-9)); REQUIRE_THAT(merged[2], Catch::Matchers::WithinAbs(0.9, 1e-9)); + + // The cooling and temperature range options follow the variant as well. + const PrintConfig &resolved = print.config(); + REQUIRE(resolved.fan_max_speed.values == std::vector{10, 20, 60, 30}); + CHECK(resolved.fan_max_speed.get_at(print.get_filament_config_indx(1, 0)) == 20); + CHECK(resolved.fan_max_speed.get_at(print.get_filament_config_indx(1, 1)) == 60); + CHECK(resolved.additional_cooling_fan_speed.get_at(print.get_filament_config_indx(2, 1)) == 3); + CHECK(resolved.nozzle_temperature_range_high.get_at(print.get_filament_config_indx(2, 1)) == 250); + // The auxiliary fan maximum takes every variant of the filaments used, and only theirs. + ToolOrdering ordering; + ordering.layer_tools().emplace_back(0.2); + ordering.layer_tools().back().extruders = {0, 2}; + CHECK(ordering.cal_max_additional_fan(resolved) == 3); + ordering.layer_tools().back().extruders = {1}; + CHECK(ordering.cal_max_additional_fan(resolved) == 6); } TEST_CASE("Selector slicing keeps the result valid across re-apply", "[Print][H2C][Dynamic]") From e1da47a72b092f75d7619374126ce72e68ff8fd3 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 1 Oct 2026 02:54:18 +0800 Subject: [PATCH 83/89] Keep the printer preset when reselecting its nozzle diameter Printers without a named nozzle variant no longer switch profiles when their current diameter is picked in the sidebar. The filament tab reads the selected variant through one helper. --- src/slic3r/GUI/ConfigManipulation.hpp | 6 ++--- src/slic3r/GUI/Plater.cpp | 19 ++++++++-------- src/slic3r/GUI/Tab.cpp | 32 ++++++++++++++++----------- src/slic3r/GUI/Tab.hpp | 2 ++ 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index bff41effad..a7a1a7ef77 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -79,9 +79,9 @@ public: void apply_null_fff_config(DynamicPrintConfig *config, std::vector const &keys, std::map const & configs); //BBS: FFF filament nozzle temperature range - void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index = 0); - void check_nozzle_temperature_range(DynamicPrintConfig* config, unsigned int variant_index = 0); - void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index = 0); + void check_nozzle_recommended_temperature_range(DynamicPrintConfig *config, unsigned int variant_index); + void check_nozzle_temperature_range(DynamicPrintConfig* config, unsigned int variant_index); + void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index); void check_adaptive_pressure_advance_model(DynamicPrintConfig* config); void check_filament_max_volumetric_speed(DynamicPrintConfig *config); void check_chamber_temperature(DynamicPrintConfig* config); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8d648c4b16..f305849ff8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1797,16 +1797,17 @@ bool Sidebar::priv::switch_diameter_to(const wxString &diameter) Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); // The combo lists printer variants, and the variant of a mixed-nozzle machine ("0.4+0.6") is no // single extruder's diameter, so the preset's own variant answers first. - if (printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) { + const std::string &printer_variant = printer_preset.config.opt_string("printer_variant"); + if (printer_variant == diameter.ToStdString()) { return true; } + // A named variant ("0.4 High Flow") shares its diameter with the standard profile, which selecting + // the plain diameter switches back to, so only a preset naming no variant is kept by its diameter. auto* nozzle_diameter = dynamic_cast(printer_preset.config.option("nozzle_diameter")); - if (nozzle_diameter && nozzle_diameter->size() > 0) { + if (printer_variant.empty() && nozzle_diameter && nozzle_diameter->size() > 0) { auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]); - // A named variant can share this diameter; selecting the plain diameter - // must still switch back to the standard profile. - if (current_nozzle_dia == diameter.ToStdString() && - printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) { + // If the selected diameter is the same as current nozzle, don't switch profiles + if (current_nozzle_dia == diameter.ToStdString()) { return true; } } @@ -3944,9 +3945,9 @@ void Sidebar::update_presets(Preset::Type preset_type) auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]); // Named variants such as "0.4HS" and "0.4 High Flow" share a physical diameter. // Retain the variant selection unless the diameter was customized. - const auto selected_variant = - diameter.substr(0, diameter.find_first_not_of("0123456789.")) == nozzle_dia && - std::find(diameters.begin(), diameters.end(), diameter) != diameters.end() ? diameter : nozzle_dia; + const bool keep_variant = diameter.substr(0, diameter.find_first_not_of("0123456789.")) == nozzle_dia && + std::find(diameters.begin(), diameters.end(), diameter) != diameters.end(); + const std::string &selected_variant = keep_variant ? diameter : nozzle_dia; // ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias if(!diameters.empty() && diameters[0].empty() && !nozzle_dia.empty()){ diameters[0] = nozzle_dia; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9d849cde4b..b6f071fb45 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4404,8 +4404,9 @@ void TabFilament::build() DynamicPrintConfig &filament_config = m_preset_bundle->filaments.get_edited_preset().config; update_dirty(); - if (!m_postpone_update_ui && (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_low" || opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_range_high")) { - m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); + const std::string opt_key_without_idx = opt_key.substr(0, opt_key.find('#')); + if (!m_postpone_update_ui && (opt_key_without_idx == "nozzle_temperature_range_low" || opt_key_without_idx == "nozzle_temperature_range_high")) { + m_config_manipulation.check_nozzle_recommended_temperature_range(&filament_config, selected_variant_index()); } on_value_change(opt_key, value); }; @@ -4520,6 +4521,7 @@ void TabFilament::build() DynamicPrintConfig& filament_config = m_preset_bundle->filaments.get_edited_preset().config; update_dirty(); + const std::string opt_key_without_idx = opt_key.substr(0, opt_key.find('#')); /*if (opt_key == "cool_plate_temp" || opt_key == "cool_plate_temp_initial_layer") { m_config_manipulation.check_bed_temperature_difference(BedType::btPC, &filament_config); } @@ -4532,11 +4534,11 @@ void TabFilament::build() else if (opt_key == "textured_plate_temp" || opt_key == "textured_plate_temp_initial_layer") { m_config_manipulation.check_bed_temperature_difference(BedType::btPTE, &filament_config); } - else */if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature") { - m_config_manipulation.check_nozzle_temperature_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); + else */if (opt_key_without_idx == "nozzle_temperature") { + m_config_manipulation.check_nozzle_temperature_range(&filament_config, selected_variant_index()); } - else if (opt_key.substr(0, opt_key.find('#')) == "nozzle_temperature_initial_layer") { - m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config, m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0); + else if (opt_key_without_idx == "nozzle_temperature_initial_layer") { + m_config_manipulation.check_nozzle_temperature_initial_layer_range(&filament_config, selected_variant_index()); } on_value_change(opt_key, value); @@ -4770,9 +4772,14 @@ void TabFilament::update_description_lines() // this->update_volumetric_flow_preset_hints(); } +unsigned int TabFilament::selected_variant_index() const +{ + return m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; +} + void TabFilament::toggle_options() { - const unsigned int variant_index = m_variant_combo ? std::max(0, m_variant_combo->GetSelection()) : 0; + const unsigned int variant_index = selected_variant_index(); if (!m_active_page) return; bool is_BBL_printer = false; @@ -4882,20 +4889,19 @@ void TabFilament::toggle_options() if (m_active_page->title() == L("Multimaterial")) { // Orca: hide specific settings for BBL printers - for (auto el : {"filament_minimal_purge_on_wipe_tower", "filament_loading_speed_start", "filament_loading_speed", + toggle_option("filament_minimal_purge_on_wipe_tower", !is_BBL_printer, 256 + variant_index); + for (auto el : {"filament_loading_speed_start", "filament_loading_speed", "filament_unloading_speed_start", "filament_unloading_speed", "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed"}) - toggle_option(el, !is_BBL_printer, filament_options_with_variant.count(el) ? 256 + variant_index : -1); + toggle_option(el, !is_BBL_printer); bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", variant_index); toggle_option("filament_multitool_ramming_volume", multitool_ramming, 256 + variant_index); toggle_option("filament_multitool_ramming_flow", multitool_ramming, 256 + variant_index); bool is_BBL_multi_extruder = is_BBL_printer && printer_cfg.option("nozzle_diameter")->size() > 1; - const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0; - const int extruder_idx = std::max(selection, 0); - toggle_line("long_retractions_when_ec", is_BBL_multi_extruder, 256 + extruder_idx); - toggle_line("retraction_distances_when_ec", is_BBL_multi_extruder && m_config->opt_bool("long_retractions_when_ec", extruder_idx), 256 + extruder_idx); + toggle_line("long_retractions_when_ec", is_BBL_multi_extruder, 256 + variant_index); + toggle_line("retraction_distances_when_ec", is_BBL_multi_extruder && m_config->opt_bool("long_retractions_when_ec", variant_index), 256 + variant_index); } } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index fa8a7ae68b..98428b148e 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -616,6 +616,8 @@ private: void add_filament_overrides_page(); void update_filament_overrides_page(const DynamicPrintConfig* printers_config); void update_volumetric_flow_preset_hints(); + // The variant index the variant switch shows, 0 without one. + unsigned int selected_variant_index() const; std::map m_overrides_options; From 35a4d941b880ef30bc8a4944cbe4fc9e18850d03 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 1 Oct 2026 02:54:18 +0800 Subject: [PATCH 84/89] Sync the AMS recommended temperature range with the tray's nozzle variant --- src/libslic3r/PresetBundle.cpp | 17 ++++++++-- src/libslic3r/PresetBundle.hpp | 11 ++++++- src/slic3r/GUI/AMSMaterialsSetting.cpp | 25 ++++++++++++--- src/slic3r/GUI/AMSMaterialsSetting.hpp | 2 ++ src/slic3r/GUI/DeviceManager.cpp | 31 +++++++++---------- src/slic3r/GUI/DeviceManager.hpp | 5 ++- .../libslic3r/test_preset_bundle_loading.cpp | 16 ++++++++++ 7 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index d2913db55e..07d20cfb8b 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -4452,8 +4452,17 @@ std::vector PresetBundle::get_filament_presets_for_machine(const std:: return compatible; } +int PresetBundle::get_filament_variant_index(const DynamicPrintConfig &filament_config, const DynamicPrintConfig &printer_config, + int extruder_id, NozzleVolumeType nozzle_volume_type) +{ + const auto *extruder_types = printer_config.option("extruder_type"); + const ExtruderType extruder_type = extruder_types && !extruder_types->empty() ? ExtruderType(extruder_types->get_at(extruder_id)) : etDirectDrive; + return std::max(0, filament_config.get_index_for_extruder(1, "", extruder_type, nozzle_volume_type, "filament_extruder_variant")); +} + bool PresetBundle::check_filament_temp_equation_by_printer_type_and_nozzle_for_mas_tray( - const std::string &printer_type, std::string& nozzle_diameter_str, std::string &setting_id, std::string &tag_uid, std::string &nozzle_temp_min, std::string &nozzle_temp_max, std::string& preset_setting_id) + const std::string &printer_type, std::string& nozzle_diameter_str, std::string &setting_id, std::string &tag_uid, std::string &nozzle_temp_min, std::string &nozzle_temp_max, std::string& preset_setting_id, + int extruder_id, NozzleVolumeType nozzle_volume_type) { bool is_equation = true; @@ -4476,13 +4485,15 @@ bool PresetBundle::check_filament_temp_equation_by_printer_type_and_nozzle_for_m // Compare only once if (!compared) { compared = true; + const Preset *printer = printers.find_preset(printer_str); + const int variant_index = printer ? get_filament_variant_index(preset->config, printer->config, extruder_id, nozzle_volume_type) : 0; bool min_temp_equation = false, max_temp_equation = false; int min_nozzle_temp = std::stoi(nozzle_temp_min); int max_nozzle_temp = std::stoi(nozzle_temp_max); ConfigOption *opt_min = const_cast(preset)->config.option("nozzle_temperature_range_low"); if (opt_min) { ConfigOptionInts *opt_min_ints = dynamic_cast(opt_min); - min_nozzle_temp = opt_min_ints->get_at(0); + min_nozzle_temp = opt_min_ints->get_at(variant_index); if (std::to_string(min_nozzle_temp) == nozzle_temp_min) min_temp_equation = true; else { @@ -4493,7 +4504,7 @@ bool PresetBundle::check_filament_temp_equation_by_printer_type_and_nozzle_for_m ConfigOption *opt_max = const_cast(preset)->config.option("nozzle_temperature_range_high"); if (opt_max) { ConfigOptionInts *opt_max_ints = dynamic_cast(opt_max); - max_nozzle_temp = opt_max_ints->get_at(0); + max_nozzle_temp = opt_max_ints->get_at(variant_index); if (std::to_string(max_nozzle_temp) == nozzle_temp_max) max_temp_equation = true; else { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 0db1f6d9b1..e09ce9dcb5 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -407,13 +407,22 @@ public: std::vector get_filament_presets_for_machine(const std::string &printer_type, const std::string &nozzle_diameter_str, bool include_user_presets); + // Orca: the variant index of a filament preset's per-variant options on extruder extruder_id of a printer + // preset, with the nozzle volume type the machine reports; 0 when the filament has no such variant. + static int get_filament_variant_index(const DynamicPrintConfig &filament_config, + const DynamicPrintConfig &printer_config, + int extruder_id, + NozzleVolumeType nozzle_volume_type); + // extruder_id and nozzle_volume_type identify the tray's nozzle, whose variant the temperature range is compared for. bool check_filament_temp_equation_by_printer_type_and_nozzle_for_mas_tray(const std::string &printer_type, std::string & nozzle_diameter_str, std::string & setting_id, std::string & tag_uid, std::string & nozzle_temp_min, std::string & nozzle_temp_max, - std::string & preset_setting_id); + std::string & preset_setting_id, + int extruder_id, + NozzleVolumeType nozzle_volume_type); Preset * get_similar_printer_preset(std::string printer_model, std::string printer_variant); PresetCollection prints; diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index f0fdf950a0..edd1827f16 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -1000,11 +1000,12 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi // update if nozzle_temperature_range is found + const int variant_index = get_filament_variant_index(*filament_it, nozzle_diameter_str); ConfigOption *opt_min = filament_it->config.option("nozzle_temperature_range_low"); if (opt_min) { ConfigOptionInts *opt_min_ints = dynamic_cast(opt_min); if (opt_min_ints) { - wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(0)); + wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(variant_index)); m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min); } } @@ -1012,7 +1013,7 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi if (opt_max) { ConfigOptionInts *opt_max_ints = dynamic_cast(opt_max); if (opt_max_ints) { - wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(0)); + wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(variant_index)); m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max); } } @@ -1218,6 +1219,21 @@ void AMSMaterialsSetting::on_select_cali_result(wxCommandEvent &evt) } } +int AMSMaterialsSetting::get_filament_variant_index(const Preset &filament, const std::string &nozzle_diameter_str) +{ + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + if (!obj || !preset_bundle) + return 0; + const std::set printer_names = + preset_bundle->get_printer_names_by_printer_type_and_nozzle(DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str); + const Preset *printer = printer_names.empty() ? nullptr : preset_bundle->printers.find_preset(*printer_names.begin()); + if (!printer) + return 0; + const int extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(ams_id)); + return PresetBundle::get_filament_variant_index(filament.config, printer->config, obj->get_preset_extruder_index(extruder_id), + DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id))); +} + void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) { // Get the flag whether to open the filament setting dialog from the device page @@ -1249,11 +1265,12 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) if (it->filament_id != filament_id) continue; // ) if nozzle_temperature_range is found + const int variant_index = get_filament_variant_index(*it, nozzle_diameter_str); ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low"); if (opt_min) { ConfigOptionInts* opt_min_ints = dynamic_cast(opt_min); if (opt_min_ints) { - wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(0)); + wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(variant_index)); m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min); } } @@ -1261,7 +1278,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) if (opt_max) { ConfigOptionInts* opt_max_ints = dynamic_cast(opt_max); if (opt_max_ints) { - wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(0)); + wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(variant_index)); m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max); } } diff --git a/src/slic3r/GUI/AMSMaterialsSetting.hpp b/src/slic3r/GUI/AMSMaterialsSetting.hpp index 2cbd58e9a9..4cfdde791d 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.hpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.hpp @@ -150,6 +150,8 @@ protected: void update_widgets(); void update_filament_editing(bool is_printing); + // Orca: the variant index of the filament's per-variant options on the nozzle this tray feeds + int get_filament_variant_index(const Preset &filament, const std::string &nozzle_diameter_str); protected: StateColor m_btn_bg_green; diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 7d9abdee12..29fd113d1f 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -5696,7 +5696,7 @@ void MachineObject::update_filament_list() PresetBundle *preset_bundle = Slic3r::GUI::wxGetApp().preset_bundle; // custom filament - typedef std::map> map_pair; + typedef std::map, std::vector>> map_pair; std::map map_list; for (auto &pair : m_nozzle_filament_data) { map_list[pair.second.printer_preset_name] = map_pair{}; @@ -5708,19 +5708,13 @@ void MachineObject::update_filament_list() for (const std::string &printer_str : printer_strs->values) { if (map_list.find(printer_str) != map_list.end()) { auto & filament_list = map_list[printer_str]; - ConfigOption *opt_min = const_cast(preset).config.option("nozzle_temperature_range_low"); - int min_temp = -1; - if (opt_min) { - ConfigOptionInts *opt_min_ints = dynamic_cast(opt_min); - min_temp = opt_min_ints->get_at(0); - } - ConfigOption *opt_max = const_cast(preset).config.option("nozzle_temperature_range_high"); - int max_temp = -1; - if (opt_max) { - ConfigOptionInts *opt_max_ints = dynamic_cast(opt_max); - max_temp = opt_max_ints->get_at(0); - } - filament_list[preset.filament_id] = std::make_pair(min_temp, max_temp); + // Every variant's range, so a change to any of them rechecks the trays + std::vector min_temps{-1}, max_temps{-1}; + if (auto *opt_min = preset.config.option("nozzle_temperature_range_low")) + min_temps = opt_min->values; + if (auto *opt_max = preset.config.option("nozzle_temperature_range_high")) + max_temps = opt_max->values; + filament_list[preset.filament_id] = std::make_pair(min_temps, max_temps); break; } } @@ -5856,10 +5850,13 @@ void MachineObject::check_ams_filament_valid() need_checked_filament_id[nozzle_diameter_str].insert(curr_tray->setting_id); try { std::string preset_setting_id; + const int extruder_id = ams->GetExtruderId(); bool is_equation = preset_bundle->check_filament_temp_equation_by_printer_type_and_nozzle_for_mas_tray(printer_model, nozzle_diameter_str, curr_tray->setting_id, curr_tray->tag_uid, curr_tray->nozzle_temp_min, - curr_tray->nozzle_temp_max, preset_setting_id); + curr_tray->nozzle_temp_max, preset_setting_id, + get_preset_extruder_index(extruder_id), + DevNozzle::ToNozzleVolumeType(m_extder_system->GetNozzleFlowType(extruder_id))); if (!is_equation) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << " ams filament is not match min max temp and reset, ams_id: " << ams_id << " tray_id" << slot_id << "filament_id: " << curr_tray->setting_id; @@ -5924,7 +5921,9 @@ void MachineObject::check_ams_filament_valid() this->printer_type), nozzle_diameter_str, vt_tray.setting_id, vt_tray.tag_uid, vt_tray.nozzle_temp_min, - vt_tray.nozzle_temp_max, preset_setting_id); + vt_tray.nozzle_temp_max, preset_setting_id, + get_preset_extruder_index(index), + DevNozzle::ToNozzleVolumeType(m_extder_system->GetNozzleFlowType(index))); if (!is_equation) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << " vt_tray filament is not match min max temp and reset, filament_id: " << vt_tray.setting_id; diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index b69e2f8f5a..94f867b93d 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -341,6 +341,8 @@ public: bool is_main_extruder_on_left() const { return false; } // only means the extruder is on the left hand when extruder id is 0 bool is_multi_extruders() const; int get_extruder_id_by_ams_id(const std::string& ams_id); + // Orca: the printer preset's extruder index of device extruder extder_id + int get_preset_extruder_index(int extder_id) const { return is_multi_extruders() ? (is_main_extruder_on_left() ? extder_id : 1 - extder_id) : 0; } /* E3D has extra nozzle flow type info */ bool has_extra_flow_type{false}; @@ -957,7 +959,8 @@ public: { std::set checked_filament; std::string printer_preset_name; - std::map> filament_list; // filament_id, pair + // filament_id, pair, one per filament variant + std::map, std::vector>> filament_list; }; std::map m_nozzle_filament_data; void update_filament_list(); diff --git a/tests/libslic3r/test_preset_bundle_loading.cpp b/tests/libslic3r/test_preset_bundle_loading.cpp index b1ec3903bb..addc33b468 100644 --- a/tests/libslic3r/test_preset_bundle_loading.cpp +++ b/tests/libslic3r/test_preset_bundle_loading.cpp @@ -5940,3 +5940,19 @@ TEST_CASE("A project's different settings always keep the preset bookkeeping key CHECK(keys.count(key) == 1); CHECK(PresetBundle::project_different_keys(std::string()).count("inherits") == 1); } + +// A connected machine's tray is checked against the filament variant of the nozzle it feeds. +TEST_CASE("A filament's variant index follows the extruder type and nozzle volume type", "[Preset]") +{ + DynamicPrintConfig filament; + filament.option("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow", + "Bowden Standard"}; + DynamicPrintConfig printer; + printer.option("extruder_type", true)->values = {etDirectDrive, etBowden}; + + CHECK(PresetBundle::get_filament_variant_index(filament, printer, 0, nvtStandard) == 0); + CHECK(PresetBundle::get_filament_variant_index(filament, printer, 0, nvtHighFlow) == 1); + CHECK(PresetBundle::get_filament_variant_index(filament, printer, 1, nvtStandard) == 2); + // the filament defines no Bowden High Flow variant + CHECK(PresetBundle::get_filament_variant_index(filament, printer, 1, nvtHighFlow) == 0); +} From 865e9963c3fb710ecd87624feef5e879dea10882 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Wed, 30 Sep 2026 16:58:22 -0500 Subject: [PATCH 85/89] perf: speed up G-code export by 7-26% via typed config apply (#16026) --- src/libslic3r/Config.hpp | 6 ++- src/libslic3r/PrintConfig.cpp | 15 +++++++ src/libslic3r/PrintConfig.hpp | 19 ++++++--- tests/libslic3r/test_config.cpp | 70 +++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 52b16f9458..9818887c1d 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2702,6 +2702,9 @@ public: virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; // Collect names of all configuration values maintained by this configuration store. virtual t_config_option_keys keys() const = 0; + // Set this config's options on target member by member, when target is of this config's static type or + // derives from it, and return true. apply() prefers this to looking every key up by name. + virtual bool apply_to(ConfigBase &/*target*/) const { return false; } protected: // Verify whether the opt_key has not been obsoleted or renamed. @@ -2753,7 +2756,8 @@ public: // Apply all keys of other ConfigBase defined by this->def() to this ConfigBase. // An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(), // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. - void apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->apply_only(other, other.keys(), ignore_nonexistent); } + void apply(const ConfigBase &other, bool ignore_nonexistent = false) + { if (! other.apply_to(*this)) this->apply_only(other, other.keys(), ignore_nonexistent); } // Apply explicitely enumerated keys of other ConfigBase defined by this->def() to this ConfigBase. // An UnknownOptionException is thrown in case some option keys are not defined by this->def(), // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 94220e31fd..5043c317c6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -11958,6 +11958,21 @@ PRINT_CONFIG_CACHE_INITIALIZE(( SLAMaterialConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAPrinterConfig, SLAFullPrintConfig)) static int print_config_static_initialized = print_config_static_initializer(); +// The same set() calls ConfigBase::apply_only() makes, without looking every key up by name. Out of line so the +// option list is expanded for this once, not in every file that includes PrintConfig.hpp. +#define PRINT_CONFIG_APPLY_TO_DEFINITION(r, data, CLASS_NAME) \ + bool CLASS_NAME::apply_to(ConfigBase &target) const \ + { \ + auto *dst = dynamic_cast(&target); \ + if (dst == nullptr) \ + return false; \ + visit_option_pairs(*dst, *this, [](const char*, ConfigOption &a, const ConfigOption &b) { a.set(&b); return true; }); \ + return true; \ + } +BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_APPLY_TO_DEFINITION, _, (PrintObjectConfig)(PrintRegionConfig)(MachineEnvelopeConfig)(GCodeConfig) + (SLAMaterialConfig)(SLAPrintConfig)(SLAPrintObjectConfig)(SLAPrinterConfig)) +#undef PRINT_CONFIG_APPLY_TO_DEFINITION + //BBS: remove unused command currently CLIActionsConfigDef::CLIActionsConfigDef() { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1e91f2e451..bbd92079c0 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1033,9 +1033,10 @@ public: \ #define PRINT_CONFIG_CLASS_ELEMENT_DEFINITION(r, data, elem) BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem); #define PRINT_CONFIG_CLASS_ELEMENT_VISIT(r, data, elem) if (! f(BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)), this->BOOST_PP_TUPLE_ELEM(1, elem), rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return; +#define PRINT_CONFIG_CLASS_ELEMENT_VISIT_SELF(r, data, elem) if (! f(BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)), self.BOOST_PP_TUPLE_ELEM(1, elem), rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return; // Each option list is expanded into the members and again into for_each_option_pair(), which calls // f(key, this->option, rhs.option) in declaration order and stops when f returns false. hash(), -// operator==, operator< and initialize() iterate the options through that visitor. +// operator==, operator<, initialize() and apply_to() iterate the options through that visitor. #define PRINT_CONFIG_CLASS_COMMON_BODY(CLASS_NAME) \ size_t hash() const throw() \ { \ @@ -1067,11 +1068,16 @@ class CLASS_NAME : public StaticPrintConfig { \ STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ public: \ BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ) \ - template void for_each_option_pair(const CLASS_NAME &rhs, F &&f) const \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_VISIT, _, PARAMETER_DEFINITION_SEQ) \ - } \ + template void for_each_option_pair(const CLASS_NAME &rhs, F &&f) const { visit_option_pairs(*this, rhs, f); } \ + /* Defined in PrintConfig.cpp. */ \ + bool apply_to(ConfigBase &target) const override; \ PRINT_CONFIG_CLASS_COMMON_BODY(CLASS_NAME) \ +private: \ + /* The one expansion of the option list, for a const self and for apply_to()'s mutable target. */ \ + template static void visit_option_pairs(Self &self, const CLASS_NAME &rhs, F &&f) \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_VISIT_SELF, _, PARAMETER_DEFINITION_SEQ) \ + } \ }; #define PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST_ITEM(r, data, i, elem) BOOST_PP_COMMA_IF(i) public elem @@ -1092,6 +1098,8 @@ class CLASS_NAME : PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST(CLASSES_PARENTS_TUPLE) public: \ PARAMETER_DEFINITION \ template void for_each_option_pair(const CLASS_NAME &rhs, F &&f) const { PARAMETER_VISIT } \ + /* Its parents each apply themselves to a target member by member, so this one keeps the lookup by name. */ \ + bool apply_to(ConfigBase &/*target*/) const override { return false; } \ size_t hash() const throw() \ { \ size_t seed = 0; \ @@ -2182,6 +2190,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0( #undef STATIC_PRINT_CONFIG_CACHE_DERIVED #undef PRINT_CONFIG_CLASS_ELEMENT_DEFINITION #undef PRINT_CONFIG_CLASS_ELEMENT_VISIT +#undef PRINT_CONFIG_CLASS_ELEMENT_VISIT_SELF #undef PRINT_CONFIG_CLASS_COMMON_BODY #undef PRINT_CONFIG_CLASS_DEFINE #undef PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index 0da7b7bafe..00c4d6d170 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -1289,3 +1289,73 @@ TEST_CASE("Static print configs compare, order and hash by their option values", REQUIRE(c.optptr("gcode_flavor") == &c.gcode_flavor); } } + +namespace { + +// Keys whose values differ between two full configs, compared as text so enum names count too. +std::vector differing_keys(const FullPrintConfig &a, const FullPrintConfig &b) +{ + std::vector keys; + for (const std::string &key : a.keys()) + if (a.opt_serialize(key) != b.opt_serialize(key)) + keys.push_back(key); + return keys; +} + +// Applies source to one full config member by member and to another key by key, as apply() did before +// static configs could apply themselves. +template void check_member_apply_matches_key_apply(const Source &source) +{ + FullPrintConfig by_member; + FullPrintConfig by_key; + by_member.apply(source); + by_key.apply_only(source, source.keys()); + CHECK(differing_keys(by_member, by_key).empty()); + CHECK_FALSE(differing_keys(by_member, FullPrintConfig()).empty()); +} + +} // namespace + +TEST_CASE("A static config applies itself onto a config of its type as a lookup by name would", "[Config]") +{ + SECTION("region config") + { + PrintRegionConfig region; + region.sparse_infill_pattern.value = ipGyroid; + region.outer_wall_speed.values = {37.}; + region.sparse_infill_density.value = 35.; + FullPrintConfig full; + REQUIRE(region.apply_to(full)); + check_member_apply_matches_key_apply(region); + } + SECTION("object config") + { + PrintObjectConfig object; + object.seam_position.value = spRear; + object.wall_generator.value = PerimeterGeneratorType::Arachne; + object.support_speed.values = {33.}; + object.enable_support.value = true; + FullPrintConfig full; + REQUIRE(object.apply_to(full)); + check_member_apply_matches_key_apply(object); + } + SECTION("G-code config, whose enum lists carry their names through a keys map") + { + GCodeConfig gcode; + gcode.z_hop_types.values = {int(zhtSpiral)}; + gcode.retraction_length.values = {1.5}; + FullPrintConfig full; + REQUIRE(gcode.apply_to(full)); + check_member_apply_matches_key_apply(gcode); + } +} + +TEST_CASE("A static config applied onto a config of another type falls back to a lookup by name", "[Config]") +{ + PrintRegionConfig region; + region.sparse_infill_pattern.value = ipGyroid; + DynamicPrintConfig dynamic; + REQUIRE_FALSE(region.apply_to(dynamic)); + dynamic.apply(region); + CHECK(dynamic.opt_serialize("sparse_infill_pattern") == "gyroid"); +} From d849b30906fc8b1022a5a74480259a4cbc44cb4d Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 1 Oct 2026 11:21:16 +0800 Subject: [PATCH 86/89] Make BBL and library filaments pass the variant width check Fan speeds, the recommended nozzle temperature range, minimal purge and multi-tool ramming flow now take one value per extruder variant. Each single value is repeated for every variant the filament lists, so every preset loads exactly what it did before. --- resources/profiles/BBL.json | 2 +- .../filament/Bambu ABS @BBL H2C 0.2 nozzle.json | 2 ++ .../filament/Bambu ABS @BBL H2C 0.6 nozzle.json | 2 ++ .../filament/Bambu ABS @BBL H2C 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ABS @BBL H2C.json | 2 ++ .../filament/Bambu ABS @BBL H2D 0.2 nozzle.json | 1 + .../filament/Bambu ABS @BBL H2D 0.6 nozzle.json | 2 ++ .../filament/Bambu ABS @BBL H2D 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ABS @BBL H2D.json | 2 ++ .../filament/Bambu ABS @BBL H2DP 0.2 nozzle.json | 1 + .../filament/Bambu ABS @BBL H2DP 0.6 nozzle.json | 1 + .../filament/Bambu ABS @BBL H2DP 0.8 nozzle.json | 1 + .../BBL/filament/Bambu ABS @BBL H2DP.json | 1 + .../filament/Bambu ABS @BBL H2S 0.2 nozzle.json | 2 ++ .../filament/Bambu ABS @BBL H2S 0.6 nozzle.json | 2 ++ .../filament/Bambu ABS @BBL H2S 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ABS @BBL H2S.json | 2 ++ .../filament/Bambu ABS @BBL P1S 0.4 nozzle.json | 1 + .../filament/Bambu ABS @BBL P1S 0.6 nozzle.json | 1 + .../filament/Bambu ABS @BBL P2S 0.2 nozzle.json | 1 + .../filament/Bambu ABS @BBL P2S 0.6 nozzle.json | 2 ++ .../filament/Bambu ABS @BBL P2S 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ABS @BBL P2S.json | 2 ++ .../filament/Bambu ABS @BBL X1C 0.2 nozzle.json | 1 + .../filament/Bambu ABS @BBL X1C 0.6 nozzle.json | 1 + .../filament/Bambu ABS @BBL X1C 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ABS @BBL X1C.json | 1 + .../filament/Bambu ABS @BBL X1E 0.2 nozzle.json | 1 + .../filament/Bambu ABS @BBL X1E 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ABS @BBL X1E.json | 1 + .../filament/Bambu ABS @BBL X2D 0.2 nozzle.json | 3 +++ .../filament/Bambu ABS @BBL X2D 0.4 nozzle.json | 5 +++++ .../filament/Bambu ABS @BBL X2D 0.8 nozzle.json | 3 +++ .../profiles/BBL/filament/Bambu ABS @BBL X2D.json | 5 +++++ .../filament/Bambu ASA @BBL P2S 0.2 nozzle.json | 2 ++ .../filament/Bambu ASA @BBL P2S 0.6 nozzle.json | 2 ++ .../filament/Bambu ASA @BBL P2S 0.8 nozzle.json | 1 + .../filament/Bambu ASA @BBL X1 0.6 nozzle.json | 1 + .../profiles/BBL/filament/Bambu ASA @BBL X1C.json | 1 + .../profiles/BBL/filament/Bambu ASA @BBL X1E.json | 1 + .../filament/Bambu ASA @BBL X2D 0.8 nozzle.json | 3 +++ .../profiles/BBL/filament/Bambu ASA @BBL X2D.json | 5 +++++ .../BBL/filament/Bambu PA-CF @BBL H2S.json | 4 ++++ .../BBL/filament/Bambu PA-CF @BBL P2S.json | 4 ++++ .../BBL/filament/Bambu PA-CF @BBL X1C.json | 2 ++ .../BBL/filament/Bambu PA-CF @BBL X1E.json | 2 ++ .../filament/Bambu PA-CF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../BBL/filament/Bambu PA-CF @BBL X2D.json | 10 ++++++++++ .../Bambu PA6-CF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../Bambu PAHT-CF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../filament/Bambu PC @BBL H2C 0.2 nozzle.json | 1 + .../filament/Bambu PC @BBL H2C 0.6 nozzle.json | 2 ++ .../filament/Bambu PC @BBL H2C 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu PC @BBL H2C.json | 2 ++ .../filament/Bambu PC @BBL H2D 0.2 nozzle.json | 1 + .../filament/Bambu PC @BBL H2D 0.4 nozzle.json | 2 ++ .../filament/Bambu PC @BBL H2D 0.6 nozzle.json | 2 ++ .../filament/Bambu PC @BBL H2D 0.8 nozzle.json | 1 + .../filament/Bambu PC @BBL H2DP 0.2 nozzle.json | 1 + .../filament/Bambu PC @BBL H2DP 0.4 nozzle.json | 1 + .../filament/Bambu PC @BBL H2DP 0.6 nozzle.json | 1 + .../filament/Bambu PC @BBL H2DP 0.8 nozzle.json | 1 + .../filament/Bambu PC @BBL H2S 0.2 nozzle.json | 1 + .../filament/Bambu PC @BBL H2S 0.6 nozzle.json | 2 ++ .../filament/Bambu PC @BBL H2S 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu PC @BBL H2S.json | 2 ++ .../filament/Bambu PC @BBL P2S 0.4 nozzle.json | 2 ++ .../filament/Bambu PC @BBL P2S 0.6 nozzle.json | 2 ++ .../filament/Bambu PC @BBL P2S 0.8 nozzle.json | 1 + .../filament/Bambu PC @BBL X1C 0.2 nozzle.json | 1 + .../filament/Bambu PC @BBL X1C 0.6 nozzle.json | 1 + .../filament/Bambu PC @BBL X1C 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu PC @BBL X1C.json | 1 + .../filament/Bambu PC @BBL X1E 0.2 nozzle.json | 1 + .../filament/Bambu PC @BBL X1E 0.6 nozzle.json | 1 + .../filament/Bambu PC @BBL X1E 0.8 nozzle.json | 1 + .../profiles/BBL/filament/Bambu PC @BBL X1E.json | 1 + .../filament/Bambu PC @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../filament/Bambu PC @BBL X2D 0.8 nozzle.json | 3 +++ .../profiles/BBL/filament/Bambu PC @BBL X2D.json | 5 +++++ .../filament/Bambu PC FR @BBL H2C 0.2 nozzle.json | 1 + .../filament/Bambu PC FR @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PC FR @BBL H2C.json | 2 ++ .../filament/Bambu PC FR @BBL H2D 0.2 nozzle.json | 1 + .../filament/Bambu PC FR @BBL H2D 0.4 nozzle.json | 2 ++ .../filament/Bambu PC FR @BBL H2D 0.6 nozzle.json | 2 ++ .../filament/Bambu PC FR @BBL H2D 0.8 nozzle.json | 1 + .../Bambu PC FR @BBL H2DP 0.2 nozzle.json | 1 + .../Bambu PC FR @BBL H2DP 0.4 nozzle.json | 1 + .../Bambu PC FR @BBL H2DP 0.6 nozzle.json | 1 + .../Bambu PC FR @BBL H2DP 0.8 nozzle.json | 1 + .../filament/Bambu PC FR @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PC FR @BBL H2S.json | 2 ++ .../filament/Bambu PC FR @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PC FR @BBL P1P.json | 1 + .../filament/Bambu PC FR @BBL P1S 0.2 nozzle.json | 1 + .../filament/Bambu PC FR @BBL P1S 0.6 nozzle.json | 1 + .../filament/Bambu PC FR @BBL P1S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PC FR @BBL P1S.json | 1 + .../filament/Bambu PC FR @BBL P2S 0.4 nozzle.json | 2 ++ .../filament/Bambu PC FR @BBL P2S 0.6 nozzle.json | 2 ++ .../filament/Bambu PC FR @BBL P2S 0.8 nozzle.json | 1 + .../filament/Bambu PC FR @BBL X1C 0.2 nozzle.json | 1 + .../filament/Bambu PC FR @BBL X1C 0.6 nozzle.json | 1 + .../filament/Bambu PC FR @BBL X1C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PC FR @BBL X1C.json | 1 + .../filament/Bambu PC FR @BBL X1E 0.2 nozzle.json | 1 + .../filament/Bambu PC FR @BBL X1E 0.6 nozzle.json | 1 + .../filament/Bambu PC FR @BBL X1E 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PC FR @BBL X1E.json | 1 + .../filament/Bambu PC FR @BBL X2D 0.4 nozzle.json | 5 +++++ .../filament/Bambu PC FR @BBL X2D 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PC FR @BBL X2D.json | 5 +++++ .../Bambu PET-CF @BBL H2C 0.4 nozzle.json | 4 ++++ .../BBL/filament/Bambu PET-CF @BBL H2C.json | 4 ++++ .../BBL/filament/Bambu PET-CF @BBL H2D.json | 4 ++++ .../BBL/filament/Bambu PET-CF @BBL H2DP.json | 2 ++ .../BBL/filament/Bambu PET-CF @BBL H2S.json | 4 ++++ .../BBL/filament/Bambu PET-CF @BBL P2S.json | 4 ++++ .../BBL/filament/Bambu PET-CF @BBL X1C.json | 2 ++ .../BBL/filament/Bambu PET-CF @BBL X1E.json | 2 ++ .../Bambu PET-CF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../BBL/filament/Bambu PET-CF @BBL X2D.json | 10 ++++++++++ .../Bambu PETG Basic @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2C 0.4 nozzle.json | 4 ++++ .../Bambu PETG Basic @BBL H2C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PETG Basic @BBL H2C.json | 2 ++ .../Bambu PETG Basic @BBL H2D 0.2 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2D 0.4 nozzle.json | 4 ++++ .../Bambu PETG Basic @BBL H2D 0.6 nozzle.json | 2 ++ .../Bambu PETG Basic @BBL H2D 0.8 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2DP 0.2 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2DP 0.4 nozzle.json | 2 ++ .../Bambu PETG Basic @BBL H2DP 0.6 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2DP 0.8 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2S 0.2 nozzle.json | 1 + .../Bambu PETG Basic @BBL H2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG Basic @BBL H2S 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG Basic @BBL H2S.json | 4 ++++ .../Bambu PETG Basic @BBL P2S 0.2 nozzle.json | 2 ++ .../Bambu PETG Basic @BBL P2S 0.4 nozzle.json | 4 ++++ .../Bambu PETG Basic @BBL P2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG Basic @BBL P2S 0.8 nozzle.json | 2 ++ .../Bambu PETG Basic @BBL X1C 0.2 nozzle.json | 2 ++ .../Bambu PETG Basic @BBL X1C 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG Basic @BBL X1C.json | 1 + .../Bambu PETG Basic @BBL X2D 0.2 nozzle.json | 6 ++++++ .../Bambu PETG Basic @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../Bambu PETG Basic @BBL X2D 0.8 nozzle.json | 6 ++++++ .../BBL/filament/Bambu PETG Basic @BBL X2D.json | 10 ++++++++++ .../Bambu PETG HF @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PETG HF @BBL H2C 0.6 nozzle.json | 2 ++ .../Bambu PETG HF @BBL H2C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PETG HF @BBL H2C.json | 2 ++ .../Bambu PETG HF @BBL H2D 0.2 nozzle.json | 1 + .../Bambu PETG HF @BBL H2D 0.4 nozzle.json | 4 ++++ .../Bambu PETG HF @BBL H2D 0.6 nozzle.json | 2 ++ .../Bambu PETG HF @BBL H2D 0.8 nozzle.json | 1 + .../Bambu PETG HF @BBL H2DP 0.2 nozzle.json | 1 + .../Bambu PETG HF @BBL H2DP 0.4 nozzle.json | 1 + .../Bambu PETG HF @BBL H2DP 0.6 nozzle.json | 1 + .../Bambu PETG HF @BBL H2DP 0.8 nozzle.json | 1 + .../Bambu PETG HF @BBL H2S 0.2 nozzle.json | 2 ++ .../Bambu PETG HF @BBL H2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG HF @BBL H2S 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG HF @BBL H2S.json | 4 ++++ .../Bambu PETG HF @BBL P1P 0.4 nozzle.json | 1 + .../Bambu PETG HF @BBL P1S 0.4 nozzle.json | 1 + .../Bambu PETG HF @BBL P2S 0.2 nozzle.json | 3 +++ .../Bambu PETG HF @BBL P2S 0.4 nozzle.json | 4 ++++ .../Bambu PETG HF @BBL P2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG HF @BBL P2S 0.8 nozzle.json | 2 ++ .../Bambu PETG HF @BBL X1C 0.2 nozzle.json | 1 + .../Bambu PETG HF @BBL X1C 0.6 nozzle.json | 1 + .../Bambu PETG HF @BBL X1C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PETG HF @BBL X1C.json | 1 + .../Bambu PETG HF @BBL X2D 0.2 nozzle.json | 6 ++++++ .../Bambu PETG HF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../Bambu PETG HF @BBL X2D 0.8 nozzle.json | 6 ++++++ .../BBL/filament/Bambu PETG HF @BBL X2D.json | 10 ++++++++++ .../Bambu PETG Matte @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PETG Matte @BBL H2C 0.4 nozzle.json | 4 ++++ .../Bambu PETG Matte @BBL H2C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PETG Matte @BBL H2C.json | 2 ++ .../Bambu PETG Matte @BBL H2D 0.2 nozzle.json | 1 + .../Bambu PETG Matte @BBL H2D 0.4 nozzle.json | 4 ++++ .../Bambu PETG Matte @BBL H2D 0.6 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL H2D 0.8 nozzle.json | 1 + .../Bambu PETG Matte @BBL H2DP 0.2 nozzle.json | 1 + .../Bambu PETG Matte @BBL H2DP 0.4 nozzle.json | 4 ++++ .../Bambu PETG Matte @BBL H2DP 0.6 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL H2DP 0.8 nozzle.json | 1 + .../Bambu PETG Matte @BBL H2S 0.2 nozzle.json | 1 + .../Bambu PETG Matte @BBL H2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG Matte @BBL H2S 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG Matte @BBL H2S.json | 4 ++++ .../Bambu PETG Matte @BBL P1P 0.2 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL P1P 0.4 nozzle.json | 1 + .../Bambu PETG Matte @BBL P1P 0.6 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL P2S 0.2 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL P2S 0.4 nozzle.json | 4 ++++ .../Bambu PETG Matte @BBL P2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG Matte @BBL P2S 0.8 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL X1C 0.2 nozzle.json | 2 ++ .../Bambu PETG Matte @BBL X1C 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG Matte @BBL X1C.json | 1 + .../Bambu PETG Matte @BBL X2D 0.2 nozzle.json | 6 ++++++ .../Bambu PETG Matte @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../Bambu PETG Matte @BBL X2D 0.8 nozzle.json | 6 ++++++ .../BBL/filament/Bambu PETG Matte @BBL X2D.json | 10 ++++++++++ ...ambu PETG Translucent @BBL H2C 0.8 nozzle.json | 2 ++ .../filament/Bambu PETG Translucent @BBL H2C.json | 4 ++++ ...ambu PETG Translucent @BBL H2D 0.6 nozzle.json | 4 ++++ ...ambu PETG Translucent @BBL H2D 0.8 nozzle.json | 2 ++ ...mbu PETG Translucent @BBL H2DP 0.6 nozzle.json | 2 ++ ...mbu PETG Translucent @BBL H2DP 0.8 nozzle.json | 2 ++ ...ambu PETG Translucent @BBL P2S 0.2 nozzle.json | 3 +++ ...ambu PETG Translucent @BBL P2S 0.6 nozzle.json | 4 ++++ ...ambu PETG Translucent @BBL P2S 0.8 nozzle.json | 2 ++ ...ambu PETG Translucent @BBL X1C 0.8 nozzle.json | 2 ++ ...ambu PETG Translucent @BBL X2D 0.2 nozzle.json | 9 +++++++++ ...ambu PETG Translucent @BBL X2D 0.4 nozzle.json | 15 +++++++++++++++ ...ambu PETG Translucent @BBL X2D 0.8 nozzle.json | 6 ++++++ .../filament/Bambu PETG Translucent @BBL X2D.json | 10 ++++++++++ .../Bambu PETG-CF @BBL H2C 0.4 nozzle.json | 4 ++++ .../Bambu PETG-CF @BBL H2C 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG-CF @BBL H2C.json | 4 ++++ .../Bambu PETG-CF @BBL H2D 0.4 nozzle.json | 4 ++++ .../Bambu PETG-CF @BBL H2D 0.6 nozzle.json | 4 ++++ .../Bambu PETG-CF @BBL H2D 0.8 nozzle.json | 2 ++ .../Bambu PETG-CF @BBL H2DP 0.4 nozzle.json | 2 ++ .../Bambu PETG-CF @BBL H2DP 0.6 nozzle.json | 2 ++ .../Bambu PETG-CF @BBL H2DP 0.8 nozzle.json | 2 ++ .../Bambu PETG-CF @BBL H2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG-CF @BBL H2S 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG-CF @BBL H2S.json | 4 ++++ .../Bambu PETG-CF @BBL P2S 0.4 nozzle.json | 4 ++++ .../Bambu PETG-CF @BBL P2S 0.6 nozzle.json | 4 ++++ .../Bambu PETG-CF @BBL P2S 0.8 nozzle.json | 2 ++ .../Bambu PETG-CF @BBL X1C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PETG-CF @BBL X1C.json | 2 ++ .../Bambu PETG-CF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../Bambu PETG-CF @BBL X2D 0.8 nozzle.json | 6 ++++++ .../BBL/filament/Bambu PETG-CF @BBL X2D.json | 10 ++++++++++ .../Bambu PLA Aero @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Aero @BBL H2C.json | 2 ++ .../BBL/filament/Bambu PLA Aero @BBL H2D.json | 2 ++ .../BBL/filament/Bambu PLA Aero @BBL H2DP.json | 1 + .../BBL/filament/Bambu PLA Aero @BBL H2S.json | 2 ++ .../BBL/filament/Bambu PLA Aero @BBL P2S.json | 2 ++ .../BBL/filament/Bambu PLA Aero @BBL X1.json | 1 + .../BBL/filament/Bambu PLA Aero @BBL X1C.json | 1 + .../Bambu PLA Aero @BBL X2D 0.4 nozzle.json | 5 +++++ .../BBL/filament/Bambu PLA Aero @BBL X2D.json | 5 +++++ .../Bambu PLA Basic @BBL H2C 0.2 nozzle.json | 3 +++ .../Bambu PLA Basic @BBL H2C 0.6 nozzle.json | 6 ++++++ .../Bambu PLA Basic @BBL H2C 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Basic @BBL H2C.json | 6 ++++++ .../Bambu PLA Basic @BBL H2D 0.2 nozzle.json | 3 +++ .../Bambu PLA Basic @BBL H2D 0.6 nozzle.json | 6 ++++++ .../Bambu PLA Basic @BBL H2D 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Basic @BBL H2D.json | 6 ++++++ .../Bambu PLA Basic @BBL H2DP 0.2 nozzle.json | 3 +++ .../Bambu PLA Basic @BBL H2DP 0.6 nozzle.json | 3 +++ .../Bambu PLA Basic @BBL H2DP 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Basic @BBL H2DP.json | 3 +++ .../Bambu PLA Basic @BBL H2S 0.2 nozzle.json | 2 ++ .../Bambu PLA Basic @BBL H2S 0.6 nozzle.json | 4 ++++ .../Bambu PLA Basic @BBL H2S 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Basic @BBL H2S.json | 4 ++++ .../Bambu PLA Dynamic @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Dynamic @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Dynamic @BBL H2C.json | 2 ++ .../Bambu PLA Dynamic @BBL H2S 0.2 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Dynamic @BBL H2S.json | 4 ++++ .../Bambu PLA Dynamic @BBL P1P 0.2 nozzle.json | 1 + .../Bambu PLA Dynamic @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/Bambu PLA Dynamic @BBL P1P.json | 1 + .../Bambu PLA Galaxy @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Galaxy @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Galaxy @BBL H2C.json | 2 ++ .../Bambu PLA Galaxy @BBL H2S 0.2 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Galaxy @BBL H2S.json | 4 ++++ .../Bambu PLA Galaxy @BBL P1P 0.2 nozzle.json | 1 + .../Bambu PLA Galaxy @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/Bambu PLA Galaxy @BBL P1P.json | 1 + .../Bambu PLA Galaxy @BBL P2S 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Galaxy @BBL P2S.json | 2 ++ .../Bambu PLA Glow @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Glow @BBL H2C.json | 2 ++ .../BBL/filament/Bambu PLA Glow @BBL H2S.json | 4 ++++ .../Bambu PLA Glow @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/Bambu PLA Glow @BBL P1P.json | 1 + .../Bambu PLA Glow @BBL P2S 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Glow @BBL P2S.json | 2 ++ .../Bambu PLA Lite @BBL H2C 0.2 nozzle.json | 3 +++ .../Bambu PLA Lite @BBL H2C 0.4 nozzle.json | 6 ++++++ .../Bambu PLA Lite @BBL H2C 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Lite @BBL H2C.json | 6 ++++++ .../Bambu PLA Lite @BBL H2D 0.2 nozzle.json | 3 +++ .../Bambu PLA Lite @BBL H2D 0.6 nozzle.json | 6 ++++++ .../Bambu PLA Lite @BBL H2D 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Lite @BBL H2D.json | 6 ++++++ .../Bambu PLA Lite @BBL H2DP 0.2 nozzle.json | 3 +++ .../Bambu PLA Lite @BBL H2DP 0.4 nozzle.json | 3 +++ .../Bambu PLA Lite @BBL H2DP 0.6 nozzle.json | 3 +++ .../Bambu PLA Lite @BBL H2DP 0.8 nozzle.json | 3 +++ .../Bambu PLA Lite @BBL H2S 0.2 nozzle.json | 1 + .../Bambu PLA Lite @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Lite @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Lite @BBL H2S.json | 2 ++ .../Bambu PLA Lite @BBL P1P 0.2 nozzle.json | 1 + .../Bambu PLA Lite @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/Bambu PLA Lite @BBL P1P.json | 1 + .../Bambu PLA Marble @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Marble @BBL H2C.json | 2 ++ .../BBL/filament/Bambu PLA Marble @BBL H2S.json | 4 ++++ .../Bambu PLA Matte @BBL H2C 0.2 nozzle.json | 2 ++ .../Bambu PLA Matte @BBL H2C 0.6 nozzle.json | 4 ++++ .../Bambu PLA Matte @BBL H2C 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Matte @BBL H2C.json | 4 ++++ .../Bambu PLA Matte @BBL H2D 0.2 nozzle.json | 3 +++ .../Bambu PLA Matte @BBL H2D 0.6 nozzle.json | 6 ++++++ .../Bambu PLA Matte @BBL H2D 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Matte @BBL H2D.json | 6 ++++++ .../Bambu PLA Matte @BBL H2DP 0.2 nozzle.json | 3 +++ .../Bambu PLA Matte @BBL H2DP 0.6 nozzle.json | 3 +++ .../Bambu PLA Matte @BBL H2DP 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu PLA Matte @BBL H2DP.json | 3 +++ .../Bambu PLA Matte @BBL H2S 0.2 nozzle.json | 1 + .../Bambu PLA Matte @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Matte @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Matte @BBL H2S.json | 2 ++ .../Bambu PLA Matte @BBL P2S 0.2 nozzle.json | 1 + .../Bambu PLA Matte @BBL P2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Matte @BBL P2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Matte @BBL P2S.json | 2 ++ .../Bambu PLA Metal @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Metal @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Metal @BBL H2C.json | 2 ++ .../Bambu PLA Metal @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Metal @BBL H2S.json | 2 ++ .../Bambu PLA Metal @BBL P2S 0.2 nozzle.json | 1 + .../Bambu PLA Metal @BBL P2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Metal @BBL P2S 0.8 nozzle.json | 1 + .../Bambu PLA Pure @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Pure @BBL H2C 0.6 nozzle.json | 2 ++ .../Bambu PLA Pure @BBL H2C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Pure @BBL H2C.json | 2 ++ .../Bambu PLA Pure @BBL H2D 0.2 nozzle.json | 1 + .../Bambu PLA Pure @BBL H2D 0.6 nozzle.json | 2 ++ .../Bambu PLA Pure @BBL H2D 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Pure @BBL H2D.json | 2 ++ .../Bambu PLA Pure @BBL H2DP 0.2 nozzle.json | 1 + .../Bambu PLA Pure @BBL H2DP 0.6 nozzle.json | 1 + .../Bambu PLA Pure @BBL H2DP 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Pure @BBL H2DP.json | 1 + .../Bambu PLA Pure @BBL H2S 0.2 nozzle.json | 1 + .../Bambu PLA Pure @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Pure @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Pure @BBL H2S.json | 2 ++ .../Bambu PLA Pure @BBL P1P 0.2 nozzle.json | 1 + .../Bambu PLA Pure @BBL P1P 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Pure @BBL P1P.json | 1 + .../Bambu PLA Pure @BBL P2S 0.2 nozzle.json | 1 + .../Bambu PLA Pure @BBL P2S 0.4 nozzle.json | 2 ++ .../Bambu PLA Pure @BBL P2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Pure @BBL P2S 0.8 nozzle.json | 1 + .../Bambu PLA Pure @BBL X2D 0.2 nozzle.json | 3 +++ .../Bambu PLA Pure @BBL X2D 0.4 nozzle.json | 5 +++++ .../Bambu PLA Pure @BBL X2D 0.6 nozzle.json | 5 +++++ .../Bambu PLA Pure @BBL X2D 0.8 nozzle.json | 3 +++ .../Bambu PLA Silk @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Silk @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Silk @BBL H2C.json | 2 ++ .../Bambu PLA Silk @BBL H2D 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk @BBL H2D.json | 2 ++ .../Bambu PLA Silk @BBL H2DP 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk @BBL H2DP.json | 1 + .../Bambu PLA Silk @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk @BBL H2S.json | 2 ++ .../Bambu PLA Silk @BBL P2S 0.2 nozzle.json | 1 + .../Bambu PLA Silk @BBL P2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Silk @BBL P2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk @BBL P2S.json | 2 ++ .../Bambu PLA Silk+ @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Silk+ @BBL H2C 0.4 nozzle.json | 2 ++ .../Bambu PLA Silk+ @BBL H2C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk+ @BBL H2C.json | 2 ++ .../Bambu PLA Silk+ @BBL H2D 0.2 nozzle.json | 1 + .../Bambu PLA Silk+ @BBL H2D 0.6 nozzle.json | 2 ++ .../Bambu PLA Silk+ @BBL H2D 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk+ @BBL H2D.json | 2 ++ .../Bambu PLA Silk+ @BBL H2DP 0.2 nozzle.json | 1 + .../Bambu PLA Silk+ @BBL H2DP 0.6 nozzle.json | 1 + .../Bambu PLA Silk+ @BBL H2DP 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk+ @BBL H2DP.json | 1 + .../Bambu PLA Silk+ @BBL H2S 0.2 nozzle.json | 1 + .../Bambu PLA Silk+ @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Silk+ @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk+ @BBL H2S.json | 2 ++ .../Bambu PLA Silk+ @BBL P2S 0.2 nozzle.json | 1 + .../BBL/filament/Bambu PLA Silk+ @BBL P2S.json | 2 ++ .../Bambu PLA Sparkle @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Sparkle @BBL H2C.json | 2 ++ .../BBL/filament/Bambu PLA Sparkle @BBL H2S.json | 4 ++++ .../Bambu PLA Sparkle @BBL P2S 0.6 nozzle.json | 2 ++ .../Bambu PLA Tough @BBL H2C 0.2 nozzle.json | 1 + .../Bambu PLA Tough @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Tough @BBL H2C.json | 2 ++ .../Bambu PLA Tough @BBL H2S 0.2 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Tough @BBL H2S.json | 4 ++++ .../BBL/filament/Bambu PLA Tough+ @BBL X1.json | 2 ++ ...Bambu PLA Translucent @BBL H2C 0.2 nozzle.json | 2 ++ ...Bambu PLA Translucent @BBL H2C 0.6 nozzle.json | 4 ++++ ...Bambu PLA Translucent @BBL H2C 0.8 nozzle.json | 2 ++ .../filament/Bambu PLA Translucent @BBL H2C.json | 4 ++++ ...Bambu PLA Translucent @BBL H2D 0.6 nozzle.json | 2 ++ ...ambu PLA Translucent @BBL H2DP 0.6 nozzle.json | 1 + ...Bambu PLA Translucent @BBL H2S 0.2 nozzle.json | 2 ++ ...Bambu PLA Translucent @BBL H2S 0.6 nozzle.json | 4 ++++ ...Bambu PLA Translucent @BBL H2S 0.8 nozzle.json | 2 ++ .../filament/Bambu PLA Translucent @BBL H2S.json | 4 ++++ ...Bambu PLA Translucent @BBL P1P 0.2 nozzle.json | 2 ++ ...Bambu PLA Translucent @BBL P1P 0.8 nozzle.json | 2 ++ .../filament/Bambu PLA Translucent @BBL P1P.json | 2 ++ ...Bambu PLA Translucent @BBL P2S 0.2 nozzle.json | 2 ++ ...Bambu PLA Translucent @BBL P2S 0.4 nozzle.json | 4 ++++ ...Bambu PLA Translucent @BBL P2S 0.6 nozzle.json | 4 ++++ ...Bambu PLA Translucent @BBL P2S 0.8 nozzle.json | 1 + ...Bambu PLA Translucent @BBL X1C 0.2 nozzle.json | 1 + ...Bambu PLA Translucent @BBL X1C 0.8 nozzle.json | 1 + .../filament/Bambu PLA Translucent @BBL X1C.json | 1 + ...Bambu PLA Translucent @BBL X2D 0.2 nozzle.json | 3 +++ ...Bambu PLA Translucent @BBL X2D 0.4 nozzle.json | 5 +++++ ...Bambu PLA Translucent @BBL X2D 0.6 nozzle.json | 5 +++++ ...Bambu PLA Translucent @BBL X2D 0.8 nozzle.json | 3 +++ .../Bambu PLA Wood @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu PLA Wood @BBL H2C.json | 2 ++ .../BBL/filament/Bambu PLA Wood @BBL H2S.json | 4 ++++ .../Bambu PLA Wood @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/Bambu PLA Wood @BBL P1P.json | 1 + .../BBL/filament/Bambu PPA-CF @BBL P2S.json | 4 ++++ .../filament/Bambu PVA @BBL P1P 0.2 nozzle.json | 1 + .../profiles/BBL/filament/Bambu PVA @BBL P1P.json | 1 + ...Bambu Support For PLA @BBL H2C 0.2 nozzle.json | 2 ++ ...Bambu Support For PLA @BBL H2C 0.4 nozzle.json | 4 ++++ ...Bambu Support For PLA @BBL H2C 0.8 nozzle.json | 2 ++ .../filament/Bambu Support For PLA @BBL H2C.json | 4 ++++ ...Bambu Support For PLA @BBL H2D 0.2 nozzle.json | 2 ++ ...Bambu Support For PLA @BBL H2D 0.6 nozzle.json | 4 ++++ ...Bambu Support For PLA @BBL H2D 0.8 nozzle.json | 2 ++ .../filament/Bambu Support For PLA @BBL H2D.json | 4 ++++ ...ambu Support For PLA @BBL H2DP 0.2 nozzle.json | 2 ++ ...ambu Support For PLA @BBL H2DP 0.6 nozzle.json | 2 ++ ...ambu Support For PLA @BBL H2DP 0.8 nozzle.json | 2 ++ .../filament/Bambu Support For PLA @BBL H2DP.json | 2 ++ ...Bambu Support For PLA @BBL H2S 0.2 nozzle.json | 3 +++ ...Bambu Support For PLA @BBL H2S 0.6 nozzle.json | 6 ++++++ ...Bambu Support For PLA @BBL H2S 0.8 nozzle.json | 3 +++ .../filament/Bambu Support For PLA @BBL H2S.json | 6 ++++++ ...Bambu Support For PLA @BBL P2S 0.2 nozzle.json | 2 ++ ...Bambu Support For PLA @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../Bambu Support For PLA-PETG @BBL H2D.json | 4 ++++ .../Bambu Support W @BBL H2S 0.2 nozzle.json | 3 +++ .../BBL/filament/Bambu Support W @BBL H2S.json | 6 ++++++ .../BBL/filament/Bambu TPU 85A @BBL H2C.json | 4 ++++ .../BBL/filament/Bambu TPU 85A @BBL H2D.json | 6 ++++++ .../BBL/filament/Bambu TPU 85A @BBL H2DP.json | 4 ++++ .../Bambu TPU 85A @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu TPU 85A @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 85A @BBL H2S.json | 2 ++ .../Bambu TPU 90A @BBL H2C 0.4 nozzle.json | 4 ++++ .../BBL/filament/Bambu TPU 90A @BBL H2C.json | 4 ++++ .../Bambu TPU 90A @BBL H2D 0.6 nozzle.json | 6 ++++++ .../Bambu TPU 90A @BBL H2D 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU 90A @BBL H2D.json | 6 ++++++ .../Bambu TPU 90A @BBL H2DP 0.6 nozzle.json | 4 ++++ .../Bambu TPU 90A @BBL H2DP 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU 90A @BBL H2DP.json | 4 ++++ .../Bambu TPU 90A @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu TPU 90A @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 90A @BBL H2S.json | 2 ++ .../Bambu TPU 90A @BBL P2S 0.6 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU 90A @BBL P2S.json | 2 ++ .../Bambu TPU 95A @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU 95A @BBL H2C.json | 2 ++ .../BBL/filament/Bambu TPU 95A @BBL H2D.json | 3 +++ .../BBL/filament/Bambu TPU 95A @BBL H2DP.json | 2 ++ .../BBL/filament/Bambu TPU 95A @BBL H2S.json | 2 ++ .../Bambu TPU 95A HF @BBL H2C 0.4 nozzle.json | 2 ++ .../Bambu TPU 95A HF @BBL H2C 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 95A HF @BBL H2C.json | 2 ++ .../Bambu TPU 95A HF @BBL H2D 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 95A HF @BBL H2D.json | 3 +++ .../Bambu TPU 95A HF @BBL H2DP 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 95A HF @BBL H2DP.json | 2 ++ .../Bambu TPU 95A HF @BBL H2S 0.6 nozzle.json | 2 ++ .../Bambu TPU 95A HF @BBL H2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 95A HF @BBL H2S.json | 2 ++ .../Bambu TPU 95A HF @BBL P2S 0.6 nozzle.json | 2 ++ .../Bambu TPU 95A HF @BBL P2S 0.8 nozzle.json | 1 + .../BBL/filament/Bambu TPU 95A HF @BBL P2S.json | 2 ++ .../Bambu TPU for AMS @BBL H2C 0.4 nozzle.json | 4 ++++ .../Bambu TPU for AMS @BBL H2C 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU for AMS @BBL H2C.json | 4 ++++ .../Bambu TPU for AMS @BBL H2D 0.6 nozzle.json | 4 ++++ .../Bambu TPU for AMS @BBL H2D 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU for AMS @BBL H2D.json | 4 ++++ .../Bambu TPU for AMS @BBL H2DP 0.6 nozzle.json | 2 ++ .../Bambu TPU for AMS @BBL H2DP 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU for AMS @BBL H2DP.json | 2 ++ .../Bambu TPU for AMS @BBL H2S 0.6 nozzle.json | 4 ++++ .../Bambu TPU for AMS @BBL H2S 0.8 nozzle.json | 2 ++ .../BBL/filament/Bambu TPU for AMS @BBL H2S.json | 4 ++++ .../Bambu TPU for AMS @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/Bambu TPU for AMS @BBL P1P.json | 1 + .../Bambu TPU for AMS @BBL P2S 0.6 nozzle.json | 6 ++++++ .../Bambu TPU for AMS @BBL P2S 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu TPU for AMS @BBL P2S.json | 6 ++++++ .../Bambu TPU for AMS @BBL X1C 0.6 nozzle.json | 1 + .../BBL/filament/Bambu TPU for AMS @BBL X1C.json | 1 + .../Bambu TPU for AMS @BBL X2D 0.4 nozzle.json | 5 +++++ .../Bambu TPU for AMS @BBL X2D 0.8 nozzle.json | 3 +++ .../BBL/filament/Bambu TPU for AMS @BBL X2D.json | 5 +++++ .../filament/Generic ABS @BBL H2C 0.2 nozzle.json | 2 ++ .../filament/Generic ABS @BBL H2S 0.2 nozzle.json | 2 ++ .../Generic PETG-CF @BBL H2C 0.4 nozzle.json | 4 ++++ .../BBL/filament/Generic PETG-CF @BBL H2C.json | 4 ++++ .../BBL/filament/Generic PETG-CF @BBL H2D.json | 4 ++++ .../BBL/filament/Generic PETG-CF @BBL H2DP.json | 2 ++ .../BBL/filament/Generic PETG-CF @BBL H2S.json | 4 ++++ .../BBL/filament/Generic PETG-CF @BBL P2S.json | 4 ++++ .../BBL/filament/Generic PETG-CF @BBL X1C.json | 2 ++ .../Generic PETG-CF @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../BBL/filament/Generic PETG-CF @BBL X2D.json | 10 ++++++++++ .../filament/Generic PLA @BBL H2C 0.2 nozzle.json | 1 + .../filament/Generic PLA @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Generic PLA @BBL H2C.json | 2 ++ .../filament/Generic PLA @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/Generic PLA @BBL H2S.json | 2 ++ ...eneric PLA High Speed @BBL H2C 0.2 nozzle.json | 1 + ...eneric PLA High Speed @BBL H2C 0.4 nozzle.json | 2 ++ .../filament/Generic PLA High Speed @BBL H2C.json | 2 ++ ...eneric PLA High Speed @BBL H2S 0.2 nozzle.json | 1 + .../filament/Generic PLA High Speed @BBL H2S.json | 2 ++ .../filament/Generic PLA High Speed @BBL P1P.json | 1 + .../Generic PLA Silk @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Generic PLA Silk @BBL H2C.json | 2 ++ .../BBL/filament/Generic PLA Silk @BBL H2S.json | 2 ++ .../Generic PPA-CF @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Generic PPA-CF @BBL H2C.json | 2 ++ .../BBL/filament/Generic PPA-CF @BBL H2D.json | 2 ++ .../BBL/filament/Generic PPA-CF @BBL H2DP.json | 1 + .../Generic PPA-CF @BBL H2S 0.4 nozzle.json | 2 ++ .../BBL/filament/Generic PPA-CF @BBL P2S.json | 2 ++ .../BBL/filament/Generic PPA-CF @BBL X1C.json | 1 + .../BBL/filament/Generic PPA-CF @BBL X1E.json | 1 + .../Generic PPA-CF @BBL X2D 0.4 nozzle.json | 5 +++++ .../BBL/filament/Generic PPA-CF @BBL X2D.json | 5 +++++ .../filament/Generic TPU @BBL H2C 0.4 nozzle.json | 2 ++ .../BBL/filament/Generic TPU @BBL H2C.json | 2 ++ .../BBL/filament/Generic TPU @BBL H2D.json | 3 +++ .../BBL/filament/Generic TPU @BBL H2DP.json | 2 ++ .../BBL/filament/Generic TPU @BBL H2S.json | 2 ++ .../Generic TPU for AMS @BBL H2C 0.4 nozzle.json | 6 ++++++ .../filament/Generic TPU for AMS @BBL H2C.json | 6 ++++++ .../filament/Generic TPU for AMS @BBL H2D.json | 6 ++++++ .../filament/Generic TPU for AMS @BBL H2DP.json | 3 +++ .../filament/Generic TPU for AMS @BBL H2S.json | 6 ++++++ .../filament/Generic TPU for AMS @BBL P1P.json | 2 ++ .../filament/Generic TPU for AMS @BBL P2S.json | 4 ++++ .../filament/Generic TPU for AMS @BBL X1C.json | 2 ++ .../Generic TPU for AMS @BBL X2D 0.4 nozzle.json | 10 ++++++++++ .../filament/Generic TPU for AMS @BBL X2D.json | 10 ++++++++++ .../Overture Matte PLA @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/Overture Matte PLA @BBL H2S.json | 2 ++ .../BBL/filament/Overture Matte PLA @BBL P1P.json | 1 + .../Overture PLA @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/Overture PLA @BBL H2S.json | 2 ++ .../BBL/filament/Overture PLA @BBL P1P.json | 1 + .../P1P/Bambu ABS @BBL P1P 0.2 nozzle.json | 1 + .../P1P/Bambu ABS @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/P1P/Bambu ABS @BBL P1P.json | 1 + .../BBL/filament/P1P/Bambu PA-CF @BBL P1P.json | 2 ++ .../P1P/Bambu PC @BBL P1P 0.2 nozzle.json | 1 + .../P1P/Bambu PC @BBL P1P 0.6 nozzle.json | 1 + .../BBL/filament/P1P/Bambu PC @BBL P1P.json | 1 + .../BBL/filament/P1P/Bambu PET-CF @BBL P1P.json | 2 ++ .../P1P/Bambu PETG-CF @BBL P1P 0.4 nozzle.json | 2 ++ .../BBL/filament/P1P/Bambu PETG-CF @BBL P1P.json | 2 ++ .../BBL/filament/P1P/Bambu PLA Aero @BBL P1P.json | 1 + .../P1P/Bambu PLA Basic @BBL P1P 0.2 nozzle.json | 1 + .../P1P/Bambu PLA Basic @BBL P1P 0.6 nozzle.json | 1 + .../filament/P1P/Bambu PLA Basic @BBL P1P.json | 1 + .../P1P/Bambu PLA Marble @BBL P1P 0.6 nozzle.json | 1 + .../filament/P1P/Bambu PLA Marble @BBL P1P.json | 1 + .../P1P/Bambu PLA Matte @BBL P1P 0.2 nozzle.json | 1 + .../P1P/Bambu PLA Matte @BBL P1P 0.6 nozzle.json | 1 + .../filament/P1P/Bambu PLA Matte @BBL P1P.json | 1 + .../P1P/Bambu PLA Metal @BBL P1P 0.2 nozzle.json | 1 + .../P1P/Bambu PLA Metal @BBL P1P 0.6 nozzle.json | 1 + .../filament/P1P/Bambu PLA Metal @BBL P1P.json | 1 + .../Bambu PLA Sparkle @BBL P1P 0.6 nozzle.json | 1 + .../filament/P1P/Bambu PLA Sparkle @BBL P1P.json | 1 + .../P1P/Bambu PLA Tough @BBL P1P 0.2 nozzle.json | 1 + .../filament/P1P/Bambu PLA Tough @BBL P1P.json | 1 + .../P1P/Bambu PLA Tough+ @BBL P1P 0.2 nozzle.json | 2 ++ .../P1P/Bambu PLA Tough+ @BBL P1P 0.6 nozzle.json | 2 ++ .../filament/P1P/Bambu PLA Tough+ @BBL P1P.json | 2 ++ .../P1P/Bambu PLA-CF @BBL P1P 0.8 nozzle.json | 1 + .../BBL/filament/P1P/Bambu PLA-CF @BBL P1P.json | 1 + ...Bambu Support For PLA @BBL P1P 0.2 nozzle.json | 1 + .../P1P/Bambu Support For PLA @BBL P1P.json | 1 + .../P1P/Bambu Support W @BBL P1P 0.2 nozzle.json | 1 + .../filament/P1P/Bambu Support W @BBL P1P.json | 1 + .../P1P/Generic ABS @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/P1P/Generic ABS @BBL P1P.json | 1 + .../P1P/Generic PC @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/P1P/Generic PC @BBL P1P.json | 1 + .../filament/P1P/Generic PETG-CF @BBL P1P.json | 2 ++ .../P1P/Generic PLA @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/P1P/Generic PLA @BBL P1P.json | 1 + .../BBL/filament/P1P/Generic PVA @BBL P1P.json | 1 + .../P1P/PolyLite PLA @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/P1P/PolyLite PLA @BBL P1P.json | 1 + .../P1P/PolyTerra PLA @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/P1P/PolyTerra PLA @BBL P1P.json | 1 + .../P1P/eSUN PLA+ @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/P1P/eSUN PLA+ @BBL P1P.json | 1 + .../BBL/filament/PolyLite ABS @BBL P1P.json | 1 + .../BBL/filament/PolyLite PETG @BBL H2D.json | 2 ++ .../BBL/filament/PolyLite PETG @BBL H2DP.json | 1 + .../PolyLite PETG @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/PolyLite PETG @BBL H2S.json | 2 ++ .../BBL/filament/PolyLite PETG @BBL P1P.json | 1 + .../BBL/filament/PolyLite PETG @BBL X1C.json | 1 + .../PolyLite PLA @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/PolyLite PLA @BBL H2S.json | 2 ++ .../PolyTerra PLA @BBL H2S 0.2 nozzle.json | 1 + .../BBL/filament/PolyTerra PLA @BBL H2S.json | 2 ++ .../filament/SUNLU/SUNLU Marble PLA @BBL P1P.json | 1 + .../SUNLU/SUNLU PETG @BBL X1C 0.8 nozzle.json | 2 ++ .../SUNLU PLA Matte @BBL P1P 0.2 nozzle.json | 1 + .../filament/SUNLU/SUNLU PLA Matte @BBL P1P.json | 1 + .../SUNLU/SUNLU PLA+ 2.0 @BBL P1P 0.2 nozzle.json | 1 + .../filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P.json | 1 + .../SUNLU/SUNLU PLA+ @BBL P1P 0.2 nozzle.json | 1 + .../BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P.json | 1 + .../filament/SUNLU/SUNLU Wood PLA @BBL P1P.json | 1 + .../filament/eSUN PLA+ @BBL H2S 0.2 nozzle.json | 1 + .../profiles/BBL/filament/eSUN PLA+ @BBL H2S.json | 2 ++ resources/profiles/OrcaFilamentLibrary.json | 2 +- .../filament/BETA/BBL/BETA ABS @BBL H2D.json | 1 + .../filament/BETA/BBL/BETA ABS @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA ABS @BBL X1C.json | 1 + .../filament/BETA/BBL/BETA PEBA 90A @BBL H2D.json | 2 ++ .../BETA/BBL/BETA PETG @BBL H2D 0.4 nozzle.json | 2 ++ .../filament/BETA/BBL/BETA PETG @BBL X1C.json | 1 + ...ETA PETG Fluorescence @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Fluorescence @BBL X1C.json | 1 + .../BETA PETG Glitter @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Glitter @BBL X1C.json | 1 + .../BBL/BETA PETG Glow @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Glow @BBL X1C.json | 1 + .../BETA PETG Gradient @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Gradient @BBL X1C.json | 1 + .../BBL/BETA PETG HF @BBL H2D 0.4 nozzle.json | 2 ++ .../filament/BETA/BBL/BETA PETG HF @BBL X1C.json | 1 + ...ETG Heat Color Change @BBL H2D 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG Heat Color Change @BBL X1C.json | 1 + .../BBL/BETA PETG Marble @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Marble @BBL X1C.json | 1 + .../BBL/BETA PETG Matte @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Matte @BBL X1C.json | 1 + .../BETA PETG Metallic @BBL H2D 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PETG Metallic @BBL X1C.json | 1 + ... PETG UV Color Change @BBL H2D 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG UV Color Change @BBL X1C.json | 1 + .../BBL/BETA PETG-CF @BBL H2D 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG-CF @BBL P1P 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG-CF @BBL X1C 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG-GF @BBL H2D 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG-GF @BBL P1P 0.4 nozzle.json | 2 ++ .../BBL/BETA PETG-GF @BBL X1C 0.4 nozzle.json | 2 ++ .../BETA/BBL/BETA PLA Basic @BBL H2D.json | 3 +++ .../BETA/BBL/BETA PLA Basic @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Chameleon @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Fluorescence @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Glitter @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA PLA Glow @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Gradient @BBL P1P.json | 1 + .../BBL/BETA PLA Heat Color Change @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA High Speed @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA High Temp @BBL H2D.json | 1 + .../BETA/BBL/BETA PLA Marble @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Matte @BBL H2D.json | 3 +++ .../BETA/BBL/BETA PLA Matte @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Metal @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Metallic @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA PLA PRO @BBL H2D.json | 3 +++ .../filament/BETA/BBL/BETA PLA PRO @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA PLA Silk @BBL H2D.json | 1 + .../BETA/BBL/BETA PLA Silk+ @BBL H2D.json | 1 + .../BETA/BBL/BETA PLA Transparent @BBL P1P.json | 2 ++ .../BETA/BBL/BETA PLA Transparent @BBL X1C.json | 1 + .../BBL/BETA PLA UV Color Change @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA PLA Wood @BBL P1P.json | 1 + .../BETA/BBL/BETA PLA Youth @BBL H2D.json | 3 +++ .../BETA/BBL/BETA PLA Youth @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA PLA-CF @BBL P1P.json | 1 + .../filament/BETA/BBL/BETA TPU 90A @BBL H2D.json | 2 ++ .../filament/BETA/BBL/BETA TPU 95A @BBL H2D.json | 1 + .../filament/BETA/BBL/BETA TPU 98A @BBL H2D.json | 1 + .../BETA/BBL/BETA TPU Matte @BBL H2D.json | 2 ++ .../filament/Bambu/Bambu ABS @System.json | 5 +++++ .../filament/Bambu/Bambu ASA @System.json | 5 +++++ .../filament/Bambu/Bambu PA-CF @System.json | 10 ++++++++++ .../filament/Bambu/Bambu PC @System.json | 5 +++++ .../filament/Bambu/Bambu PC FR @System.json | 5 +++++ .../filament/Bambu/Bambu PETG HF @System.json | 5 +++++ .../filament/Bambu/Bambu PETG-CF @System.json | 10 ++++++++++ .../filament/Bambu/Bambu PLA Aero @System.json | 5 +++++ .../filament/Generic PA-CF @System.json | 5 +++++ .../filament/Generic PETG HF @System.json | 5 +++++ .../filament/Generic PETG-CF @System.json | 15 +++++++++++++++ .../filament/Generic PLA High Speed @System.json | 5 +++++ .../filament/Generic PLA Matte @System.json | 5 +++++ .../filament/Generic PP-CF @System.json | 5 +++++ .../filament/Generic PP-GF @System.json | 5 +++++ .../filament/Generic PPA-CF @System.json | 5 +++++ .../INSLOGIC/BBL/INSLOGIC PA6-66 @BBL P2S.json | 6 ++++++ .../INSLOGIC/BBL/INSLOGIC PA6-66 @P1-X1.json | 4 ++++ .../INSLOGIC/BBL/INSLOGIC PA6-CF @BBL P2S.json | 6 ++++++ .../INSLOGIC/BBL/INSLOGIC PA6-CF @P1-X1.json | 4 ++++ .../INSLOGIC/BBL/INSLOGIC PETG Pro @BBL P2S.json | 6 ++++++ .../INSLOGIC/BBL/INSLOGIC PETG Pro @P1-X1.json | 4 ++++ .../INSLOGIC/BBL/INSLOGIC PLA Pro @BBL P2S.json | 3 +++ .../INSLOGIC/BBL/INSLOGIC PLA Pro @P1-X1.json | 4 ++++ .../JAYO/BBL/JAYO PETG @BBL P2S 0.4 nozzle.json | 1 + .../BBL/Numakers PLA+ @BBL P1P 0.2 nozzle.json | 1 + .../Numakers/BBL/Numakers PLA+ @BBL P1P.json | 1 + .../Polymaker/BBL/Fiberon PETG-ESD @BBL X1.json | 1 + .../Polymaker/BBL/Fiberon PETG-rCF08 @BBL X1.json | 1 + .../filament/addnorth/BBL/addnorth ABS rABS.json | 6 ++++++ .../addnorth/BBL/addnorth PA Adura FDA.json | 6 ++++++ .../filament/addnorth/BBL/addnorth PA Adura.json | 6 ++++++ .../addnorth/BBL/addnorth PA-CF Adura X.json | 6 ++++++ .../addnorth/BBL/addnorth PA6 Addlantis.json | 6 ++++++ .../addnorth/BBL/addnorth PC BLend HT LCF.json | 6 ++++++ .../filament/addnorth/BBL/addnorth PETG Base.json | 6 ++++++ .../filament/addnorth/BBL/addnorth PETG ESD.json | 6 ++++++ .../addnorth/BBL/addnorth PETG Economy.json | 6 ++++++ .../addnorth/BBL/addnorth PETG Flame v0.json | 6 ++++++ .../addnorth/BBL/addnorth PETG PRO Matte.json | 6 ++++++ .../addnorth/BBL/addnorth PETG rPETG Matte.json | 6 ++++++ .../addnorth/BBL/addnorth PETG-CF Rigid X.json | 6 ++++++ .../filament/addnorth/BBL/addnorth PLA E-PLA.json | 6 ++++++ .../addnorth/BBL/addnorth PLA Economy.json | 6 ++++++ .../BBL/addnorth PLA HT-PLA PRO Matte.json | 6 ++++++ .../addnorth/BBL/addnorth PLA Premium Silk.json | 6 ++++++ .../addnorth/BBL/addnorth PLA Textura.json | 6 ++++++ .../filament/addnorth/BBL/addnorth PLA Wood.json | 6 ++++++ .../BBL/addnorth PLA X-PLA High Speed.json | 6 ++++++ .../filament/addnorth/BBL/addnorth PLA X-PLA.json | 6 ++++++ .../addnorth/BBL/addnorth PLA rPLA RE-ADD.json | 6 ++++++ .../addnorth/BBL/addnorth PVDF Adamant S1.json | 6 ++++++ .../addnorth/BBL/addnorth TPU EasyFlex.json | 6 ++++++ .../addnorth/BBL/addnorth TPU Pro Matte 85A.json | 6 ++++++ .../addnorth/BBL/addnorth TPU Pro Matte 95A.json | 6 ++++++ 770 files changed, 1997 insertions(+), 2 deletions(-) diff --git a/resources/profiles/BBL.json b/resources/profiles/BBL.json index 3f4f91efac..68d2028473 100644 --- a/resources/profiles/BBL.json +++ b/resources/profiles/BBL.json @@ -1,7 +1,7 @@ { "name": "Bambulab", "url": "http://www.bambulab.com/Parameters/vendor/BBL.json", - "version": "02.08.00.11", + "version": "02.08.00.12", "force_update": "0", "description": "the initial version of BBL configurations", "machine_model_list": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.2 nozzle.json index fd561c9b02..c00681818c 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.2 nozzle.json @@ -21,9 +21,11 @@ "0.0241" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.6 nozzle.json index 43827c7a5e..538d2920ec 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.3341" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.8 nozzle.json index 7fd3b25a7e..798ed443ba 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C 0.8 nozzle.json @@ -21,6 +21,7 @@ "0.0241" ], "fan_max_speed": [ + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C.json index 410db132d7..28b7404dc2 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2C.json @@ -21,6 +21,8 @@ "0.3341" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.2 nozzle.json index e357dc5e05..ba4c0fd8b8 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.2 nozzle.json @@ -21,6 +21,7 @@ "0.0241" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.6 nozzle.json index 328543f9c0..9434bd4159 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.0241" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.8 nozzle.json index 5d75f70a48..6cc753de96 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D 0.8 nozzle.json @@ -21,6 +21,7 @@ "0.0241" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D.json index dcbe4c9c9b..10b4722918 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2D.json @@ -21,6 +21,8 @@ "0.0241" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.2 nozzle.json index afb7d2af99..a13bfa07e8 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.2 nozzle.json @@ -9,6 +9,7 @@ "65" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.6 nozzle.json index 5ed4484f7f..980bdef186 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.6 nozzle.json @@ -9,6 +9,7 @@ "65" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.8 nozzle.json index 5f5a3435d7..284578284a 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP 0.8 nozzle.json @@ -9,6 +9,7 @@ "65" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP.json index ed170c8a30..9e9162545e 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2DP.json @@ -9,6 +9,7 @@ "65" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.2 nozzle.json index 6f18702239..2945800452 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.2 nozzle.json @@ -21,9 +21,11 @@ "0.3341" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.6 nozzle.json index d995f5cf2f..22595bb69d 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.3341" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.8 nozzle.json index af38270fbb..d6ab8201db 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S 0.8 nozzle.json @@ -21,6 +21,7 @@ "0.3341" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S.json b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S.json index 35a8d05943..84027cf8dc 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL H2S.json @@ -21,6 +21,8 @@ "0.3341" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.4 nozzle.json index d2b3ac673b..025925b578 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.4 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_38", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.6 nozzle.json index df087aa06b..e83e751cb1 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL P1S 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_39", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.2 nozzle.json index c82f324007..4788f5e566 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.2 nozzle.json @@ -25,6 +25,7 @@ "0.0241" ], "fan_min_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.6 nozzle.json index 6627fb2e62..98be4a6887 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.3341" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.8 nozzle.json index de71006ade..3c42f060f5 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S 0.8 nozzle.json @@ -25,6 +25,7 @@ "0.0241" ], "fan_max_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S.json b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S.json index 31fa4e25a1..c22e0202c5 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL P2S.json @@ -26,6 +26,8 @@ "0.3341" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.2 nozzle.json index ef901b0dc1..a55b483e20 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_00", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.6 nozzle.json index b7d3a06ea2..aecbbff44d 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_40", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.8 nozzle.json index c25c690d8b..597c843568 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_01", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C.json index 4632ca054f..d95cb4a119 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.2 nozzle.json index 63b7b1ea67..ba65ac0e69 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_05", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.8 nozzle.json index 5022a70244..18639ac21f 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1E 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_06", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X1E.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X1E.json index 996212ace3..22b568affb 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X1E.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X1E.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_04", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.2 nozzle.json index 31edcc6add..bb895bc66a 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.2 nozzle.json @@ -21,6 +21,9 @@ "0.0241" ], "fan_min_speed": [ + "80", + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.4 nozzle.json index e647fa477c..0defaafc07 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.4 nozzle.json @@ -21,6 +21,11 @@ "0.0241" ], "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.8 nozzle.json index ab10c78829..33bf55cdf1 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D 0.8 nozzle.json @@ -21,6 +21,9 @@ "0.0241" ], "fan_max_speed": [ + "60", + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D.json b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D.json index 6a0850497b..6f9adbd82a 100644 --- a/resources/profiles/BBL/filament/Bambu ABS @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu ABS @BBL X2D.json @@ -21,6 +21,11 @@ "0.0241" ], "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.2 nozzle.json index 53676c7bc2..3b07712c16 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.2 nozzle.json @@ -25,9 +25,11 @@ "-0.0092" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.6 nozzle.json index 463333a491..035e6e1269 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.6 nozzle.json @@ -26,6 +26,8 @@ "0.2258" ], "fan_min_speed": [ + "25", + "25", "25" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.8 nozzle.json index 284e476b53..1967867290 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL P2S 0.8 nozzle.json @@ -21,6 +21,7 @@ "-0.0092" ], "fan_min_speed": [ + "25", "25" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL X1 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu ASA @BBL X1 0.6 nozzle.json index 63e38090f7..01b0a9ec05 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL X1 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL X1 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB01_04", "instantiation": "true", "fan_min_speed": [ + "25", "25" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL X1C.json b/resources/profiles/BBL/filament/Bambu ASA @BBL X1C.json index ab1677285e..a4b301716c 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSB01_00", "instantiation": "true", "fan_min_speed": [ + "25", "25" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL X1E.json b/resources/profiles/BBL/filament/Bambu ASA @BBL X1E.json index 9336323c61..7bf1d303ca 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL X1E.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL X1E.json @@ -6,6 +6,7 @@ "setting_id": "GFSB01_05", "instantiation": "true", "fan_min_speed": [ + "25", "25" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu ASA @BBL X2D 0.8 nozzle.json index 4bf30b3fce..45a24500f6 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL X2D 0.8 nozzle.json @@ -21,6 +21,9 @@ "-0.0092" ], "fan_min_speed": [ + "25", + "25", + "25", "25" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu ASA @BBL X2D.json b/resources/profiles/BBL/filament/Bambu ASA @BBL X2D.json index 38c96bff13..9f702c0058 100644 --- a/resources/profiles/BBL/filament/Bambu ASA @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu ASA @BBL X2D.json @@ -21,6 +21,11 @@ "-0.0092" ], "fan_min_speed": [ + "25", + "25", + "25", + "25", + "25", "25" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PA-CF @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PA-CF @BBL H2S.json index b1a9ff5a1b..dce0062f7a 100644 --- a/resources/profiles/BBL/filament/Bambu PA-CF @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PA-CF @BBL H2S.json @@ -12,9 +12,13 @@ "5" ], "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PA-CF @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PA-CF @BBL P2S.json index 2d6934a50f..70d1a9841c 100644 --- a/resources/profiles/BBL/filament/Bambu PA-CF @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PA-CF @BBL P2S.json @@ -17,9 +17,13 @@ "5" ], "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1C.json index 52af58d8ba..a6b2b9f83a 100644 --- a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1C.json @@ -9,9 +9,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1E.json b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1E.json index b12f9f014e..38e1aa5902 100644 --- a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1E.json +++ b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X1E.json @@ -12,9 +12,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D 0.4 nozzle.json index 4cd5698e7f..c94de9de77 100644 --- a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D 0.4 nozzle.json @@ -15,9 +15,19 @@ "5" ], "fan_max_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "fan_min_speed": [ + "10", + "10", + "10", + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D.json index 384d2e4f3b..d0b17e5af0 100644 --- a/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PA-CF @BBL X2D.json @@ -15,9 +15,19 @@ "5" ], "fan_max_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "fan_min_speed": [ + "10", + "10", + "10", + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PA6-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PA6-CF @BBL X2D 0.4 nozzle.json index 2f8844aac0..34a181b789 100644 --- a/resources/profiles/BBL/filament/Bambu PA6-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PA6-CF @BBL X2D 0.4 nozzle.json @@ -21,9 +21,19 @@ "-0.038" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "20", + "20", + "20", + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PAHT-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PAHT-CF @BBL X2D 0.4 nozzle.json index 0f02b6ea95..778e557421 100644 --- a/resources/profiles/BBL/filament/Bambu PAHT-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PAHT-CF @BBL X2D 0.4 nozzle.json @@ -21,9 +21,19 @@ "-0.055" ], "fan_max_speed": [ + "45", + "45", + "45", + "45", + "45", "45" ], "fan_min_speed": [ + "25", + "25", + "25", + "25", + "25", "25" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.2 nozzle.json index a3ba75304a..5a84b1af84 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.2 nozzle.json @@ -21,6 +21,7 @@ "0.0183" ], "fan_max_speed": [ + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.6 nozzle.json index b53e6eaf93..cd61e78fd6 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.2208" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.8 nozzle.json index f4f455a8b2..ac5133e81e 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2C 0.8 nozzle.json @@ -21,6 +21,7 @@ "0.0183" ], "fan_max_speed": [ + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2C.json index b2bb924ca0..90fb772a06 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2C.json @@ -21,6 +21,8 @@ "0.2208" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.2 nozzle.json index fea45a13bd..2830345d52 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.2 nozzle.json @@ -21,6 +21,7 @@ "0.0183" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.4 nozzle.json index 7877739470..b5fb71be51 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.4 nozzle.json @@ -21,6 +21,8 @@ "0.0183" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.6 nozzle.json index 50a080979a..5abbb98402 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.0183" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.8 nozzle.json index 37aa3eb857..f287b4b916 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2D 0.8 nozzle.json @@ -21,6 +21,7 @@ "0.0183" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.2 nozzle.json index 55575cee0b..fdcc06fe39 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.4 nozzle.json index 4f843feadd..1fd18e3279 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.4 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.6 nozzle.json index 1417c3c5a0..31025856e9 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.6 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.8 nozzle.json index 01f3c1431f..159942cfe2 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2DP 0.8 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.2 nozzle.json index 8b6b883bb6..6887890740 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.2 nozzle.json @@ -18,6 +18,7 @@ "0.2208" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.6 nozzle.json index 21a6b513d5..7cffb5f3d7 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.6 nozzle.json @@ -21,6 +21,8 @@ "0.2208" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.8 nozzle.json index 6564a05f22..1d18c887cd 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2S 0.8 nozzle.json @@ -21,6 +21,7 @@ "0.2208" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PC @BBL H2S.json index 0a6ae94079..8cdcb1f9b5 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL H2S.json @@ -21,6 +21,8 @@ "0.2208" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.4 nozzle.json index 37390058bf..c7b0d65ca6 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.4 nozzle.json @@ -27,6 +27,8 @@ "100" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.6 nozzle.json index 0e8d81de92..fcad5a9ccf 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.6 nozzle.json @@ -27,6 +27,8 @@ "100" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.8 nozzle.json index 27a717ab89..d22d8550ef 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL P2S 0.8 nozzle.json @@ -27,6 +27,7 @@ "100" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.2 nozzle.json index b2227e037e..dd9ab06970 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSC00_02", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.6 nozzle.json index e8b0cf29d0..47dbdb48ac 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSC00_01", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.8 nozzle.json index 7b18784bc6..4c2820a6f7 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSC00_00", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1C.json index 2462d8ccb9..ce801cb6c2 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSC00", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.2 nozzle.json index 8cab064d11..4bfeb106eb 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.6 nozzle.json index 6166c3653e..265364e4fd 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.6 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.8 nozzle.json index 299637f67d..1fe2bd26c4 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1E 0.8 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X1E.json b/resources/profiles/BBL/filament/Bambu PC @BBL X1E.json index a0f287e1e5..d452548dfc 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X1E.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X1E.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.4 nozzle.json index ef08e157a6..8c844e320e 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.4 nozzle.json @@ -21,9 +21,19 @@ "0.0183" ], "fan_max_speed": [ + "55", + "55", + "55", + "55", + "55", "55" ], "fan_min_speed": [ + "25", + "25", + "25", + "25", + "25", "25" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.8 nozzle.json index 45a270ee3a..0fe7536db8 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X2D 0.8 nozzle.json @@ -21,6 +21,9 @@ "0.0183" ], "fan_max_speed": [ + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PC @BBL X2D.json index dfd7ca334f..94e8ba6fc5 100644 --- a/resources/profiles/BBL/filament/Bambu PC @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PC @BBL X2D.json @@ -21,6 +21,11 @@ "0.0183" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.2 nozzle.json index ffde022ea3..40e596e5a9 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.4 nozzle.json index fcf1a079ce..460057a736 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C 0.4 nozzle.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C.json index 957304bc2a..5446841b6e 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2C.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.2 nozzle.json index e768297ad6..02269c8464 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.4 nozzle.json index 75dffbd64b..a3c4f6355a 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.4 nozzle.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.6 nozzle.json index e30efad3f5..3b16cfe4ce 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.6 nozzle.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.8 nozzle.json index 9806d53088..27a87f6ce9 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2D 0.8 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.2 nozzle.json index d61c1da460..a68b8ebf75 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.4 nozzle.json index 8902b0bd27..42bbef7019 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.4 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.6 nozzle.json index 0ba2dc14ed..99e6d107ab 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.6 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.8 nozzle.json index 1b1768ef51..4d8873fd12 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2DP 0.8 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S 0.2 nozzle.json index ebcb2129dd..d773154c7d 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S.json index cf9963a435..aaf725a515 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL H2S.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P 0.2 nozzle.json index b9e028b071..8fbd9582bc 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P 0.2 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P.json index 50c66f2864..c532964815 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1P.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.2 nozzle.json index 472acab285..53d7d9f9f0 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.2 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.6 nozzle.json index 1089e6e4d7..1d12b0f53f 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.6 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.8 nozzle.json index a8d3cea654..f7a86aaf4c 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S 0.8 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S.json index 0150515d70..93a219b4dd 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P1S.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.4 nozzle.json index ea21badf01..12cc5c1f31 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.4 nozzle.json @@ -15,6 +15,8 @@ "100" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.6 nozzle.json index ae0a394959..ad42035768 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.6 nozzle.json @@ -15,6 +15,8 @@ "100" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.8 nozzle.json index 6698de66a4..e1b6bcbb96 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL P2S 0.8 nozzle.json @@ -15,6 +15,7 @@ "100" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.2 nozzle.json index a58b3a34fc..4b764c32bf 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSC01_03", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.6 nozzle.json index 5f9fc6b9f8..41825cbce6 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSC01_01", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.8 nozzle.json index bf7f605858..f7704e22bf 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSC01_02", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C.json index c30bef59b3..b3b011aefe 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSC01_00", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.2 nozzle.json index d786e82f33..01ea062c32 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.2 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.6 nozzle.json index f5545427f1..d323b5ff9b 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.6 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.8 nozzle.json index 050855f467..9f7506e488 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E 0.8 nozzle.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E.json index 5314f3101a..5c062b2d0f 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X1E.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.4 nozzle.json index ccdeb880a0..fcf2b10b28 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.4 nozzle.json @@ -9,6 +9,11 @@ "60" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.8 nozzle.json index 5c58c59432..8661cafc38 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D 0.8 nozzle.json @@ -9,6 +9,9 @@ "60" ], "fan_max_speed": [ + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D.json index 9dd52a836f..032233bbfa 100644 --- a/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PC FR @BBL X2D.json @@ -9,6 +9,11 @@ "60" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C 0.4 nozzle.json index 4633db0021..08692a967c 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C 0.4 nozzle.json @@ -30,9 +30,13 @@ "80" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", "1" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C.json index cee2b21e4d..8fc8fa046b 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2C.json @@ -30,9 +30,13 @@ "80" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", "1" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2D.json index 204fcc2b98..b6824b4c0b 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2D.json @@ -30,9 +30,13 @@ "80" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", "1" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2DP.json index d6a563e31f..d60c7cecff 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2DP.json @@ -24,9 +24,11 @@ "80" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "1", "1" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2S.json index 9eb410e996..d1726e1a55 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL H2S.json @@ -30,9 +30,13 @@ "80" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", "1" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL P2S.json index 8d5f30f117..5331a62893 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL P2S.json @@ -30,9 +30,13 @@ "80" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", "1" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1C.json index e119b37000..5c48a06982 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1C.json @@ -9,9 +9,11 @@ "80" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "1", "1" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1E.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1E.json index 2e750018cf..1ce4d8ac33 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1E.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X1E.json @@ -12,9 +12,11 @@ "80" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "1", "1" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D 0.4 nozzle.json index 55a854c514..42156c4444 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D 0.4 nozzle.json @@ -30,9 +30,19 @@ "80" ], "fan_max_speed": [ + "80", + "80", + "80", + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", + "1", + "1", + "1", "1" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D.json index 277b655bf1..f902e4b8c7 100644 --- a/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PET-CF @BBL X2D.json @@ -30,9 +30,19 @@ "80" ], "fan_max_speed": [ + "80", + "80", + "80", + "80", + "80", "80" ], "fan_min_speed": [ + "1", + "1", + "1", + "1", + "1", "1" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.2 nozzle.json index 1767dc81c5..5b3ffab072 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_24", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.4 nozzle.json index 9611d1f270..cd0d3bc27d 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG00_23", "instantiation": "true", "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.8 nozzle.json index f84645846a..b37b8dffc7 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_44", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C.json index 1b09bc58f6..fc20f3b466 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSG00_25", "instantiation": "true", "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.2 nozzle.json index bc8b057d8a..754241c419 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_15", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.4 nozzle.json index a2fd083bc2..08293df379 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG00_09", "instantiation": "true", "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.6 nozzle.json index 22ad00b017..bb30864d70 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSG00_16", "instantiation": "true", "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.8 nozzle.json index 122c8a04cc..e3e9d11068 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_45", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.2 nozzle.json index 36bb96bd39..f3ace78797 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_20", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.4 nozzle.json index c2a14583c4..7e6b7ce770 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG00_19", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.6 nozzle.json index 53a228357b..bd9579de8a 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_21", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.8 nozzle.json index 50663c56b2..82534872ba 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_46", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.2 nozzle.json index 7c16a6a6c9..a513dff4d9 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_11", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.6 nozzle.json index 48fdf41ed8..50b170db33 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.6 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG00_32", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.8 nozzle.json index 3887491880..5c04eb6cce 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG00_47", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S.json index a640e8927f..11db629a3b 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL H2S.json @@ -6,9 +6,13 @@ "setting_id": "GFSG00_10", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.2 nozzle.json index c718d3d5a3..16e0ee2324 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.2 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG00_17", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.4 nozzle.json index 8c24685000..cc6375362e 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG00_14", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.6 nozzle.json index 1b5a5fb77e..1a01f48967 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.6 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG00_18", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.8 nozzle.json index 0bcf1a6a65..375ecfbb25 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL P2S 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG00_48", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json index 9d1f36e9f3..de6792eb32 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG00_00", "instantiation": "true", "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json index b7a18afcd6..31a23708dd 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG00_02", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json index a43717b952..96139668a3 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSG00_01", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.2 nozzle.json index 69852348e4..8f22458ba7 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.2 nozzle.json @@ -6,9 +6,15 @@ "setting_id": "GFSG00_13", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.4 nozzle.json index c6cba824fd..99741b8e2b 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.4 nozzle.json @@ -6,9 +6,19 @@ "setting_id": "GFSG00_12", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.8 nozzle.json index bf242cb1c1..4523b64c71 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D 0.8 nozzle.json @@ -6,9 +6,15 @@ "setting_id": "GFSG00_49", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D.json index 829448ab91..6f26175cfd 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X2D.json @@ -6,9 +6,19 @@ "setting_id": "GFSG00_22", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.2 nozzle.json index 0fbf4068cb..10cf829118 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.2 nozzle.json @@ -21,6 +21,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.6 nozzle.json index 71292a2121..c41df16615 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.6 nozzle.json @@ -21,6 +21,8 @@ "20" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.8 nozzle.json index 6be40ba26d..bd71516546 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C 0.8 nozzle.json @@ -21,6 +21,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C.json index 77226518e4..7298848d07 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2C.json @@ -21,6 +21,8 @@ "20" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.2 nozzle.json index d7e22db57d..6e36e81da8 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.2 nozzle.json @@ -21,6 +21,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.4 nozzle.json index e53509cfa5..f290a019f6 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.4 nozzle.json @@ -21,9 +21,13 @@ "20" ], "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.6 nozzle.json index 4ebbc2da27..e7e359704c 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.6 nozzle.json @@ -21,6 +21,8 @@ "20" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.8 nozzle.json index 20402f5724..3303ea7265 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2D 0.8 nozzle.json @@ -21,6 +21,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.2 nozzle.json index e0eaa5b1f4..63e2a461f8 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.2 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.4 nozzle.json index 7fec884516..71189a22a9 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.4 nozzle.json @@ -18,6 +18,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.6 nozzle.json index 9a75d0fb5c..612992bf14 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.6 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.8 nozzle.json index d4f215c1f2..3d2fd0c48c 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2DP 0.8 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.2 nozzle.json index ecfadc5c2f..98b79fb3ad 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.2 nozzle.json @@ -21,9 +21,11 @@ "20" ], "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.6 nozzle.json index a06b5d9f2a..28d6e8e8aa 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.6 nozzle.json @@ -21,9 +21,13 @@ "20" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.8 nozzle.json index 2c38bb2fd7..9f165d9c76 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S 0.8 nozzle.json @@ -21,9 +21,11 @@ "20" ], "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S.json index 1fc3f03964..43787a6732 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL H2S.json @@ -21,9 +21,13 @@ "20" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1P 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1P 0.4 nozzle.json index c50087e617..67cb2723cf 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1P 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1P 0.4 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1S 0.4 nozzle.json index 782a19a045..00fdfaf9d2 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P1S 0.4 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.2 nozzle.json index 7160dd0f0d..eef6358ed2 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG02_17", "instantiation": "true", "additional_cooling_fan_speed": [ + "40", "40" ], "counter_coef_2": [ @@ -24,9 +25,11 @@ "20" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.4 nozzle.json index 4ff218f195..2fa9117a78 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.4 nozzle.json @@ -21,9 +21,13 @@ "20" ], "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.6 nozzle.json index 10b8b449d7..ad0d25672c 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.6 nozzle.json @@ -21,9 +21,13 @@ "20" ], "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.8 nozzle.json index f214212555..6a519754da 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL P2S 0.8 nozzle.json @@ -21,9 +21,11 @@ "20" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.2 nozzle.json index 52cf7e42be..11baeedb6e 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.2 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.6 nozzle.json index 5461db84fe..d9f680dd17 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.6 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.8 nozzle.json index e0df40dfcc..29c4ed85e3 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C 0.8 nozzle.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C.json index 38d2d0a11b..cf3d77288c 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X1C.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.2 nozzle.json index 20e69622e7..9666dae7d0 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.2 nozzle.json @@ -21,9 +21,15 @@ "20" ], "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.4 nozzle.json index 81ffc9bf01..2b5576971e 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.4 nozzle.json @@ -21,9 +21,19 @@ "20" ], "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.8 nozzle.json index 6db5064f92..af398088dc 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D 0.8 nozzle.json @@ -21,9 +21,15 @@ "20" ], "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D.json index 84f2ede658..0b5bcd8fa4 100644 --- a/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PETG HF @BBL X2D.json @@ -21,9 +21,19 @@ "20" ], "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.2 nozzle.json index 11e340a26f..a9159c0e51 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_10", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.4 nozzle.json index 1feb3bc3d1..33fffd64a6 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_11", "instantiation": "true", "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.8 nozzle.json index 57ada86690..ed24e8c0f2 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_12", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C.json index c848448b3b..e4987f9444 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSG03_13", "instantiation": "true", "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.2 nozzle.json index a50efe69d3..48de52a8ee 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_14", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.4 nozzle.json index 037dbb39fc..2c637e48aa 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_15", "instantiation": "true", "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.6 nozzle.json index a60362042d..0339fb0505 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSG03_16", "instantiation": "true", "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.8 nozzle.json index 003c19bb28..2fea9bb488 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_17", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.2 nozzle.json index 8ba7e1fd4e..f28b6e63da 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_18", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.4 nozzle.json index 201b082b3f..19dee4f9c7 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_19", "instantiation": "true", "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.6 nozzle.json index 292a5d108e..264ba6b25b 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSG03_20", "instantiation": "true", "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.8 nozzle.json index 085e159b28..7779e219c1 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_21", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.2 nozzle.json index ca2e8219fa..99d5279359 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_22", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.6 nozzle.json index 052d7c8068..a5ab2326bc 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.6 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_23", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.8 nozzle.json index 3346abab97..8bf7065e8c 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_24", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S.json index 0f95f15edd..c3716d514d 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL H2S.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_25", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.2 nozzle.json index 4f6e6b260a..2d35e4fb91 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.2 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_49", "instantiation": "true", "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "20", "20" ], "filament_density": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.4 nozzle.json index 793997445f..7d20fb5b1f 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.4 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_50", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_density": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.6 nozzle.json index 6c379dd43d..abf497bc62 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P1P 0.6 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_51", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_density": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.2 nozzle.json index 8f17112e84..a743638555 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.2 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_38", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.4 nozzle.json index 252bc6f136..03eae4d6d3 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.4 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_39", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.6 nozzle.json index 4297e9278c..913a646355 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.6 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG03_40", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.8 nozzle.json index df51c24fd2..1d3e65ee6f 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL P2S 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_41", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.2 nozzle.json index 31a5b61e21..b878af8540 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.2 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_42", "instantiation": "true", "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "20", "20" ], "filament_density": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.8 nozzle.json index 23db5e43c6..42bda23e2f 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG03_43", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_density": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C.json index e56d030427..7d4ede9cae 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSG03_44", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_density": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.2 nozzle.json index 0e56e63a37..3cb97fc7e5 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.2 nozzle.json @@ -6,9 +6,15 @@ "setting_id": "GFSG03_45", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.4 nozzle.json index a95be25e66..083cf77ded 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.4 nozzle.json @@ -6,9 +6,19 @@ "setting_id": "GFSG03_46", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.8 nozzle.json index 9b9fa384c8..d9f44afcf5 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D 0.8 nozzle.json @@ -6,9 +6,15 @@ "setting_id": "GFSG03_47", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D.json index 8e90a43b96..9343f1265f 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PETG Matte @BBL X2D.json @@ -6,9 +6,19 @@ "setting_id": "GFSG03_48", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C 0.8 nozzle.json index 75ac270250..1caa272343 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG01_40", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C.json index 73e0247e95..7942bf46f5 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2C.json @@ -6,9 +6,13 @@ "setting_id": "GFSG01_23", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.6 nozzle.json index 0c2c4df008..adadc5b30d 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.6 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG01_11", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.8 nozzle.json index 66e2bbd006..b7532312a2 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2D 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG01_41", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.6 nozzle.json index 236296a6ce..d31ac20557 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.6 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG01_17", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.8 nozzle.json index fc0bf555ea..5f9c5dc247 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL H2DP 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG01_42", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.2 nozzle.json index af938e3c2b..7415dbb6f7 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.2 nozzle.json @@ -6,12 +6,15 @@ "setting_id": "GFSG01_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "20", "20" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.6 nozzle.json index 27b5f3a1fd..81620208c5 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.6 nozzle.json @@ -6,9 +6,13 @@ "setting_id": "GFSG01_14", "instantiation": "true", "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.8 nozzle.json index cfbc2d52a7..eb44d5da56 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL P2S 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG01_43", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X1C 0.8 nozzle.json index 3ec40f01c5..68bf87dc3a 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X1C 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSG01_01", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.2 nozzle.json index 5d5cb6d173..47b3c851e3 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.2 nozzle.json @@ -6,12 +6,21 @@ "setting_id": "GFSG01_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "20", + "20", + "20", "20" ], "fan_max_speed": [ + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "20", + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.4 nozzle.json index 974b3528d6..212b9f5036 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.4 nozzle.json @@ -6,12 +6,27 @@ "setting_id": "GFSG01_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "10", + "10", + "10", + "10", + "10", "10" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "20", + "20", + "20", + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.8 nozzle.json index 4281c834f7..3f67d59a15 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D 0.8 nozzle.json @@ -6,9 +6,15 @@ "setting_id": "GFSG01_29", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D.json index aca3950f6e..eda8949e91 100644 --- a/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PETG Translucent @BBL X2D.json @@ -6,9 +6,19 @@ "setting_id": "GFSG01_20", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "fan_min_speed": [ + "20", + "20", + "20", + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.4 nozzle.json index 4de1312338..72a1b3bacc 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.4 nozzle.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.8 nozzle.json index f327384244..c85b6dc200 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C 0.8 nozzle.json @@ -21,9 +21,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C.json index fa1cab99a7..5c0f7497a8 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2C.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.4 nozzle.json index cf5ed3198f..161f672a53 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.4 nozzle.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.6 nozzle.json index 347a56d4bf..54feeaf911 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.6 nozzle.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.8 nozzle.json index 4ae2558fb2..a976aa1b55 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2D 0.8 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.4 nozzle.json index 93bb7ca67d..47056bd31a 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.4 nozzle.json @@ -18,9 +18,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.6 nozzle.json index 62bc309cfa..90dd4f26cf 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.6 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.8 nozzle.json index 62b7d54979..4b0f63c90c 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2DP 0.8 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.6 nozzle.json index 236fa4d02d..ed7f2ee8c7 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.6 nozzle.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.8 nozzle.json index c9ce999176..cd56208ee1 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S 0.8 nozzle.json @@ -21,9 +21,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S.json index f85e5e820c..5e6d734d53 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL H2S.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.4 nozzle.json index f7987b06fe..731a8539c2 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.4 nozzle.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.6 nozzle.json index 3958df13ad..b6d7bcd845 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.6 nozzle.json @@ -21,9 +21,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.8 nozzle.json index 7239839b3d..80bd8b59d6 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL P2S 0.8 nozzle.json @@ -21,9 +21,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C 0.4 nozzle.json index c136712f57..0d405a8ea8 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C 0.4 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C.json index e953110fe0..6e926af484 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X1C.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.4 nozzle.json index 24b3775fd5..59de3f7aeb 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.4 nozzle.json @@ -21,9 +21,19 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.8 nozzle.json index 378ad209c9..fe7a25ebe5 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D 0.8 nozzle.json @@ -21,9 +21,15 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D.json index f6e03276e0..b9bcb45fb4 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @BBL X2D.json @@ -21,9 +21,19 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C 0.4 nozzle.json index ed720d6732..ad7ecb17d9 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA11_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C.json index 0d5865af10..f89fdd8bcb 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA11_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2D.json index 43a47a4722..5b6a9e0f5d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA11_05", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2DP.json index 4f30395743..71c62a47b2 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSA11_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2S.json index 3a651c0105..d77572787e 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA11_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL P2S.json index f2ea01ad72..56ac92382a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA11_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1.json index 9e5ac03bf3..9a42cebace 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1.json @@ -6,6 +6,7 @@ "setting_id": "GFSA11_00", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", "0" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1C.json index 9fe7a958e1..9e7e06ad66 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSA11_01", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", "0" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D 0.4 nozzle.json index 40f718ed86..dbd40cffc8 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D 0.4 nozzle.json @@ -6,6 +6,11 @@ "setting_id": "GFSA11_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", + "0", + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D.json b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D.json index d3472e210f..4c08f1619f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Aero @BBL X2D.json @@ -6,6 +6,11 @@ "setting_id": "GFSA11_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", + "0", + "0", + "0", "0" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.2 nozzle.json index 07ee98f7e6..1466508a02 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.6 nozzle.json index 63a6a766b0..1905ccf9c0 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA00_31", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.8 nozzle.json index a8f668860c..ea9d7d781f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_32", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C.json index fb7e781c55..025c7658c3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA00_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.2 nozzle.json index 53dfefb6b8..97bdac45bb 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_13", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.6 nozzle.json index b201c04ae8..a39249eeb1 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA00_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.8 nozzle.json index 155afbbc68..bcd8e2994d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D.json index 0cb3530b9c..9f3286c359 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA00_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.2 nozzle.json index 9c49c6d367..07baeac758 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_18", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.6 nozzle.json index 2efcf62609..8328a49d6f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_28", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -24,9 +25,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.8 nozzle.json index ded3b4e3d9..b44ce50908 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP.json index 6df1b590b0..364a6dc643 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_17", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -24,9 +25,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.2 nozzle.json index a26fae8231..25ea7ee5db 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,6 +28,7 @@ "55" ], "fan_min_speed": [ + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.6 nozzle.json index 5e8717b0fc..253b994f0d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA00_30", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,6 +29,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.8 nozzle.json index fcfed64523..c1f368c66a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA00_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,6 +28,7 @@ "55" ], "fan_min_speed": [ + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S.json index 2b26b5a33b..fd525c42ca 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Basic @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA00_05", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,6 +29,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.2 nozzle.json index 31708faa3f..8936eee190 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA13_25", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.4 nozzle.json index 203111f93d..e688214030 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA13_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C.json index d6ae035131..42a99c95bb 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA13_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S 0.2 nozzle.json index 2e4df09afb..58caf94a5d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA13_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "55" ], "fan_min_speed": [ + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S.json index 6bd6a008d5..7dc39f15c6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA13_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.2 nozzle.json index 92b19310cb..5e8b2724c3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.6 nozzle.json index 6f4d0fdbdc..a09807cbf2 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P.json index 8dd853022d..3cbae45547 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Dynamic @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.2 nozzle.json index d7d9738881..90a2d75d36 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA15_25", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.4 nozzle.json index 5fdb9b941f..98fe16b9e3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA15_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C.json index 3c4a3181fd..8152a27d3f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA15_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S 0.2 nozzle.json index c9beecd89c..d8ed6ca17b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA15_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "55" ], "fan_min_speed": [ + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S.json index 91d24f8332..eceb331c7f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA15_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.2 nozzle.json index 7aa84eccd3..8967431e24 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.6 nozzle.json index b5d4190792..4f33e1c9ba 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P.json index 2c0bd7a5fe..949d6bdc40 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S 0.2 nozzle.json index 049e3ad804..d1773e3953 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA15_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S.json index 52fe7324be..5a5f0e4688 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Galaxy @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA15_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C 0.4 nozzle.json index f0601b5374..018bcd48fe 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA12_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C.json index 7130e8e127..63cf2055ac 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA12_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2S.json index 4530262350..4b9feb7ea3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL H2S.json @@ -7,6 +7,8 @@ "setting_id": "GFSA12_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -16,6 +18,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P 0.6 nozzle.json index 930f3a2160..0776e594b0 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P.json index 0d37c339c1..cd60db4f0a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S 0.2 nozzle.json index 1ac8c17a57..264f9ac893 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA12_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S.json index 43a636a896..d2cd72ccba 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Glow @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA12_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.2 nozzle.json index 938f70ef5f..f3c08d0276 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_17", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.4 nozzle.json index a435869e4d..e47f6b0cfa 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA18_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.8 nozzle.json index b10f90036d..c43ee9c55d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_44", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C.json index a30e801edd..e0423ed4f4 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA18_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.2 nozzle.json index fcd3c66c1c..04084d28de 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.6 nozzle.json index fbb2fb9994..5ab62e7c67 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA18_45", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.8 nozzle.json index fb7b165418..b64c4716c8 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D.json index f7bcb1ad8f..fa4dd81219 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA18_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.2 nozzle.json index 3844c578ee..d35ffc498e 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_30", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.4 nozzle.json index e2aea610ee..90df178b13 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.4 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.6 nozzle.json index 758129964a..b896b19979 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_28", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.8 nozzle.json index 924d3416d2..58bb62979a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.2 nozzle.json index c28b9c1e67..29f52784ee 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.6 nozzle.json index e1580f9a56..0a63f0b344 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA18_46", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.8 nozzle.json index a1435c6c6f..553ed9ea7e 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA18_47", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S.json index da3587b191..834a9456f6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA18_18", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.2 nozzle.json index 897af5a38f..6c11a968f3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.6 nozzle.json index a270dea0ab..a85ef0d269 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P.json index e7a9400504..d1eaa7e721 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Lite @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C 0.4 nozzle.json index 1cdc32a534..3d1e35961b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA07_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C.json index 6840297c89..b20ddba103 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA07_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2S.json index 077bbab752..bb34a9a0b7 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Marble @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA07_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.2 nozzle.json index 8b970d07f2..52ba7791f1 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,6 +28,7 @@ "55" ], "fan_min_speed": [ + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.6 nozzle.json index 7ff6555cec..861d01de6c 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_31", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,6 +29,8 @@ "55" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.8 nozzle.json index 9bcd43edb4..0b325ba2e6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_32", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,6 +28,7 @@ "55" ], "fan_min_speed": [ + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C.json index 06643057f8..ace8143221 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,6 +29,8 @@ "55" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.2 nozzle.json index b190108aed..b432c42994 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_13", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.6 nozzle.json index be51abe9d3..bed53a5d5f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.8 nozzle.json index 3ada24f209..07f10c4332 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D.json index 2a0b55932e..c78634c8a8 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_05", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ @@ -27,9 +29,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.2 nozzle.json index f69cf85eca..41eb682f5a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_18", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.6 nozzle.json index 477bf3492a..e213adcb5b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_28", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.8 nozzle.json index 85a1fbcf4f..cec1466727 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP.json index 08a123c6c4..ba91002588 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_17", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.2 nozzle.json index eeea4e7a78..131148ccae 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.6 nozzle.json index 6c611715da..390537664d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_30", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.8 nozzle.json index 0c38e3d228..309723835c 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S.json index f794e7e0f5..fcfee24f76 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.2 nozzle.json index 7af0bca0ae..e7eaf4e62f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.6 nozzle.json index 8845412d00..62ddb12047 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_26", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.8 nozzle.json index 369c15e2cd..0a04e30c0f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA01_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S.json index 85051a2210..079b580168 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Matte @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA01_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.2 nozzle.json index d500051bb1..ee08893a69 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA02_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.4 nozzle.json index 75b221dcd3..6d9f35a444 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA02_20", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C.json index 1ec6730b1a..a061873ce1 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA02_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S 0.2 nozzle.json index e437ccd9f9..535253427f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA02_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S.json index 9c5648425d..b09fb08051 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA02_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.2 nozzle.json index d178683ef1..7c878d42bc 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA02_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.6 nozzle.json index 7c26b3e980..37e8e36af1 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA02_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.8 nozzle.json index 8211ae0b54..e875278247 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Metal @BBL P2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA02_25", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.2 nozzle.json index 2fce1d0a33..5a3bbcbc90 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.6 nozzle.json index 54866db466..ff6d82be48 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA19_42", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.8 nozzle.json index 88ab702e3e..bfd283ae88 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_43", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C.json index 8a01b018c3..cc7b2051af 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA19_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.2 nozzle.json index e30e6203b4..0071b46427 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.6 nozzle.json index 8126977f4b..b6c9a22b07 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA19_44", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.8 nozzle.json index aa981c5177..7a0e25ccb4 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D.json index 002589887f..9f7d65ea19 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA19_13", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.2 nozzle.json index 27c1be9d5f..c78968507b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.6 nozzle.json index 66a86f93ae..88074952fe 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_45", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.8 nozzle.json index dc62f9cecc..f3cf3e9800 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP.json index 516cb30a90..d019971078 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.2 nozzle.json index 1206f03c01..25266ec00f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_17", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.6 nozzle.json index 9a14fb210c..8f69445623 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.6 nozzle.json @@ -7,6 +7,8 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.8 nozzle.json index a780734dc8..23d1564a08 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S 0.8 nozzle.json @@ -7,6 +7,7 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S.json index a3ce5df6ea..71d49c9453 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL H2S.json @@ -7,6 +7,8 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.2 nozzle.json index 9c77055180..eae13723b6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "60", "60" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.8 nozzle.json index 18d8fd4a8a..b2bc0cb4f6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P 0.8 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "60", "60" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P.json index ff2ff52185..f4147158b6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "60", "60" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.2 nozzle.json index 053c0cc82a..f81a615252 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_26", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.4 nozzle.json index cf2c8c5584..37529fde6f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.4 nozzle.json @@ -7,6 +7,8 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.6 nozzle.json index 081ae17a81..0c7542dc06 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA19_28", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.8 nozzle.json index 51b2957e47..d727ad3f43 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL P2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA19_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.2 nozzle.json index 32120fd0c8..b945c2ed24 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.2 nozzle.json @@ -6,6 +6,9 @@ "setting_id": "GFSA19_33", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.4 nozzle.json index b6079ea5d2..b37d3bbdd3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.4 nozzle.json @@ -7,6 +7,11 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", + "75", + "75", + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.6 nozzle.json index 358b0ff624..dc8a1569ab 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.6 nozzle.json @@ -6,6 +6,11 @@ "setting_id": "GFSA19_35", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", + "75", + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.8 nozzle.json index 73a363cfab..5eb09d1b12 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Pure @BBL X2D 0.8 nozzle.json @@ -6,6 +6,9 @@ "setting_id": "GFSA19_36", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.2 nozzle.json index 5f6ac11da9..082074f8be 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.4 nozzle.json index d738fa5afd..2ea73c7c6e 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA05_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C.json index e690d6b168..81ba2dbba1 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA05_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D 0.2 nozzle.json index c14f4c2f2b..b33bf9d81e 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D.json index 991a51de13..7fc75dc921 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA05_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP 0.2 nozzle.json index 7a619beb01..2642126fd7 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_18", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP.json index 3a26b9d69e..b0795534f7 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S 0.2 nozzle.json index ad5e9778c7..6f32b716f0 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S.json index 6221528724..3b3737d286 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA05_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.2 nozzle.json index 3600a41bbb..caafdde46c 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.6 nozzle.json index 71b9f5522b..207d52231d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA05_26", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.8 nozzle.json index 95374a8d59..3ab3db3646 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA05_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S.json index a650da3329..bcd959725c 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA05_17", "instantiation": "true", "additional_cooling_fan_speed": [ + "80", + "80", "80" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.2 nozzle.json index 6d0f939e60..36f37b26ef 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_20", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.4 nozzle.json index 40b37f32b9..ee9d9b4098 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_18", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.8 nozzle.json index 4d460cde49..f51d644ad9 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_40", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C.json index e3a787b51c..8da4bf0b6b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.2 nozzle.json index 80791f03c7..45d7f7b13c 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.6 nozzle.json index 8c97969bf2..b6deafebea 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_41", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.8 nozzle.json index f8508cd0f9..e9a49ec749 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_42", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D.json index a1e9d5a6a9..25af3f5fa5 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.2 nozzle.json index 6ef42bcf5f..fa1a9ebcdc 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.6 nozzle.json index acb22ba23a..fe2e4f047a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_43", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.8 nozzle.json index a4b9e799a4..ff86fe004f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_44", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP.json index cba3b52ae0..a56401ff23 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_13", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.2 nozzle.json index b1bf42971c..756829d4df 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.6 nozzle.json index 8868530a19..abdd991834 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_45", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.8 nozzle.json index 7e3270c32a..af15bde51b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_46", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S.json index 7d143d2360..9543ecc828 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S 0.2 nozzle.json index 05f3286f11..fee682b69f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA06_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S.json index 027b694a64..4bc7d8b4e6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Silk+ @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA06_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "80", + "80", "80" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C 0.4 nozzle.json index d635be71a7..85141b91ff 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA08_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C.json index 59f9880889..5223c79445 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA08_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2S.json index 53ce3da06c..01582d9f28 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA08_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL P2S 0.6 nozzle.json index 8368e4c558..3696f420aa 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Sparkle @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA08_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.2 nozzle.json index afc18f86e9..3d77b54d7f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA09_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.4 nozzle.json index 395e917d6d..1d4ad13d8b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA09_20", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C.json index 687135999b..20c96446c3 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA09_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S 0.2 nozzle.json index 935d868854..0a3f3ac5a4 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA09_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "55" ], "fan_min_speed": [ + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S.json index 0f202955c9..3fb79ac36b 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Tough @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA09_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Tough+ @BBL X1.json b/resources/profiles/BBL/filament/Bambu PLA Tough+ @BBL X1.json index 80e7689736..ed66381b35 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Tough+ @BBL X1.json +++ b/resources/profiles/BBL/filament/Bambu PLA Tough+ @BBL X1.json @@ -6,12 +6,14 @@ "setting_id": "GFSA10_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "70", "70" ], "fan_cooling_layer_time": [ "80" ], "fan_min_speed": [ + "90", "90" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.2 nozzle.json index 743a5d09aa..160791f8fe 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA17_30", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -65,6 +66,7 @@ "fdm_filament_template_direct_dual" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.6 nozzle.json index bf8d33de15..55f6e27d6a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA17_43", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -115,6 +117,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "pre_start_fan_time": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.8 nozzle.json index 7c19dbe1a7..406cabd416 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA17_44", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -101,6 +102,7 @@ "220" ], "nozzle_temperature_range_low": [ + "200", "200" ], "pre_start_fan_time": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C.json index 6a059d251b..d2de2598db 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA17_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -103,6 +105,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "pre_start_fan_time": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2D 0.6 nozzle.json index 595316f8b8..d9be5d6c45 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2D 0.6 nozzle.json @@ -82,6 +82,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "slow_down_min_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2DP 0.6 nozzle.json index d4e2e52372..23c3a5de4a 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2DP 0.6 nozzle.json @@ -73,6 +73,7 @@ "220" ], "nozzle_temperature_range_low": [ + "200", "200" ], "slow_down_min_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.2 nozzle.json index 8ee80929b7..1bbe592a2d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA17_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -48,6 +49,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.6 nozzle.json index 264712fbc9..7d82407ece 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.6 nozzle.json @@ -7,6 +7,8 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -86,6 +88,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "pre_start_fan_time": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.8 nozzle.json index 398d122abe..3091014ddf 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S 0.8 nozzle.json @@ -7,6 +7,7 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -75,6 +76,7 @@ "220" ], "nozzle_temperature_range_low": [ + "200", "200" ], "pre_start_fan_time": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S.json index 5a1f621995..f041cc80c5 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL H2S.json @@ -7,6 +7,8 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -80,6 +82,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "pre_start_fan_time": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.2 nozzle.json index 00bc9dac11..72d39255e4 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ @@ -41,6 +42,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.8 nozzle.json index 670d60514e..a79b0e1316 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P 0.8 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ @@ -41,6 +42,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P.json index 7ae191d7af..a6017bd39d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ @@ -41,6 +42,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.2 nozzle.json index f4d864f29e..5fee15f5e0 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSA17_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -66,6 +67,7 @@ "240" ], "nozzle_temperature_range_low": [ + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.4 nozzle.json index c09cce8eaf..a5902e29e6 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.4 nozzle.json @@ -7,6 +7,8 @@ "instantiation": "true", "description": "The generic presets are conservatively tuned for compatibility with a wider range of filaments. For higher printing quality and speeds, please use Bambu filaments with Bambu presets.", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -59,6 +61,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.6 nozzle.json index 2725f38791..d49dceff5e 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA17_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -68,6 +70,8 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.8 nozzle.json index fda701016e..0099579353 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL P2S 0.8 nozzle.json @@ -51,6 +51,7 @@ "220" ], "nozzle_temperature_range_low": [ + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.2 nozzle.json index 56c2cb1916..749eb63e59 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.2 nozzle.json @@ -29,6 +29,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.8 nozzle.json index 41f54caeb0..bcb3002712 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C 0.8 nozzle.json @@ -29,6 +29,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C.json index cfe114c228..cb52e670ff 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X1C.json @@ -30,6 +30,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.2 nozzle.json index 9ff9f3c7c9..f6b9163be8 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.2 nozzle.json @@ -108,6 +108,9 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.4 nozzle.json index 8fbd944596..828f755d23 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.4 nozzle.json @@ -115,6 +115,11 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", + "200", + "200", + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.6 nozzle.json index 2efa9652d0..6219086298 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.6 nozzle.json @@ -114,6 +114,11 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", + "200", + "200", + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.8 nozzle.json index e8bba0ada4..27f1d0ada2 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Translucent @BBL X2D 0.8 nozzle.json @@ -90,6 +90,9 @@ "220" ], "nozzle_temperature_range_low": [ + "200", + "200", + "200", "200" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C 0.4 nozzle.json index 2165790d3a..88e688db95 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSA16_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C.json b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C.json index 1d718aa068..715c72353f 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSA16_15", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2S.json b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2S.json index 8d45019fdd..746c38b10d 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSA16_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "55" ], "fan_min_speed": [ + "80", + "80", "80" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P 0.6 nozzle.json index abb60dfc31..8dcd364d90 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P.json index 16208882d0..4d8785a24c 100644 --- a/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PLA Wood @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu PPA-CF @BBL P2S.json b/resources/profiles/BBL/filament/Bambu PPA-CF @BBL P2S.json index 01846e0988..3a8b52097d 100644 --- a/resources/profiles/BBL/filament/Bambu PPA-CF @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu PPA-CF @BBL P2S.json @@ -26,9 +26,13 @@ "-0.0075" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu PVA @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PVA @BBL P1P 0.2 nozzle.json index 597e2d83ba..76ebb00b63 100644 --- a/resources/profiles/BBL/filament/Bambu PVA @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu PVA @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu PVA @BBL P1P.json b/resources/profiles/BBL/filament/Bambu PVA @BBL P1P.json index 4c98d516d1..1167c54593 100644 --- a/resources/profiles/BBL/filament/Bambu PVA @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu PVA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.2 nozzle.json index 50022ade4b..afa2910113 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.2 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.4 nozzle.json index c8b6e555c3..46e55e63ba 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.4 nozzle.json @@ -12,9 +12,13 @@ "55" ], "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.8 nozzle.json index 6186fbbfb1..7e9e769bec 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C 0.8 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C.json index ccdcb88f8b..0300745302 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2C.json @@ -12,9 +12,13 @@ "55" ], "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.2 nozzle.json index 9ffdfdde10..2701b25584 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.2 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.6 nozzle.json index 0157469f7b..9a66fa6176 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.6 nozzle.json @@ -12,9 +12,13 @@ "55" ], "fan_max_speed": [ + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.8 nozzle.json index 18f993668b..4eda30ff3e 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D 0.8 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D.json index 224744fbe2..6a0624836e 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2D.json @@ -12,9 +12,13 @@ "55" ], "fan_max_speed": [ + "15", + "15", "15" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.2 nozzle.json index 6d3d64f4e4..5970e18cdf 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.2 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.6 nozzle.json index 96927d4949..d24ff505b5 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.6 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.8 nozzle.json index 1790f3dc7e..5584bbb12d 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP 0.8 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP.json index ddf96b9502..6c61a92acc 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2DP.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.2 nozzle.json index c3bc4b262f..112de8a48e 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSS02_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.6 nozzle.json index e1a5cabe18..f6f0016403 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSS02_39", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +17,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.8 nozzle.json index 3c6c49d850..71e743880c 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSS02_40", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S.json index 9a65ce122d..9ff211ad6f 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSS02_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +17,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL P2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL P2S 0.2 nozzle.json index 67d1b7513c..2b7667a143 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL P2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL P2S 0.2 nozzle.json @@ -12,9 +12,11 @@ "55" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL X2D 0.4 nozzle.json index f6241c25d3..3dddf75456 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA @BBL X2D 0.4 nozzle.json @@ -12,9 +12,19 @@ "55" ], "fan_max_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "fan_min_speed": [ + "20", + "20", + "20", + "20", + "20", "20" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @BBL H2D.json b/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @BBL H2D.json index 658e24aee0..2af3930c39 100644 --- a/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu Support For PLA-PETG @BBL H2D.json @@ -12,9 +12,13 @@ "60" ], "fan_max_speed": [ + "15", + "15", "15" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support W @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu Support W @BBL H2S 0.2 nozzle.json index 0c9ad96894..312df41753 100644 --- a/resources/profiles/BBL/filament/Bambu Support W @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu Support W @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSS00_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu Support W @BBL H2S.json b/resources/profiles/BBL/filament/Bambu Support W @BBL H2S.json index e1bb915584..2a9ca56c04 100644 --- a/resources/profiles/BBL/filament/Bambu Support W @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu Support W @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSS00_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ @@ -15,9 +17,13 @@ "55" ], "fan_max_speed": [ + "80", + "80", "80" ], "fan_min_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2C.json b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2C.json index 50da178634..ec735c5deb 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU04_05", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -115,6 +117,8 @@ "fdm_filament_template_direct_dual_e3d" ], "nozzle_temperature_range_high": [ + "250", + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2D.json b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2D.json index a1c364a9ec..b0f686e2b3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2D.json @@ -6,6 +6,9 @@ "setting_id": "GFSU04_02", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", + "100", "100" ], "eng_plate_temp": [ @@ -252,6 +255,9 @@ "225" ], "nozzle_temperature_range_high": [ + "250", + "250", + "250", "250" ], "override_process_overhang_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2DP.json index 5afdd55df6..de3bccb27b 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2DP.json @@ -6,6 +6,8 @@ "setting_id": "GFSU04_03", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -214,6 +216,8 @@ "235" ], "nozzle_temperature_range_high": [ + "250", + "250", "250" ], "override_process_overhang_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.6 nozzle.json index 493fb2004d..c3766b23b3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU04_8", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.8 nozzle.json index b39cbdfe34..9585ab1101 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU04_9", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S.json b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S.json index 32401c7021..403197ee60 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU 85A @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU04_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C 0.4 nozzle.json index 9a41e06a29..9e63a28406 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -112,6 +114,8 @@ "fdm_filament_template_direct_dual_e3d" ], "nozzle_temperature_range_high": [ + "250", + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C.json index 28f6778d20..2191881728 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -112,6 +114,8 @@ "fdm_filament_template_direct_dual_e3d" ], "nozzle_temperature_range_high": [ + "250", + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.6 nozzle.json index feb9db8a13..dacefeec95 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.6 nozzle.json @@ -6,6 +6,9 @@ "setting_id": "GFSU03_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", + "100", "100" ], "eng_plate_temp": [ @@ -240,6 +243,9 @@ "1" ], "nozzle_temperature_range_high": [ + "250", + "250", + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.8 nozzle.json index b1a45e6d9a..6615572218 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU03_26", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -72,6 +73,7 @@ "220" ], "nozzle_temperature_range_high": [ + "250", "250" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D.json index f2d39bc05c..24ca1c2dca 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2D.json @@ -6,6 +6,9 @@ "setting_id": "GFSU03_02", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", + "100", "100" ], "eng_plate_temp": [ @@ -252,6 +255,9 @@ "225" ], "nozzle_temperature_range_high": [ + "250", + "250", + "250", "250" ], "override_process_overhang_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.6 nozzle.json index cb24fbdbdd..261a5ccf41 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_25", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -214,6 +216,8 @@ "220" ], "nozzle_temperature_range_high": [ + "250", + "250", "250" ], "override_process_overhang_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.8 nozzle.json index 6f8fc61c4e..a131ceacbe 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU03_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -72,6 +73,7 @@ "220" ], "nozzle_temperature_range_high": [ + "250", "250" ], "retraction_distances_when_ec": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP.json index a77efbc6d7..02871a3337 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2DP.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_03", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -214,6 +216,8 @@ "225" ], "nozzle_temperature_range_high": [ + "250", + "250", "250" ], "override_process_overhang_speed": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.6 nozzle.json index c56c11ff05..b365a9a185 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_13", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.8 nozzle.json index d6358536a6..7dc1555cb3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU03_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S.json index 0cae43b7ab..132086eef3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S 0.6 nozzle.json index b294fbacc4..6a2f08a138 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_16", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S.json b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S.json index 1942641481..89648b3efe 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU 90A @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU03_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C 0.4 nozzle.json index bbd0dbc809..8c480a2ff2 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU01_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C.json b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C.json index 20331454b3..54023d7e7d 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU01_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2D.json b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2D.json index fa3cb556e2..88c16df8f3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2D.json @@ -6,6 +6,9 @@ "setting_id": "GFSU01_03", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2DP.json index b2c79b2b94..487b31709b 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2DP.json @@ -6,6 +6,8 @@ "setting_id": "GFSU01_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2S.json b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2S.json index af483d6b70..6ae911cb74 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU01_04", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.4 nozzle.json index e482736c0e..e37518a172 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_13", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.8 nozzle.json index d2368268d3..081cfb90a3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU00_26", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C.json index 52fdb8633b..1ec5931109 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_14", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D 0.8 nozzle.json index 04662c4e58..3ce09e805e 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU00_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D.json index e4333a1576..08f1fda6f3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2D.json @@ -6,6 +6,9 @@ "setting_id": "GFSU00_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP 0.8 nozzle.json index 68762079c0..a3eab351a5 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU00_28", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP.json index d0b0b6191f..a3eb1be1c1 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2DP.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.6 nozzle.json index c9afffaaf8..6f26afb875 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.8 nozzle.json index bbe70e2225..e1f6f3ceab 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU00_30", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S.json index 82964bc87a..0f81463bfc 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.6 nozzle.json index 949d9c0906..d7a54596c3 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_31", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.8 nozzle.json index a2665f6707..a55b3fbcdb 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU00_32", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S.json b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S.json index ab6761cea9..40799c1dd6 100644 --- a/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU 95A HF @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU00_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.4 nozzle.json index 73f4e42208..f8a39ec2e4 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "35" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.8 nozzle.json index 0e54a2e1d5..77446cdc1a 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_25", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "35" ], "fan_max_speed": [ + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C.json index 74253e1452..c3f47d4ffd 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "35" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.6 nozzle.json index 24405af162..4136556b4f 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_26", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "35" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.8 nozzle.json index cc9f65412d..0fa63dbfce 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_27", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "35" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D.json index 22d29a0b08..29e9cbbf73 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_04", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "35" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.6 nozzle.json index 728ec60720..66a4403a4d 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_28", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "35" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.8 nozzle.json index 405b96caa6..bc564266ef 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_29", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "35" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP.json index 641c6bd8b2..14173055e1 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "35" ], "fan_max_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.6 nozzle.json index 3791a37dfb..5d958d75df 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_24", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "35" ], "fan_max_speed": [ + "40", + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.8 nozzle.json index 785f6512f1..22d4b92b7a 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_23", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +16,7 @@ "35" ], "fan_max_speed": [ + "40", "40" ], "filament_change_length": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S.json index 195742bdb0..525f771285 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,6 +17,8 @@ "35" ], "fan_max_speed": [ + "60", + "60", "60" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P 0.6 nozzle.json index bf9b7554f8..dde9ae028a 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_16", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P.json index 80d40378a7..fa0005a6f0 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P1P.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_01", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_retraction_length": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.6 nozzle.json index 4f2c364ef4..0e11fa482f 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.6 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_30", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,9 +17,13 @@ "35" ], "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.8 nozzle.json index fe6a92f21c..d5249e0707 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S 0.8 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_31", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -15,9 +16,11 @@ "35" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S.json index bf1249ebcb..437fd35001 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL P2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU02_05", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -15,9 +17,13 @@ "35" ], "fan_max_speed": [ + "60", + "60", "60" ], "fan_min_speed": [ + "30", + "30", "30" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C 0.6 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C 0.6 nozzle.json index f7e164fe81..caef461097 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_13", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C.json index 45b784e741..42271bba10 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSU02_00", "instantiation": "true", "fan_max_speed": [ + "40", "40" ], "filament_retraction_length": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.4 nozzle.json index 696a79e84a..99183ac320 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.4 nozzle.json @@ -12,6 +12,11 @@ "35" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.8 nozzle.json index 116476ceeb..100cfb14bc 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D 0.8 nozzle.json @@ -12,6 +12,9 @@ "35" ], "fan_max_speed": [ + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D.json b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D.json index ee378baeff..02d5532acd 100644 --- a/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D.json +++ b/resources/profiles/BBL/filament/Bambu TPU for AMS @BBL X2D.json @@ -12,6 +12,11 @@ "35" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic ABS @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Generic ABS @BBL H2C 0.2 nozzle.json index a95f8b7000..8d1a12f356 100644 --- a/resources/profiles/BBL/filament/Generic ABS @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Generic ABS @BBL H2C 0.2 nozzle.json @@ -9,9 +9,11 @@ "60" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic ABS @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Generic ABS @BBL H2S 0.2 nozzle.json index e412d22e80..b2d460207f 100644 --- a/resources/profiles/BBL/filament/Generic ABS @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Generic ABS @BBL H2S 0.2 nozzle.json @@ -9,9 +9,11 @@ "60" ], "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "40", "40" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C 0.4 nozzle.json index e2dc6c43da..609200aaf7 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C 0.4 nozzle.json @@ -9,9 +9,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C.json index fffb6a6e49..a8d158bf12 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2C.json @@ -9,9 +9,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2D.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2D.json index 063d30a279..627dcf1ea7 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2D.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2D.json @@ -9,9 +9,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2DP.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2DP.json index d0e4ff8752..4e15261404 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2DP.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2S.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2S.json index 585f656ab1..123c23d5d7 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2S.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL H2S.json @@ -9,9 +9,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL P2S.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL P2S.json index e1944cca5d..eaf44c29a4 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL P2S.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL P2S.json @@ -9,9 +9,13 @@ "30" ], "fan_max_speed": [ + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL X1C.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL X1C.json index 46dda5c26d..fe45f29947 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL X1C.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL X1C.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D 0.4 nozzle.json index 73db99b2ce..f21d59a9ca 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D 0.4 nozzle.json @@ -9,9 +9,19 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D.json b/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D.json index 2309a91389..59f4b1c1f1 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @BBL X2D.json @@ -9,9 +9,19 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", + "5", + "5", "5" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.2 nozzle.json index 634dc781c4..0109d97d23 100644 --- a/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL99_22", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.4 nozzle.json index 9489ef729a..6d00ddb005 100644 --- a/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSL99_20", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA @BBL H2C.json b/resources/profiles/BBL/filament/Generic PLA @BBL H2C.json index 334c18e36c..18fbaaa915 100644 --- a/resources/profiles/BBL/filament/Generic PLA @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic PLA @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSL99_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Generic PLA @BBL H2S 0.2 nozzle.json index c04a5a7f1d..b5ab0c7b0e 100644 --- a/resources/profiles/BBL/filament/Generic PLA @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL99_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA @BBL H2S.json b/resources/profiles/BBL/filament/Generic PLA @BBL H2S.json index d43e487d30..b02c02007f 100644 --- a/resources/profiles/BBL/filament/Generic PLA @BBL H2S.json +++ b/resources/profiles/BBL/filament/Generic PLA @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL99_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.2 nozzle.json b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.2 nozzle.json index 736f9d5b73..58b1f98c68 100644 --- a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL95_21", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.4 nozzle.json index 1978b96cf3..491d03de56 100644 --- a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSL95_19", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C.json b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C.json index e7083107ef..762249175f 100644 --- a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSL95_20", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S 0.2 nozzle.json index 23633f4ce4..ae178912f4 100644 --- a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL95_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S.json b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S.json index 97706a18ab..42372243fc 100644 --- a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S.json +++ b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL95_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL P1P.json b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL P1P.json index 9c36600779..7d40128b9b 100644 --- a/resources/profiles/BBL/filament/Generic PLA High Speed @BBL P1P.json +++ b/resources/profiles/BBL/filament/Generic PLA High Speed @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C 0.4 nozzle.json index c48a46337c..e4c891915d 100644 --- a/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSL96_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C.json b/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C.json index 61201273f0..f3a82bdd05 100644 --- a/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSL96_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2S.json b/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2S.json index 100e03b3c9..dae0e857d4 100644 --- a/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2S.json +++ b/resources/profiles/BBL/filament/Generic PLA Silk @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL96_04", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C 0.4 nozzle.json index 6307ce8fd1..4d1f2c5c06 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C 0.4 nozzle.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C.json index 99fbafe181..7fefc47632 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2C.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2D.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2D.json index 5798bede95..0e0b947fa0 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2D.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2D.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2DP.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2DP.json index ab2c95d6ec..3a6d182a8b 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2DP.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2S 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2S 0.4 nozzle.json index 1424641376..8f134d6350 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2S 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL H2S 0.4 nozzle.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL P2S.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL P2S.json index 77b17fb0d8..1ac2b972ff 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL P2S.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL P2S.json @@ -9,6 +9,8 @@ "60" ], "fan_max_speed": [ + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1C.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1C.json index 6b543ecd14..235dc251c6 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1C.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "GFSN97_00", "instantiation": "true", "fan_max_speed": [ + "35", "35" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1E.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1E.json index 636204070f..b455fbb083 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1E.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X1E.json @@ -9,6 +9,7 @@ "60" ], "fan_max_speed": [ + "35", "35" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D 0.4 nozzle.json index c171b03fe9..9963bf7c53 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D 0.4 nozzle.json @@ -9,6 +9,11 @@ "60" ], "fan_max_speed": [ + "35", + "35", + "35", + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D.json b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D.json index 97b164c7a4..f3f53400d6 100644 --- a/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D.json +++ b/resources/profiles/BBL/filament/Generic PPA-CF @BBL X2D.json @@ -9,6 +9,11 @@ "60" ], "fan_max_speed": [ + "35", + "35", + "35", + "35", + "35", "35" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic TPU @BBL H2C 0.4 nozzle.json index 9dda2e9f85..fe62720d18 100644 --- a/resources/profiles/BBL/filament/Generic TPU @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic TPU @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU99_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic TPU @BBL H2C.json b/resources/profiles/BBL/filament/Generic TPU @BBL H2C.json index 063f446f6d..818944cfa2 100644 --- a/resources/profiles/BBL/filament/Generic TPU @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic TPU @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU99_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic TPU @BBL H2D.json b/resources/profiles/BBL/filament/Generic TPU @BBL H2D.json index 1d2e1a447d..b5d28a37e9 100644 --- a/resources/profiles/BBL/filament/Generic TPU @BBL H2D.json +++ b/resources/profiles/BBL/filament/Generic TPU @BBL H2D.json @@ -6,6 +6,9 @@ "setting_id": "GFSU99_02", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic TPU @BBL H2DP.json b/resources/profiles/BBL/filament/Generic TPU @BBL H2DP.json index 456941431d..2fd9bf50e7 100644 --- a/resources/profiles/BBL/filament/Generic TPU @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Generic TPU @BBL H2DP.json @@ -6,6 +6,8 @@ "setting_id": "GFSU99_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic TPU @BBL H2S.json b/resources/profiles/BBL/filament/Generic TPU @BBL H2S.json index be7f778869..aa6a537525 100644 --- a/resources/profiles/BBL/filament/Generic TPU @BBL H2S.json +++ b/resources/profiles/BBL/filament/Generic TPU @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU99_04", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C 0.4 nozzle.json index 7232187281..58cf7270b5 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C 0.4 nozzle.json @@ -6,6 +6,8 @@ "setting_id": "GFSU98_10", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -18,9 +20,13 @@ "40" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C.json index 4980e672f8..700c90e567 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2C.json @@ -6,6 +6,8 @@ "setting_id": "GFSU98_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -18,9 +20,13 @@ "40" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2D.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2D.json index 37f4f2ffa4..9eec32f435 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2D.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2D.json @@ -6,6 +6,8 @@ "setting_id": "GFSU98_04", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -18,9 +20,13 @@ "40" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2DP.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2DP.json index 42f65f4680..1e3b9ac042 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2DP.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2DP.json @@ -6,6 +6,7 @@ "setting_id": "GFSU98_08", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -18,9 +19,11 @@ "40" ], "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2S.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2S.json index 7931394b1f..6cd4066909 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2S.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSU98_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", + "100", "100" ], "eng_plate_temp": [ @@ -18,9 +20,13 @@ "40" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P1P.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P1P.json index 437aadfa54..e93e38f3dd 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P1P.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P1P.json @@ -9,9 +9,11 @@ "40" ], "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "10", "10" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P2S.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P2S.json index 24c8b552fb..cde0a1e7f7 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P2S.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL P2S.json @@ -15,9 +15,13 @@ "40" ], "fan_max_speed": [ + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X1C.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X1C.json index 85b053a0cd..189175d6c1 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X1C.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X1C.json @@ -9,9 +9,11 @@ "40" ], "fan_max_speed": [ + "50", "50" ], "fan_min_speed": [ + "10", "10" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D 0.4 nozzle.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D 0.4 nozzle.json index 446cb91fbb..0e71a2ab70 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D 0.4 nozzle.json @@ -15,9 +15,19 @@ "40" ], "fan_max_speed": [ + "50", + "50", + "50", + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", + "10", + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D.json b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D.json index 3223336a76..6b7ccfcb34 100644 --- a/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D.json +++ b/resources/profiles/BBL/filament/Generic TPU for AMS @BBL X2D.json @@ -15,9 +15,19 @@ "40" ], "fan_max_speed": [ + "50", + "50", + "50", + "50", + "50", "50" ], "fan_min_speed": [ + "10", + "10", + "10", + "10", + "10", "10" ], "filament_cooling_before_tower": [ diff --git a/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S 0.2 nozzle.json index 5c258fbd47..843ad953e4 100644 --- a/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL05_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S.json b/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S.json index eb84bfac73..c2f8591551 100644 --- a/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S.json +++ b/resources/profiles/BBL/filament/Overture Matte PLA @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL05_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Overture Matte PLA @BBL P1P.json b/resources/profiles/BBL/filament/Overture Matte PLA @BBL P1P.json index b6747d4d27..0b4c7295e5 100644 --- a/resources/profiles/BBL/filament/Overture Matte PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/Overture Matte PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/Overture PLA @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/Overture PLA @BBL H2S 0.2 nozzle.json index 015bd25840..d810f76495 100644 --- a/resources/profiles/BBL/filament/Overture PLA @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/Overture PLA @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL04_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Overture PLA @BBL H2S.json b/resources/profiles/BBL/filament/Overture PLA @BBL H2S.json index e66b7c9405..f65421b43c 100644 --- a/resources/profiles/BBL/filament/Overture PLA @BBL H2S.json +++ b/resources/profiles/BBL/filament/Overture PLA @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL04_11", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/Overture PLA @BBL P1P.json b/resources/profiles/BBL/filament/Overture PLA @BBL P1P.json index c8ad574304..9c24fa069a 100644 --- a/resources/profiles/BBL/filament/Overture PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/Overture PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.2 nozzle.json index dcbd382ffe..d86054d733 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_02", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.6 nozzle.json index ea6060ba15..bd88551b01 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P 0.6 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_41", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P.json index c21004ca75..c667b7bb37 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu ABS @BBL P1P.json @@ -6,6 +6,7 @@ "setting_id": "GFSB00_03", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PA-CF @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PA-CF @BBL P1P.json index b7cfc61878..b4e7c13da7 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PA-CF @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PA-CF @BBL P1P.json @@ -9,9 +9,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.2 nozzle.json index d64fff5af9..d211d9acff 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.2 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.6 nozzle.json index bc23bb1607..4167792881 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P 0.6 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P.json index 0977c4fbb8..0d40c57d7f 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PC @BBL P1P.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PET-CF @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PET-CF @BBL P1P.json index 5095859d67..f31d1108a6 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PET-CF @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PET-CF @BBL P1P.json @@ -9,9 +9,11 @@ "80" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "1", "1" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P 0.4 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P 0.4 nozzle.json index 6f21d17509..1d0a40bbc1 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P 0.4 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P 0.4 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P.json index bf140d0d31..514ff302b6 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PETG-CF @BBL P1P.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Aero @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Aero @BBL P1P.json index 01eb23cd11..eb6c25d774 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Aero @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Aero @BBL P1P.json @@ -6,6 +6,7 @@ "setting_id": "GFSA11_02", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", "0" ], "fan_cooling_layer_time": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.2 nozzle.json index db4088ef94..14ffc1f3af 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.6 nozzle.json index f30ef29c1f..3afb4e04d7 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P.json index eb897cf359..c8d5579415 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Basic @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P 0.6 nozzle.json index 2cbd700b75..98d76749a1 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P.json index 1cc0306b4b..dbdfba324d 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Marble @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.2 nozzle.json index 6dbead6f4d..58d82e9947 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.6 nozzle.json index 6107e5ba72..444a896d66 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P.json index a70c160320..54b746b622 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Matte @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.2 nozzle.json index c33ce2a479..ddab76f59b 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.6 nozzle.json index f0fd865b66..2b4247d4df 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P.json index 9c600caafe..58c8d0f1f6 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Metal @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P 0.6 nozzle.json index eaa8896497..15251bcf61 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P 0.6 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P.json index 240da6d165..1f13647b2f 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Sparkle @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P 0.2 nozzle.json index f99ea5200f..ed776be75b 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P.json index 8a1a0fd274..c50562687f 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.2 nozzle.json index 1a0c12f0ff..f578485c6e 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.2 nozzle.json @@ -6,12 +6,14 @@ "setting_id": "GFSA10_33", "instantiation": "true", "additional_cooling_fan_speed": [ + "70", "70" ], "fan_cooling_layer_time": [ "80" ], "fan_min_speed": [ + "90", "90" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.6 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.6 nozzle.json index c090797121..09fd6e3614 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P 0.6 nozzle.json @@ -6,12 +6,14 @@ "setting_id": "GFSA10_38", "instantiation": "true", "additional_cooling_fan_speed": [ + "70", "70" ], "fan_cooling_layer_time": [ "80" ], "fan_min_speed": [ + "90", "90" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P.json index b4e25f845b..13c6ae6a58 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Tough+ @BBL P1P.json @@ -6,12 +6,14 @@ "setting_id": "GFSA10_34", "instantiation": "true", "additional_cooling_fan_speed": [ + "70", "70" ], "fan_cooling_layer_time": [ "80" ], "fan_min_speed": [ + "90", "90" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P 0.8 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P 0.8 nozzle.json index dcf38827ce..def3a2b834 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P 0.8 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P.json index 82a84b4d1f..607ad46ade 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA-CF @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P 0.2 nozzle.json index 0a789a6941..3e06730971 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P.json index 3bbd78f489..bed7a8a83f 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu Support For PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P 0.2 nozzle.json index 43c99264b1..34d24468ed 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P.json index ca08dc8ce3..1d0ddf542f 100644 --- a/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Bambu Support W @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P 0.2 nozzle.json index a11c8b406c..b8ba38cc18 100644 --- a/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSB99_02", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P.json index 77f537261e..e2d57d2407 100644 --- a/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Generic ABS @BBL P1P.json @@ -6,6 +6,7 @@ "setting_id": "GFSB99_01", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P 0.2 nozzle.json index 75837ececb..9fac30cbfc 100644 --- a/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P 0.2 nozzle.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P.json index 08086f47f6..df1ea38f65 100644 --- a/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Generic PC @BBL P1P.json @@ -12,6 +12,7 @@ "100" ], "fan_max_speed": [ + "20", "20" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic PETG-CF @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Generic PETG-CF @BBL P1P.json index fc0eb4132b..59049ae30c 100644 --- a/resources/profiles/BBL/filament/P1P/Generic PETG-CF @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Generic PETG-CF @BBL P1P.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P 0.2 nozzle.json index fb08c2ca3c..b4f52cf6e2 100644 --- a/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P.json index 23782d1f63..fefc87018c 100644 --- a/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Generic PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/Generic PVA @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Generic PVA @BBL P1P.json index 55cbed4c0f..32406f8d79 100644 --- a/resources/profiles/BBL/filament/P1P/Generic PVA @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/Generic PVA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P 0.2 nozzle.json index c7e57b79d2..d5d4db0ceb 100644 --- a/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P.json b/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P.json index 9d5f349afb..1bd2bda10c 100644 --- a/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/PolyLite PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P 0.2 nozzle.json index c07ce9f912..608b2c4654 100644 --- a/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P.json b/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P.json index 69b0921119..d9389ab744 100644 --- a/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/PolyTerra PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P 0.2 nozzle.json index 07b2229f3c..1bab33f91d 100644 --- a/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P.json b/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P.json index c035419a13..b4bfa2366f 100644 --- a/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P.json +++ b/resources/profiles/BBL/filament/P1P/eSUN PLA+ @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/PolyLite ABS @BBL P1P.json b/resources/profiles/BBL/filament/PolyLite ABS @BBL P1P.json index 57d32de9a7..32b64c1349 100644 --- a/resources/profiles/BBL/filament/PolyLite ABS @BBL P1P.json +++ b/resources/profiles/BBL/filament/PolyLite ABS @BBL P1P.json @@ -6,6 +6,7 @@ "setting_id": "GFSB60_04", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2D.json b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2D.json index 3e90dda823..ccc0b59c48 100644 --- a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2D.json +++ b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2D.json @@ -24,6 +24,8 @@ "fdm_filament_template_direct_dual_e3d" ], "nozzle_temperature_range_high": [ + "270", + "270", "270" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2DP.json b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2DP.json index 24fcafab4f..f8179f7c73 100644 --- a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2DP.json +++ b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2DP.json @@ -29,6 +29,7 @@ "255" ], "nozzle_temperature_range_high": [ + "270", "270" ], "slow_down_min_speed": [ diff --git a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S 0.2 nozzle.json index 0bdcad3735..d4e4978024 100644 --- a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S 0.2 nozzle.json @@ -33,6 +33,7 @@ "0" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S.json b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S.json index 912cb9a3ed..58e42dba4e 100644 --- a/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S.json +++ b/resources/profiles/BBL/filament/PolyLite PETG @BBL H2S.json @@ -34,6 +34,8 @@ "0" ], "nozzle_temperature_range_high": [ + "270", + "270", "270" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/PolyLite PETG @BBL P1P.json b/resources/profiles/BBL/filament/PolyLite PETG @BBL P1P.json index 9b0b409cb9..ec05e93824 100644 --- a/resources/profiles/BBL/filament/PolyLite PETG @BBL P1P.json +++ b/resources/profiles/BBL/filament/PolyLite PETG @BBL P1P.json @@ -27,6 +27,7 @@ "0" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/PolyLite PETG @BBL X1C.json b/resources/profiles/BBL/filament/PolyLite PETG @BBL X1C.json index fb7f801850..a69f499126 100644 --- a/resources/profiles/BBL/filament/PolyLite PETG @BBL X1C.json +++ b/resources/profiles/BBL/filament/PolyLite PETG @BBL X1C.json @@ -21,6 +21,7 @@ "0" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature": [ diff --git a/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S 0.2 nozzle.json index bcd5226a9e..f623a98acb 100644 --- a/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL00_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S.json b/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S.json index 8ff8f89d35..c4d8c7413f 100644 --- a/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S.json +++ b/resources/profiles/BBL/filament/PolyLite PLA @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL00_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S 0.2 nozzle.json index 38d5fec0ca..d966dce73c 100644 --- a/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL01_07", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S.json b/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S.json index ee6b8e2e36..533c00c5f8 100644 --- a/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S.json +++ b/resources/profiles/BBL/filament/PolyTerra PLA @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL01_06", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU Marble PLA @BBL P1P.json b/resources/profiles/BBL/filament/SUNLU/SUNLU Marble PLA @BBL P1P.json index 0a103c995c..bc6e1338f0 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU Marble PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU Marble PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PETG @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PETG @BBL X1C 0.8 nozzle.json index 99dc78c918..608f860fcc 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PETG @BBL X1C 0.8 nozzle.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PETG @BBL X1C 0.8 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GFSNLS08_01", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P 0.2 nozzle.json index 464e056c8c..7150b7078b 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P.json index 6054f5fd07..6fa8309b85 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA Matte @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P 0.2 nozzle.json index f8329334a4..5e6555da12 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P.json index b6bccf5151..ae8bcda763 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ 2.0 @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P 0.2 nozzle.json index 23227067a2..5f46964241 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P.json b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P.json index 092d213cca..c936fed91a 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU PLA+ @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/BBL/filament/SUNLU/SUNLU Wood PLA @BBL P1P.json b/resources/profiles/BBL/filament/SUNLU/SUNLU Wood PLA @BBL P1P.json index ff61f6d3ce..9b7811ef7b 100644 --- a/resources/profiles/BBL/filament/SUNLU/SUNLU Wood PLA @BBL P1P.json +++ b/resources/profiles/BBL/filament/SUNLU/SUNLU Wood PLA @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S 0.2 nozzle.json b/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S 0.2 nozzle.json index eb6e63dd5c..c8d99496b4 100644 --- a/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S 0.2 nozzle.json +++ b/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S 0.2 nozzle.json @@ -6,6 +6,7 @@ "setting_id": "GFSL03_12", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S.json b/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S.json index 0a60b92b11..770b3ae75f 100644 --- a/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S.json +++ b/resources/profiles/BBL/filament/eSUN PLA+ @BBL H2S.json @@ -6,6 +6,8 @@ "setting_id": "GFSL03_09", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/OrcaFilamentLibrary.json b/resources/profiles/OrcaFilamentLibrary.json index 4ef905b799..d5f972a222 100644 --- a/resources/profiles/OrcaFilamentLibrary.json +++ b/resources/profiles/OrcaFilamentLibrary.json @@ -1,6 +1,6 @@ { "name": "OrcaFilamentLibrary", - "version": "02.04.00.17", + "version": "02.04.00.18", "force_update": "0", "description": "Orca Filament Library", "filament_list": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL H2D.json index b02dd7539c..d656750212 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL H2D.json @@ -9,6 +9,7 @@ "65" ], "fan_max_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL P1P.json index 8210765185..f04ace20fe 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL P1P.json @@ -6,6 +6,7 @@ "setting_id": "MPYyfCKMsJZy8GaY", "instantiation": "true", "fan_max_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL X1C.json index 196053eb0a..c7d4e82ed6 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA ABS @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "vMA7EhgNDT4MwMRr", "instantiation": "true", "fan_max_speed": [ + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PEBA 90A @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PEBA 90A @BBL H2D.json index a5b0a4e46b..a165447b05 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PEBA 90A @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PEBA 90A @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "91b8WTzvOky9wAVK", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -113,6 +114,7 @@ "1" ], "nozzle_temperature_range_high": [ + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL H2D 0.4 nozzle.json index c508f9041c..057c9f8242 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "KnKvUsD4rSpQU413", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL X1C.json index 5547610fcf..04710b1fa5 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "uM6wHdt0N5ftZRge", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL H2D 0.4 nozzle.json index 19f8879c41..7e48c959e7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "JHg4y0pxAOtoJSuw", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL X1C.json index 353498bf5c..4912d88dab 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Fluorescence @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "pCetfFvyLLw3vS2j", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL H2D 0.4 nozzle.json index 7d4b3bb5d5..fedcc166de 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "ijW4zvNAQ3DIK984", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL X1C.json index 18c99695f4..c8a290d952 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glitter @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "UI220qldPoLY8VZN", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL H2D 0.4 nozzle.json index 9196aff31b..40643ae178 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "3s9w3mH6M4OdbrUV", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL X1C.json index 42a4521212..789ea04781 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Glow @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "wv5FRKdnsHaIFA7L", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL H2D 0.4 nozzle.json index b468069cb7..794d273b55 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "GqmCuPg6baUxeoYS", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL X1C.json index 48c3f30535..64edc74c5a 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Gradient @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "oYJjUiaCI4UBZ0ur", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL H2D 0.4 nozzle.json index f8bc99dd97..1550a34be4 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL H2D 0.4 nozzle.json @@ -18,6 +18,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ @@ -109,6 +110,7 @@ "0" ], "fan_max_speed": [ + "30", "30" ], "hole_coef_2": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL X1C.json index d67f8d619e..b58c82e3f2 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG HF @BBL X1C.json @@ -9,6 +9,7 @@ "20" ], "fan_min_speed": [ + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL H2D 0.4 nozzle.json index 5f6706766a..1bae511a03 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "DpWgpTiKdm70Xt1i", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL X1C.json index b0e0ee39ca..59bb2ed2b0 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Heat Color Change @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "3lJe6ftSHQDkQZ1S", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL H2D 0.4 nozzle.json index decfcb73a1..9597d6cf2c 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "RxCTxdvym4iNOB5j", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL X1C.json index 49ac9206f0..92d80895bf 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Marble @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "3BgsPAgilo1MgtOV", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL H2D 0.4 nozzle.json index 3287a32971..f092a437d8 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "sqz8Vluq7GMd3siv", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL X1C.json index d8f5548ba0..432feb1f44 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Matte @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "NXgqYCqHeVCgFF3I", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL H2D 0.4 nozzle.json index c04e4513b5..5493034671 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "giq8BW6qcwz91e4C", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL X1C.json index eaa3a9dae2..bd40641cf8 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG Metallic @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "tZzoc2LdgWkpEiox", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL H2D 0.4 nozzle.json index 03f0000a35..664acba408 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL H2D 0.4 nozzle.json @@ -6,9 +6,11 @@ "setting_id": "Ic0NUfwLmR0NSh5q", "instantiation": "true", "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL X1C.json index 32eaa333cf..f379f7822c 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG UV Color Change @BBL X1C.json @@ -6,6 +6,7 @@ "setting_id": "BUKCpukEUmocfz3v", "instantiation": "true", "fan_min_speed": [ + "20", "20" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL H2D 0.4 nozzle.json index 22af1e06ff..2baf05c347 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL H2D 0.4 nozzle.json @@ -18,9 +18,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL P1P 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL P1P 0.4 nozzle.json index f1a7334810..818f5cfa7f 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL P1P 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL P1P 0.4 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL X1C 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL X1C 0.4 nozzle.json index 273260a31f..59a3c628e1 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL X1C 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-CF @BBL X1C 0.4 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL H2D 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL H2D 0.4 nozzle.json index 277d1915b1..7b819b21b9 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL H2D 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL H2D 0.4 nozzle.json @@ -18,9 +18,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL P1P 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL P1P 0.4 nozzle.json index a94fc48630..ec1c4d0d6c 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL P1P 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL P1P 0.4 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL X1C 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL X1C 0.4 nozzle.json index db3ecc90d9..d17270dc91 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL X1C 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PETG-GF @BBL X1C 0.4 nozzle.json @@ -9,9 +9,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL H2D.json index 97e462a487..0ee70ddaa9 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "LZBgJ5xFtzrt3v02", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -24,9 +25,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL P1P.json index 99214e744d..2630b20b48 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Basic @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Chameleon @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Chameleon @BBL P1P.json index ea66c51982..07a08de4ae 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Chameleon @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Chameleon @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Fluorescence @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Fluorescence @BBL P1P.json index a786a7aa36..d03578a040 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Fluorescence @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Fluorescence @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glitter @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glitter @BBL P1P.json index c7cb14bea3..c938541308 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glitter @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glitter @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glow @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glow @BBL P1P.json index f19705f369..7054e30d3c 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glow @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Glow @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Gradient @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Gradient @BBL P1P.json index 15b6d22f5e..5797b492b0 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Gradient @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Gradient @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Heat Color Change @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Heat Color Change @BBL P1P.json index b789e28638..6ba21121f6 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Heat Color Change @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Heat Color Change @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Speed @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Speed @BBL P1P.json index b2f9e5ad9d..741ec66d99 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Speed @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Speed @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Temp @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Temp @BBL H2D.json index 4047850988..d47c4fddb6 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Temp @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA High Temp @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "5F1rml3AGNMD6F1g", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Marble @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Marble @BBL P1P.json index a198637db6..eccd20aeeb 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Marble @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Marble @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL H2D.json index bc595e11b5..cb47647899 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "Zw3CZVyBon930GCH", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL P1P.json index 6331679d0c..aaee41348e 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Matte @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metal @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metal @BBL P1P.json index 512647c8ac..bd9857150f 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metal @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metal @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metallic @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metallic @BBL P1P.json index cdb7e0a72d..3438bd4c8c 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metallic @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Metallic @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL H2D.json index 605e1e9a37..b82556d9db 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "Mth7IAdxkWf7pSSj", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -24,9 +25,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL P1P.json index 35d88a6d9f..8f85e07ab2 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA PRO @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk @BBL H2D.json index 943bdfaf11..76525381a6 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "qXuac1ixZ4VdWMyA", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk+ @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk+ @BBL H2D.json index 35d9da0d5c..da4b170101 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk+ @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Silk+ @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "4aj9YjpmJDQoTIv7", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "eng_plate_temp": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL P1P.json index 3ab7af716f..b009240a89 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_retract_before_wipe": [ @@ -110,6 +111,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL X1C.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL X1C.json index 0c564fe5d0..5d3cafd4c7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL X1C.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Transparent @BBL X1C.json @@ -99,6 +99,7 @@ "0" ], "nozzle_temperature_range_low": [ + "200", "200" ], "nozzle_temperature": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA UV Color Change @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA UV Color Change @BBL P1P.json index 15309bbca5..fee733bc53 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA UV Color Change @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA UV Color Change @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Wood @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Wood @BBL P1P.json index 62d5a28a78..09fb339399 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Wood @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Wood @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_flow_ratio": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL H2D.json index 4b1b4b9a50..cc5a192fda 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "ZtgqauY30xyhRIB3", "instantiation": "true", "additional_cooling_fan_speed": [ + "75", "75" ], "counter_coef_2": [ @@ -27,9 +28,11 @@ "55" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "60", "60" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL P1P.json index a40b68fbc3..14995c7334 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA Youth @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA-CF @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA-CF @BBL P1P.json index d7e1cf9423..a5015a81c3 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA-CF @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA PLA-CF @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 90A @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 90A @BBL H2D.json index 8eb8e80392..d1533707ee 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 90A @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 90A @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "lm5PEdmOJODAz6KJ", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -113,6 +114,7 @@ "1" ], "nozzle_temperature_range_high": [ + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 95A @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 95A @BBL H2D.json index 62cdeadad1..dad06c4758 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 95A @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 95A @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "k6clcTFJXTGVdmZg", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 98A @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 98A @BBL H2D.json index ab3c0989cb..eb4cdc710e 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 98A @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU 98A @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "p0qjTRymaVctbU11", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU Matte @BBL H2D.json b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU Matte @BBL H2D.json index a1571446ab..ccb5715f92 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU Matte @BBL H2D.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/BETA/BBL/BETA TPU Matte @BBL H2D.json @@ -6,6 +6,7 @@ "setting_id": "PeJPI88l3Ui7jZnj", "instantiation": "true", "additional_cooling_fan_speed": [ + "100", "100" ], "eng_plate_temp": [ @@ -113,6 +114,7 @@ "1" ], "nozzle_temperature_range_high": [ + "250", "250" ], "nozzle_temperature": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ABS @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ABS @System.json index 9a66aa85db..6409e3aaa7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ABS @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ABS @System.json @@ -6,6 +6,11 @@ "setting_id": "iXiD2dx3qXFVPHRi", "instantiation": "true", "fan_max_speed": [ + "60", + "60", + "60", + "60", + "60", "60" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ASA @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ASA @System.json index 0706ff5cc5..85d822e0e7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ASA @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu ASA @System.json @@ -6,6 +6,11 @@ "setting_id": "NEYuPnFvaHGhAdpD", "instantiation": "true", "fan_min_speed": [ + "25", + "25", + "25", + "25", + "25", "25" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PA-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PA-CF @System.json index a37d362c30..8ff5ce30ef 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PA-CF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PA-CF @System.json @@ -22,9 +22,19 @@ "290" ], "fan_max_speed": [ + "30", + "30", + "30", + "30", + "30", "30" ], "fan_min_speed": [ + "10", + "10", + "10", + "10", + "10", "10" ], "fan_cooling_layer_time": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC @System.json index cf5cc74a10..f11e3cc8bb 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC @System.json @@ -6,6 +6,11 @@ "setting_id": "piqS3ekvyUfN9dgG", "instantiation": "true", "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "slow_down_layer_time": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC FR @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC FR @System.json index a3abfb5bd5..3f650afda5 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC FR @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PC FR @System.json @@ -6,6 +6,11 @@ "setting_id": "m7TaVFWJZUqzf0rP", "instantiation": "true", "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "slow_down_layer_time": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG HF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG HF @System.json index 453ccc6400..97c8436453 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG HF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG HF @System.json @@ -9,6 +9,11 @@ "20" ], "fan_min_speed": [ + "20", + "20", + "20", + "20", + "20", "20" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG-CF @System.json index 28ae43ae59..f5590b772d 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG-CF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PETG-CF @System.json @@ -9,9 +9,19 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", + "5", + "5", "5" ], "filament_long_retractions_when_cut": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PLA Aero @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PLA Aero @System.json index 511914839a..28ae999df2 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PLA Aero @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Bambu/Bambu PLA Aero @System.json @@ -6,6 +6,11 @@ "setting_id": "R499nyjJA5r7khOd", "instantiation": "true", "additional_cooling_fan_speed": [ + "0", + "0", + "0", + "0", + "0", "0" ], "slow_down_layer_time": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PA-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PA-CF @System.json index fdef334123..74036889c0 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PA-CF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PA-CF @System.json @@ -19,6 +19,11 @@ "PA-CF" ], "filament_multitool_ramming_flow": [ + "8", + "8", + "8", + "8", + "8", "8" ], "compatible_printers": [] diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG HF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG HF @System.json index e46645a5e1..3c787ca1d8 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG HF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG HF @System.json @@ -14,6 +14,11 @@ "21" ], "filament_multitool_ramming_flow": [ + "20", + "20", + "20", + "20", + "20", "20" ], "compatible_printers": [] diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG-CF @System.json index 4127e51392..f7536223af 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG-CF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PETG-CF @System.json @@ -9,9 +9,19 @@ "30" ], "fan_max_speed": [ + "40", + "40", + "40", + "40", + "40", "40" ], "fan_min_speed": [ + "5", + "5", + "5", + "5", + "5", "5" ], "filament_max_volumetric_speed": [ @@ -23,6 +33,11 @@ "11.5" ], "filament_multitool_ramming_flow": [ + "13", + "13", + "13", + "13", + "13", "13" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA High Speed @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA High Speed @System.json index 303ad6ac2f..90aee0b556 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA High Speed @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA High Speed @System.json @@ -31,6 +31,11 @@ "30" ], "filament_multitool_ramming_flow": [ + "25", + "25", + "25", + "25", + "25", "25" ], "slow_down_layer_time": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA Matte @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA Matte @System.json index a7dd869115..800f9f4e0a 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA Matte @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PLA Matte @System.json @@ -14,6 +14,11 @@ "25" ], "filament_multitool_ramming_flow": [ + "11", + "11", + "11", + "11", + "11", "11" ], "filament_retraction_length": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-CF @System.json index 3e55ce0703..4c035469ed 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-CF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-CF @System.json @@ -21,6 +21,11 @@ "6" ], "filament_multitool_ramming_flow": [ + "6", + "6", + "6", + "6", + "6", "6" ], "filament_type": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-GF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-GF @System.json index 101c2be4e7..c24caddef0 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-GF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PP-GF @System.json @@ -21,6 +21,11 @@ "6" ], "filament_multitool_ramming_flow": [ + "6", + "6", + "6", + "6", + "6", "6" ], "filament_type": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Generic PPA-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Generic PPA-CF @System.json index 33c6747a68..a909c0bc38 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Generic PPA-CF @System.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Generic PPA-CF @System.json @@ -7,6 +7,11 @@ "filament_id": "OF8tPByX", "instantiation": "true", "fan_max_speed": [ + "35", + "35", + "35", + "35", + "35", "35" ], "filament_extruder_variant": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @BBL P2S.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @BBL P2S.json index e421095237..cbd4beb609 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @BBL P2S.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @BBL P2S.json @@ -10,6 +10,7 @@ "1" ], "additional_cooling_fan_speed": [ + "70", "70" ], "close_fan_the_first_x_layers": [ @@ -42,9 +43,11 @@ "20" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "filament_adaptive_volumetric_speed": [ @@ -137,6 +140,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_pre_cooling_temperature": [ @@ -222,9 +226,11 @@ "255" ], "nozzle_temperature_range_high": [ + "280", "280" ], "nozzle_temperature_range_low": [ + "250", "250" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @P1-X1.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @P1-X1.json index 6a01ae3255..96a607e349 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @P1-X1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-66 @P1-X1.json @@ -66,6 +66,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_retraction_minimum_travel": [ @@ -119,9 +120,11 @@ "2" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "slow_down_min_speed": [ @@ -200,6 +203,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "compatible_printers": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @BBL P2S.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @BBL P2S.json index 2e2b7b82b5..3a8fad133d 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @BBL P2S.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @BBL P2S.json @@ -10,6 +10,7 @@ "1" ], "additional_cooling_fan_speed": [ + "70", "70" ], "close_fan_the_first_x_layers": [ @@ -42,9 +43,11 @@ "20" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "filament_adaptive_volumetric_speed": [ @@ -137,6 +140,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_pre_cooling_temperature": [ @@ -222,9 +226,11 @@ "280" ], "nozzle_temperature_range_high": [ + "290", "290" ], "nozzle_temperature_range_low": [ + "270", "270" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @P1-X1.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @P1-X1.json index b50fbd38e9..87c0461e53 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @P1-X1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PA6-CF @P1-X1.json @@ -66,6 +66,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_retraction_minimum_travel": [ @@ -119,9 +120,11 @@ "2" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "slow_down_min_speed": [ @@ -200,6 +203,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "compatible_printers": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @BBL P2S.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @BBL P2S.json index 7522dfad87..5cfadde8ee 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @BBL P2S.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @BBL P2S.json @@ -21,9 +21,11 @@ "25" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "filament_flow_ratio": [ @@ -41,6 +43,7 @@ "70" ], "nozzle_temperature_range_high": [ + "260", "260" ], "overhang_fan_speed": [ @@ -182,6 +185,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "close_fan_the_first_x_layers": [ @@ -262,6 +266,7 @@ "0" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_prime_volume": [ @@ -305,6 +310,7 @@ "10" ], "nozzle_temperature_range_low": [ + "240", "240" ], "reduce_fan_stop_start_freq": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @P1-X1.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @P1-X1.json index 9269f53bc5..1e441cfcb9 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @P1-X1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PETG Pro @P1-X1.json @@ -66,6 +66,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_retraction_minimum_travel": [ @@ -119,9 +120,11 @@ "2" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "slow_down_min_speed": [ @@ -200,6 +203,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "compatible_printers": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @BBL P2S.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @BBL P2S.json index b9a4e0b377..443fd76caa 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @BBL P2S.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @BBL P2S.json @@ -176,12 +176,15 @@ "55" ], "fan_min_speed": [ + "60", "60" ], "fan_max_speed": [ + "100", "100" ], "additional_cooling_fan_speed": [ + "70", "70" ], "compatible_printers": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @P1-X1.json b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @P1-X1.json index 52cbbd3596..90ce6f5192 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @P1-X1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/INSLOGIC/BBL/INSLOGIC PLA Pro @P1-X1.json @@ -66,6 +66,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_retraction_minimum_travel": [ @@ -119,9 +120,11 @@ "2" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "60", "60" ], "slow_down_min_speed": [ @@ -200,6 +203,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "compatible_printers": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/JAYO/BBL/JAYO PETG @BBL P2S 0.4 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/JAYO/BBL/JAYO PETG @BBL P2S 0.4 nozzle.json index fc70479c7b..afced56591 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/JAYO/BBL/JAYO PETG @BBL P2S 0.4 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/JAYO/BBL/JAYO PETG @BBL P2S 0.4 nozzle.json @@ -48,6 +48,7 @@ "25" ], "additional_cooling_fan_speed": [ + "70", "70" ], "slow_down_layer_time": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P 0.2 nozzle.json b/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P 0.2 nozzle.json index f2c1c78883..e6242c5aa0 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P 0.2 nozzle.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P 0.2 nozzle.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_max_volumetric_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P.json b/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P.json index a9e5da69b6..988868d757 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Numakers/BBL/Numakers PLA+ @BBL P1P.json @@ -9,6 +9,7 @@ "80" ], "fan_min_speed": [ + "50", "50" ], "filament_deretraction_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-ESD @BBL X1.json b/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-ESD @BBL X1.json index 99fcec0536..943ad93ee4 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-ESD @BBL X1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-ESD @BBL X1.json @@ -19,6 +19,7 @@ "Cool Plate" ], "fan_max_speed": [ + "80", "80" ], "temperature_vitrification": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-rCF08 @BBL X1.json b/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-rCF08 @BBL X1.json index d564918a1e..00c9cbc3d3 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-rCF08 @BBL X1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/Polymaker/BBL/Fiberon PETG-rCF08 @BBL X1.json @@ -15,6 +15,7 @@ "nil" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_retraction_minimum_travel": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json index b9ee6494c5..9c41c99100 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json @@ -11,6 +11,7 @@ "1" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "25", "25" ], "fan_min_speed": [ + "0", "0" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "250" ], "nozzle_temperature_range_high": [ + "260", "260" ], "nozzle_temperature_range_low": [ + "230", "230" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json index 8532e514a3..b9cfdd4fa7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json @@ -11,6 +11,7 @@ "1" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "11" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "265" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature_range_low": [ + "245", "245" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json index c7690e1178..7b5f99ee60 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json @@ -11,6 +11,7 @@ "1" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "11" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "265" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature_range_low": [ + "245", "245" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json index 7dab608a6f..bb38a38852 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json @@ -11,6 +11,7 @@ "1" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "15" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "275" ], "nozzle_temperature_range_high": [ + "280", "280" ], "nozzle_temperature_range_low": [ + "260", "260" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json index 6e068507f1..f8da458e21 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json @@ -11,6 +11,7 @@ "1" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "5" ], "fan_max_speed": [ + "30", "30" ], "fan_min_speed": [ + "10", "10" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "11" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.62 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "275" ], "nozzle_temperature_range_high": [ + "285", "285" ], "nozzle_temperature_range_low": [ + "265", "265" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json index de13a67d83..a58028b69f 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "40", "40" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "15" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "275" ], "nozzle_temperature_range_high": [ + "290", "290" ], "nozzle_temperature_range_low": [ + "265", "265" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json index b1120899d7..3fdf2fee27 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "10" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "255" ], "nozzle_temperature_range_high": [ + "275", "275" ], "nozzle_temperature_range_low": [ + "240", "240" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json index cc7012c75d..25e253f757 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "20" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 high reliability - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "290" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature_range_low": [ + "255", "255" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json index 48119c7b04..bd7e0764f7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "10" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "255" ], "nozzle_temperature_range_high": [ + "275", "275" ], "nozzle_temperature_range_low": [ + "240", "240" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json index 0650202cea..cb94413ef6 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "20" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.4 high reliability - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "290" ], "nozzle_temperature_range_high": [ + "270", "270" ], "nozzle_temperature_range_low": [ + "255", "255" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json index 978db741a7..52da3757e3 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "8" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "260" ], "nozzle_temperature_range_high": [ + "275", "275" ], "nozzle_temperature_range_low": [ + "235", "235" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json index 8514e62315..159fcca4d7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "40", "40" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "10" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "255" ], "nozzle_temperature_range_high": [ + "260", "260" ], "nozzle_temperature_range_low": [ + "225", "225" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json index d2789e2d15..b62613ae92 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "100", "100" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "30" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "5", "5" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "11" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026 – Special thanks to Mats.Z (3DP24, Jonkoping University / Campus Varnamo) for profile improvements during internship autumn 2025.", @@ -299,9 +303,11 @@ "270" ], "nozzle_temperature_range_high": [ + "275", "275" ], "nozzle_temperature_range_low": [ + "245", "245" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json index b92f8c27b5..1cf251f555 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "240", "240" ], "nozzle_temperature_range_low": [ + "190", "190" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json index de0cdb9f05..4271837beb 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "240", "240" ], "nozzle_temperature_range_low": [ + "200", "200" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json index e8e2fca28a..03f7f6ff13 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "11" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "245", "245" ], "nozzle_temperature_range_low": [ + "210", "210" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json index f3cb324a87..73c52acce6 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "50", "50" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "80" ], "fan_max_speed": [ + "90", "90" ], "fan_min_speed": [ + "70", "70" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "10" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "245" ], "nozzle_temperature_range_high": [ + "255", "255" ], "nozzle_temperature_range_low": [ + "235", "235" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json index 9b49ebac26..bfe7676c25 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "80" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "14" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "240", "240" ], "nozzle_temperature_range_low": [ + "190", "190" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json index e29534a5ad..1be018e431 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "8" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "230" ], "nozzle_temperature_range_high": [ + "230", "230" ], "nozzle_temperature_range_low": [ + "200", "200" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json index 98a98c4536..cd8b897e5f 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "12" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "240", "240" ], "nozzle_temperature_range_low": [ + "190", "190" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json index def3487467..59aa588cd4 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "14" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "230" ], "nozzle_temperature_range_high": [ + "240", "240" ], "nozzle_temperature_range_low": [ + "190", "190" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json index ef614b39ae..0aa4ca73b7 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "16" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "240", "240" ], "nozzle_temperature_range_low": [ + "190", "190" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json index e0a99b8b05..96ba6ace88 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json @@ -11,6 +11,7 @@ "1" ], "additional_cooling_fan_speed": [ + "0", "0" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "5" ], "fan_max_speed": [ + "80", "80" ], "fan_min_speed": [ + "25", "25" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "4" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.6 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "265" ], "nozzle_temperature_range_high": [ + "260", "260" ], "nozzle_temperature_range_low": [ + "240", "240" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json index a1d9deab44..2b05356f4e 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "5" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.7 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "250", "250" ], "nozzle_temperature_range_low": [ + "230", "230" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json index e944d203a9..b14dd41fba 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "5" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.2 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "250" ], "nozzle_temperature_range_high": [ + "250", "250" ], "nozzle_temperature_range_low": [ + "230", "230" ], "overhang_fan_speed": [ diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json index d0e272d902..c35736f4b5 100644 --- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json +++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json @@ -11,6 +11,7 @@ "0" ], "additional_cooling_fan_speed": [ + "70", "70" ], "chamber_temperatures": [ @@ -124,9 +125,11 @@ "100" ], "fan_max_speed": [ + "100", "100" ], "fan_min_speed": [ + "100", "100" ], "filament_adhesiveness_category": [ @@ -171,6 +174,7 @@ "5" ], "filament_minimal_purge_on_wipe_tower": [ + "15", "15" ], "filament_notes": "File Version:2.3 - V.Pack:18_05_2026", @@ -299,9 +303,11 @@ "235" ], "nozzle_temperature_range_high": [ + "250", "250" ], "nozzle_temperature_range_low": [ + "230", "230" ], "overhang_fan_speed": [ From 2a9cb32c1ff20bd102ae4dbf43b8ec12b2b47425 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 1 Oct 2026 12:29:57 +0800 Subject: [PATCH 87/89] Fix bundled Python crashing on macOS 26 and older when built with Xcode 27 Building deps with Xcode 27 on macOS 26 failed at the Python install step, and a libpython built with Xcode 27 segfaulted on macOS 12-26 whenever a plugin started a subprocess. --- deps/python3/python3.cmake | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/deps/python3/python3.cmake b/deps/python3/python3.cmake index 3e063fac4e..6b4443ef3a 100644 --- a/deps/python3/python3.cmake +++ b/deps/python3/python3.cmake @@ -151,6 +151,12 @@ elseif(APPLE) # the post-install -add_rpath below. set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names") + # The macOS 27 SDK declares pipe2() and dup3() as available from macOS 27, so + # configure finds them and CPython 3.12 calls them without a runtime check. + # Below a macOS 27 deployment target they are weak-linked and resolve to NULL + # on older systems, where os.pipe() then segfaults -- in `make install` + # (compileall, ensurepip) and in the shipped app alike. Every configure below + # keeps the pipe()/dup2() fallbacks (python/cpython#153711). if(IS_CROSS_COMPILE) set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin) set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") @@ -174,7 +180,8 @@ elseif(APPLE) --enable-shared \ --without-static-libpython \ --disable-test-modules \ - --build=${_python_build_arch}-apple-darwin && \ + --build=${_python_build_arch}-apple-darwin \ + ac_cv_func_pipe2=no ac_cv_func_dup3=no && \ make -j${NPROC} python && \ cd '' && \ env \ @@ -191,6 +198,7 @@ elseif(APPLE) --without-static-libpython \ --with-openssl='${DESTDIR}' \ --disable-test-modules \ + ac_cv_func_pipe2=no ac_cv_func_dup3=no \ ${_python_build_tgt} \ --with-build-python='${_python_build_python}' \ py_cv_module__tkinter=n/a" @@ -213,6 +221,8 @@ elseif(APPLE) --with-openssl=${DESTDIR} --disable-test-modules ${_python_build_tgt} + ac_cv_func_pipe2=no + ac_cv_func_dup3=no # Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's # _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway. py_cv_module__tkinter=n/a From 1a5bc8982d63b78b73b5adc278d134561f110bb6 Mon Sep 17 00:00:00 2001 From: HanifKoh <76276251+HanifKoh@users.noreply.github.com> Date: Thu, 1 Oct 2026 14:41:50 +0800 Subject: [PATCH 88/89] Stop Logged-Out Login Polling from Hitting the System Keychain (#15979) With stealth mode off the home page asks for the login status every 2 s, and while nobody is logged in that ends in clear_user_secret(), which opened the system keychain and deleted the OrcaSlicer/Auth entry on the UI thread every tick. A working keychain cost a D-Bus round trip per tick; a keychain that never answers blocked the UI for 25 s per tick. It also deleted a login another running instance had just saved, and ignored use_encrypted_token_file, so opting out of the keychain did not help. Remember whether this process read a secret from the store or wrote one, and only then touch the store when a logged-out poll asks for a logout. A logged-out instance never touches the keychain, and in encrypted-file mode the poll no longer opens the keychain at all. A secret this process cannot read, such as a token file encrypted for another OS user sharing the data directory, is left alone by the poll. An explicit logout still wipes both backends, so a token stranded by switching the token storage option cannot sign the account back in later. --- src/slic3r/Utils/OrcaCloudServiceAgent.cpp | 31 ++++++-- src/slic3r/Utils/OrcaCloudServiceAgent.hpp | 9 ++- tests/slic3rutils/CMakeLists.txt | 1 + tests/slic3rutils/test_orca_cloud_agent.cpp | 86 +++++++++++++++++++++ 4 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 tests/slic3rutils/test_orca_cloud_agent.cpp diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp index f84d250c0d..e5c781f477 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp @@ -819,7 +819,9 @@ int OrcaCloudServiceAgent::user_logout(bool request) } } - clear_session(); + // An explicit logout also wipes the backend the token storage option is not using, so a token + // stranded by switching that option cannot sign the account back in later. + clear_session(/*all_backends=*/request); return BAMBU_NETWORK_SUCCESS; } @@ -1604,7 +1606,9 @@ void OrcaCloudServiceAgent::persist_user_secret(const std::string& secret) } } - (void) stored; + if (stored) { + secret_stored = true; + } } bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret) @@ -1644,6 +1648,7 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret) } if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) { + secret_stored = true; out_secret = plain; // Upgrade legacy payloads to signed format if (payload.rfind("v2:", 0) != 0) { @@ -1661,6 +1666,7 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret) if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) { out_secret.assign(static_cast(secret.GetData()), secret.GetSize()); if (!out_secret.empty()) { + secret_stored = true; return true; } } @@ -1670,11 +1676,20 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret) return false; } -void OrcaCloudServiceAgent::clear_user_secret() +void OrcaCloudServiceAgent::clear_user_secret(bool all_backends) { - wxSecretStore store = wxSecretStore::GetDefault(); - if (store.IsOk()) { - store.Delete(SECRET_STORE_SERVICE); + // Nothing this process loaded or saved: leave the store alone. Deleting would only cost a + // keychain round trip (or a hang while the keychain is unresponsive) and could remove a + // login another instance just saved. + if (!secret_stored.exchange(false) && !all_backends) { + return; + } + + if (all_backends || !m_use_encrypted_token_file) { + wxSecretStore store = wxSecretStore::GetDefault(); + if (store.IsOk()) { + store.Delete(SECRET_STORE_SERVICE); + } } compute_fallback_path(); @@ -2023,13 +2038,13 @@ bool OrcaCloudServiceAgent::set_user_session(const json& session_json, bool noti return success; } -void OrcaCloudServiceAgent::clear_session() +void OrcaCloudServiceAgent::clear_session(bool all_backends) { { std::lock_guard lock(session_mutex); session = SessionInfo{}; } - clear_user_secret(); + clear_user_secret(all_backends); } // ============================================================================ diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp index 565772cf96..7e060da84a 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp @@ -326,7 +326,7 @@ public: void persist_user_secret(const std::string& secret); bool load_user_secret(std::string& out_secret); - void clear_user_secret(); + void clear_user_secret(bool all_backends = false); // Token refresh helpers bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason); @@ -344,7 +344,7 @@ public: bool persist = true); // Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON. bool set_user_session(const nlohmann::json& session_json, bool notify_login = true); - void clear_session(); + void clear_session(bool all_backends = false); static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = ""); @@ -413,6 +413,11 @@ private: // Member variables - auth state PkceBundle pkce_bundle; std::string secret_fallback_path; + // Set once this process has read a secret from the store or written one. Unless the user logs + // out explicitly, clear_user_secret() only touches the store while it is set, so a logged-out + // instance (the GUI polls the login status every 2 s) makes no keychain calls and cannot wipe + // a login another instance saved. + std::atomic_bool secret_stored{false}; SessionHandler session_handler; OnLoginCompleteHandler on_login_complete_handler; SessionInfo session; diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index f38dfeaa98..4d419a4b57 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(${_TEST_NAME}_tests test_prebuild_queue.cpp test_staged_build.cpp test_network_versions.cpp + test_orca_cloud_agent.cpp test_action_source.cpp test_plugin_host_api.cpp # Exercise seam enums and predicates through the embedded Python host API. diff --git a/tests/slic3rutils/test_orca_cloud_agent.cpp b/tests/slic3rutils/test_orca_cloud_agent.cpp new file mode 100644 index 0000000000..76f4e8df16 --- /dev/null +++ b/tests/slic3rutils/test_orca_cloud_agent.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include + +#include +#include + +#include "slic3r/Utils/OrcaCloudServiceAgent.hpp" +#include "test_utils.hpp" + +using namespace Slic3r; +namespace fs = boost::filesystem; + +namespace { + +// The encrypted token file is the one secret backend a test can observe without a system +// keychain. Every agent pointed at the same directory shares it, like separate app instances +// share the keychain entry. +std::unique_ptr make_file_backed_agent(const fs::path& dir) +{ + auto agent = std::make_unique(dir.string()); + agent->set_use_encrypted_token_file(true); + agent->set_config_dir(dir.string()); + return agent; +} + +fs::path secret_file(const fs::path& dir) { return dir / secret_constants::USER_SECRET_FILENAME; } + +} // namespace + +TEST_CASE("Logging out removes the secret this instance saved", "[OrcaCloudServiceAgent]") +{ + ScopedTemporaryDir dir("orca-secret"); + auto agent = make_file_backed_agent(dir.path()); + + agent->persist_user_secret("refresh-token"); + REQUIRE(fs::exists(secret_file(dir.path()))); + + agent->user_logout(false); + CHECK_FALSE(fs::exists(secret_file(dir.path()))); +} + +TEST_CASE("Logging out removes a secret this instance loaded from the store", "[OrcaCloudServiceAgent]") +{ + ScopedTemporaryDir dir("orca-secret"); + make_file_backed_agent(dir.path())->persist_user_secret("refresh-token"); + + auto agent = make_file_backed_agent(dir.path()); + std::string secret; + REQUIRE(agent->load_user_secret(secret)); + CHECK(secret == "refresh-token"); + + agent->user_logout(false); + CHECK_FALSE(fs::exists(secret_file(dir.path()))); +} + +TEST_CASE("Logging out leaves a secret this instance never loaded or saved alone", "[OrcaCloudServiceAgent]") +{ + ScopedTemporaryDir dir("orca-secret"); + make_file_backed_agent(dir.path())->persist_user_secret("refresh-token"); + + // A logged-out instance is asked to log out on every login-status poll. + auto other = make_file_backed_agent(dir.path()); + other->user_logout(false); + other->user_logout(false); + CHECK(fs::exists(secret_file(dir.path()))); + + std::string secret; + REQUIRE(make_file_backed_agent(dir.path())->load_user_secret(secret)); + CHECK(secret == "refresh-token"); +} + +TEST_CASE("Logging out leaves a secret this instance could not read alone", "[OrcaCloudServiceAgent]") +{ + ScopedTemporaryDir dir("orca-secret"); + // Written under another encryption key, e.g. by another OS user sharing the data directory. + fs::ofstream(secret_file(dir.path())) << "v2:0000:not-a-payload-this-user-can-decrypt"; + + auto agent = make_file_backed_agent(dir.path()); + std::string secret; + REQUIRE_FALSE(agent->load_user_secret(secret)); + + agent->user_logout(false); + CHECK(fs::exists(secret_file(dir.path()))); +} From 6842d9c7787d8dccb15c4051a3452054a2bf8d03 Mon Sep 17 00:00:00 2001 From: HanifKoh <76276251+HanifKoh@users.noreply.github.com> Date: Thu, 1 Oct 2026 14:42:20 +0800 Subject: [PATCH 89/89] Keep the Plugin Tests' Python Packages Out of the Working Directory (#15981) The plugin test fixtures start the interpreter before the test points data_dir at its temporary directory, so PythonInterpreter creates {data_dir}/python/packages and {data_dir}/log with an empty data_dir: a python/ and log/ folder in whatever directory the tests run from. When that is the test binary's folder, the next run's embedded-interpreter tests took the stray python/ as their home and failed to start Python. Give each fixture that initializes the plugin manager its own temporary data directory, set up before initialize(), and only use the python/ folder next to the test binary as the interpreter's home when it holds a standard library. --- tests/slic3rutils/python_test_support.hpp | 10 +++++++++- tests/slic3rutils/test_plugin_capability_config.cpp | 3 +++ tests/slic3rutils/test_plugin_cloud_metadata.cpp | 3 +++ tests/slic3rutils/test_plugin_lifecycle.cpp | 3 +++ tests/slic3rutils/test_plugin_printer_agent.cpp | 5 +++++ tests/slic3rutils/test_slicing_pipeline_config.cpp | 3 +++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/slic3rutils/python_test_support.hpp b/tests/slic3rutils/python_test_support.hpp index ab66f05887..29747a88c4 100644 --- a/tests/slic3rutils/python_test_support.hpp +++ b/tests/slic3rutils/python_test_support.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -25,8 +26,15 @@ void ensure_python_initialized() config.parse_argv = 0; const auto python_home = boost::dll::program_location().parent_path() / "python"; +#ifdef _WIN32 + const auto stdlib = python_home / "Lib"; +#else + const auto stdlib = python_home / "lib" / + ("python" + std::to_string(PY_MAJOR_VERSION) + "." + std::to_string(PY_MINOR_VERSION)); +#endif - if (boost::filesystem::exists(python_home)) { + // Only a real runtime: a stray python/ folder (packages a test left behind) is not a home. + if (boost::filesystem::exists(stdlib / "encodings")) { const std::string home = python_home.string(); const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str()); diff --git a/tests/slic3rutils/test_plugin_capability_config.cpp b/tests/slic3rutils/test_plugin_capability_config.cpp index f224f92308..39ecd1002d 100644 --- a/tests/slic3rutils/test_plugin_capability_config.cpp +++ b/tests/slic3rutils/test_plugin_capability_config.cpp @@ -38,6 +38,9 @@ namespace { // before this destructor's shutdown() runs. struct ScopedPluginManager { + // Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log, + // which would otherwise land in the working directory. + ScopedDataDir python_data_dir{"plugin-python"}; bool initialized = PluginManager::instance().initialize(); ~ScopedPluginManager() diff --git a/tests/slic3rutils/test_plugin_cloud_metadata.cpp b/tests/slic3rutils/test_plugin_cloud_metadata.cpp index c34b596ee0..241ef3ed90 100644 --- a/tests/slic3rutils/test_plugin_cloud_metadata.cpp +++ b/tests/slic3rutils/test_plugin_cloud_metadata.cpp @@ -31,6 +31,9 @@ namespace { // same as any other plugin. struct ScopedManagerShutdown { + // Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log, + // which would otherwise land in the working directory. + ScopedDataDir python_data_dir{"plugin-python"}; bool initialized = PluginManager::instance().initialize(); ~ScopedManagerShutdown() diff --git a/tests/slic3rutils/test_plugin_lifecycle.cpp b/tests/slic3rutils/test_plugin_lifecycle.cpp index 0c96f8c0b3..d90a166835 100644 --- a/tests/slic3rutils/test_plugin_lifecycle.cpp +++ b/tests/slic3rutils/test_plugin_lifecycle.cpp @@ -42,6 +42,9 @@ namespace { // Declare this FIRST in a test so it is destroyed last. struct ScopedPluginManager { + // Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log, + // which would otherwise land in the working directory. + ScopedDataDir python_data_dir{"plugin-python"}; bool initialized = false; ScopedPluginManager() { initialized = PluginManager::instance().initialize(); } diff --git a/tests/slic3rutils/test_plugin_printer_agent.cpp b/tests/slic3rutils/test_plugin_printer_agent.cpp index fc85363a39..c1bba8a88e 100644 --- a/tests/slic3rutils/test_plugin_printer_agent.cpp +++ b/tests/slic3rutils/test_plugin_printer_agent.cpp @@ -12,6 +12,8 @@ #include #include +#include "plugin_test_utils.hpp" + namespace py = pybind11; using namespace Slic3r; @@ -21,6 +23,9 @@ namespace { // into Python unless PythonInterpreter::instance() reports initialized. struct ScopedPluginManager { + // Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log, + // which would otherwise land in the working directory. + ScopedDataDir python_data_dir{"plugin-python"}; bool initialized = PluginManager::instance().initialize(); ~ScopedPluginManager() diff --git a/tests/slic3rutils/test_slicing_pipeline_config.cpp b/tests/slic3rutils/test_slicing_pipeline_config.cpp index ff8cddcba6..37984cff34 100644 --- a/tests/slic3rutils/test_slicing_pipeline_config.cpp +++ b/tests/slic3rutils/test_slicing_pipeline_config.cpp @@ -35,6 +35,9 @@ namespace { struct ScopedPluginManager { + // Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log, + // which would otherwise land in the working directory. + ScopedDataDir python_data_dir{"plugin-python"}; bool initialized = false; ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }

    yop5!?__o)S6go;_vH41NZ5Tl}^sNko^ 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 the grazing silhouette - a, b = OUT[i], OUT[(i+1) % n] - F.append(([(a[0],a[1],0.0),(b[0],b[1],0.0),(b[0],b[1],PLATE),(a[0],a[1],PLATE)], "body", True)) - F.append(([(x,y,PLATE) for x,y in OUT], "body", True)) # plate top - zm = PLATE + 0.004 - for cx,cy,r in MARKS: # eyes + cheek dot - F.append(([(cx+r*math.cos(2*math.pi*i/12), cy+r*math.sin(2*math.pi*i/12), zm) for i in range(12)], "mark", False)) - F.append(([(x,y,zm) for x,y in CHIN], "mark", False)) # chin bar - A, B = CREST # THE MUZZLE: base quad + crest - nl=(SBASE[0][0],SBASE[0][1],PLATE); nr=(SBASE[1][0],SBASE[1][1],PLATE) - tr=(SBASE[2][0],SBASE[2][1],PLATE); tl=(SBASE[3][0],SBASE[3][1],PLATE) - F += [([nl,tl,B,A],"body",True), # left flank - ([nr,A,B,tr],"body",True), # right flank - ([nl,A,nr],"body",True), # nose cap, sloping because the base overhangs the crest - ([tr,B,tl],"body",True)] # tail cap - return F -FACETS = facets() - -BODY=(0.42,0.46,0.52); MARK=(0.126,0.138,0.156) -def render(px, elev_deg, ss=8): - S=px*ss; a=math.radians(elev_deg); ca,sa=math.cos(a),math.sin(a) - # camera orbits down; the connector's +Z (relief) tips toward the horizon - xf=lambda p:(p[0], p[1]*sa + p[2]*ca, -p[1]*ca + p[2]*sa) - light=(-0.70,0.30,0.45) - img=Image.new("RGB",(S,S),(24,27,32)); d=ImageDraw.Draw(img) - tris=[] - for pts,kind,shade in FACETS: - q=[xf(p) for p in pts] - tris.append((sum(v[2] for v in q)/len(q), q, kind, shade)) - tris.sort(key=lambda t:t[0]) # far first - for _,q,kind,shade in tris: - (x0,y0,z0),(x1,y1,z1),(x2,y2,z2)=q[0],q[1],q[2] - ux,uy,uz=x1-x0,y1-y0,z1-z0; vx,vy,vz=x2-x0,y2-y0,z2-z0 - nx,ny,nz=uy*vz-uz*vy, uz*vx-ux*vz, ux*vy-uy*vx - nn=math.sqrt(nx*nx+ny*ny+nz*nz) or 1.0 - nx,ny,nz=nx/nn,ny/nn,nz/nn - if nz<0: nx,ny,nz=-nx,-ny,-nz - base=BODY if kind=="body" else MARK - k=(0.42+0.58*max(0.0,nx*light[0]+ny*light[1]+nz*light[2])) if shade else 1.0 - col=tuple(min(255,int(255*c*k)) for c in base) - d.polygon([(S/2+p[0]*S*0.92, S/2-p[1]*S*0.92) for p in q], fill=col) - return img.resize((px,px), Image.LANCZOS) - -SIZES=[22,32,48]; ELEVS=[(90,"flat on"),(47,"47"),(16,"16"),(6,"6")] -pad,cell=8,58 -W=pad+len(SIZES)*len(ELEVS)*cell+pad; H=pad+cell+pad -sheet=Image.new("RGB",(W,H),(24,27,32)) -for ci,(e,_) in enumerate(ELEVS): - for si,px in enumerate(SIZES): - g=render(px,e) - sheet.paste(g, (pad+(ci*len(SIZES)+si)*cell+(cell-px)//2, pad+(cell-px)//2)) -sheet.resize((W*2,H*2), Image.NEAREST).save(os.path.join(HERE,"glyph-preview.png")) - -# how much of the glyph is the snout: render with and without the tent and diff -def render_no_tent(px, elev): - global FACETS - keep=FACETS; FACETS=FACETS[:-4] - try: return render(px, elev) - finally: FACETS=keep -print(f"{'elev':>8} {'lit px@32':>10} {'snout px':>9} {'snout share':>12}") -for e,_ in ELEVS: - a=render(32,e); b=render_no_tent(32,e) - la=sum(1 for p in a.get_flattened_data() if p!=(24,27,32)) - diff=sum(1 for p,q in zip(a.get_flattened_data(), b.get_flattened_data()) if p!=q) - print(f"{e:>8} {la:>10} {diff:>9} {100.0*diff/max(1,la):>11.1f}%") -print("WROTE glyph-preview.png") diff --git a/docs/CAD/design/mate-connectors/glyph_probe.py b/docs/CAD/design/mate-connectors/glyph_probe.py deleted file mode 100644 index f4d48fdd6c..0000000000 --- a/docs/CAD/design/mate-connectors/glyph_probe.py +++ /dev/null @@ -1,143 +0,0 @@ -# Mate-connector glyph probe — built as REAL solids on REAL mechanical geometry, -# so the shape can be judged in a 3D viewport instead of in a browser mock. -# -# Four polarity treatments, side by side on one bracket: -# A Onshape baseline ...... ring + roll quadrant + three short axis arms -# B solid cone ............ ring + quadrant + one-sided Z arrow, filled head (driven) -# C hollow collar ......... ring + quadrant + one-sided Z arrow, shell head (fixed) -# D pin / cup ............. polarity by RELIEF: a raised pin vs a sunk cup -# -# D is the one that only a 3D test can settle: in a shaded viewport, solid-vs-hollow is a -# weak cue that depends on angle and lighting, while convex-vs-concave is a strong one -- -# and male/female is the mechanical language for polarity anyway. -# -# Scale note: in the real viewport gizmos are screen-constant (~15-40 px via upp = 1/zoom). -# At a zoom where a 60 mm part fills ~600 px, 40 px is about 4 mm, so R = 4.5 mm here. - -import FreeCAD as App -import FreeCADGui as Gui -import Part -from FreeCAD import Vector - -DOC = "GlyphProbe" -for d in list(App.listDocuments()): - App.closeDocument(d) -doc = App.newDocument(DOC) - -R = 4.5 # disc radius, the module everything scales from -GOLD = (0.93, 0.66, 0.09) -BLUE = (0.18, 0.44, 0.93) -GREY = (0.42, 0.46, 0.52) -RED = (0.85, 0.29, 0.24) -GREEN = (0.23, 0.65, 0.35) - -def add(name, shape, color, transparency=0): - o = doc.addObject("Part::Feature", name) - o.Shape = shape - o.ViewObject.ShapeColor = color - o.ViewObject.LineColor = color - o.ViewObject.PointColor = color - o.ViewObject.Transparency = transparency - return o - -def frame(origin, zdir, xdir): - """Right-handed placement matrix from origin + Z + X (X orthonormalised against Z).""" - z = Vector(*zdir); z.normalize() - xr = Vector(*xdir) - x = xr.sub(Vector(z).multiply(z.dot(xr))); x.normalize() - y = z.cross(x) - return App.Matrix(x.x, y.x, z.x, origin[0], - x.y, y.y, z.y, origin[1], - x.z, y.z, z.z, origin[2], - 0, 0, 0, 1) - -# ---------------------------------------------------------------- the bracket -plate = Part.makeBox(120, 46, 8) -bore = Part.makeCylinder(7, 40, Vector(96, 23, -6)) # a real bore, curved face -boss = Part.makeCylinder(11, 7, Vector(96, 23, 8)) -part = plate.fuse(boss).cut(bore) -add("Bracket", part, (0.60, 0.63, 0.66)) - -# ---------------------------------------------------------------- glyph pieces -def ring(t=None): - t = t or R * 0.10 - return Part.makeCylinder(R, t).cut(Part.makeCylinder(R * 0.84, t)) - -def quadrant(t=None): - t = t or R * 0.10 - return Part.makeCylinder(R * 0.84, t, Vector(0, 0, 0), Vector(0, 0, 1), 90) - -def stem(L=None, r=None): - return Part.makeCylinder(r or R * 0.09, L or R * 2.3) - -def solid_head(): - return Part.makeCone(R * 0.32, 0, R * 0.80, Vector(0, 0, R * 2.3)) - -def shell_head(): - outer = Part.makeCone(R * 0.32, 0, R * 0.80, Vector(0, 0, R * 2.3)) - inner = Part.makeCone(R * 0.22, 0, R * 0.62, Vector(0, 0, R * 2.3)) - return outer.cut(inner) - -def short_axis(direction, L=None): - L = L or R * 1.15 - return Part.makeCylinder(R * 0.07, L, Vector(0, 0, 0), Vector(*direction)) - -def place(shape, m): - s = shape.copy() - s.transformShape(m) - return s - -# ---------------------------------------------------------------- the variants -def variant_A(tag, origin): # Onshape baseline - m = frame(origin, (0, 0, 1), (1, 0, 0)) - add(tag + "_ring", place(ring(), m), GREY) - add(tag + "_quad", place(quadrant(), m), GOLD) - add(tag + "_x", place(short_axis((1, 0, 0)), m), RED) - add(tag + "_y", place(short_axis((0, 1, 0)), m), GREEN) - add(tag + "_z", place(short_axis((0, 0, 1), R * 1.6), m), BLUE) - -def variant_B(tag, origin, zdir=(0, 0, 1)): # solid cone = driven - m = frame(origin, zdir, (1, 0, 0)) - add(tag + "_ring", place(ring(), m), BLUE) - add(tag + "_quad", place(quadrant(), m), GOLD) - add(tag + "_body", place(stem().fuse(solid_head()), m), BLUE) - -def variant_C(tag, origin, zdir=(0, 0, 1)): # hollow collar = fixed - m = frame(origin, zdir, (1, 0, 0)) - add(tag + "_ring", place(ring(), m), GREY) - add(tag + "_quad", place(quadrant(), m), GOLD) - add(tag + "_body", place(stem().fuse(shell_head()), m), GREY) - -def variant_D_pin(tag, origin, zdir=(0, 0, 1)): # polarity by relief: raised PIN - m = frame(origin, zdir, (1, 0, 0)) - pin = Part.makeCylinder(R * 0.30, R * 1.5).fuse( - Part.makeCone(R * 0.30, 0, R * 0.55, Vector(0, 0, R * 1.5))) - add(tag + "_ring", place(ring(), m), BLUE) - add(tag + "_quad", place(quadrant(), m), GOLD) - add(tag + "_pin", place(pin, m), BLUE) - -def variant_D_cup(tag, origin, zdir=(0, 0, 1)): # polarity by relief: sunk CUP - m = frame(origin, zdir, (1, 0, 0)) - cup = Part.makeCylinder(R * 0.62, R * 0.9).cut( - Part.makeCylinder(R * 0.40, R * 0.9, Vector(0, 0, -0.01))) - add(tag + "_ring", place(ring(), m), GREY) - add(tag + "_quad", place(quadrant(), m), GOLD) - add(tag + "_cup", place(cup, m), GREY) - -# four treatments across the plate, all on the same flat face, same Z -variant_A("A", (14, 30, 8)) -variant_B("B", (40, 30, 8)) -variant_C("C", (64, 30, 8)) -variant_D_pin("Dpin", (14, 10, 8)) -variant_D_cup("Dcup", (40, 10, 8)) - -# the hard cases, which is the whole reason for doing this in 3D: -variant_B("Bore", (96, 23, 15)) # on the boss above a bore -variant_B("Edge", (64, 0, 8), (0, -0.7071, 0.7071)) # tilted, on an edge, oblique Z - -doc.recompute() - -v = Gui.activeDocument().activeView() -v.viewIsometric() -Gui.SendMsgToActiveView("ViewFit") -App.Console.PrintMessage("glyph probe built: %d objects\n" % len(doc.Objects)) diff --git a/docs/CAD/design/mate-connectors/handedness-sheet.png b/docs/CAD/design/mate-connectors/handedness-sheet.png deleted file mode 100644 index b44c37032835822a0aaab0a0ddbeff029c2e643b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35843 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 diff --git a/docs/CAD/design/mate-connectors/handedness.py b/docs/CAD/design/mate-connectors/handedness.py deleted file mode 100644 index 68fa07bb55..0000000000 --- a/docs/CAD/design/mate-connectors/handedness.py +++ /dev/null @@ -1,112 +0,0 @@ -"""Give the bear a handedness mark that survives rasterisation — wi3z, Tommaso's call 2. - -The study showed the left/right cue lives in sub-millimetre corner radii and is therefore invisible -at glyph size: one pixel is 2.6 mm at 32 px. Roll and verse are safe; handedness is not. - -THE MEASURE IS THE QUESTION ITSELF. Render the glyph, render its mirror image, and count how many -pixels differ. If a human is to tell left from right, the two must differ on screen; a candidate -that scores near zero is invisible however elegant it looks in CAD. Reported as a percentage of the -glyph's own lit area, so the sizes are comparable. -""" -import json, math, os -from PIL import Image, ImageDraw, ImageChops - -HERE = os.path.dirname(os.path.abspath(__file__)) -D = json.load(open(os.path.join(HERE, "bear_outline.json"))) -def unit(pts): - p = [(x, -z) for x, z in pts] - return p -outer = unit(D["outer"]); holes = [unit(h["pts"]) for h in D["holes"]] -ALL = outer + [p for h in holes for p in h] -xs=[p[0] for p in ALL]; ys=[p[1] for p in ALL] -CX,CY = (min(xs)+max(xs))/2,(min(ys)+max(ys))/2 -SPAN = max(max(xs)-min(xs), max(ys)-min(ys)) -U = lambda pts: [((x-CX)/SPAN,(y-CY)/SPAN) for x,y in pts] -OUT = U(outer) -EYES = [U(h) for h,m in zip(holes, D["holes"]) if m["d"] < 20] -MUZ = U([h for h,m in zip(holes, D["holes"]) if m["d"] >= 20][0]) - -def rdp(pts, eps): - if len(pts) < 3: return pts - ax,ay=pts[0]; bx,by=pts[-1]; dx,dy=bx-ax,by-ay - n=math.hypot(dx,dy); best,bi=-1.0,0 - for i in range(1,len(pts)-1): - px,py=pts[i] - d=abs(dx*(ay-py)-(ax-px)*dy)/n if n>1e-12 else math.hypot(px-ax,py-ay) - if d>best: best,bi=d,i - if best<=eps: return [pts[0],pts[-1]] - return rdp(pts[:bi+1],eps)[:-1]+rdp(pts[bi:],eps) -def simp(pts,eps): - r=rdp(pts+[pts[0]],eps); return r[:-1] - -BASE = simp(OUT, .030) # the 22-vertex outline the study settled on -def centroid(p): return (sum(q[0] for q in p)/len(p), sum(q[1] for q in p)/len(p)) -def circ(cx,cy,r,n=16): return [(cx+r*math.cos(2*math.pi*i/n), cy+r*math.sin(2*math.pi*i/n)) for i in range(n)] -EYE_D = [] -for e in EYES: - c=centroid(e); r=(max(p[0] for p in e)-min(p[0] for p in e))/2 - EYE_D.append((c[0],c[1],r)) -EYE_D.sort() # [0] = left (x<0), [1] = right - -TOP = max(p[1] for p in BASE) -H = TOP - min(p[1] for p in BASE) -def ear_tip(sign): - cands=[p for p in BASE if p[1] > TOP-0.18*H and (p[0]*sign) > 0] - return max(cands, key=lambda p: p[0]*sign) if cands else None -LT, RT = ear_tip(-1), ear_tip(+1) - -def notch(tip, sign, k=0.085): - """A wedge bitten out of one ear — background-filled, exactly how the eyes are already drawn.""" - x,y = tip - return [(x, y+0.02), (x - sign*k, y - k*0.55), (x + sign*k*0.15, y - k*1.05)] - -CANDS = { - "H0 none": dict(cuts=[], eyes=EYE_D), - "H1 notch R ear": dict(cuts=[notch(RT, +1)], eyes=EYE_D), - "H2 notch both": dict(cuts=[notch(RT, +1), notch(LT, -1, 0.045)], eyes=EYE_D), - "H3 cheek dot": dict(cuts=[circ(EYE_D[1][0]+0.085, EYE_D[1][1]-0.10, 0.038)], eyes=EYE_D), - "H4 uneven eyes": dict(cuts=[], eyes=[EYE_D[0], (EYE_D[1][0], EYE_D[1][1], EYE_D[1][2]*1.55)]), -} - -def render(c, px, ss=8, mirror=False): - S=px*ss; img=Image.new("L",(S,S),0); d=ImageDraw.Draw(img) - m = lambda p: (S/2 + (-p[0] if mirror else p[0])*S*0.92, S/2 - p[1]*S*0.92) - d.polygon([m(p) for p in BASE], fill=255) - d.polygon([m(p) for p in MUZ], fill=0) - for cx,cy,r in c["eyes"]: - a=m((cx-r,cy+r)); b=m((cx+r,cy-r)) - d.ellipse([min(a[0],b[0]), min(a[1],b[1]), max(a[0],b[0]), max(a[1],b[1])], fill=0) - for cut in c["cuts"]: - d.polygon([m(p) for p in cut], fill=0) - return img.resize((px,px), Image.LANCZOS) - -SIZES=[22,32,48] -print(f"{'candidate':16} " + " ".join(f"{s}px" for s in SIZES) + " (pixels differing from own mirror, % of lit area)") -print("-"*84) -scores={} -for name,c in CANDS.items(): - row=[] - for px in SIZES: - a=render(c,px); b=render(c,px,mirror=True) - diff=ImageChops.difference(a,b) - nd=sum(1 for v in diff.getdata() if v>40) - lit=sum(1 for v in a.getdata() if v>40) or 1 - row.append(100.0*nd/lit) - scores[name]=row - print(f"{name:16} " + " ".join(f"{v:5.1f}" for v in row)) - -pad,cell=8,58 -W=pad+len(SIZES)*2*cell+pad; Hh=pad+len(CANDS)*cell+pad -sheet=Image.new("RGB",(W,Hh),(24,27,32)) -for r,(name,c) in enumerate(CANDS.items()): - for mi,mir in enumerate((False,True)): - for si,px in enumerate(SIZES): - g=render(c,px,mirror=mir) - tile=Image.new("RGB",(px,px),(24,27,32)) - tile.paste(Image.new("RGB",(px,px),(237,168,23)),(0,0),g) - x=pad+(mi*len(SIZES)+si)*cell+(cell-px)//2 - y=pad+r*cell+(cell-px)//2 - sheet.paste(tile,(x,y)) -sheet.resize((W*2,Hh*2), Image.NEAREST).save(os.path.join(HERE,"handedness-sheet.png")) -print("\nleft block = as drawn, right block = mirrored. rows: " + ", ".join(CANDS)) -print("WROTE handedness-sheet.png") diff --git a/docs/CAD/design/mate-connectors/linearedgemateconnectors.png b/docs/CAD/design/mate-connectors/linearedgemateconnectors.png deleted file mode 100644 index fd5e64190db6b9b824fe9d1e8a0d10e0eb10e892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20301 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 insertion along +Y, mouth at Y={bb.YMin:.2f}") - -# ---- 1. can the male even be withdrawn along the insertion axis? ---------------------- -# Ray-cast a grid along +Y through the tessellated male and count crossings. A straight pull is -# possible only if no ray enters the solid more than once; a second entry is an undercut. -verts, facets = male.tessellate(0.15) -V = [(v.x, v.y, v.z) for v in verts] -worst, undercut_pts = 0, 0 -NX = NZ = 90 -for i in range(NX): - x = bb.XMin + (i + 0.5) * bb.XLength / NX - for j in range(NZ): - z = bb.ZMin + (j + 0.5) * bb.ZLength / NZ - hits = 0 - for (ia, ib, ic) in facets: # ray (x, *, z) along +Y vs triangle - ax, ay, az = V[ia]; bx, by, bz = V[ib]; cx, cy, cz = V[ic] - # 2D point-in-triangle in the XZ plane - d = (bz - cz) * (ax - cx) + (cx - bx) * (az - cz) - if abs(d) < 1e-12: continue - u = ((bz - cz) * (x - cx) + (cx - bx) * (z - cz)) / d - v = ((cz - az) * (x - cx) + (ax - cx) * (z - cz)) / d - if u < 0 or v < 0 or u + v > 1: continue - hits += 1 - worst = max(worst, hits) - if hits > 2: undercut_pts += 1 -print(f"pull : max crossings along +Y = {worst}, undercut samples = {undercut_pts}/{NX*NZ}") -if undercut_pts: - print("FAIL: the male has an undercut along +Y; a straight pocket cannot release it") - sys.exit(1) -print(" no undercut -> a straight-pull pocket works") - -# ---- 2. grow the male by the clearance ----------------------------------------------- -grown = None -for join, name in ((2, "Intersection"), (1, "Tangent"), (0, "Arc")): - try: - g = male.makeOffsetShape(CLEAR, 1e-6, False, False, 0, join, False) - if g.isValid() and g.Solids: - grown = g.Solids[0]; print(f"offset: join={name}, {grown.Volume/1000:.2f} cm3"); break - except Exception as e: - print(f"offset: join={name} failed -- {e}") -if grown is None: - print("FAIL: could not offset the male; refusing to emit a zero-clearance pocket"); sys.exit(1) - -# ---- 3. the block: walls in X and Z, depth in +Y, OPEN at the Y=0 mouth --------------- -gb = grown.BoundBox -y_mouth = bb.YMin # the male's flat back plane -depth = gb.YMax - y_mouth -block = Part.makeBox(gb.XLength + 2*WALL, depth + FLOOR, gb.ZLength + 2*WALL, - App.Vector(gb.XMin - WALL, y_mouth, gb.ZMin - WALL)) -print(f"block : {gb.XLength + 2*WALL:.2f} x {depth + FLOOR:.2f} x {gb.ZLength + 2*WALL:.2f} mm, " - f"mouth on the Y={y_mouth:.2f} plane") - -female = block.cut(grown) - -# ---- 4. verify -------------------------------------------------------------------------- -ok = True -if not female.isValid(): print("FAIL: invalid shape"); ok = False -if len(female.Solids) != 1: print(f"FAIL: {len(female.Solids)} solids"); ok = False - -clash = male.common(female) -cv = clash.Volume if clash.Solids else 0.0 -print(f"check : male ∩ female = {cv:.6f} mm3 (seated fit, must be ~0)") -if cv > 1e-3: print("FAIL: male collides with female"); ok = False - -# the mouth must actually be open: the pocket has to reach the Y=y_mouth face of the block -mouth_face_area = 0.0 -for f in female.Faces: - c = f.CenterOfMass - if abs(c.y - y_mouth) < 1e-6: - mouth_face_area += f.Area -solid_mouth = (gb.XLength + 2*WALL) * (gb.ZLength + 2*WALL) -open_area = solid_mouth - mouth_face_area -print(f"check : mouth plane -- material {mouth_face_area:.1f} mm2, opening {open_area:.1f} mm2 " - f"({100*open_area/solid_mouth:.1f}% of the face)") -if open_area < 100: - print("FAIL: the pocket is sealed -- the male cannot be inserted"); ok = False - -cavity = block.Volume - female.Volume -print(f"check : cavity {cavity/1000:.2f} cm3 vs male {male.Volume/1000:.2f} cm3 " - f"-> clearance shell {(cavity-male.Volume)/1000:.2f} cm3") -if cavity < male.Volume: print("FAIL: cavity smaller than the male"); ok = False - -if not ok: - print("\nREFUSING to write the STEP"); sys.exit(1) - -doc = App.newDocument("Female") -obj = doc.addObject("Part::Feature", "BearConnector_Female") -obj.Shape = female -doc.recompute() -Part.export([obj], OUT_STEP) -fb = female.BoundBox -print(f"\nwrote {OUT_STEP}") -print(f"female: {fb.XLength:.2f} x {fb.YLength:.2f} x {fb.ZLength:.2f} mm, " - f"{len(female.Faces)} faces, {female.Volume/1000:.2f} cm3") diff --git a/docs/CAD/design/mate-connectors/mate-connector-virtual-sharp.png b/docs/CAD/design/mate-connectors/mate-connector-virtual-sharp.png deleted file mode 100644 index f9a80c1e1089d1afa6581293c4556967cb6b47a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31835 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 diff --git a/docs/CAD/design/mate-connectors/mateconnector-planarcentroid.png b/docs/CAD/design/mate-connectors/mateconnector-planarcentroid.png deleted file mode 100644 index 16ca1c01e6a937095b077246ec74a7a116658b59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26069 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 Tz3ksR00000NkvXXu0mjfGevF( diff --git a/docs/CAD/design/mate-connectors/mateconnector-planarpoints.png b/docs/CAD/design/mate-connectors/mateconnector-planarpoints.png deleted file mode 100644 index d952ddc1f0f424267f5b55caf42aff4d78497638..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27133 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 diff --git a/docs/CAD/design/mate-connectors/matepointiconLG.png b/docs/CAD/design/mate-connectors/matepointiconLG.png deleted file mode 100644 index 2f3022d116119c9e5db54cef99de0a292b3695ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2368 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 RkmrC%Bw|`Xwh2`Er=))y|Y9OqD2d< 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 E0*GbF-2eap diff --git a/docs/CAD/design/mate-connectors/relief_sheet.py b/docs/CAD/design/mate-connectors/relief_sheet.py deleted file mode 100644 index 4b8bc6aa74..0000000000 --- a/docs/CAD/design/mate-connectors/relief_sheet.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Flat glyph vs 3D relief, at the elevations that killed the disc — wi3z. - -The flat study collapsed at 16 deg because anything drawn IN the connector's plane foreshortens by -sin(elevation). This renders the SAME bear as its real relief (1508 facets off the supplied male) -with a simple lambert shade, so the silhouette does the work at a grazing angle. Two rows, same -sizes, same elevations, so the comparison is direct. -""" -import json, math, os -from PIL import Image, ImageDraw - -HERE = os.path.dirname(os.path.abspath(__file__)) -M = json.load(open(os.path.join(HERE, "bear_mesh.json"))) -V, F = M["v"], M["f"] - -# Part frame: face carried by X (right) and Z (down-negative), relief along +Y. -P = [(v[0], -v[2], v[1]) for v in V] # -> (x right, y up, z out of the face) -xs=[p[0] for p in P]; ys=[p[1] for p in P]; zs=[p[2] for p in P] -CX,CY,CZ = (min(xs)+max(xs))/2, (min(ys)+max(ys))/2, (min(zs)+max(zs))/2 -SPAN = max(max(xs)-min(xs), max(ys)-min(ys)) -P = [((x-CX)/SPAN, (y-CY)/SPAN, (z-CZ)/SPAN) for x,y,z in P] - -def shade(px, elev_deg, supersample=8): - """Camera orbits down from straight-on (90) to grazing (small). Rotate about the screen x-axis.""" - S = px*supersample - a = math.radians(elev_deg) - ca, sa = math.cos(a), math.sin(a) - # view: rotate the model so the face normal tips away from the camera - def xf(p): - x,y,z = p - return (x, y*sa + z*ca, -y*ca + z*sa) # third component = depth toward camera - Q = [xf(p) for p in P] - img = Image.new("L", (S,S), 0) - d = ImageDraw.Draw(img) - order = [] - for tri in F: - a3 = [Q[i] for i in tri] - order.append((sum(v[2] for v in a3)/3.0, tri, a3)) - order.sort(key=lambda t: t[0]) # painter: far first - light = (-0.35, 0.55, 0.76) - for _, tri, a3 in order: - (x0,y0,z0),(x1,y1,z1),(x2,y2,z2) = a3 - ux,uy,uz = x1-x0, y1-y0, z1-z0 - vx,vy,vz = x2-x0, y2-y0, z2-z0 - nx,ny,nz = uy*vz-uz*vy, uz*vx-ux*vz, ux*vy-uy*vx - n = math.sqrt(nx*nx+ny*ny+nz*nz) or 1.0 - nx,ny,nz = nx/n, ny/n, nz/n - if nz < 0: nx,ny,nz = -nx,-ny,-nz # face the camera - lam = max(0.0, nx*light[0] + ny*light[1] + nz*light[2]) - val = int(70 + 185*lam) - pts = [(S/2 + x*S*0.92, S/2 - y*S*0.92) for x,y,_ in a3] - d.polygon(pts, fill=val) - return img.resize((px,px), Image.LANCZOS) - -# flat outline, for the side-by-side -D = json.load(open(os.path.join(HERE, "bear_outline.json"))) -def unit(pts): - p=[(x,-z) for x,z in pts] - return [((x-CX)/SPAN,(y-CY)/SPAN) for x,y in p] -OUT = unit(D["outer"]) -HOLES = [unit(h["pts"]) for h in D["holes"]] - -def flat(px, elev_deg, supersample=8): - S=px*supersample - img=Image.new("L",(S,S),0); d=ImageDraw.Draw(img) - k=math.sin(math.radians(elev_deg)) - m=lambda p:(S/2+p[0]*S*0.92, S/2-p[1]*S*0.92*k) - d.polygon([m(p) for p in OUT], fill=255) - for h in HOLES: d.polygon([m(p) for p in h], fill=0) - return img.resize((px,px), Image.LANCZOS) - -SIZES=[22,32,48]; ELEVS=[(90,"flat on"),(47,"47"),(16,"16"),(6,"6")] -pad,cell=8,58 -W=pad+len(SIZES)*len(ELEVS)*cell+pad; H=pad+2*cell+pad -sheet=Image.new("RGB",(W,H),(24,27,32)) -for r,fn in enumerate((flat, shade)): - for ci,(elev,_) in enumerate(ELEVS): - for si,px in enumerate(SIZES): - g=fn(px,elev) - tile=Image.new("RGB",(px,px),(24,27,32)) - if fn is flat: - tile.paste(Image.new("RGB",(px,px),(237,168,23)),(0,0),g) - else: - gg=g.convert("L") - tile=Image.merge("RGB",(gg.point(lambda v:min(255,int(v*1.00))), - gg.point(lambda v:int(v*0.71)), - gg.point(lambda v:int(v*0.16)))) - x=pad+(ci*len(SIZES)+si)*cell+(cell-px)//2 - y=pad+r*cell+(cell-px)//2 - sheet.paste(tile,(x,y)) -sheet.resize((W*2,H*2), Image.NEAREST).save(os.path.join(HERE,"relief-sheet.png")) - -# how much ink survives — the same measure used on the disc glyph -print(f"{'elev':>6} {'flat px@32':>11} {'relief px@32':>13}") -for elev,_ in ELEVS: - f32=flat(32,elev); s32=shade(32,elev) - fi=sum(1 for v in f32.getdata() if v>40) - si=sum(1 for v in s32.getdata() if v>40) - print(f"{elev:>6} {fi:>11} {si:>13}") -print("WROTE relief-sheet.png") diff --git a/docs/CAD/design/mate-connectors/relief_test.py b/docs/CAD/design/mate-connectors/relief_test.py deleted file mode 100644 index 3fb83b2a8e..0000000000 --- a/docs/CAD/design/mate-connectors/relief_test.py +++ /dev/null @@ -1,9 +0,0 @@ -# Export the real male's relief as a triangle mesh, so the grazing test uses the actual geometry. -import os, json -import Part -HERE = os.path.dirname(os.path.abspath(__file__)) -s = Part.Shape(); s.read(os.path.join(HERE, "bear.step")) -verts, facets = s.Solids[0].tessellate(0.25) -V = [[round(p.x,4), round(p.y,4), round(p.z,4)] for p in verts] -json.dump({"v": V, "f": facets}, open(os.path.join(HERE, "bear_mesh.json"), "w")) -print(f"verts {len(V)} facets {len(facets)}") diff --git a/docs/CAD/design/mate-connectors/render_key.py b/docs/CAD/design/mate-connectors/render_key.py deleted file mode 100644 index eb4bcdbd99..0000000000 --- a/docs/CAD/design/mate-connectors/render_key.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python3 -"""Flat-shade the faceted ridge key from several camera directions. - -The point is not a pretty picture. It is one question: does a low-poly solid, flat-shaded, -let a human read its orientation from an arbitrary viewpoint -- and specifically, is the -view ALONG the ridge ambiguous between front and back, as the geometry suggests it must be -in silhouette? - -Flat shading (one normal per facet, no smoothing) is deliberate: it is what the concept -claims to rely on, and it is what a CAD viewport with hard normals actually produces. -""" -import numpy as np -from PIL import Image, ImageDraw - -# ---- the key, same numbers as faceted_ridge_key.scad -L, W, tf, H, pr, pf, hf = 12.0, 4.0, 0.45, 4.5, 0.22, 0.62, 0.35 -Wf, xr0, xr1, Hf = W * tf, -L / 2 + L * pr, -L / 2 + L * pf, H * hf - -V = np.array([(-L/2, -W, 0), (-L/2, W, 0), (L/2, Wf, 0), (L/2, -Wf, 0), - (xr0, 0, H), (xr1, 0, Hf)], dtype=float) -F = [[0, 1, 2, 3], [0, 4, 1], [0, 3, 5], [0, 5, 4], [1, 4, 5], [1, 5, 2], [3, 2, 5]] - -LIGHT = np.array([0.35, -0.5, 0.78]) # a headlight-ish key light -LIGHT /= np.linalg.norm(LIGHT) - - -def look_at(eye, target, up=(0, 0, 1)): - f = np.array(target, float) - np.array(eye, float) - f /= np.linalg.norm(f) - up = np.array(up, float) - if abs(np.dot(f, up)) > 0.999: - up = np.array([0, 1, 0], float) - r = np.cross(f, up); r /= np.linalg.norm(r) - u = np.cross(r, f) - return r, u, f - - -def render(eye, target, path, size=(620, 460), scale=26.0, label=""): - r, u, f = look_at(eye, target) - eye = np.array(eye, float) - cam = np.stack([r, u, f]) # world -> camera rows - P = (V - eye) @ cam.T # orthographic: x,y screen, z depth - - w, h = size - img = Image.new("RGB", size, (238, 240, 243)) - d = ImageDraw.Draw(img) - - def to_px(p): - return (w / 2 + p[0] * scale, h / 2 - p[1] * scale) - - faces = [] - for face in F: - pts = V[face] - n = np.cross(pts[1] - pts[0], pts[2] - pts[0]) - n /= np.linalg.norm(n) - centre = pts.mean(axis=0) - if np.dot(n, centre - eye) > 0: # back-face cull - continue - depth = P[face][:, 2].mean() - lam = max(0.0, float(np.dot(n, LIGHT))) - shade = 0.22 + 0.78 * lam # flat: ONE value for the whole facet - col = tuple(int(255 * shade * c) for c in (0.86, 0.72, 0.35)) - faces.append((depth, [to_px(P[i]) for i in face], col)) - - for _, poly, col in sorted(faces, key=lambda t: -t[0]): # painter's algorithm - d.polygon(poly, fill=col) - - if label: - d.rectangle([8, 8, 8 + 9 * len(label), 30], fill=(255, 255, 255)) - d.text((14, 14), label, fill=(20, 20, 20)) - img.save(path) - return path - - -if __name__ == "__main__": - t = (0, 0, H * 0.35) - views = [ - ((26, -22, 20), "iso: the reference view"), - ((30, 0, 6), "ALONG +X (from the FRONT, low end)"), - ((-30, 0, 6), "ALONG -X (from the BACK, tall end)"), - ((0, 0, 34), "ALONG +Z (straight down the mating axis)"), - ((2, -32, 5), "ALONG -Y (broadside, grazing)"), - ] - for i, (eye, lab) in enumerate(views): - print(render(eye, t, f"rk-{i}.png", label=lab)) diff --git a/docs/CAD/design/mate-connectors/render_stl.py b/docs/CAD/design/mate-connectors/render_stl.py deleted file mode 100644 index 16ad0e92a9..0000000000 --- a/docs/CAD/design/mate-connectors/render_stl.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -"""Flat-shade an ASCII/binary STL from several directions. - -Used to answer one question with a picture instead of an argument: does a RECESSED faceted -pocket read as an oriented feature, or does a concave feature collapse into a dark hole? -""" -import struct -import sys -import numpy as np -from PIL import Image, ImageDraw - -LIGHT = np.array([0.35, -0.5, 0.78]); LIGHT /= np.linalg.norm(LIGHT) - - -def load_stl(path): - data = open(path, "rb").read() - if data[:5] == b"solid" and b"facet" in data[:2000]: - tris, cur = [], [] - for line in data.decode("ascii", "ignore").splitlines(): - s = line.split() - if s and s[0] == "vertex": - cur.append([float(x) for x in s[1:4]]) - if len(cur) == 3: - tris.append(cur); cur = [] - return np.array(tris, dtype=float) - n = struct.unpack(" 0.999: up = np.array([0, 1.0, 0]) - r = np.cross(f, up); r /= np.linalg.norm(r) - u = np.cross(r, f) - cam = np.stack([r, u, f]) - - w, h = size - img = Image.new("RGB", size, (238, 240, 243)); d = ImageDraw.Draw(img) - faces = [] - for t in tris: - n = np.cross(t[1] - t[0], t[2] - t[0]) - ln = np.linalg.norm(n) - if ln < 1e-12: continue - n /= ln - c = t.mean(axis=0) - if np.dot(n, c - eye) > 0: continue # cull back faces - P = (t - eye) @ cam.T - lam = max(0.0, float(np.dot(n, LIGHT))) - shade = 0.20 + 0.80 * lam - col = tuple(int(255 * shade * ch) for ch in (0.86, 0.72, 0.35)) - poly = [(w / 2 + p[0] * scale, h / 2 - p[1] * scale) for p in P] - faces.append((P[:, 2].mean(), poly, col)) - for _, poly, col in sorted(faces, key=lambda x: -x[0]): - d.polygon(poly, fill=col) - if label: - d.rectangle([8, 8, 8 + 9 * len(label), 30], fill=(255, 255, 255)) - d.text((14, 14), label, fill=(20, 20, 20)) - img.save(path) - - -if __name__ == "__main__": - tris = load_stl(sys.argv[1]) - print("triangles:", len(tris)) - views = [((26, -22, 20), "iso"), ((0, 0, 34), "straight down +Z"), - ((4, -30, 9), "grazing"), ((-28, -10, 12), "from the tall end")] - for i, (eye, lab) in enumerate(views): - render(tris, eye, (0, 0, 0), f"fem-{i}.png", label=f"FEMALE POCKET — {lab}") - print(f"fem-{i}.png") diff --git a/docs/CAD/design/mate-connectors/rk-0.png b/docs/CAD/design/mate-connectors/rk-0.png deleted file mode 100644 index 1d7208238a4477cbd925fdaf8c6b3043177edd28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4974 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 diff --git a/docs/CAD/design/mate-connectors/rk-1.png b/docs/CAD/design/mate-connectors/rk-1.png deleted file mode 100644 index 282bd0a2fe8f3ba5153a7b69547bb4e87a2c18c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5548 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 diff --git a/docs/CAD/design/mate-connectors/rk-2.png b/docs/CAD/design/mate-connectors/rk-2.png deleted file mode 100644 index 75d83e140210e9e56247b2a712e1f69fa1228667..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4940 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 M#>4_DH~Ks3AGV$yfdBvi diff --git a/docs/CAD/design/mate-connectors/rk-4.png b/docs/CAD/design/mate-connectors/rk-4.png deleted file mode 100644 index 794fd4bd97a550fad6e87346368fb13cf32bc5db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4992 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 QBm?#HSI*_B{q^9#0rC@2KmY&$ diff --git a/docs/CAD/design/mate-connectors/simplify-sheet.png b/docs/CAD/design/mate-connectors/simplify-sheet.png deleted file mode 100644 index 9c05180c598898c565e68cc8813631b6e935925b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50634 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%0DD=}CcvK452WfgiGQJ_RcnYhTPPZ&JL8 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 ah5T*u`mvYAah!O-OYO;1CHP~DH~$MqeTaVm diff --git a/docs/CAD/design/mate-connectors/simplify_study.py b/docs/CAD/design/mate-connectors/simplify_study.py deleted file mode 100644 index c0bbcf63ab..0000000000 --- a/docs/CAD/design/mate-connectors/simplify_study.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Reduce the bear face to the fewest marks that still read at glyph size — wi3z. - -Geometry comes from bear_outline.json, which extract_outline.py pulled off the supplied male -B-rep's back plate: the outer wire IS the silhouette, the inner wires are the two eyes and the -muzzle opening. Nothing here is traced by eye. - -The glyph is drawn IN the connector's plane, so a grazing view foreshortens it along one axis by -sin(elevation) — exactly what collapsed the disc's roll quadrant to 3 pixels at 10 deg. Every -candidate is therefore rendered at three elevations as well as three pixel sizes. -""" -import json, math, os -from PIL import Image, ImageDraw - -HERE = os.path.dirname(os.path.abspath(__file__)) -D = json.load(open(os.path.join(HERE, "bear_outline.json"))) - -def norm(pts): - """Part frame (X right, Z down-negative) -> glyph frame (x right, y up), centred, unit height.""" - p = [(x, -z) for x, z in pts] - return p - -outer = norm(D["outer"]) -holes = [norm(h["pts"]) for h in D["holes"]] -# the two Ø9.8 wires are the eyes; the wide one is the muzzle -eyes = [h for h, meta in zip(holes, D["holes"]) if meta["d"] < 20] -muzzle = [h for h, meta in zip(holes, D["holes"]) if meta["d"] >= 20] - -ALL = outer + [p for h in holes for p in h] -xs = [p[0] for p in ALL]; ys = [p[1] for p in ALL] -CX, CY = (min(xs)+max(xs))/2, (min(ys)+max(ys))/2 -SPAN = max(max(xs)-min(xs), max(ys)-min(ys)) -def to_unit(pts): return [((x-CX)/SPAN, (y-CY)/SPAN) for x, y in pts] - -def rdp(pts, eps): - """Douglas-Peucker. Vertex count is the honest measure of 'how simplified'.""" - if len(pts) < 3: return pts - ax, ay = pts[0]; bx, by = pts[-1] - dx, dy = bx-ax, by-ay - n = math.hypot(dx, dy) - best, bi = -1.0, 0 - for i in range(1, len(pts)-1): - px, py = pts[i] - d = abs(dx*(ay-py) - (ax-px)*dy)/n if n > 1e-12 else math.hypot(px-ax, py-ay) - if d > best: best, bi = d, i - if best <= eps: - return [pts[0], pts[-1]] - return rdp(pts[:bi+1], eps)[:-1] + rdp(pts[bi:], eps) - -def simp_closed(pts, eps): - r = rdp(pts + [pts[0]], eps) - return r[:-1] - -def centroid(pts): - return (sum(p[0] for p in pts)/len(pts), sum(p[1] for p in pts)/len(pts)) - -U_OUT = to_unit(outer) -U_EYE = [to_unit(e) for e in eyes] -U_MUZ = [to_unit(m) for m in muzzle] - -def eye_dots(scale=1.0): - out = [] - for e in U_EYE: - cx, cy = centroid(e) - r = max(max(p[0] for p in e)-min(p[0] for p in e), - max(p[1] for p in e)-min(p[1] for p in e))/2*scale - out.append((cx, cy, r)) - return out - -def muzzle_tri(): - """The muzzle reduced to one filled triangle: its two lower corners and its apex.""" - m = U_MUZ[0] - lo = min(p[1] for p in m); hi = max(p[1] for p in m) - bottom = [p for p in m if p[1] < lo + 0.06*(hi-lo)] - apex = max(m, key=lambda p: p[1]) - return [min(bottom), max(bottom), apex] - -CANDIDATES = { - "C0 full": dict(out=U_OUT, eyes=eye_dots(), muz=U_MUZ[0]), - "C1 eps .004": dict(out=simp_closed(U_OUT, .004), eyes=eye_dots(), muz=simp_closed(U_MUZ[0], .004)), - "C2 eps .012": dict(out=simp_closed(U_OUT, .012), eyes=eye_dots(), muz=muzzle_tri()), - "C3 eps .030": dict(out=simp_closed(U_OUT, .030), eyes=eye_dots(1.15), muz=muzzle_tri()), - "C4 no eyes": dict(out=simp_closed(U_OUT, .012), eyes=[], muz=muzzle_tri()), -} - -def sym_report(pts, tol=0.02): - """Trivial symmetry group is the property doing the work. If a simplification restores a - mirror or a 180 deg rotation, that simplification is wrong.""" - def match(tf): - t = [tf(p) for p in pts] - hit = 0 - for q in t: - if min(math.hypot(q[0]-p[0], q[1]-p[1]) for p in pts) <= tol: hit += 1 - return hit, len(pts) - return { - "mirror-x": match(lambda p: (-p[0], p[1])), - "mirror-y": match(lambda p: ( p[0], -p[1])), - "rot-180": match(lambda p: (-p[0], -p[1])), - } - -def render(c, px, elev_deg, supersample=8): - S = px*supersample - img = Image.new("L", (S, S), 0) - d = ImageDraw.Draw(img) - k = math.sin(math.radians(elev_deg)) - def m(p): - return (S/2 + p[0]*S*0.92, S/2 - p[1]*S*0.92*k) - d.polygon([m(p) for p in c["out"]], fill=255) - if c["muz"]: d.polygon([m(p) for p in c["muz"]], fill=0) - for cx, cy, r in c["eyes"]: - a = m((cx-r, cy+r)); b = m((cx+r, cy-r)) - d.ellipse([a[0], a[1], b[0], b[1]], fill=0) - return img.resize((px, px), Image.LANCZOS) - -print(f"{'candidate':14} {'verts':>6} {'marks':>6} symmetry (matched/total, lower is better)") -print("-"*78) -for name, c in CANDIDATES.items(): - s = sym_report(c["out"]) - marks = 1 + (1 if c["muz"] else 0) + len(c["eyes"]) - sym = " ".join(f"{k} {v[0]}/{v[1]}" for k, v in s.items()) - print(f"{name:14} {len(c['out']):6} {marks:6} {sym}") - -SIZES = [22, 32, 48] -ELEVS = [(90, "flat on"), (47, "47 deg"), (16, "16 deg"), (6, "6 deg")] -pad, cell = 8, 56 -W = pad + len(SIZES)*len(ELEVS)*cell + pad -H = pad + len(CANDIDATES)*cell + pad -sheet = Image.new("RGB", (W, H), (24, 27, 32)) -for r, (name, c) in enumerate(CANDIDATES.items()): - for ci, (elev, _) in enumerate(ELEVS): - for si, px in enumerate(SIZES): - g = render(c, px, elev) - tile = Image.new("RGB", (px, px), (24, 27, 32)) - gold = Image.new("RGB", (px, px), (237, 168, 23)) - tile.paste(gold, (0, 0), g) - x = pad + (ci*len(SIZES)+si)*cell + (cell-px)//2 - y = pad + r*cell + (cell-px)//2 - sheet.paste(tile, (x, y)) -sheet = sheet.resize((W*2, H*2), Image.NEAREST) -sheet.save(os.path.join(HERE, "simplify-sheet.png")) -print("\ncolumns: " + " | ".join(f"{e[1]} @ 22/32/48px" for e in ELEVS)) -print("rows: " + ", ".join(CANDIDATES)) -print("WROTE simplify-sheet.png") diff --git a/docs/CAD/design/mate-connectors/size-test.png b/docs/CAD/design/mate-connectors/size-test.png deleted file mode 100644 index 941f76ffb8c29111c31f04d21e5858d8a9d46e56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9289 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 diff --git a/docs/CAD/design/mate-connectors/sz-22-down+Z.png b/docs/CAD/design/mate-connectors/sz-22-down+Z.png deleted file mode 100644 index 85cbf9ba4e1e384dc7fa443fb61613b0609d754e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 293 zcmV+=0owkFP)q^RQ{m5oL=9Ssm~ z8M@I73=DA7@mN5NO$3Z4#&l%hZY`_ry9B~!!Ib{<=O41^CoaAs#sYV1Su_zWMq>qd zEWk7rpV4RzN45ZNIxZ98faEN5;^M0l7he%HmjRcnd&@0xY2aXEhO^&&{7JIW1Pz4% zA%1o|c3?3Y)5(~I5@QEpSK~KV(rzva7m1B-G_sSi8j5BX9)R7|a6^eV2os=W^!s}f rIM|peF%GBE)CI$!0vk1&rbYt*>*Y&$ZV_*~00000NkvXXu0mjf1!Ql} diff --git a/docs/CAD/design/mate-connectors/sz-22-grazing.png b/docs/CAD/design/mate-connectors/sz-22-grazing.png deleted file mode 100644 index 047f2c6e0607da99d50f350acb37206ccd5b559c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1SGcvS$+jllRaG=Ln`JZ*L;6y%$&<0+^e`6`zqJ&HJv zl<=87E0?b~GBC*at#@$oB*C3~jy=`jGfRv4b-(sc=V_)n>y9PM7zsQ|udmaPe8kDo zaJ!}8>u2YaM`pCjd)zwPx>?d)dqd!2_g=YwZp;kqugsdo!#DT>oyXwm>gTe~DWM4f DTD?$7 diff --git a/docs/CAD/design/mate-connectors/sz-22-iso.png b/docs/CAD/design/mate-connectors/sz-22-iso.png deleted file mode 100644 index 55b8a13eed4a4f7361c252503061356aa2853a50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1SGcvS$+jlTRmMILn`JZ*L;6y%$&<0+hW_5~4@-l{-AAevp mC1^2c)5ECKJyO293=HQiOa2;uy!Hj?0R~T3KbLh*2~7ZmbYDUM diff --git a/docs/CAD/design/mate-connectors/sz-32-down+Z.png b/docs/CAD/design/mate-connectors/sz-32-down+Z.png deleted file mode 100644 index 683fc3d0aa8fb0f223dc82c511ce134878ffe0a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmV-t0hs=YP)C`X{P}HLT&U!J~e4J?|z1r}#J5uvf`u(*V${m!3P* z*$5xKhXNLC3Ll~e2@c9rs;J?3Z5FF-)&|?x`GKCaonr{zYMb(= zC+xa+<HqIj+38ut$j=W7FNiaS%vjJc+X%I;gyqdLqk1%o>wmw~M@xXNQ znPMSNEP=tuxH2h+l~6YDguPS>Nof$^szZ zl}CZy?z(bYD`rI1)cYS|h~U{cRjf={`pWn3DgoE4YQ&Onxw!83^ozOj$;z+$<7~g3 zcY7kD6^_ha{bEytO}FOR<0;j$-TfyGx|@4{-aS5_hll6ZKJOD-W>iF5-k7nmYya;b zlOxWP;Idc;fR*aeM!|l&V&Q@6E5rzFWxUuBJjDr*!?5a} zDKWpt_)W9?$cBfFNmUNJIUDXZW*p$TD1{K){f+wIw=wV9mU$*v)l(O>J5)Q7Rr9`Y zTu52B*T+k(@n_z@%*{;MM#;RwU?0RXz5@M^?{B>(^b M07*qoM6N<$g5$r35dZ)H diff --git a/docs/CAD/design/mate-connectors/sz-48-down+Z.png b/docs/CAD/design/mate-connectors/sz-48-down+Z.png deleted file mode 100644 index ec451714ff626fc437e50aa464aef49673bfe441..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 502 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 sq*kfk-s zY)h4uE$KTUseKr^%Oe%}slO;Te(d_qmPGd0=(ukZo#k7m9qs{@rozvU`5FPF~YS;cs%}< zE#>@&%T>couND^;;*uA-?+WX9wCnlJjEd%L3QbB%vUG1BIFsd;jqNI`koSAdcEqsI zK3#Yp18T%@g~FSgow&K)6_stLm2cA%ea_$N2|APGQP_QFt^L3d8U1RYHom#3N&9EC zxxs9^ck^qzMy73tH`TDmMePJWV#S4@rb?~2zz_JvnH`jZ4`ed@p3Es`yR)1ljTQ!5 zvs_W_UR_1_BhfqUGBGqwe`Y9|EbrU*fZ{*>s<5z7vuN&8yxd9jjlue7Wua1xwj4&~ z1LcusibD}@z9IF952N!Oy2+1G=+kkd`P|JGCpldYH+q`Et%>gHCp%(xoy+s;ufDrV zIQzh+f{Fq0hQ>x#V=`xI@~dX?ASpE4qocK8cD`-v4evSr;|9ITn&nS*jpZMqY8A@833zx$VM^NES7@dVSFOCi|7|8}c9?IJfdEoLi zif0QM8Ze)_1pUyB8Ve&1+}q>1^zqiLkwl-)+>y8W)U!*yBFM%y4W1fisUSoc<0Gm<>iOUHp7+J%rBI0Y9DX-d4mO!xpn=+CFp7j(mSJ zGB#E?5a*hNpJ|bDz%jqQw5b(Vz)h4y8|rYwGMsA{Xp#Pz9vykQowpoYJ~(L>PBm!x zDGZ>~3}RO);NJCTxg_Ifj_G#reAtV#nhr%vsWIneaVuBO+?r`|>4L(sJ?mKp1u$E> zR`Y1GQQ>@Tety26pC2V}nCz0|>pVN&f#A+lQ3rA~xY8+JZ3v;gc|?EiVi#e$$<}AQ zeLhtae+5F3)8)VTLwTVmSiz&SU_Pt){9?Z*VZK}r>(>79J%xg;jnj6RueNP%Nl8ni zLq55sQbq5sHj&9>C`q3>efqxhb9Dxc(I4*}IZN||g~sF;mWj6~`>I+pTom-Kr7%a| z3X8}~o(kn|JEuN!Gq=rih%$RxY`)j+!Sq8SCjSll2A0HpkWeg8P z!8b$D5uCZ+=cvfK*xim_yxTfDG)S2u*7v~!7 z;fHM)MT_I`QoqL4DyKiEX!=d+k(jsV^0S$tI zvKE5XI%tPRhil%o%9-tP(5~r3;l{f6YQ1AV-e$8>-ZEZi>`RDvEq0n&p`gpD@8~de zEDtL|3~4de9e?qjbBR_ajWwE*vXFEh(LRwd9UqIAtYaHouEk;^D>fwOD@} zbQ6^JVYd@#^jfK1?H`f(J6w-~k5 zBo9c>g-;LS$@wbs`Z3ge!f z$1N^<7BOFC)d>Z@*SY32EOw}I-FGFQyt=wI)4X(vlY?a+p141S5Lq_<)c4)A=jJEfpwZYKM+q~k2qNUEScjxT%$;maJt?V zvd7-F`7OId*k233Fx6WTsas^dQc>)mbx8-X^$%>-s_X+0C;d{Kiqle+C1I zq6fzNsV#~C)e6B!c%c35#$08W+M={rN+Lj`-RGDL_~)=GGWdBS(72xYl(^xT5uYUr zwTd`cTyGh0a}aa4tB$QED2RC!*wSs0NnUaVt33^eEl!&7-Kn9HC`kMgaI~GA#^l

    G{N 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�x0Sm6~&D|6y0PApNkeatD8t_u1hi6rMbQK5)H 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ĘP7kcKg-36EBVJA4xyFli3NkA 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 #g3XNC7BP%J5}T3Q~E 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 diff --git a/docs/CAD/design/mate-connectors/bear-flat.png b/docs/CAD/design/mate-connectors/bear-flat.png deleted file mode 100644 index 16375b9d5c5a624e8c6a095efa86a98f7009071b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20664 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 diff --git a/docs/CAD/design/mate-connectors/bear-sil-cmp.png b/docs/CAD/design/mate-connectors/bear-sil-cmp.png deleted file mode 100644 index eb2f283f95fc4f888e55a9f750280eaab9fcd4bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1330 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 i(&g6_0Iy+$5dH%|#O}qQmyHPk0000 tail (short) - {-0.0048, -0.1793, +0.2073}, - {-0.0048, +0.1605, +0.1279}, -}; diff --git a/docs/CAD/design/mate-connectors/bear_mesh.json b/docs/CAD/design/mate-connectors/bear_mesh.json deleted file mode 100644 index 4bfb6aa361..0000000000 --- a/docs/CAD/design/mate-connectors/bear_mesh.json +++ /dev/null @@ -1 +0,0 @@ -{"v": [[-36.7146, 0.0, -1.7146], [-36.0624, 0.0, -1.152], [-36.5065, 0.0, -1.5165], [-36.2889, 0.0, -1.3288], [-39.9409, 0.0, -4.9818], [-40.1196, 0.0, -5.2115], [-39.751, 0.0, -4.7612], [-39.5503, 0.0, -4.5503], [-35.8275, 0.0, -0.9866], [-35.3348, 0.0, -0.6912], [-35.5848, 0.0, -0.8329], [-40.4417, 0.0, -5.6962], [-40.5842, 0.0, -5.95], [-40.2867, 0.0, -5.4499], [-35.0781, 0.0, -0.5621], [-40.714, 0.0, -6.2105], [-34.5473, 0.0, -0.3422], [-34.8154, 0.0, -0.4456], [-40.8307, 0.0, -6.4772], [-40.934, 0.0, -6.7493], [-41.0236, 0.0, -7.0262], [-33.9976, 0.0, -0.1754], [-34.2745, 0.0, -0.2521], [-41.161, 0.0, -7.5917], [-41.2085, 0.0, -7.8789], [-41.0994, 0.0, -7.3072], [-33.149, 0.0, -0.0282], [-33.7172, 0.0, -0.1125], [-33.4341, 0.0, -0.0634], [-32.8624, 0.0, -0.0071], [-32.5752, 0.0, -0.0], [-41.2544, 0.0, -9.0404], [-41.2298, 0.0, -9.3304], [-41.2646, 0.0, -8.7495], [-41.2604, 0.0, -8.4585], [-41.2417, 0.0, -8.168], [-41.1908, 0.0, -9.6189], [-41.1374, 0.0, -9.905], [-41.07, 0.0, -10.1881], [-40.9885, 0.0, -10.4676], [-21.9255, 0.0, -9.1038], [-21.9255, 0.0, -3.5345], [-20.0463, 0.0, -44.6525], [-19.8744, 0.0, -44.4763], [-19.694, 0.0, -44.309], [-20.2091, 0.0, -44.837], [-19.5055, 0.0, -44.1508], [-20.3625, 0.0, -45.0294], [-19.3094, 0.0, -44.0022], [-20.5062, 0.0, -45.2292], [-19.106, 0.0, -43.8635], [-20.6397, 0.0, -45.436], [-18.896, 0.0, -43.7352], [-20.7628, 0.0, -45.6491], [-18.6799, 0.0, -43.6175], [-20.875, 0.0, -45.8681], [-18.4582, 0.0, -43.5108], [-20.9762, 0.0, -46.0924], [-18.2314, 0.0, -43.4152], [-21.0662, 0.0, -46.3215], [-18.0002, 0.0, -43.331], [-17.765, 0.0, -43.2585], [-17.5265, 0.0, -43.1977], [22.3077, 0.0, -3.5345], [-17.2853, 0.0, -43.149], [-17.042, 0.0, -43.1123], [-16.7971, 0.0, -43.0878], [-16.5513, 0.0, -43.0755], [-16.3052, 0.0, -43.0755], [-16.0594, 0.0, -43.0878], [-15.8146, 0.0, -43.1123], [-15.5712, 0.0, -43.149], [-15.33, 0.0, -43.1977], [-0.0, 0.0, -9.1038], [-15.0915, 0.0, -43.2585], [-14.8564, 0.0, -43.331], [-21.2112, 0.0, -49.2274], [-26.7105, 0.0, -55.2632], [-21.1445, 0.0, -49.4643], [-21.2659, 0.0, -48.9875], [-21.3087, 0.0, -48.7451], [-21.3393, 0.0, -48.5009], [-21.3577, 0.0, -48.2555], [-21.3638, 0.0, -48.0095], [-21.3577, 0.0, -47.7635], [-21.3393, 0.0, -47.5181], [-21.3087, 0.0, -47.2739], [-21.2659, 0.0, -47.0316], [-21.2112, 0.0, -46.7916], [-21.1445, 0.0, -46.5547], [-21.0662, 0.0, -49.6976], [-14.6251, 0.0, -43.4152], [-20.9762, 0.0, -49.9266], [-14.3984, 0.0, -43.5108], [-20.875, 0.0, -50.151], [-14.1766, 0.0, -43.6175], [-20.7628, 0.0, -50.37], [-13.9605, 0.0, -43.7352], [-20.6397, 0.0, -50.5831], [-13.7505, 0.0, -43.8635], [-20.5062, 0.0, -50.7898], [-13.5472, 0.0, -44.0022], [-20.3625, 0.0, -50.9896], [-13.351, 0.0, -44.1508], [-20.2091, 0.0, -51.182], [-13.1625, 0.0, -44.309], [-20.0463, 0.0, -51.3665], [-19.8744, 0.0, -51.5427], [-19.694, 0.0, -51.7101], [-19.5055, 0.0, -51.8683], [-19.3094, 0.0, -52.0169], [-19.106, 0.0, -52.1555], [35.0, 0.0, 0.0], [19.0522, 0.0, -18.4636], [16.4743, 0.0, -9.1399], [-27.6672, 0.0, -65.6993], [-27.6141, 0.0, -65.7466], [-27.7179, 0.0, -65.6494], [-27.5588, 0.0, -65.7913], [-27.4419, 0.0, -65.8722], [-27.3806, 0.0, -65.9082], [-27.5013, 0.0, -65.8332], [-27.3177, 0.0, -65.9412], [-27.2531, 0.0, -65.9711], [-27.1872, 0.0, -65.9978], [-27.1201, 0.0, -66.0212], [-27.0519, 0.0, -66.0413], [-26.9828, 0.0, -66.058], [-26.9129, 0.0, -66.0713], [-26.8425, 0.0, -66.0811], [-26.7007, 0.0, -66.0904], [-26.7717, 0.0, -66.0875], [-26.6296, 0.0, -66.0899], [-26.4879, 0.0, -66.0782], [-26.4177, 0.0, -66.0672], [-26.5586, 0.0, -66.0858], [-26.3481, 0.0, -66.0528], [-26.2792, 0.0, -66.035], [-26.2114, 0.0, -66.0138], [-26.1446, 0.0, -65.9893], [-26.0792, 0.0, -65.9615], [-26.0151, 0.0, -65.9306], [-25.9527, 0.0, -65.8966], [-25.892, 0.0, -65.8595], [-25.8332, 0.0, -65.8196], [-25.7765, 0.0, -65.7767], [-25.7219, 0.0, -65.7312], [-25.6696, 0.0, -65.683], [-28.9439, 0.0, -64.3825], [-29.1102, 0.0, -64.202], [-29.2675, 0.0, -64.0136], [-29.4153, 0.0, -63.8176], [-29.5533, 0.0, -63.6147], [-29.6811, 0.0, -63.4052], [-29.7986, 0.0, -63.1898], [-29.9053, 0.0, -62.9688], [-30.0011, 0.0, -62.7428], [-30.0856, 0.0, -62.5124], [-30.1588, 0.0, -62.2782], [-30.2204, 0.0, -62.0406], [-30.2702, 0.0, -61.8003], [-30.3082, 0.0, -61.5579], [-30.3342, 0.0, -61.3138], [-30.3483, 0.0, -61.0688], [-30.3503, 0.0, -60.8234], [-30.3402, 0.0, -60.5782], [-30.3182, 0.0, -60.3338], [-30.2841, 0.0, -60.0908], [-30.2382, 0.0, -59.8497], [-30.1805, 0.0, -59.6111], [-30.1112, 0.0, -59.3757], [-30.0304, 0.0, -59.144], [-29.9383, 0.0, -58.9165], [-29.8352, 0.0, -58.6938], [-29.7213, 0.0, -58.4764], [-29.5969, 0.0, -58.2649], [-13.7505, 0.0, -52.1555], [-20.6129, 0.0, -60.7895], [-13.5472, 0.0, -52.0169], [-13.9605, 0.0, -52.2838], [-14.1766, 0.0, -52.4015], [-14.3984, 0.0, -52.5083], [-14.6251, 0.0, -52.6039], [-14.8564, 0.0, -52.688], [-15.0915, 0.0, -52.7606], [-15.33, 0.0, -52.8213], [-15.5712, 0.0, -52.8701], [-15.8146, 0.0, -52.9067], [-16.0594, 0.0, -52.9312], [-16.3052, 0.0, -52.9435], [-16.5513, 0.0, -52.9435], [-16.7971, 0.0, -52.9312], [-17.042, 0.0, -52.9067], [-17.2853, 0.0, -52.8701], [-17.5265, 0.0, -52.8213], [-17.765, 0.0, -52.7606], [-18.0002, 0.0, -52.688], [-18.2314, 0.0, -52.6039], [-18.4582, 0.0, -52.5083], [-18.6799, 0.0, -52.4015], [-18.896, 0.0, -52.2838], [-29.4622, 0.0, -58.0598], [-29.3176, 0.0, -57.8615], [-29.1635, 0.0, -57.6705], [-29.0002, 0.0, -57.4873], [-28.828, 0.0, -57.3124], [42.0711, 0.0, -7.0711], [-12.9821, 0.0, -44.4763], [12.9821, 0.0, -44.4763], [-12.8103, 0.0, -44.6525], [12.8103, 0.0, -44.6525], [-12.6474, 0.0, -44.837], [13.1625, 0.0, -44.309], [12.6474, 0.0, -44.837], [13.351, 0.0, -44.1508], [-12.494, 0.0, -45.0294], [12.494, 0.0, -45.0294], [13.5472, 0.0, -44.0022], [-12.3504, 0.0, -45.2292], [12.3504, 0.0, -45.2292], [-12.2168, 0.0, -45.436], [13.7505, 0.0, -43.8635], [-12.0938, 0.0, -45.6491], [12.2168, 0.0, -45.436], [13.9605, 0.0, -43.7352], [12.0938, 0.0, -45.6491], [14.1766, 0.0, -43.6175], [-11.9815, 0.0, -45.8681], [11.9815, 0.0, -45.8681], [14.3984, 0.0, -43.5108], [-11.8803, 0.0, -46.0924], [11.8803, 0.0, -46.0924], [14.6251, 0.0, -43.4152], [11.7904, 0.0, -46.3215], [-11.7904, 0.0, -46.3215], [-11.712, 0.0, -46.5547], [14.8564, 0.0, -43.331], [11.712, 0.0, -46.5547], [15.0915, 0.0, -43.2585], [-11.6454, 0.0, -46.7916], [11.6454, 0.0, -46.7916], [15.33, 0.0, -43.1977], [-11.5906, 0.0, -47.0316], [11.5906, 0.0, -47.0316], [15.5712, 0.0, -43.149], [-11.5479, 0.0, -47.2739], [11.5479, 0.0, -47.2739], [15.8146, 0.0, -43.1123], [-11.5173, 0.0, -47.5181], [11.5173, 0.0, -47.5181], [16.0594, 0.0, -43.0878], [11.4989, 0.0, -47.7635], [-11.4989, 0.0, -47.7635], [16.3052, 0.0, -43.0755], [11.4928, 0.0, -48.0095], [-11.4928, 0.0, -48.0095], [16.5513, 0.0, -43.0755], [-11.4989, 0.0, -48.2555], [11.4989, 0.0, -48.2555], [-11.5173, 0.0, -48.5009], [16.7971, 0.0, -43.0878], [11.5173, 0.0, -48.5009], [-11.5479, 0.0, -48.7451], [17.042, 0.0, -43.1123], [11.5479, 0.0, -48.7451], [17.2853, 0.0, -43.149], [11.5906, 0.0, -48.9875], [-11.5906, 0.0, -48.9875], [17.5265, 0.0, -43.1977], [11.6454, 0.0, -49.2274], [-11.6454, 0.0, -49.2274], [17.765, 0.0, -43.2585], [-11.712, 0.0, -49.4643], [11.712, 0.0, -49.4643], [-11.7904, 0.0, -49.6976], [18.0002, 0.0, -43.331], [11.7904, 0.0, -49.6976], [18.2314, 0.0, -43.4152], [-11.8803, 0.0, -49.9266], [11.8803, 0.0, -49.9266], [18.4582, 0.0, -43.5108], [-11.9815, 0.0, -50.151], [11.9815, 0.0, -50.151], [18.6799, 0.0, -43.6175], [-12.0938, 0.0, -50.37], [12.0938, 0.0, -50.37], [18.896, 0.0, -43.7352], [-12.2168, 0.0, -50.5831], [12.2168, 0.0, -50.5831], [-12.3504, 0.0, -50.7898], [19.106, 0.0, -43.8635], [12.3504, 0.0, -50.7898], [19.3094, 0.0, -44.0022], [-12.494, 0.0, -50.9896], [12.494, 0.0, -50.9896], [19.5055, 0.0, -44.1508], [-12.6474, 0.0, -51.182], [12.6474, 0.0, -51.182], [-12.8103, 0.0, -51.3665], [19.694, 0.0, -44.309], [12.8103, 0.0, -51.3665], [-12.9821, 0.0, -51.5427], [19.8744, 0.0, -44.4763], [-13.1625, 0.0, -51.7101], [12.9821, 0.0, -51.5427], [20.0463, 0.0, -44.6525], [13.1625, 0.0, -51.7101], [20.2091, 0.0, -44.837], [13.351, 0.0, -51.8683], [-13.351, 0.0, -51.8683], [20.3625, 0.0, -45.0294], [13.5472, 0.0, -52.0169], [20.5062, 0.0, -45.2292], [20.6397, 0.0, -45.436], [20.7628, 0.0, -45.6491], [20.875, 0.0, -45.8681], [20.9762, 0.0, -46.0924], [20.6129, 0.0, -60.7895], [18.896, 0.0, -52.2838], [19.106, 0.0, -52.1555], [18.6799, 0.0, -52.4015], [18.4582, 0.0, -52.5083], [18.2314, 0.0, -52.6039], [18.0002, 0.0, -52.688], [17.765, 0.0, -52.7606], [17.5265, 0.0, -52.8213], [17.2853, 0.0, -52.8701], [17.042, 0.0, -52.9067], [16.7971, 0.0, -52.9312], [16.5513, 0.0, -52.9435], [16.3052, 0.0, -52.9435], [16.0594, 0.0, -52.9312], [15.8146, 0.0, -52.9067], [15.5712, 0.0, -52.8701], [15.33, 0.0, -52.8213], [15.0915, 0.0, -52.7606], [14.8564, 0.0, -52.688], [14.6251, 0.0, -52.6039], [14.3984, 0.0, -52.5083], [14.1766, 0.0, -52.4015], [13.9605, 0.0, -52.2838], [13.7505, 0.0, -52.1555], [26.7105, 0.0, -55.2632], [21.3577, 0.0, -48.2555], [21.3638, 0.0, -48.0095], [21.3393, 0.0, -48.5009], [21.3087, 0.0, -48.7451], [21.2659, 0.0, -48.9875], [21.2112, 0.0, -49.2274], [21.1445, 0.0, -49.4643], [21.0662, 0.0, -49.6976], [20.9762, 0.0, -49.9266], [20.875, 0.0, -50.151], [20.7628, 0.0, -50.37], [20.6397, 0.0, -50.5831], [20.5062, 0.0, -50.7898], [20.3625, 0.0, -50.9896], [20.2091, 0.0, -51.182], [20.0463, 0.0, -51.3665], [19.8744, 0.0, -51.5427], [19.694, 0.0, -51.7101], [21.0662, 0.0, -46.3215], [19.5055, 0.0, -51.8683], [21.1445, 0.0, -46.5547], [19.3094, 0.0, -52.0169], [21.2112, 0.0, -46.7916], [21.2659, 0.0, -47.0316], [21.3087, 0.0, -47.2739], [21.3393, 0.0, -47.5181], [21.3577, 0.0, -47.7635], [26.7105, 0.0, -66.6904], [32.4211, 0.0, -60.7895], [-40.1196, 3.0, -5.2115], [-39.9409, 3.0, -4.9818], [-39.751, 3.0, -4.7612], [-39.5503, 3.0, -4.5503], [-35.3348, 3.0, -0.6912], [-35.8275, 3.0, -0.9866], [-35.5848, 3.0, -0.8329], [-36.0624, 3.0, -1.152], [-40.714, 3.0, -6.2105], [-40.5842, 3.0, -5.95], [-40.4417, 3.0, -5.6962], [-40.2867, 3.0, -5.4499], [-34.5473, 3.0, -0.3422], [-35.0781, 3.0, -0.5621], [-34.8154, 3.0, -0.4456], [-41.0236, 3.0, -7.0262], [-40.934, 3.0, -6.7493], [-40.8307, 3.0, -6.4772], [-33.9976, 3.0, -0.1754], [-34.2745, 3.0, -0.2521], [-36.5065, 3.0, -1.5165], [-36.2889, 3.0, -1.3288], [-41.2085, 3.0, -7.8789], [-41.161, 3.0, -7.5917], [-41.0994, 3.0, -7.3072], [-33.149, 3.0, -0.0282], [-33.7172, 3.0, -0.1125], [-33.4341, 3.0, -0.0634], [-36.7146, 3.0, -1.7146], [-41.2417, 3.0, -8.168], [-32.8624, 3.0, -0.0071], [-32.5752, 3.0, -0.0], [-41.2298, 3.0, -9.3304], [-41.2544, 3.0, -9.0404], [-41.2646, 3.0, -8.7495], [-41.2604, 3.0, -8.4585], [-41.07, 3.0, -10.1881], [-41.1374, 3.0, -9.905], [-41.1908, 3.0, -9.6189], [-40.9885, 3.0, -10.4676], [-21.9255, 3.0, -9.1038], [-21.9255, 3.0, -3.5345], [5.6569, 3.0, -13.2042], [-5.6569, 3.0, -13.2042], [-0.0, 3.0, -9.1038], [-20.0463, 3.0, -44.6525], [-19.8744, 3.0, -44.4763], [-19.694, 3.0, -44.309], [-20.2091, 3.0, -44.837], [-19.5055, 3.0, -44.1508], [-20.3625, 3.0, -45.0294], [-19.3094, 3.0, -44.0022], [-20.5062, 3.0, -45.2292], [-19.106, 3.0, -43.8635], [-20.6397, 3.0, -45.436], [-18.896, 3.0, -43.7352], [-20.7628, 3.0, -45.6491], [-18.6799, 3.0, -43.6175], [-20.875, 3.0, -45.8681], [-18.4582, 3.0, -43.5108], [-20.9762, 3.0, -46.0924], [-18.2314, 3.0, -43.4152], [-21.0662, 3.0, -46.3215], [-18.0002, 3.0, -43.331], [-17.765, 3.0, -43.2585], [16.4743, 3.0, -9.1399], [-17.5265, 3.0, -43.1977], [22.3077, 3.0, -3.5345], [-17.2853, 3.0, -43.149], [-17.042, 3.0, -43.1123], [-16.7971, 3.0, -43.0878], [-16.5513, 3.0, -43.0755], [-16.3052, 3.0, -43.0755], [-16.0594, 3.0, -43.0878], [-15.8146, 3.0, -43.1123], [-15.5712, 3.0, -43.149], [-15.33, 3.0, -43.1977], [-15.0915, 3.0, -43.2585], [-14.8564, 3.0, -43.331], [-26.7105, 3.0, -55.2632], [-21.2112, 3.0, -49.2274], [-21.1445, 3.0, -49.4643], [-21.2659, 3.0, -48.9875], [-21.3087, 3.0, -48.7451], [-21.3393, 3.0, -48.5009], [-21.3577, 3.0, -48.2555], [-21.3638, 3.0, -48.0095], [-21.3577, 3.0, -47.7635], [-21.3393, 3.0, -47.5181], [-21.3087, 3.0, -47.2739], [-21.2659, 3.0, -47.0316], [-21.2112, 3.0, -46.7916], [-21.1445, 3.0, -46.5547], [-21.0662, 3.0, -49.6976], [-14.6251, 3.0, -43.4152], [-20.9762, 3.0, -49.9266], [-14.3984, 3.0, -43.5108], [-20.875, 3.0, -50.151], [-14.1766, 3.0, -43.6175], [-20.7628, 3.0, -50.37], [-13.9605, 3.0, -43.7352], [-20.6397, 3.0, -50.5831], [-13.7505, 3.0, -43.8635], [-20.5062, 3.0, -50.7898], [-13.5472, 3.0, -44.0022], [-20.3625, 3.0, -50.9896], [-13.351, 3.0, -44.1508], [-20.2091, 3.0, -51.182], [-13.1625, 3.0, -44.309], [-20.0463, 3.0, -51.3665], [-12.9821, 3.0, -44.4763], [-19.8744, 3.0, -51.5427], [-19.694, 3.0, -51.7101], [-19.5055, 3.0, -51.8683], [-19.3094, 3.0, -52.0169], [-19.106, 3.0, -52.1555], [35.0, 3.0, 0.0], [19.0522, 3.0, -18.4636], [-27.6141, 3.0, -65.7466], [-27.6672, 3.0, -65.6993], [-27.7179, 3.0, -65.6494], [-27.5588, 3.0, -65.7913], [-27.3806, 3.0, -65.9082], [-27.4419, 3.0, -65.8722], [-27.5013, 3.0, -65.8332], [-27.2531, 3.0, -65.9711], [-27.3177, 3.0, -65.9412], [-27.1872, 3.0, -65.9978], [-27.0519, 3.0, -66.0413], [-27.1201, 3.0, -66.0212], [-26.9129, 3.0, -66.0713], [-26.9828, 3.0, -66.058], [-26.8425, 3.0, -66.0811], [-26.7007, 3.0, -66.0904], [-26.7717, 3.0, -66.0875], [-26.5586, 3.0, -66.0858], [-26.6296, 3.0, -66.0899], [-26.2792, 3.0, -66.035], [-26.3481, 3.0, -66.0528], [-26.4177, 3.0, -66.0672], [-26.4879, 3.0, -66.0782], [-26.1446, 3.0, -65.9893], [-26.2114, 3.0, -66.0138], [-26.0792, 3.0, -65.9615], [-25.9527, 3.0, -65.8966], [-26.0151, 3.0, -65.9306], [-25.8332, 3.0, -65.8196], [-25.892, 3.0, -65.8595], [-25.7765, 3.0, -65.7767], [-25.6696, 3.0, -65.683], [-25.7219, 3.0, -65.7312], [-28.9439, 3.0, -64.3825], [-29.1102, 3.0, -64.202], [-29.2675, 3.0, -64.0136], [-29.4153, 3.0, -63.8176], [-29.5533, 3.0, -63.6147], [-29.6811, 3.0, -63.4052], [-29.7986, 3.0, -63.1898], [-29.9053, 3.0, -62.9688], [-30.0011, 3.0, -62.7428], [-30.0856, 3.0, -62.5124], [-30.1588, 3.0, -62.2782], [-30.2204, 3.0, -62.0406], [-30.2702, 3.0, -61.8003], [-30.3082, 3.0, -61.5579], [-30.3342, 3.0, -61.3138], [-30.3483, 3.0, -61.0688], [-30.3503, 3.0, -60.8234], [-30.3402, 3.0, -60.5782], [-30.3182, 3.0, -60.3338], [-30.2841, 3.0, -60.0908], [-30.2382, 3.0, -59.8497], [-30.1805, 3.0, -59.6111], [-30.1112, 3.0, -59.3757], [-30.0304, 3.0, -59.144], [-29.9383, 3.0, -58.9165], [-29.8352, 3.0, -58.6938], [-29.7213, 3.0, -58.4764], [-29.5969, 3.0, -58.2649], [-20.6129, 3.0, -60.7895], [-13.9605, 3.0, -52.2838], [-13.7505, 3.0, -52.1555], [-14.1766, 3.0, -52.4015], [-14.3984, 3.0, -52.5083], [-14.6251, 3.0, -52.6039], [-14.8564, 3.0, -52.688], [-15.0915, 3.0, -52.7606], [-15.33, 3.0, -52.8213], [-15.5712, 3.0, -52.8701], [-15.8146, 3.0, -52.9067], [-16.0594, 3.0, -52.9312], [-16.3052, 3.0, -52.9435], [-16.5513, 3.0, -52.9435], [-16.7971, 3.0, -52.9312], [-17.042, 3.0, -52.9067], [-17.2853, 3.0, -52.8701], [-17.5265, 3.0, -52.8213], [-17.765, 3.0, -52.7606], [-18.0002, 3.0, -52.688], [-18.2314, 3.0, -52.6039], [-18.4582, 3.0, -52.5083], [-18.6799, 3.0, -52.4015], [-18.896, 3.0, -52.2838], [-29.4622, 3.0, -58.0598], [-29.3176, 3.0, -57.8615], [-29.1635, 3.0, -57.6705], [-29.0002, 3.0, -57.4873], [-28.828, 3.0, -57.3124], [-2.5595, 3.0, -49.5058], [-11.4989, 3.0, -48.2555], [-11.4928, 3.0, -48.0095], [-11.5173, 3.0, -48.5009], [-11.5479, 3.0, -48.7451], [-11.5906, 3.0, -48.9875], [-11.6454, 3.0, -49.2274], [-11.712, 3.0, -49.4643], [-11.7904, 3.0, -49.6976], [-11.8803, 3.0, -49.9266], [-11.9815, 3.0, -50.151], [-12.8103, 3.0, -44.6525], [-12.0938, 3.0, -50.37], [-12.6474, 3.0, -44.837], [-12.2168, 3.0, -50.5831], [-12.494, 3.0, -45.0294], [-12.3504, 3.0, -50.7898], [-12.3504, 3.0, -45.2292], [-12.494, 3.0, -50.9896], [-12.2168, 3.0, -45.436], [-12.6474, 3.0, -51.182], [-12.0938, 3.0, -45.6491], [-12.8103, 3.0, -51.3665], [-11.9815, 3.0, -45.8681], [-12.9821, 3.0, -51.5427], [-11.8803, 3.0, -46.0924], [-13.1625, 3.0, -51.7101], [-11.7904, 3.0, -46.3215], [-13.351, 3.0, -51.8683], [-11.712, 3.0, -46.5547], [-13.5472, 3.0, -52.0169], [-11.6454, 3.0, -46.7916], [-11.5906, 3.0, -47.0316], [-11.5479, 3.0, -47.2739], [-11.5173, 3.0, -47.5181], [-11.4989, 3.0, -47.7635], [42.0711, 3.0, -7.0711], [2.5595, 3.0, -49.5058], [12.9821, 3.0, -44.4763], [12.8103, 3.0, -44.6525], [13.1625, 3.0, -44.309], [12.6474, 3.0, -44.837], [13.351, 3.0, -44.1508], [12.494, 3.0, -45.0294], [13.5472, 3.0, -44.0022], [12.3504, 3.0, -45.2292], [13.7505, 3.0, -43.8635], [12.2168, 3.0, -45.436], [13.9605, 3.0, -43.7352], [12.0938, 3.0, -45.6491], [14.1766, 3.0, -43.6175], [11.9815, 3.0, -45.8681], [14.3984, 3.0, -43.5108], [11.8803, 3.0, -46.0924], [14.6251, 3.0, -43.4152], [11.7904, 3.0, -46.3215], [14.8564, 3.0, -43.331], [11.712, 3.0, -46.5547], [15.0915, 3.0, -43.2585], [11.6454, 3.0, -46.7916], [15.33, 3.0, -43.1977], [11.5906, 3.0, -47.0316], [15.5712, 3.0, -43.149], [11.5479, 3.0, -47.2739], [15.8146, 3.0, -43.1123], [11.5173, 3.0, -47.5181], [16.0594, 3.0, -43.0878], [11.4989, 3.0, -47.7635], [16.3052, 3.0, -43.0755], [11.4928, 3.0, -48.0095], [16.5513, 3.0, -43.0755], [11.4989, 3.0, -48.2555], [16.7971, 3.0, -43.0878], [11.5173, 3.0, -48.5009], [17.042, 3.0, -43.1123], [11.5479, 3.0, -48.7451], [17.2853, 3.0, -43.149], [11.5906, 3.0, -48.9875], [17.5265, 3.0, -43.1977], [11.6454, 3.0, -49.2274], [17.765, 3.0, -43.2585], [11.712, 3.0, -49.4643], [18.0002, 3.0, -43.331], [11.7904, 3.0, -49.6976], [18.2314, 3.0, -43.4152], [11.8803, 3.0, -49.9266], [18.4582, 3.0, -43.5108], [11.9815, 3.0, -50.151], [18.6799, 3.0, -43.6175], [12.0938, 3.0, -50.37], [18.896, 3.0, -43.7352], [12.2168, 3.0, -50.5831], [19.106, 3.0, -43.8635], [12.3504, 3.0, -50.7898], [19.3094, 3.0, -44.0022], [12.494, 3.0, -50.9896], [19.5055, 3.0, -44.1508], [12.6474, 3.0, -51.182], [19.694, 3.0, -44.309], [12.8103, 3.0, -51.3665], [19.8744, 3.0, -44.4763], [12.9821, 3.0, -51.5427], [20.0463, 3.0, -44.6525], [13.1625, 3.0, -51.7101], [20.2091, 3.0, -44.837], [13.351, 3.0, -51.8683], [20.3625, 3.0, -45.0294], [13.5472, 3.0, -52.0169], [20.5062, 3.0, -45.2292], [13.7505, 3.0, -52.1555], [20.6397, 3.0, -45.436], [20.7628, 3.0, -45.6491], [20.875, 3.0, -45.8681], [20.9762, 3.0, -46.0924], [20.6129, 3.0, -60.7895], [13.9605, 3.0, -52.2838], [14.1766, 3.0, -52.4015], [14.3984, 3.0, -52.5083], [14.6251, 3.0, -52.6039], [14.8564, 3.0, -52.688], [15.0915, 3.0, -52.7606], [15.33, 3.0, -52.8213], [15.5712, 3.0, -52.8701], [15.8146, 3.0, -52.9067], [16.0594, 3.0, -52.9312], [16.3052, 3.0, -52.9435], [16.5513, 3.0, -52.9435], [16.7971, 3.0, -52.9312], [17.042, 3.0, -52.9067], [17.2853, 3.0, -52.8701], [17.5265, 3.0, -52.8213], [17.765, 3.0, -52.7606], [18.0002, 3.0, -52.688], [18.2314, 3.0, -52.6039], [18.4582, 3.0, -52.5083], [18.6799, 3.0, -52.4015], [18.896, 3.0, -52.2838], [19.106, 3.0, -52.1555], [26.7105, 3.0, -55.2632], [21.3638, 3.0, -48.0095], [21.3577, 3.0, -47.7635], [21.3393, 3.0, -47.5181], [21.3087, 3.0, -47.2739], [21.2659, 3.0, -47.0316], [21.2112, 3.0, -46.7916], [21.1445, 3.0, -46.5547], [19.3094, 3.0, -52.0169], [21.0662, 3.0, -46.3215], [19.5055, 3.0, -51.8683], [19.694, 3.0, -51.7101], [19.8744, 3.0, -51.5427], [20.0463, 3.0, -51.3665], [20.2091, 3.0, -51.182], [20.3625, 3.0, -50.9896], [20.5062, 3.0, -50.7898], [20.6397, 3.0, -50.5831], [20.7628, 3.0, -50.37], [20.875, 3.0, -50.151], [20.9762, 3.0, -49.9266], [21.0662, 3.0, -49.6976], [21.1445, 3.0, -49.4643], [21.2112, 3.0, -49.2274], [21.2659, 3.0, -48.9875], [21.3087, 3.0, -48.7451], [21.3393, 3.0, -48.5009], [21.3577, 3.0, -48.2555], [26.7105, 3.0, -66.6904], [32.4211, 3.0, -60.7895], [-0.0, 17.2725, -18.399], [-0.0, 10.6576, -46.7187]], "f": [[0, 1, 2], [2, 1, 3], [4, 5, 6], [6, 5, 7], [8, 9, 10], [11, 12, 13], [13, 12, 5], [8, 14, 9], [1, 14, 8], [12, 15, 5], [14, 16, 17], [18, 19, 15], [19, 20, 15], [5, 20, 7], [15, 20, 5], [16, 21, 22], [14, 21, 16], [1, 21, 14], [23, 24, 25], [25, 24, 20], [21, 26, 27], [27, 26, 28], [1, 26, 21], [0, 26, 1], [0, 29, 26], [0, 30, 29], [31, 32, 33], [33, 32, 34], [34, 32, 35], [36, 37, 32], [24, 38, 20], [32, 38, 35], [37, 38, 32], [35, 38, 24], [20, 39, 7], [38, 39, 20], [7, 39, 0], [0, 40, 30], [39, 40, 0], [30, 40, 41], [39, 42, 43], [43, 44, 39], [39, 45, 42], [44, 46, 39], [39, 47, 45], [46, 48, 39], [39, 49, 47], [39, 50, 40], [48, 50, 39], [39, 51, 49], [50, 52, 40], [39, 53, 51], [52, 54, 40], [39, 55, 53], [54, 56, 40], [39, 57, 55], [56, 58, 40], [39, 59, 57], [58, 60, 40], [60, 61, 40], [61, 62, 40], [41, 63, 30], [62, 64, 40], [64, 65, 40], [65, 66, 40], [66, 67, 40], [67, 68, 40], [68, 69, 40], [69, 70, 40], [70, 71, 40], [71, 72, 40], [40, 72, 73], [72, 74, 73], [74, 75, 73], [76, 77, 78], [79, 77, 76], [80, 77, 79], [81, 77, 80], [82, 77, 81], [83, 77, 82], [84, 77, 83], [85, 77, 84], [86, 77, 85], [87, 77, 86], [88, 77, 87], [89, 77, 88], [59, 77, 89], [39, 77, 59], [77, 90, 78], [75, 91, 73], [77, 92, 90], [91, 93, 73], [77, 94, 92], [93, 95, 73], [77, 96, 94], [95, 97, 73], [77, 98, 96], [97, 99, 73], [77, 100, 98], [99, 101, 73], [77, 102, 100], [101, 103, 73], [77, 104, 102], [103, 105, 73], [77, 106, 104], [77, 107, 106], [77, 108, 107], [77, 109, 108], [77, 110, 109], [77, 111, 110], [63, 112, 30], [73, 113, 114], [115, 116, 117], [116, 118, 117], [119, 120, 121], [121, 120, 118], [122, 123, 120], [118, 123, 117], [120, 123, 118], [123, 124, 117], [125, 126, 124], [124, 126, 117], [127, 128, 126], [128, 129, 126], [129, 130, 126], [131, 130, 129], [130, 132, 126], [133, 134, 135], [135, 134, 132], [132, 136, 126], [134, 136, 132], [137, 138, 136], [138, 139, 136], [139, 140, 136], [140, 141, 136], [142, 143, 141], [141, 143, 136], [143, 144, 136], [136, 145, 126], [144, 145, 136], [146, 147, 145], [145, 147, 126], [117, 147, 148], [148, 147, 149], [149, 147, 150], [150, 147, 151], [151, 147, 152], [152, 147, 153], [153, 147, 154], [154, 147, 155], [155, 147, 156], [156, 147, 157], [157, 147, 158], [158, 147, 159], [159, 147, 160], [160, 147, 161], [161, 147, 162], [162, 147, 163], [163, 147, 164], [164, 147, 165], [165, 147, 166], [166, 147, 167], [167, 147, 168], [168, 147, 169], [169, 147, 170], [170, 147, 171], [171, 147, 172], [172, 147, 173], [173, 147, 174], [174, 147, 175], [126, 147, 117], [176, 177, 178], [179, 177, 176], [180, 177, 179], [181, 177, 180], [182, 177, 181], [183, 177, 182], [184, 177, 183], [185, 177, 184], [186, 177, 185], [187, 177, 186], [188, 177, 187], [189, 177, 188], [190, 177, 189], [191, 177, 190], [192, 177, 191], [193, 177, 192], [194, 177, 193], [195, 177, 194], [77, 177, 111], [196, 177, 195], [197, 177, 196], [198, 177, 197], [199, 177, 198], [200, 177, 199], [111, 177, 200], [175, 177, 201], [201, 177, 202], [202, 177, 203], [203, 177, 204], [204, 177, 205], [205, 177, 77], [147, 177, 175], [113, 206, 63], [63, 206, 112], [207, 208, 105], [209, 210, 207], [211, 210, 209], [207, 210, 208], [105, 212, 73], [208, 212, 105], [211, 213, 210], [212, 214, 73], [215, 216, 211], [211, 216, 213], [73, 217, 113], [214, 217, 73], [218, 219, 215], [220, 219, 218], [215, 219, 216], [217, 221, 113], [222, 223, 220], [220, 223, 219], [221, 224, 113], [222, 225, 223], [224, 226, 113], [227, 228, 222], [222, 228, 225], [226, 229, 113], [230, 231, 227], [227, 231, 228], [229, 232, 113], [230, 233, 231], [234, 233, 230], [235, 233, 234], [232, 236, 113], [235, 237, 233], [236, 238, 113], [239, 240, 235], [235, 240, 237], [238, 241, 113], [242, 243, 239], [239, 243, 240], [241, 244, 113], [245, 246, 242], [242, 246, 243], [244, 247, 113], [248, 249, 245], [245, 249, 246], [247, 250, 113], [248, 251, 249], [252, 251, 248], [250, 253, 113], [252, 254, 251], [255, 254, 252], [253, 256, 113], [257, 258, 255], [259, 258, 257], [255, 258, 254], [256, 260, 113], [259, 261, 258], [262, 261, 259], [260, 263, 113], [262, 264, 261], [263, 265, 113], [262, 266, 264], [267, 266, 262], [265, 268, 113], [267, 269, 266], [270, 269, 267], [268, 271, 113], [272, 273, 270], [274, 273, 272], [270, 273, 269], [271, 275, 113], [274, 276, 273], [275, 277, 113], [278, 279, 274], [274, 279, 276], [277, 280, 113], [281, 282, 278], [278, 282, 279], [280, 283, 113], [284, 285, 281], [281, 285, 282], [283, 286, 113], [287, 288, 284], [289, 288, 287], [284, 288, 285], [286, 290, 113], [289, 291, 288], [290, 292, 113], [293, 294, 289], [289, 294, 291], [292, 295, 113], [296, 297, 293], [298, 297, 296], [293, 297, 294], [295, 299, 113], [298, 300, 297], [301, 300, 298], [299, 302, 113], [303, 304, 301], [301, 304, 300], [302, 305, 113], [303, 306, 304], [305, 307, 113], [303, 308, 306], [309, 308, 303], [178, 308, 309], [113, 310, 206], [307, 310, 113], [178, 311, 308], [310, 312, 206], [312, 313, 206], [313, 314, 206], [314, 315, 206], [315, 316, 206], [177, 317, 178], [318, 317, 319], [320, 317, 318], [321, 317, 320], [322, 317, 321], [323, 317, 322], [324, 317, 323], [325, 317, 324], [326, 317, 325], [327, 317, 326], [178, 317, 311], [328, 317, 327], [329, 317, 328], [330, 317, 329], [331, 317, 330], [332, 317, 331], [333, 317, 332], [334, 317, 333], [335, 317, 334], [336, 317, 335], [337, 317, 336], [338, 317, 337], [339, 317, 338], [340, 317, 339], [341, 317, 340], [311, 317, 341], [316, 342, 206], [343, 342, 344], [345, 342, 343], [346, 342, 345], [347, 342, 346], [348, 342, 347], [349, 342, 348], [350, 342, 349], [351, 342, 350], [352, 342, 351], [353, 342, 352], [354, 342, 353], [355, 342, 354], [356, 342, 355], [357, 342, 356], [317, 342, 319], [358, 342, 357], [359, 342, 358], [360, 342, 359], [361, 342, 316], [362, 342, 360], [363, 342, 361], [364, 342, 362], [365, 342, 363], [319, 342, 364], [366, 342, 365], [367, 342, 366], [368, 342, 367], [369, 342, 368], [344, 342, 369], [342, 370, 371], [317, 370, 342], [372, 373, 374], [372, 374, 375], [376, 377, 378], [376, 379, 377], [380, 381, 382], [380, 382, 383], [380, 383, 372], [384, 385, 386], [387, 380, 372], [387, 388, 389], [387, 389, 380], [387, 372, 375], [390, 379, 376], [390, 376, 385], [390, 384, 391], [390, 385, 384], [390, 392, 393], [390, 393, 379], [394, 395, 396], [394, 396, 387], [397, 390, 398], [397, 398, 399], [397, 392, 390], [397, 400, 392], [401, 387, 375], [401, 394, 387], [402, 400, 397], [403, 400, 402], [404, 405, 406], [404, 406, 407], [404, 407, 401], [408, 401, 375], [408, 404, 401], [408, 409, 410], [408, 410, 404], [411, 408, 375], [411, 375, 400], [412, 411, 400], [412, 400, 403], [412, 403, 413], [414, 415, 416], [417, 411, 418], [419, 418, 411], [420, 411, 417], [421, 419, 411], [422, 411, 420], [423, 421, 411], [424, 411, 422], [425, 411, 412], [425, 423, 411], [426, 411, 424], [427, 425, 412], [428, 411, 426], [429, 427, 412], [430, 411, 428], [431, 429, 412], [432, 411, 430], [433, 431, 412], [434, 411, 432], [435, 433, 412], [436, 435, 412], [437, 414, 416], [438, 436, 412], [439, 413, 403], [440, 438, 412], [441, 440, 412], [442, 441, 412], [443, 442, 412], [443, 412, 415], [444, 443, 415], [445, 444, 415], [446, 445, 415], [447, 446, 415], [448, 447, 415], [449, 448, 415], [450, 449, 415], [451, 411, 434], [451, 452, 453], [451, 454, 452], [451, 455, 454], [451, 456, 455], [451, 457, 456], [451, 458, 457], [451, 459, 458], [451, 460, 459], [451, 461, 460], [451, 462, 461], [451, 463, 462], [451, 464, 463], [451, 434, 464], [465, 451, 453], [466, 450, 415], [467, 451, 465], [468, 466, 415], [469, 451, 467], [470, 468, 415], [471, 451, 469], [472, 470, 415], [473, 451, 471], [474, 472, 415], [475, 451, 473], [476, 474, 415], [477, 451, 475], [478, 476, 415], [479, 451, 477], [480, 478, 415], [481, 451, 479], [482, 480, 415], [483, 451, 481], [484, 451, 483], [485, 451, 484], [486, 451, 485], [487, 451, 486], [488, 439, 403], [489, 414, 437], [490, 491, 492], [493, 490, 492], [494, 495, 496], [494, 496, 493], [497, 493, 492], [497, 498, 494], [497, 494, 493], [499, 497, 492], [500, 501, 499], [500, 499, 492], [502, 503, 500], [504, 502, 500], [505, 506, 504], [505, 504, 500], [507, 508, 505], [509, 510, 511], [509, 511, 512], [509, 512, 507], [513, 514, 509], [515, 507, 505], [515, 509, 507], [515, 513, 509], [516, 517, 515], [516, 515, 505], [518, 519, 516], [518, 516, 505], [520, 505, 500], [520, 518, 505], [521, 522, 520], [521, 500, 492], [521, 492, 523], [521, 520, 500], [521, 523, 524], [521, 524, 525], [521, 525, 526], [521, 526, 527], [521, 527, 528], [521, 528, 529], [521, 529, 530], [521, 530, 531], [521, 531, 532], [521, 532, 533], [521, 533, 534], [521, 534, 535], [521, 535, 536], [521, 536, 537], [521, 537, 538], [521, 538, 539], [521, 539, 540], [521, 540, 541], [521, 541, 542], [521, 542, 543], [521, 543, 544], [521, 544, 545], [521, 545, 546], [521, 546, 547], [521, 547, 548], [521, 548, 549], [521, 549, 550], [551, 552, 553], [551, 554, 552], [551, 555, 554], [551, 556, 555], [551, 557, 556], [551, 558, 557], [551, 559, 558], [551, 560, 559], [551, 561, 560], [551, 562, 561], [551, 563, 562], [551, 564, 563], [551, 451, 487], [551, 565, 564], [551, 566, 565], [551, 567, 566], [551, 568, 567], [551, 569, 568], [551, 570, 569], [551, 571, 570], [551, 572, 571], [551, 573, 572], [551, 574, 573], [551, 487, 574], [551, 550, 575], [551, 575, 576], [551, 576, 577], [551, 577, 578], [551, 578, 579], [551, 579, 451], [551, 521, 550], [580, 482, 415], [580, 581, 582], [580, 583, 581], [580, 584, 583], [580, 585, 584], [580, 586, 585], [580, 587, 586], [580, 588, 587], [580, 589, 588], [580, 590, 589], [580, 591, 482], [580, 592, 590], [580, 593, 591], [580, 594, 592], [580, 595, 593], [580, 596, 594], [580, 597, 595], [580, 598, 596], [580, 599, 597], [580, 600, 598], [580, 601, 599], [580, 602, 600], [580, 603, 601], [580, 604, 602], [580, 605, 603], [580, 606, 604], [580, 607, 605], [580, 608, 606], [580, 609, 607], [580, 610, 608], [580, 611, 609], [580, 553, 610], [580, 612, 611], [580, 613, 612], [580, 614, 613], [580, 615, 614], [580, 582, 615], [580, 551, 553], [616, 439, 488], [616, 489, 439], [617, 551, 580], [618, 617, 414], [619, 617, 618], [620, 618, 414], [621, 617, 619], [622, 620, 414], [623, 617, 621], [624, 622, 414], [625, 617, 623], [626, 624, 414], [627, 617, 625], [628, 626, 414], [629, 617, 627], [630, 414, 489], [630, 628, 414], [631, 617, 629], [632, 630, 489], [633, 617, 631], [634, 632, 489], [635, 617, 633], [636, 634, 489], [637, 617, 635], [638, 636, 489], [639, 617, 637], [640, 638, 489], [641, 617, 639], [642, 640, 489], [643, 617, 641], [644, 642, 489], [645, 617, 643], [646, 644, 489], [647, 617, 645], [648, 646, 489], [649, 617, 647], [650, 648, 489], [651, 617, 649], [652, 650, 489], [653, 617, 651], [654, 652, 489], [655, 617, 653], [656, 654, 489], [657, 617, 655], [658, 656, 489], [659, 617, 657], [660, 658, 489], [661, 617, 659], [662, 660, 489], [663, 617, 661], [664, 662, 489], [665, 617, 663], [666, 664, 489], [667, 617, 665], [668, 666, 489], [669, 617, 667], [670, 668, 489], [671, 617, 669], [672, 670, 489], [673, 617, 671], [674, 672, 489], [675, 617, 673], [676, 674, 489], [677, 617, 675], [678, 676, 489], [679, 617, 677], [680, 678, 489], [681, 617, 679], [682, 680, 489], [683, 617, 681], [684, 682, 489], [685, 617, 683], [686, 489, 616], [686, 684, 489], [687, 617, 685], [688, 686, 616], [689, 617, 687], [690, 688, 616], [691, 690, 616], [692, 691, 616], [693, 692, 616], [694, 617, 689], [694, 689, 695], [694, 695, 696], [694, 696, 697], [694, 697, 698], [694, 698, 699], [694, 699, 700], [694, 700, 701], [694, 701, 702], [694, 702, 703], [694, 703, 704], [694, 704, 705], [694, 705, 706], [694, 706, 707], [694, 707, 708], [694, 708, 709], [694, 709, 710], [694, 710, 711], [694, 711, 712], [694, 712, 713], [694, 713, 714], [694, 714, 715], [694, 715, 716], [694, 716, 717], [694, 551, 617], [718, 693, 616], [718, 694, 717], [718, 719, 720], [718, 720, 721], [718, 721, 722], [718, 722, 723], [718, 723, 724], [718, 724, 725], [718, 717, 726], [718, 725, 727], [718, 726, 728], [718, 727, 693], [718, 728, 729], [718, 729, 730], [718, 730, 731], [718, 731, 732], [718, 732, 733], [718, 733, 734], [718, 734, 735], [718, 735, 736], [718, 736, 737], [718, 737, 738], [718, 738, 739], [718, 739, 740], [718, 740, 741], [718, 741, 742], [718, 742, 743], [718, 743, 744], [718, 744, 745], [718, 745, 719], [746, 718, 747], [746, 694, 718], [412, 416, 415], [718, 616, 342], [342, 616, 206], [747, 718, 371], [371, 718, 342], [746, 747, 370], [370, 747, 371], [694, 746, 317], [317, 746, 370], [694, 317, 551], [551, 317, 177], [147, 551, 177], [521, 551, 147], [115, 117, 492], [115, 492, 491], [116, 491, 490], [116, 115, 491], [118, 490, 493], [118, 116, 490], [121, 493, 496], [121, 118, 493], [119, 496, 495], [119, 121, 496], [120, 495, 494], [120, 119, 495], [122, 494, 498], [122, 120, 494], [123, 122, 498], [123, 498, 497], [124, 123, 497], [124, 499, 501], [124, 497, 499], [125, 124, 501], [126, 125, 501], [126, 501, 500], [127, 126, 500], [127, 503, 502], [127, 500, 503], [128, 127, 502], [129, 128, 502], [129, 502, 504], [131, 129, 504], [131, 504, 506], [130, 131, 506], [130, 506, 505], [132, 130, 505], [132, 505, 508], [135, 132, 508], [135, 507, 512], [135, 508, 507], [133, 512, 511], [133, 135, 512], [134, 133, 511], [136, 510, 509], [136, 511, 510], [136, 134, 511], [137, 509, 514], [137, 136, 509], [138, 137, 514], [139, 514, 513], [139, 138, 514], [140, 513, 515], [140, 139, 513], [141, 515, 517], [141, 140, 515], [142, 516, 519], [142, 517, 516], [142, 141, 517], [143, 142, 519], [144, 519, 518], [144, 143, 519], [145, 518, 520], [145, 144, 518], [146, 520, 522], [146, 145, 520], [147, 522, 521], [147, 146, 522], [492, 117, 148], [523, 492, 148], [204, 205, 579], [204, 579, 578], [203, 578, 577], [203, 204, 578], [202, 577, 576], [202, 203, 577], [201, 576, 575], [201, 202, 576], [175, 575, 550], [175, 201, 575], [174, 550, 549], [174, 175, 550], [173, 549, 548], [173, 174, 549], [172, 173, 548], [172, 548, 547], [171, 172, 547], [171, 547, 546], [170, 171, 546], [170, 546, 545], [169, 170, 545], [169, 545, 544], [168, 169, 544], [168, 544, 543], [167, 168, 543], [167, 542, 541], [167, 543, 542], [166, 167, 541], [165, 166, 541], [165, 541, 540], [164, 165, 540], [164, 540, 539], [163, 164, 539], [163, 539, 538], [162, 163, 538], [162, 538, 537], [161, 537, 536], [161, 162, 537], [160, 536, 535], [160, 161, 536], [159, 160, 535], [159, 535, 534], [158, 533, 532], [158, 534, 533], [158, 159, 534], [157, 158, 532], [156, 532, 531], [156, 157, 532], [155, 531, 530], [155, 156, 531], [154, 529, 528], [154, 530, 529], [154, 155, 530], [153, 154, 528], [152, 528, 527], [152, 153, 528], [151, 527, 526], [151, 152, 527], [150, 526, 525], [150, 151, 526], [149, 525, 524], [149, 150, 525], [148, 524, 523], [148, 149, 524], [451, 205, 77], [451, 579, 205], [411, 77, 39], [411, 451, 77], [6, 7, 375], [6, 375, 374], [4, 374, 373], [4, 6, 374], [5, 372, 383], [5, 373, 372], [5, 4, 373], [13, 5, 383], [11, 383, 382], [11, 13, 383], [12, 382, 381], [12, 11, 382], [15, 381, 380], [15, 12, 381], [18, 389, 388], [18, 380, 389], [18, 15, 380], [19, 18, 388], [20, 388, 387], [20, 19, 388], [25, 387, 396], [25, 20, 387], [23, 396, 395], [23, 25, 396], [24, 23, 395], [24, 394, 401], [24, 395, 394], [35, 24, 401], [34, 35, 401], [34, 401, 407], [33, 34, 407], [33, 407, 406], [31, 33, 406], [31, 405, 404], [31, 406, 405], [32, 31, 404], [36, 404, 410], [36, 32, 404], [37, 36, 410], [37, 410, 409], [38, 409, 408], [38, 37, 409], [39, 408, 411], [39, 38, 408], [400, 7, 0], [400, 375, 7], [29, 30, 403], [29, 403, 402], [26, 402, 397], [26, 29, 402], [28, 397, 399], [28, 26, 397], [27, 398, 390], [27, 399, 398], [27, 28, 399], [21, 27, 390], [22, 390, 391], [22, 21, 390], [16, 391, 384], [16, 22, 391], [17, 384, 386], [17, 16, 384], [14, 386, 385], [14, 17, 386], [9, 385, 376], [9, 14, 385], [10, 376, 378], [10, 9, 376], [8, 377, 379], [8, 378, 377], [8, 10, 378], [1, 8, 379], [3, 379, 393], [3, 1, 379], [2, 393, 392], [2, 3, 393], [0, 392, 400], [0, 2, 392], [112, 488, 403], [112, 403, 30], [616, 112, 206], [616, 488, 112], [748, 415, 414], [414, 617, 749], [748, 414, 749], [749, 617, 580], [749, 415, 748], [749, 580, 415], [582, 581, 255], [255, 581, 257], [257, 583, 259], [259, 583, 262], [581, 583, 257], [583, 584, 262], [262, 585, 267], [267, 585, 270], [584, 585, 262], [270, 586, 272], [585, 586, 270], [586, 587, 272], [272, 588, 274], [587, 588, 272], [274, 589, 278], [278, 589, 281], [588, 589, 274], [589, 590, 281], [281, 592, 284], [590, 592, 281], [592, 594, 284], [284, 594, 287], [594, 596, 287], [287, 596, 289], [596, 598, 289], [289, 598, 293], [598, 600, 293], [293, 600, 296], [600, 602, 296], [296, 602, 298], [602, 604, 298], [298, 604, 301], [604, 606, 301], [301, 606, 303], [606, 608, 303], [303, 608, 309], [608, 610, 309], [309, 610, 178], [610, 553, 178], [178, 553, 176], [176, 553, 179], [553, 552, 179], [552, 554, 179], [179, 554, 180], [180, 554, 181], [554, 555, 181], [181, 556, 182], [182, 556, 183], [555, 556, 181], [556, 557, 183], [183, 558, 184], [557, 558, 183], [184, 559, 185], [185, 559, 186], [558, 559, 184], [186, 560, 187], [559, 560, 186], [560, 561, 187], [187, 562, 188], [188, 562, 189], [561, 562, 187], [562, 563, 189], [189, 564, 190], [190, 564, 191], [563, 564, 189], [564, 565, 191], [191, 566, 192], [192, 566, 193], [565, 566, 191], [566, 567, 193], [567, 568, 193], [193, 568, 194], [568, 569, 194], [194, 569, 195], [569, 570, 195], [195, 570, 196], [570, 571, 196], [196, 571, 197], [571, 572, 197], [197, 572, 198], [198, 572, 199], [572, 573, 199], [573, 574, 199], [199, 574, 200], [574, 487, 200], [200, 487, 111], [487, 486, 111], [111, 486, 110], [486, 485, 110], [110, 485, 109], [485, 484, 109], [109, 484, 108], [484, 483, 108], [108, 483, 107], [483, 481, 107], [107, 481, 106], [481, 479, 106], [106, 479, 104], [479, 477, 104], [104, 477, 102], [477, 475, 102], [102, 475, 100], [475, 473, 100], [100, 473, 98], [473, 471, 98], [98, 471, 96], [471, 469, 96], [96, 469, 94], [469, 467, 94], [94, 467, 92], [467, 465, 92], [92, 465, 90], [465, 453, 90], [90, 453, 78], [78, 452, 76], [453, 452, 78], [76, 454, 79], [452, 454, 76], [79, 455, 80], [454, 455, 79], [80, 456, 81], [455, 456, 80], [456, 457, 81], [81, 457, 82], [457, 458, 82], [82, 458, 83], [458, 459, 83], [83, 459, 84], [459, 460, 84], [84, 460, 85], [460, 461, 85], [85, 461, 86], [461, 462, 86], [86, 462, 87], [462, 463, 87], [87, 463, 88], [463, 464, 88], [88, 464, 89], [464, 434, 89], [89, 434, 59], [434, 432, 59], [59, 432, 57], [432, 430, 57], [57, 430, 55], [430, 428, 55], [55, 428, 53], [428, 426, 53], [53, 426, 51], [51, 424, 49], [426, 424, 51], [49, 422, 47], [424, 422, 49], [47, 420, 45], [422, 420, 47], [45, 417, 42], [420, 417, 45], [42, 418, 43], [417, 418, 42], [43, 419, 44], [418, 419, 43], [44, 421, 46], [419, 421, 44], [46, 423, 48], [421, 423, 46], [48, 425, 50], [423, 425, 48], [50, 427, 52], [425, 427, 50], [52, 429, 54], [427, 429, 52], [54, 431, 56], [429, 431, 54], [56, 433, 58], [431, 433, 56], [433, 435, 58], [58, 435, 60], [435, 436, 60], [60, 436, 61], [436, 438, 61], [61, 438, 62], [438, 440, 62], [62, 440, 64], [64, 441, 65], [440, 441, 64], [65, 442, 66], [66, 442, 67], [441, 442, 65], [442, 443, 67], [67, 444, 68], [443, 444, 67], [68, 445, 69], [444, 445, 68], [69, 446, 70], [445, 446, 69], [70, 447, 71], [446, 447, 70], [71, 448, 72], [72, 448, 74], [447, 448, 71], [448, 449, 74], [74, 450, 75], [449, 450, 74], [75, 466, 91], [450, 466, 75], [91, 468, 93], [93, 468, 95], [466, 468, 91], [468, 470, 95], [95, 472, 97], [470, 472, 95], [97, 474, 99], [472, 474, 97], [99, 476, 101], [101, 476, 103], [474, 476, 99], [476, 478, 103], [103, 480, 105], [105, 480, 207], [478, 480, 103], [480, 482, 207], [207, 591, 209], [482, 591, 207], [591, 593, 209], [209, 593, 211], [593, 595, 211], [211, 595, 215], [595, 597, 215], [215, 597, 218], [597, 599, 218], [218, 599, 220], [599, 601, 220], [220, 601, 222], [601, 603, 222], [222, 603, 227], [603, 605, 227], [227, 605, 230], [605, 607, 230], [230, 607, 234], [607, 609, 234], [234, 609, 235], [609, 611, 235], [235, 611, 239], [611, 612, 239], [239, 612, 242], [612, 613, 242], [242, 613, 245], [245, 614, 248], [613, 614, 245], [614, 615, 248], [248, 615, 252], [252, 582, 255], [615, 582, 252], [719, 745, 344], [344, 745, 343], [343, 744, 345], [745, 744, 343], [345, 743, 346], [744, 743, 345], [346, 742, 347], [743, 742, 346], [347, 741, 348], [742, 741, 347], [348, 740, 349], [741, 740, 348], [349, 739, 350], [740, 739, 349], [350, 738, 351], [739, 738, 350], [351, 737, 352], [738, 737, 351], [352, 736, 353], [737, 736, 352], [736, 735, 353], [353, 735, 354], [735, 734, 354], [354, 734, 355], [734, 733, 355], [355, 733, 356], [733, 732, 356], [356, 732, 357], [732, 731, 357], [357, 731, 358], [731, 730, 358], [358, 730, 359], [730, 729, 359], [359, 729, 360], [729, 728, 360], [360, 728, 362], [728, 726, 362], [362, 726, 364], [726, 717, 364], [364, 717, 319], [717, 716, 319], [319, 716, 318], [716, 715, 318], [318, 715, 320], [320, 714, 321], [715, 714, 320], [321, 713, 322], [714, 713, 321], [322, 712, 323], [713, 712, 322], [323, 711, 324], [712, 711, 323], [324, 710, 325], [711, 710, 324], [325, 709, 326], [710, 709, 325], [326, 708, 327], [709, 708, 326], [327, 707, 328], [708, 707, 327], [328, 706, 329], [707, 706, 328], [329, 705, 330], [706, 705, 329], [330, 704, 331], [705, 704, 330], [331, 703, 332], [704, 703, 331], [332, 702, 333], [703, 702, 332], [333, 701, 334], [702, 701, 333], [701, 700, 334], [334, 700, 335], [700, 699, 335], [335, 699, 336], [699, 698, 336], [336, 698, 337], [698, 697, 337], [337, 697, 338], [697, 696, 338], [338, 696, 339], [696, 695, 339], [339, 695, 340], [695, 689, 340], [340, 689, 341], [689, 687, 341], [341, 687, 311], [687, 685, 311], [311, 685, 308], [685, 683, 308], [308, 683, 306], [683, 681, 306], [306, 681, 304], [681, 679, 304], [304, 679, 300], [679, 677, 300], [300, 677, 297], [677, 675, 297], [297, 675, 294], [675, 673, 294], [294, 673, 291], [673, 671, 291], [291, 671, 288], [671, 669, 288], [288, 669, 285], [669, 667, 285], [285, 667, 282], [667, 665, 282], [282, 665, 279], [665, 663, 279], [279, 663, 276], [663, 661, 276], [276, 661, 273], [273, 659, 269], [661, 659, 273], [269, 657, 266], [659, 657, 269], [266, 655, 264], [657, 655, 266], [264, 653, 261], [655, 653, 264], [653, 651, 261], [261, 651, 258], [651, 649, 258], [258, 649, 254], [649, 647, 254], [254, 647, 251], [647, 645, 251], [251, 645, 249], [645, 643, 249], [249, 643, 246], [643, 641, 246], [246, 641, 243], [641, 639, 243], [243, 639, 240], [639, 637, 240], [240, 637, 237], [637, 635, 237], [237, 635, 233], [635, 633, 233], [233, 633, 231], [633, 631, 231], [231, 631, 228], [631, 629, 228], [228, 629, 225], [223, 627, 219], [629, 627, 225], [225, 627, 223], [627, 625, 219], [219, 623, 216], [625, 623, 219], [216, 621, 213], [623, 621, 216], [213, 619, 210], [210, 619, 208], [621, 619, 213], [619, 618, 208], [208, 620, 212], [618, 620, 208], [212, 622, 214], [214, 622, 217], [620, 622, 212], [622, 624, 217], [217, 626, 221], [624, 626, 217], [221, 628, 224], [626, 628, 221], [224, 630, 226], [628, 630, 224], [226, 632, 229], [229, 632, 232], [630, 632, 226], [632, 634, 232], [634, 636, 232], [232, 636, 236], [236, 636, 238], [636, 638, 238], [238, 638, 241], [638, 640, 241], [241, 642, 244], [640, 642, 241], [244, 644, 247], [642, 644, 244], [247, 646, 250], [644, 646, 247], [250, 648, 253], [646, 648, 250], [253, 650, 256], [648, 650, 253], [256, 652, 260], [260, 652, 263], [650, 652, 256], [652, 654, 263], [263, 656, 265], [654, 656, 263], [265, 658, 268], [268, 658, 271], [656, 658, 265], [658, 660, 271], [271, 662, 275], [660, 662, 271], [275, 664, 277], [662, 664, 275], [277, 666, 280], [664, 666, 277], [280, 668, 283], [666, 668, 280], [283, 670, 286], [668, 670, 283], [286, 672, 290], [670, 672, 286], [290, 674, 292], [672, 674, 290], [292, 676, 295], [674, 676, 292], [295, 678, 299], [299, 678, 302], [676, 678, 295], [678, 680, 302], [302, 682, 305], [305, 682, 307], [680, 682, 302], [682, 684, 307], [684, 686, 307], [307, 686, 310], [686, 688, 310], [310, 688, 312], [688, 690, 312], [312, 690, 313], [690, 691, 313], [313, 691, 314], [691, 692, 314], [314, 692, 315], [315, 692, 316], [692, 693, 316], [693, 727, 316], [316, 727, 361], [727, 725, 361], [361, 725, 363], [725, 724, 363], [363, 724, 365], [724, 723, 365], [365, 723, 366], [723, 722, 366], [366, 722, 367], [722, 721, 367], [367, 721, 368], [368, 720, 369], [721, 720, 368], [369, 719, 344], [720, 719, 369], [437, 73, 114], [437, 416, 73], [40, 73, 412], [73, 416, 412], [40, 412, 41], [41, 412, 413], [439, 63, 413], [413, 63, 41], [63, 489, 113], [439, 489, 63], [113, 437, 114], [489, 437, 113]]} \ No newline at end of file diff --git a/docs/CAD/design/mate-connectors/circularmateconnectors.png b/docs/CAD/design/mate-connectors/circularmateconnectors.png deleted file mode 100644 index 3150a5667b67a24c6182218f6c04f0625bb31cde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13095 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 - - - - -Mate connector glyph — polarity and verse - - - -

    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%0Djng{n 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 literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Mosaic Tiles.png b/resources/textures/displacement/Mosaic Tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e30e0bc2cae6a2fa6b8a49a1157f6fbf7e7be6 GIT binary patch literal 25998 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 z�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 literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Pyramids.png b/resources/textures/displacement/Pyramids.png new file mode 100644 index 0000000000000000000000000000000000000000..bb83b97b84090c9637ee9d1bdfc46ac239fadd72 GIT binary patch literal 9529 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

  • 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 Rr!W8j002ovPDHLkV1gxihs^*0 literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Herringbone.png b/resources/textures/displacement/Herringbone.png new file mode 100644 index 0000000000000000000000000000000000000000..d773b9f615f62eec7eba144799596160ede9db30 GIT binary patch literal 159017 zcmV)LK)Jt(P)+XQwk-%+7jwy7!U3ny5+~!{xX=0jf0WrW0!h?3l0ZT|A7){rG_xgqpLT<@&))8U zq&15%Y!tmfDR^@H^u^hK{h$BqfBld17dsU5i|c<1OY#e)z z5dDM1&tswc(cwp-`d;B*L@B(x`TF_&)#c^o)rZgj-2N(fa(w#y&H4ZMpa1j!wSTr; zKjl{xELVu_1JU1s`5KqMgzaC&B`g=h^@6Cs81g$HzC6}Xx7_Ap@4ftAY+oks?A`U( zPam$XF0bBy{QUj)SHY9r>GN0T|HuFMpZ~|%i{s@J0QD0s|5Q-6e+KmZ*dDsRD*eZ$ zeq6*)lJ!6OYiRy5-9uXLc=Xh_+dBV+yX&u?t}ZVwF0MX&`nEj(7iVw(=l}XY|Ht|B zW7Mx#58Yia#a-6_Cq9V!>+;=pZ;$(_XuR_Ju|wRu_;#r;zPtYV@%_cc#l_|O&);sQ zC%+0Ge*L|4nP&HRuO|-KqSV+h2tb56knv-p$t!4!yGBUt4ZZ z`>bj_&g7Z$fN#ZdM*6?M114IT`uKx&_BcLV#eK{)rAwok&SV=#=PNWB=@-0y&5ul?L z_RB;@M#0gOCzvxpnKMAalW8|SIl>XP69D4Ud;(C10(9mN(=zOP+l(dq@-VCX%r~O? zFA*ION5Yx`Q3@ZP{7*jodLV%#ET`j=0XTj-qm(rHnlsKE1n`d^166>%gN++1GBI=p zK&=5-%F+DF*Xsw;gv=m;>4OG-E~0`F5c#W^oPn&<5%JFhYRj#CUo)}G04%_K1CG`| z(g{GsP*4IOInm*pX@u`e{`A=YO{g;nE(zLeC}mD_A_K4#938FC|4Gz81oMF98LANx z;*T<3>P88GX+aJLe1YM=FcA|I^T(F#ZAA4|R(7%?sPo0v}JF?##! zhfLomQ~2509|#i7o`~~k6ok49X5373liXl;=%%=zyWEcX$Ip|XItl#&ks{nAnCr)$ zKRWCTaD8eZn7#F&A4%v3f7o0&8NaF55H6HN10V6bLbLPKFBOtPn1dSxu_{Iukn<;B zPk`y1H3QU@@q7mGz?y6cZ&NDk7ZdrC3k+ZLZsJi{1RQxmSTLOw)@NYOfRhRC>241# zn^~bfEUxRgxdMorSVZ^1-4yy6t%E+;S9YtD=FhM@m9=-xfG{+d#S#VEc=wOAWYIj7#Dy>cN4&$leNo{0`NaU{pC~(3W2O z)64+o&zybv_WYL$pwUf@PJhJlU*BW&7b!$C|4j$O40|p5sT{!t4*?kg+wcO|!PUKSLZC-} zvA(_W@h&};&kn~xc<-jkIZfu z7DP-~1U}E82{peIF>u~Av{-sb&4+L zASk7r9_!&0f>po6AJMd6`sfqgo^i{JlWN`={onM zJ+$I3m?bN9!QjRb;cT=tPcIYA{rR6s7=^&@w3O*({i2&+H}WCW48X<$Hq35j;xHcl zl+b?$KMx^j~Z1c!ZfWfmB{n`um|0ttCVnby`Q z+^L_v_Q6!Qda9)bflki%z+0jia zKBG+JU-Nu9h*p0`BqSv;px#6VZ}OcK_L2 zNhhM|ME;H2U$?S?Ki!oB&S~WMKbrqWFI1s{OUcVXU5e&8fI+o3XQADO5t^&u+agoI z4i3F6%m1({=3y&51n)}ygz|1B;jgz_<}{7sUlvTuE@TBY&hJ0h9B{hvLc{_@7q~Dl zybONd1PlV2x|^`d%P=nm4`UycGM!AP@(T{g0F4-K{4tSXm`%WnF#v+iQ+|SM=Pj7+ zG3B?i%%EGfnA?pX%Z5ALW~K0NKH9&3N=c;KvYG;68(qDf6*LLvNy$1TJM~xby6C!+cTxWgj8DQoB z+Zjl6SfS1Gu`4GA=Q{l&0VDHXsvJ;35V|V@(-%nXA+S4@D0{UJuQdQ7zMI%(44ph2 zCp&I9Eb9M;fFyfzt%nJECpy*nFTqle2?`>a?`QXq@g|6! zJI*q#n4l%q-@3J@U2|L|{?!byz?n~XNA{M@yWC}?%hnEJbb}R@Y1poX^}lKF zAj8fOt^;s?P2BeI1YGa&=}yp}w+ygBs+L;CprE(Qua0r_H)EOyO%^`Fa7_liQPa^cG0EzUZqbW-)y9U3 zEtubbskbs`fGUB&U<*pwxk4pLMx;KD^wKQZ7p_SN9td>6HhG%@EIClm0Yr#YL`3iX zXRre_yJ)n=9323;m7r)~O#|>L^I8Bqpt4#jCTujZfJ&0P+ZCymmprh=)bZ2IsOw;cXLjSB^6y zq1jv;A(})6fFR4={JJdyAF^O#;Rz7poTh!&0SdEs$Po$B+(SHV5x~(dEJ*-M4d4+$ z^FDZ|R=ll|g53c^;3&dJ(gc8QW0zL-z)n6I>B5#Oczp@BS@_b`?*JaslOU%ng+*Ab zGNL*J#?enx-6gLNk|tYV;;J+W%p7E%13;JmawLWX!>e2Pfc;W&Dlx_%s-vk3fI5&T z;w*;|;JFyl9IMgI&3ygqxqiB zs{ptz05SteZeab{{$>ZQHx81vO3oA?{IpL{e}YGKr&B5UBn0a&pvAIb11E@L#?&~Z zLFsLjc(8OF6T%3NmBfh40Bz=4iC?Rn#hn$!QNN%oFN2n%X8OP($SspiqytrNAEU~D z7*ue9JRwB}a65c^2-34X>2pBoL#&Ia{@3;e0aYSAdZELgUYcy>#%O$D?{El#J5fmkBd_RK?2DO>-1?&1gBD)Wvc-oY5@Blrh5Q0nn75G5_u&L zQD&NHH%zFUN8~%7|8*xik6M(n&A2=>}`91F)T9@`W`(R?rqpLLa?8q_n-76xhfB=~#j}G(HoeJjN>XQ}i4J zP?*5F3h?zi(R_f}XoC4$0}Wyq;6Sy6K(oNq;ZGQqKURg1QWaw4^PjhYb#w}q0+^!p z7~wqpzypl|7SkC!D^QB&GkE*~BYnD3O5wELLa1r~oB`^QC^CSZtjkU=)guNIm9@wI zA|m=zl1%OU!HCO0&w>kDU4UCfGmnh#f3H?&%I2ysBQEDJqHaA~0@KnDf$N)kiVV7}!I=Vf%BE00->E*rHa2+rJV0$DHF62=cGOMxBriKBqPIX_FO?!1(2*6 z^3Bs$I!^*y6lDOr4U8IX1h2H0%is`fPxQ9zvc8#I7rP~Qh!meSQc%)e028!2xcC=?idiL$;T)ix>=e-%L40pH&<_en)*j#k-wi_fe02iIS zK$VgQ=72~Kz{ou^hc+Z*2z}mkt8XT-fJG_MyaCIGNN0e?OXW`bc<@O(j?e@_f8I6j_Z5x9b>~|1KraFOBP`CQ{d~1*oWkatg?{SY-)}exMf> zwF_`Mk=)esvC5>|fsoJ-$$9`rZQxe!F&O__w1>i>q3e=cpg{Ca#dvK2wq&JMl5Vt0 z(x#DOgOiWr`02v}v^z~^gei8|h|DIw>?v+&9W>VmA|@Llca6{z6^MXIkPjJE2kd^7f-;?!kRM+sL8Uz6;8p&#F_<{i(lMLYL>fO8fG?pq zj64_%ZjA zm!&@40*&$hAX|P=tAl9hEX{t7lbaMOCAjSkn7=D!4bVo#y81_LYlE5Cde-IUQXPfL z(kK8LY&BCAuNolhO|;v<2@OwpqW?M3GEd@RjP7=VfbILW|0rTVgOwfI*NQ$M*Z1~; zy>I2iUH60Lr9n~y+U19V8!Remr-@^X>;fb?Fb5tJL(0vKPM&GY3$6H-EdmOq5e;2` zHU)dwItQR~3?UUeIF)#b)6P-EdIevafN;e0HY8Q`US8Qp6qg^ z)2dp)4(>0q$uNzHo{U%kT|5;aG7WVU<3H|w$dy7!{a2j8Zrng3p_8+8`e#Zh(<#zt3FZLx z6+GPyfl`=wOG&HdmZyxZf8_vp5Id<(iqJ|V7LqCnDs-`oFn9nbl-n3d6K!@5qZFW~ zBFxP;#Y2Q;I|dHqmKBGFMo!EQAL>Myb0|wYQ-pvwWHDw*^=?HP3kb+g zrUGzFOWC}vN}&!FK68e5p1*n^U%PMM*wT9ALEoxshn+%Af@yjZ5PLm zuo&zEcbHZ8I53@%CGG;&NhlcTWLlg&V#z1UkB+DSNcOC5sgAh5fLl?j3cyL1*GhHH z0LSWcuIKo&2P)`44?BcfS*#|G%P&$>#RNubyT&|(5%XEzn23#4^y1@Itv z-UYH z|E(2jn_kG)SiywrlsWdU`%vW?RuMsAN}}rkqWUMs30mZ9%z;(Yb_ST!i14RM0LWP) zQru3;4yDxys_ZXX`FL1?x}XY(gzzaC-Ym{AWyCHUaR=NAfBp5x5)ia0#jf9M*Z2;| zLBJf~Gnrj4571l=oQ76Lvw`2B>ufV4f|5z_=`I{mh<6J{v?LtPc=uS(i#3&Pi(bG; zL7?WNdOQo)98MO#u{j4y&mbzt1Qss)Py>Q~yrjR90u(l%w8eIvvS>>r_T{0r7g!Dx zypm&e%m^~TwjbKrl4oqvlQc(2&`_Eg#V!HK8I%y__>VI{^lKMsL}62iL36R$4*#(B zrv>(PXMQjooLKYFn3{@dK!}b}bAb9=0W-+1Az^jP6dthej0rQ-Pg+0$Fz{L`kuZjC z0rZHPVhf-d9xP?n2;!0m>o%~I!i^UYtvQK*W7DH=>i}?EXC!#jzFK@}CCymJFU$ZH z5i^;RCzyFU)@|yLs`j*_B-%0n6w-mOTVen1=&pReegty>6r~ZA6rl;7g9E+~!=`hH z*F#1gR6l<-=r*d(KV4IAzzZn55}H1X24G_;)us?-r~zQs1msd70h`PW0TRWq$(%~k zQ+!t_mQMl|Omg0bvy4fy)SpibNPunX@1Yx{nX~oc1%3*ciD*-YYEvWTWP&4uYQN05 z+hfo3)8oMF^xvTagQRK4T)O;6iBmx2r?xv4kWj4;fxtZ;DTFoaSvxu&4k{OJ{5I@e z^dRA;!06Qewa`Mo4X${Q$dhs#0jwt&2F`z*(bq1YP9RS=YQ7H(d}T^8mQcy)4X9yA7hf+vw8J@B` zU2_5S9BAxV!5RelRSDT@*BBx34U zINaRs1p^rtmT1!z&Hs{{Kq-+{3T8~Gs{vNquwCEpmu+ua##0hD{=L9!|GSoup6lTD zowjBPI+m0du+@NtVv$(wLxg5>H8Sxuc-XP!{OVJ0wV0r%drAPZ0+5+Sr_>2ekec3y zSNG&XvYaSpL>C)$xSd`Vpgo*CUi99l75`p-XItKE8Nl%ZW^Npl*hU^{Ze(NpCEVy= z{U+EGJr7zC1l2#lyCU*$}$3 zgpz$K+v+)pK!kCt0jc-bv;#)b7^1s;QM;ZH5z^HUi7EgElGcO6DH0A6KU1qEi(Nn= zc`^xR#Hj6uCC&uC)s(VJjQ3pw7MLdfvx6X-Gi(pg*8rk5FT2(ULD(Co#vtqYF2N`+-uKqL-0nC6=s?}Fx+ zVB>|98v1q+Bxi83Ivr(FN8QlTnf{ORy`CaDMl!W^Xg=mcY!QVnLg1{^L0S z$izt?g$uCk0)QA1wAKI&4MJ({3zMl!SV1ubkry|{P;|FOmI!?qd$y6{`Loc>0hR)q zRkYlcGt5j+p@W*;uK1G0%V$iC1W@{rO;bh;GItB;M@;h11cPlXt3x- zxiQL7A#WZeeOfDoSSB-O(o|7|mtf|dom@%@4q z3t@jKpp*)=R|!l4NQkv&*29$>wKPHtKN?PvmXG0$i=$o@ zeiM0GIu(ryC_`_jgP;Nka3N*~gEr`lDFJJ(0Z7jcT$n+w`==9Q%0(k%I(+h}*ippx z96>8mW5RBdvr#9wv3&aJRJ6TTBV~RARCQrANHU!UV=Kbq79yfQ659+*GoT|&rz3cz z-T>Jn&~p%G?%{}LeyKhX;gORK2PMV@0oWs2A}oCnE~J&h6k=vnGzWOPQvy|8J^KK_ zB|J9f?}V0$wjJ?D0yK<}U+X*Cbh!=I7Rff4OOLZlfS^t4`yqNN>{imTBsR^4->>?F zJ8(h6jt7DtPcsSQ;+mAQmKBe$c(1w~7X*QRQ+Ot}1=XWto(R;jO~Vlu05dmiP5&%p zoB`57bBJyy4x`9{F=nR=z=p0tI}?B;iNzUUjmgD1v(VWU$n|Au4EO~*o2_7p?=!YF z6!f^!t$vEg0A6}KfL|$%DAFOq*wIO}LJZNumLaSg4As4QuLr$@cxNC#4Y}H53UTqQ zI&p+or-^;z18)gL#5V;Es+-=Z31D94V=%1hTNM7q;0|{oPGbW!L_x@8MWa5DRVC}F7^Fuk{?%J`bEpOo9RS$y z#PC?is0KjOfw6MqLf^>6IaEI%mS#;@GDm?LK87fOe8nesM!MWE7jC>MS{)c*Xk>6GSdjExcCmXC3k{{#3UUopP@qqC4@fP9st8lDbbh% zKEUH{K z0~*07P;_6Fk^pNDTjC0!`4LmC+=5bCdhvdRsdxv_?+cavX$%M|(xo!Cy$;a6tsJ@$ zkCSN}GG4&i1s(T?I~b#Xwh6RMI~YfPD{Y6I1Am1X0D)2{_4P0Srj{mJtWMOD|A85* zE5Balw^TaJv~0Or;$2W(1zCj{>|)4CIE}^-xto!j027l7KYghrskSodRLUqdIX?qS zDNiGnC^?IulqP`zeZ}4n!eb=>3WPShA@wI#?sno5augMG-AM*m=)TLuk9jeb83gkT22Tt5m*dAchxBI>|rCP_4mH$v>NALPsl4M}pO;^bi) z)64*~N)|_G3T-`L$Mue!jOD4SQ6q&1?%G29X(&pE-3i5uT6F?7I~vgddIlXk)Yb%s9=`%oEDJy=hgjJ#k1~BNTK+BNDF(gK1zW(i8LZFk!0c!}l_;&>Wn>~Pf z=V2WIdK^rbJQ=2dQqal(k!zeB*)YbzA?1!Ea8(Z=%?2{koB*rUIC~J(Gy{|n6%JsB z2GVNuAoupK9RF5&eo51PhqrtxEwVZf*@xqFcN9^+)MrRtQA3ZnA~EJ`gVc7FXkn5n z;G7!L2H3z-TIXhu4j5+uvt1P#pqO2l_`Xj?2EcET;_NFyG_>my zcCj?ogQ_6VO#n!{xD*#%W<&_uE6HS%$p#cQ5W#CfInnSdF$#$Y^uf@1pW+OFMSDI` z$?}ai9T$XQ*&o&WJlq>=z_%13ImIknh^z)!RpQ`i%V0}4tCvxx$(HI=YPX3 zJ7JgsLsWEw{*$}AnzdB_C=C%a|H2`HpipV=0)$#TTm66MXQTOH2{&JLCM08S!rAa& z6Hlnw!6%_Y3;+Pj35Y?9n8zn1jF(x>ndCHBe~GpjyHnc*tm+U$Rd5pk%r-cthlgnh z%M+PdN91A72Cgwwh*&wS=K4`$^$WF{bXyP~`-8ldu@qoT${IaDM5acf5++pQB5_OW zOBqar)rbh5&n9#f&{N9inea+BZJ{_R{S zQR*OO!*U_tX0h@jSW-+R;XH{uehGwJYv~MdaP*g+J4-R<&2~^^?6Siy07fNjnT?+8 z8eYC$uR7k?A}jal5f(bv@Br!!Bt5}9(+Gm|{C_JXHS7ZB@Nc-Lq07UdI|98LcH7g` z8+D-F156>Jp71hrkR}MnSWK6VXG(|*N66$H3V+7r5_Y>I_6d;w#P^j0j)> zrhh`}Av&4_EN?<5Jo+uB`nx-Y`CaBRWL7Fmi#(=%EwiKl0Oo5$imM9T?27e_kukVKM7@qALYRU%j6p(kMB(E< zB}6#oT@BHq4T}UeFr60X7r@0Zrkw1=xF(iGv;yf!y+4A9pJkXbx}R)3b*-OjnC6-u zphbE&1>#-Zd{s>7QUcU2Ft%zqusdmLH6&?P)i|(L%1AT;gC_$T(!~bs-JL? zHl&F$fCgqi?Nu%gQCf;XXq^AmXP_KIj{X|{=>b7HmLQDVe_D7vyOtX)XfBkLc&qpk z$e#t>A515yUjP!FMDxZbr+<C>X1*zG@8003O76MsYLDIN1#_ON0WG zlIu}UwW+KXV?`ML3z)H@k(h}_)Dr%%c!1gkdI%^E*@V{ta1@e4T_r04;KhMO2AFTv z;gYHdz+ye`tHfy zu$$n&fxAcz`6}odmTXO3`UhdOCpoSp%iUiVe1ENh!H(Xeu?>+RU;`xh1MF; zxfu2*fGxM`l(63No{TX_P^X!!HEm$Jeq4OBj2TqjCYb)jfgAu1Qaja(;Ye}RgTU8(LSWw91&#(wsn-7S@{%@P7$iIL7M5V zec)5ikGI=Eq6BVS;l{6o>6S@Lo|UF7fgd}JG6UhExXD?Si?3py129!dOELgQ_+V7;!l59@;Gfw(w_W-ND0vP}OI%ew4`xPPAZ$W@0n5L+jhN-2uu;pw@Lj zYjDZfQffT@MGsTrZMesjuw)ucQhF%zZe6vKS9X-sM~D@WqP8Z;5eEBIY<1W-&%@h^Xvr}cBwb$u zu-iw$f8bc3n4@glq%UX3J2=Y#kFk63B|w-1H0}$f;mIaVY37Vl^&>J@6Ck=J159-&bCi*+^5Ao-74q(MY5QTjU=$c`tkc3BDpCS{ z%CH7t*9C~u33E+JlNX&spv;`hPoxpRc@mguQ7~@)nfwWRYng^p3aI5R(m?=I3>ngz zW19mYR!i?=wS1pQBMsbzkwmeFXKCnmOW4HjR6Z2R4xTdr`W2k($!wa5 z_I?Fp8aWXs3$duWeo+Qx=XP}!SYWro>#lHpwgb9=29qpP0DPy~r~&d6szd@NHizx# zI#Hl~22Lo>(Al0&g zl42fhKHzrXr2Na4_eSWNCc;i?#1x?x9*sMY*XCKev+~i!Em&KuV7T>Bkq;(@uTi?M z(JkYQMw0gnV0A4~cb#QqGgDetQp0-tza`9PW-g}i!VVDkTYE+ouMyN!#kSpELdakW zZ63Pfx&{n+C5dEY%>rH}ie4*~OO51BUpjhc45poWC!5EAgLR3`8M32|7mN@Bzz`)W zG~k96tCE&+nqZL$bdwKg!!++?j1eP{lFRUp{Vt6aBZXS_`ZsQV-B7Z_C*QU@tOsJO z47BxEx6o%SY>pC>YP()*8hH(7>}byns(JaenE^W3w%#$%E@>lw1H=}9A%?(s%Qx01 zwFaP{g;GpHR3>Go{n3>IJth!s(t3l~MEFs6-t?hcu+7eZ>pNh(faB8PGRTRyyK*MR z5wJCjZULHzKu;RClFhlH6oV+5=K!?}xOEyq1yRRENk>YgTo8zfcz{3*fS`^4;Srp! z5I4R41c$X-pFON&j70>QW@munG6+`0XuoU036n#<4WO41XyoM1iQ6_rK4zvuETEUd z_e!~q&jqK4?{%S9CJM|xI#KNpSh;2nuxTPmzks<4i1^`*sTXPz!jvUY&2VF}nUZ;9 z51?gQ&c-xs-t9Y2>+@%j0m_8H%bmUlV4D(13x!2z0TfYb$2x*-PAp6_(m!GTJ8M!pY`MD>vSftEdDiQQHjq>5hINrCB}PGz!QGr)om z2)KbX=8=90=#1uaKx0_vMz@G@PbY2ZWXmsDm~GdtQg-SU3thw9s5PbnQH_Ax$W*lT zblHIQtv72@5WpT@3dwYhxmxzR4{h9l6y!uZv}Tpl7;RFz6z{$y2 z_UT!dVMjjQL7a1Ao(6L2XU>e#41ivCU?>*I9zX+r6t;8$0V0TnWl_pR@XU#DlO47PNyX^p|w z46ylAg3aNHX1@yO*op|FVLippa1p;*a~Z{p*v|s#G-$r6={GVD7|a1;$B<1aGu`I& zu$XLAd`bsy91lXY3^Gf?(M+D$f(Y3nP@_E&NCU93`cn)F z!K-!}F^J|MZ1KY>4UnVwcl$mi`COPydeD|(3`+2Nib5|_5V{4M35DebcD16}R-(^S z0|QngWQk2As@mD{AAl|N2%uq=vJKQvB??W=fi_e)1AIS5IVi|aqQ~%|SWq~zp&UgF zeNW070HJ5fx-HjUrM*FWM2?FEf9b`5*`mWllO{O5r2r<4Mn5=f#AGUI&NYo*se@4t z7%&22G48aDYX zTL6GL33GRf;Qim`KtfV8=%&Gt_!*2c+%$I#+7L4+rsf=oL-4|tT7{A+WiFVwf>Az1 zs>#ey)Q6cx7{=W)t$s6upT8deS%DU4#8Bmjlnvv1oemvEiJibJUgYxUO!Tu^H*)&; zPeqD!+CtI=)*7fpBSi#q8Bo(c*XwCOs!gN}r-&RjLlx4@OW98EKpeySc4~L#xuGR) z0!rFewyb1b(Rz?`?D_$wM6n9Qp?0#1b7G4bRP@GYz~2bXAt{jz20BXQZLOFZ{h;s7 zBAcFV{FY3D%Aya@bO=H$i^A0P%K~4a=YZ&HNU+56+y{(N^sR63La=CQd!1!8H@Pt& z34TIUqbs2n9ysl)!$2*{+!V~%b^*vTK#~I5?u2-Zk7>jIT^uh{%d!U69=45koldxQ8-ASZl` z!)hBr*k*=p+dvNWWND)pQaOcWz0UxQ`j#ekhLJl+zZ~8vRVMG5g;HFZB7O!(Gp0fe|r__u0h@kbRs53CZMl-NTOB@{ICkHG3SxrlQ~Zvs?oLv>o9-V@l#ls zK_@KS2%_~2Ah}dQ+t{1VADag^G;UDpxeQ3GBXA%&yyZFX>s+mpwtVU74i@1Qm=i?> z07?w|QHLRZGlBnJ`qK(AWT8I;Cky;WAk|>?f7XA=pnS~67sMD8h-o*fY_UB`0xX7# zIjiMMR5-$I=;QTE1~CG%ML_J1HYWhsV*`0gV7^zb&nK(_D7Rso>=>ibw-dt(y%91S zOH^(=vf6+_NcDQ(fGrYIEN!Q2w_y|56>W**Ukw4&qCa(lLq|j~^Heo}V5Y8QQ9U(t zLm4q+%B&S^^ez#tq3JD6*yPk#HE5xipmoHAJR!?4EnmN+}QuDODpX z)POy#69`u`Pz-2>p|4rD6GD5r284$3ibZO6Oec&dftzOvi;{t1$b|@^pV4wpj%J}u zGBeQdVLO6>6-%u;xH2CYu%lVX(_QcB2Jzq4K8QKo$jlAECi$m|SSMzMnP!$#hzYL0 z@N`lUr3Pp1QlT!dWWCf%)S5>nke*;twq%emw=y#5*GMXN^V~(A!MF)tAX@h9i<})U&Uc#+F z#d1?gOCylU><_Wse2^$`c-ff9NE1m0@DB%S4kDkaMnzYJkcy^608{s?T?Gbhql6Jo z+C|IZ7IBGNeI&%hjDB8f7a*N;YX(4^0h%JDU|Il#>RLGLsGH=$A9o9bo}@yvhh;h; z8@qst2VmL)_=-g|HJL;itTaxGH-x?rMs58!xMCs>0`>OB&Y@hqb9beKZcP>dSy$6S zG`k%aOAp##oK&dE03}EPu)6@($EY--Sjwc!bkW-g2iul|1W~dIsk$6MQI_cZW03M3 z8S}XwV$Mj)ZC))B@8? z(>7!GR15;UT>wW1;Y>0>s2Y=N#EX)7Iw<~`Ev{(FcE;}6SpDO7LIuhw{<3NKig*oH zd<766>*2-^89G?{GSL^pqJ+hKSF1nyG6}VFfLS7=jf-cD2ie#{B z1*}=XW`JYc|`V%p$vE7Cn14NRO|1JF6LGLpe! z8{ywiVF*XK@eWKOKk2w)wI{ST?DJh&U9&=5{K+o0-eI377d6h&P!t0$)NIWDboPX> z4in0a5o&;v9gGYWPQ63ZKT-RWn7A<3pdnn9L5bM|KruLvL#bXwy8viZxSB+n46tCv zi9jWpfqyk0g#m!m|M7oqGV}AnqhD>UWA2K%1ELC~W5)(cc-!G>q1esv4>pl|We0pJe+?I?&)>5-?k^zgd z2ahubG2q)1`mvkZCo&iWGa2NgeSWC5rQfQH`N%T?}O&PpF z*BhJUQQ~(F8K728|8)T%y+u$GY{zuM025agGEl-*6uN9I^pZRaTIRlm|C)m}Zu z$`56pKPHnvBOOvi?Xj5x7G?ZVHvroV$2JU2r4v_toA_@Uvm3(Z(gmb7fChhu@y>AB zlSsRyW1%JvAOo9=(H9Rw%87AVlOMk7q&d%@M@fB-V){ZXfg5u|sfRPdBMPO4M0h9O zg=}4cQOj;g*3e0Qh_wwHun&9sr>;a86izl=zTA8?)rX+)4Md5(OjYd;3l4)TOpL(t z3Nn*!hDJJINL0E+u(XDFg*^&pUOKD5owW@Zl%KorCTYH2I-O#7M8sq@9+En+GnVILo>-n)pr@N!6tv7kh zRoxC_ipFcq2qB;A^9(0>Qvce7?}Z?8{-s3fzAM)txs^EKQ9@4%C0{UDt|aN}gx`Ct z2s0I7w3(SX3>4w^@0x`R0<(qL>hMHw=qi5(p_$I7bGb1tfuR1&MubEBIuF-EqD#+_ z%HlN0Edr0VteuepXLy=o-zd(4Zz_Q`GixH<5`O5Yi{~AJEy>eiFZ<+V z>escsmU8rO%Tb9iGCX{hSI!B;asjo=ciy5EE|;%mvxY|_CG$sG%oX+sV46+G4<@l; zTQu}!eck&Mzp9zSK!^6^nL+U<1iqhRhKf{poS63AqF`<82STuA&sR|+>b%lRJ+rm% z!;r{D5#P$5wQnnXV4)HY4aGV10T7GaMsZ6?qATZvr6_M-tv`PbfkJWu?;5tbkjIQX z{!)KO=_hhn+Y@_NQib6)z4yBY%#uj~3Vmn_+x8c9%?XvTM5J(8>y2XRaVmjA6wVea zcB2>g9j4@mwO7xqR56WlJFb_0vdMuNcyhn^6tA=mEimzgnZ=U*roa)fJ5y41C3;q@ zw)qeEi^BB^x(Y}4uG7jzr@s}#Q2JRRp!&lZ+(B)Bm=l2ze(z2_(4yt$1bg)BpEN`W zd>MX-5dNFn^CH&Bf1Wq#d_8kUnoxBeC%M#m?M@;oaib2>NP=ZgmFHoM&HloZ-0At7 zFqjvn?!SN=Yj!>V?Ca3D;05NAyz2D@zr(eFmirP~)plERoHyqmd&-`NAwBPyQ2Ne_ zX7+iy$(7Hn7(*BWZYqkzIZ@eh<{o=14d9N0ffC#ZC%aSPUU@1`BN;^uRGQFOO6wpS znO_s3vo_Z|AvxwvkgGkKBugvP8Lck3Sm7?l!0*K^8OyhhwddY@gUdp@kAZu`J+Ey!M1AY*zEix)=*22x|Rdn4$HRYyl7to9oL- zw~v0=4I1i!WiWb6%!fj)r8N}XshvFmgyLw=CFqhZbR6eOc+68IqT2B%pE|0$Mcu<8C6`vi=dxQ3=K zZv5qBb3rrD^eIJJ0mp{uMSdJV6V`9}4)!_#l$bX?8@l62GB z>qci>`oK?m224OSf3D`1@YA*OA9oyqY0#){7IKS35 zGYV*}%GX?$9I54SYu)?1<~^PrCVVCk|FEOedEP83(Gj57I%FoDXg#;(fk~okn%A2+ zn2MIQ0Aep|PUYWMJJaEMj_rfJe?@8$P4{k`;~mjG!|eC^lV|y3LX(%>PhjY>GF~+l zTl_lX2ZWS~6r|g@OKF%$HQrX99}9aL$v*{ST|_yW^X|B&c^nS-!j&q5^0%TlH)(Wv zX*I@`Dc-LAP;kz9**soX|Gv=hDg>qpvNhUn{m5M_+CN~;<4e}+nEp0JX;y$h=VM}a z1(pY)Q$vyS4_(V3MLvX3X&RQKfVP0aVa<2@=a8(g1>elXu-q`g^iMI}MCUASH$CjA zoIohEuZ#h}#5Sa?>jJh(UCN+4%s)b^E9cBEP%Xr5 z->5UGn9@D_88Ntjv@Yx4gQY2rn_QGkc}m`d_D$y}jznNi{`$Yxvhtp2-w;+0d+DIt z*q$xLw7%5$B?Fu5%Ga;-y5Q{6-^L?_vje=M&Q9tw#Md7lNk^E$Pa3Xzf{BD0R2Q+E zE(w4AOn~@4v>Gz55YlP`=Z1SFw*q9ZsdAoP$RJ7(ERntHVW8~IU z3H##h5jFW1hv+dFF_Il6cC~dP|9e;=NKSCq+EAf1J zTlVMeSN;s0MDJ`N`-B!s9$^_<7KLn6lCLkhiGv}~HOLC)TC>j2_`|Kc=Y7?xHQjMB zLyX718+nZ&dqI`iQKbIkva`s@_DGO!at~XfE_XJ$sVYe`-4M7We)+R^ zNP^w&oi><5fMWpN#od~~exfdy$V=4Co(LzfgUo9GTk}u8oQr}y2R``Rz5Ww1g~P+x zH?hn{+|yfe4E4~S zYj7o7{cfaB0h_h|kK8V+6-Xktk&pm) z7o76QznNj{n}zgttqtp01H7;h>DSY3*H~o4nVQZ*Qo1iNhCbz1AtbLX7@h?1W>CZN zrCHCuLZ#%g7VSE}v$BtXTTNe%%-LBaS3fV{xH90UapxNry0XIRxZlfr4Lo14fwd~A z`X8=XCPh7c(};>Y*s!Kgr&2q-XvLE3ltFB#ZLfNVy^1wzu?^1)&sMY(mTy=+f9>=u zChp5SF6?tlU9Mm#$}hZUSy%n;PJ4qvlYsKW6Lgj!bc}nRpTH}mI*;FfD-gry-eDI5 zw!qL^uc!Sp^4kd+>1kxxc|PGOB6lN-v8$txj61SKYrv9>RFSlAKw)66!GKAmHpyx5 z_V*sJY{7>@Y~bHL!e^-&7!uh2D7^_k;R1#Q0{5-czoviDvq!9Nbt3WI@#5tHFM8BE zqOW4zpKG{3b!&C<@n>g-oSmLR}Ju(VGo8d zVE=k7X87ZphCw`yxgov;Rq&#nJoN7F}>#)zLhy`9Ia)DR$$-mNfa3hpMt)Uu=@4j<0_h97^DGjS!%lCxq1eFzA= zM$P8!Xv|=fqsa`rDyHI#OnHm z3|aAZ@Co^l8Y417b-qv5G&9+-&mGRgm)w2UGo5Ik)H&(Br#2oY|Kri(7g&eh9X2w5 zpzdE+;8mf7wKKfEa2YA!Z$4yEI+nN?y<`A#sHuNkmr1(@*~&`>^L*lTwLl9%vP-pD zy^SuiXZOEA?H&!^rg5w?^a?@lx_V7sE zbtWJn@W{0l`RHwYTVx6gGwp65XK3X*&D})%sTwx$4D}8c}|Y`86=|nze|@--j01=UA0+_FQXv%2zIM)pq=Lghq06TDafGa%}X8r z9Jj0eXLw)XJslm^Met744o=@GH>-PnaK_tk7}@@d(1OAgoXEd0Ld2m^mge^Ab&;6@JG_tqKCE9h&v2w-!yjPiNw zQ=E~24noL2)j*tjy{Y9F1&VY#(>4W%PB`Mb9BtoL53*{ybfCbA6g~9yHJ<*%Ha^3zevYn$t<#m& z@P}K)A;50h4jCw&)t&Ft&HgD2!AS%-tsF9&l?JLYFd>pzC1Wgv26!pX%6k6+9!LPO z_+@WeFKFjA3fm^~_<>m8IA}NnsDK~W{!(pGtA=-5 zQM!uCIR7L{^(-!S@*EdiSu92kO-yh&{P5^079Za*?My%g30xxw?JB!p1wTyxLzzd! zj^eFL?PiO+KgH3quCI6D-c%#*d-Y@zMh&j!$m{aOr}TeVm;VYUI|q)>avBREa{LfD zL@qMgfSIKZq_w;zw1WO!SoN<)Cg#M+C7WN@XmmILIl2YzcgCqJhj;d_56&~Pn2GiZ zK9YC0Si8^wEo2{bE@a#|s+NN7Mssb(*s!!~0 zBH{Ba9LRgwiFcBg^=ie(vH{4W8(?tO1`+z;%(Vt>C^RH_t#>`f8j~2tjLr7ZWn8e3 zMGAwkM`)ggfw1`wXQF!w*tv#B`v1FVgp8#@<#p9LFQ!b>UXozKG-}aC$taI$p|Dg; z3nkL`Rx$Nxz3{xfqOAw$!|vob{O$O(kAG8Rq2@9NiDij^yZBOOxZxBbL&*nWCkEMQ z#4*Zs4Y0Ab_TB^58`paZi-1|12vqJcOkof`v$#34CqkuhGaPCDk^n0n4VzzGZsP65 zgqG4;sU4s4;w`)b`S)c6FugXh2z6frlJMf=LzvAPRDbFi;JkhMCPr1PO*?)6)U561 z@9EGmuoMaGBA9oE7a?yAKB~RTjVpTwClOxGH(pQWDy`&Z*!)7NT1Uulcm4io{nr|B zv-PlkW(NVvkhgbxhC*KxowxPa%@6Gr?F8dpa^@%vGhch7qLI44IKW!JlOHa(3(eOW1#SF9pyb3|ij zZB+i4KVzRncz8fNtt{>K&e8-2Oyqgn{-2-cZ!|#`ovkAuTs!4#7(e(hLYVvpUGYHN zi8>W@a-LPImdtd1@dQ<_CN;`IY#tBLl5HsT-`4c2*LNzsaiyS322dxPbK)DA(LJpr z52NSq#>i3l+{$9FxfpT~`79B*lsJ8;R8e81ot8fOs%MVJB6_Kk3pTs6d5MERDpK#= zFZkIc{U%Tz2t*bYJZ^mr8DZpIF(T8!H$kFwj}SHFi|>;|=Or%3uGpQt+Sc#-jQ-on zWZ!S)(OtHjz*Ue%y{P+7D>_!3uWEz9eN#d5f6G2-(YRnQ7ngmBvas4+8lrL|A!TO# zW%&4j`bD@$$8i2>QGaZiWQGel5CQ&U8_;N3uDPVUU2YWjdGQk19p|D3t{>JN?-#aM z?a!;QFO6f9+&-Dswu3q|!~?PZioxR+JxxK{n-ACR`e%*&`~qvqx9k7yy(c`VRNOX_ zELTxlW*{iQ*LSWFWB6TD|ZX5TtdUmng-;n06weh(IFAiv~u8e3yZPkY+j3n3$> zu_#o;6@xi4CjoF3uq`^dIl9idLO@pM=Y~9IV3ElC;kb`Pj+b3u>J)3_ISFvhtN0I5 zz-oCCaQSe7!atY#f$7k%9CEx?f=vB)`9bTuHTw!WMd~!Z6V61>Ze#;gfWJ{b=@tTk zyuQ3x;={^U?d!pRci!06<%#7~arYy;kb#V&o`|BKBy)k=c@40OJ-2IQ_9lv6f}n09 zr3!5SZ5W`5XkKCI*7x7u73Be}+rXK*2eL>{y4j0VP~G>Ch8*Hk#l-B6zwu@tCp&`l z=xe7__D`C-!J1vCBb40`5DERdKD}u7?FP#9rf`ZDbX*C$xDT)|EukcB0r6uBexOT2 z0WS)GqO+Xa;p1G8Kejz`n^X+4Ip#9LK2sXKL=|1k)mN2R_4z0>EC3(%x*t$IPZKT} zBQdZcQDNqb=qI^@+&MFxR2S`scC%*ifvTRHyKG>z5&Y>mhk8ER`Lr$YfOIm~IxI*j z4M6j!6sOq^aMIgJY%P6m1<4K)zzpmZTR%c>uMTECp6i!IfubTEx*ItUO!V>Zc3piV zBhE7hzY(Gx=?ep%&ABhfmri=dsvSoxJS%F7G34LOk^@J zu1yQH0!v<6CCfdt1t7s8Uxt_Wo|_|jHdR?FB9`Rcob7^3R`2&F0Q|>020a{NbIXoQ z5H)fzy$yA{kLF(|(@!#A`Q&IW=$B^P+V3^eedKe51d@LN^S5@t^jlwuEGrxf_KogK z*@;ySa1{wfYnb z$JOtde)hk^tASzbdRA%|K)(W>+t8BU8VPHbEl_bCt$hpDc{WBX4Xz@aF|iToSw{4h z>tpjI_w_^0IrQs)c8eH&64mKo`%T`$=lg|9-`nr1GynD|`!tCD&gpAhz*nFL4>xpx z2kRGq|971GdlI0pV1+u}AL1{MJ%Uu;$){*c2{KjkU+O0V7Dq+bU(V*{S5RG9kS5_5 zbsrE-0gb^rf`puZ=Frz{;)cjgX)8V6O<`eh;0+@ElK~0W`^(FRqnv5|g_$)5|6RZ= z1cy&f32I2{nfyYdNq+yyGABlv0vIefbv@G2qK#awNc(HIx2OFe@W4i*uap%msss-BM9NCybhWHhxd3Wr7Us`wTQxJV zGtX;8IZ^(1#4^BMWtPldY3AqT^aKXc0iiN$;}bo4i*^~1f#rm+^0z+vn^TuHU=#u5 zUp_lZ*|h|AAIKzwcTIDRJ}vtCrRbZdub{EF2lj{l7GLWU`t{6y2u0_?hVkUD_#q6|ZX zuh$MSo#O~ZJlTfROw&kU=VC|wqRa=s(tXh+hFLtZ`9?p%?fsdFiM>yCo8HMVAPk7R zFVW+(O@KUUOg61d=XLx6r>A0tlmHm>g z{sThOpbMfdL}D?NqYB&{snY|2sB#7mgQnVZVw#&{gQunk&%~aI$R<&(S^9N{8k`R3 z7%}{aTrxwAmlr|jZgaAnwjUbZ5%DZ*v!EILhH5jB$tNrf?RlKZk>{_FjcI_=_sH?3 zX3fH_GO_%n-_@_G{{Gy5X^FajO%HUT^3Iql$d{^Wcu3^YL;gCmIcNR>51d*Aj+jl; zYx<|ox=B8@yM}lI@Ci7-y8PsP%&l6(1)^fVPb;KPJ$Yg)>L#|#X%IVK4tmU}yl>mRk&E(82rLFC#o0M8 z;a=_HWOcZ;!`qB2MnvyCbm`yvIBI?BptzM~hZdAsDUCkxxr1>54?WiR6PeqhhEj~# zOTEOXvPuRj*r4@_W785?-m*UR8o#=6{=2#$==ll z{s~C{QW^LxRl6{o7flnU;GC$UqGR`$tYyL2xHvCmp)nY$9bHNDH9tQ8h*1lf?e8Zu zEp$WqkxxUik19`#2gL=I2eNQ0z-QML_NXc2O^^vf|B4>Oxj5u_b=iJrego%4xz<7t zC~$;zimy`+J}D5Oyrb_ccNX%}!FTH)A5jq()Ns}xsP{SZ^g$aPD2z;WTgh+bBao$- zQ@FxFjazJoYqS4vq!;kujiCMmr3GrNm$m|fd7Nc4Y-Y$x?#3xCyZVh)?am{YH$?;2 zWOV<~0gfn}y`paWTth8~=jR|Q(&0%NdgQY-7iK_&s4s*^y2{*d3a){{xd%x-f;$5~ z+jeLq!AbwZco{@gutKaco#Zl?=O7htnQO|OUI2k&(*UIUsOSg?c-_kTl*hsamQDFO zCG(`Mc|=H&f@(W@`!JwuAbH~!zV!ep5FfBuAcsuf&qZpCj-Is)Xx_qk=RAGQHWshM zdJzaH;tF+9Pz_AWq2kxF1V=%iWYCd%-`wQ=OpLo*9_q-JaD$my4w)-~&vpm_V~$@| zkeh(d_RM^28gn3V|9=gca-{cd!$&m%-$A5K*-Ct@sF#VrL%umL%9w0rZY469?woDm zFtRfAt{Dd$oyyP+n@0NRJ>EJmsl<^z$jq3CNa=f5N`uF zsX|rQ&QaLHC~Sq)=UAW=e~uKV44c%ur`$%rq=|n$VUd*)xBl~AQg5DfrhPh?ry|1vD zu#U=0ALJt(yEyHR0v3m$A%$1aIWC1DG(0#A4Ls!fr;zgZ5O&-d#lF+EM07)Z^di*! zdR-QP(3V_}5WrxJ3{$UK9BJ=L38Nx+ia7=NoYdCe%J`vxFM~iG@JiS#T9J^qf{{h% z*<){Dww(-y_^Pb$GdEa-WQy!BvWneDG%M;>qBfj!b+#7VtJjVnCX1?rn(!1C0?>rV zSh9A{|IQKL?z4l^VjVYwvW1o^2J2I#Vb^)1B!3{*#&_t9oO-V<3VHuJ0&qs=qbm@9 z3a;$bdcS~!C)}s-d=5QykmuMICBZ|#djt;&_CsvWLL{t^(|5!ko%1fu_R{IoSN|64 zThv0`_gEY&m>7U4Fa7Zsq$T2HNm#_L@#Z2ZS0sA0S66G~fo^o%TB&iRlFv`bg^`G1 z!C{y(_9HMdXa-DOGtbH$8&F#Z)w7Wmn6?*!jix~XCD;P8`KQ?2h$7}j{s*MTTWD^D zLqu$qi}}C^KMPys&*n|`N(JZi^!F~L)9+>&KYJt2+}{EjaNDnI0+;I)RDQnU<{s+X5oy z4{)L<2{wW&3C_>_kr3)FHE;YfXk3PYnXJOS{b5Lp0d-%7@B~FSC?9OeOk)H%$N?OH z)E1mmd`!h5Pj9gwYO47&LqSrc(0yu($-l7{gZVS_jFFzr^*X%n7D@{E?4ewL2axrt z`GGsnjA-oxCnW!ANoZ8eFHAJaLI1w?HO8my&hG;r#w%-Wk=R*OS7!AEV2pJBUnZ8oqTbOIw8%OreuI#)iG ze_M2ZhNE`i(ha-AD*m?Qy-TOS`ecwLI^P2`o^N{w>wDX~m@6|zQ$X@9kPDRmgro1a zyMr1cNrn0#g73dO2tGg56QlU!c@0RTsFCCR6JoTj@Tsug7YRpbGhr1vSRfbtzOk4o zhxHoJr^`ysE$B8>qMsr*TlA@JWY04$!y3-~pHCDn-VT~A;ix-Zlc+jMC)Gf^2LK65 zpmE{OE~IX#>tEJUSr&;;C7Y)yn35+Nl!IzkAnd3yHps*Apb@vMs~i zZ@IuNamSKl^YnEbf4HU=1LP4NZ-Ivz;M-fW)iG&Teb%x*NtZsl;Iagjcm3f@nPxC? zu(B2Z%LNEBq7 ztTCb~R)_^&!hm&nzqkDy@`s4L6eTT@{M?#lP$Txm3hewJml;OvWt>n zEE@aXSkYqgYz^W3i=l7gEEij{;b|K7OmNReORWbDVD=$NIC78Sq2_Y>F^qcX9RO-gfodar^a#RXkGylx za?$#}=Ej>GAv4eFW^fDtKai-`b1~(2;R}DQZB%GMm|Yt z`;~zu!`o3hy=WHUz9ei(HM~&KP5l=hvPzWYV043aOCBn!hjCGqe&vRRT9W>XEzFd4 z;1FHGowHpnpFus2M(WaJ-#~ki1~r|v1%Zc^ZNmp;l%lZ}l4;}C2w}HI2g4zaX39L%U{50Uw8HK3` zqg;FK9aLUk-|`}M1jMZ%r5C7}m-*U;n41v@xCc>*Iai~#Ox~_vD?zOAPxKfij9x#H zL+b`Dqh5;`lLMagWYxEe_Yis@@$eNpI?GvflSl76;Z#a}*UV?ICZT15DaHEt4L+R*10Id9me^R2iWR8p~C`sIr<=ENA*v#1m90ko*$Yx(s@$&_2xROsym1E^!+`Ao^!vC}u$M+C_}%hf>wVadx#=FRjHB=Kq9m_4 zIySDXxylaLp%X}tACOXhp4KO@Tv@HU!4F_3qRfD_Ym^{aBDZ6y5L-4(!;9K$+o2gK zxG!Qs&=YNMueAqAy=`Aj z_0KxDZfM0PIn%l(BnTmWctu=&r1+f>tg6X{vw%E@*Xfn|5h+^!?=Bq%f!i1rowoJh)h#v>82oDBv zm2{W<@tznUHd##j|K2U1pFQRLHq1-6^KIY$8Pq8V{L29;yZHi@iT6*{8E@zsdG9-) z3Z>0!Lo24Z6`F%#Nthg4%X)0qD+g6Hvxbi#3=Cs17wFvyd}{HoNba=tV*=?{-rv<+T)yYr-efrH3963U zehEfSF|2%BfHDI>c>l+(zG@HTLAiep8ZVMx6`S+A=a6T3MW*JxIerg4IENSlja%d{ z*mdFIykp>QLxRcQkE*y_v3JEB_DsYX_VYPxviJOMAEDJl)4C)ax)|mwKw;(doJ=p}G37UApBHrgI7u|y6 zcHRlf#+)#^lnclc^j{>S+koi~Ao%JddCXoCNXZhje`8DHI-J{DwEVr;Yj(#$s@NxP z#ha1Vu_!|k6utaP%N{K23z^N55jqB~!NJaWUKe)bTUDTHSb7H$Y=MmDgatO6+WA{E zw=ZKgnl|O?E%>~R^+{^!txRPQ1@JM)&+o$6~1=TUy&Mk~GnNdhDKyW5CvyDzBG z?qn8}9S)++qIJDLYV(`j)41Q78+U}R#p&Mm=DDP5DbKVL&nyL%Ds~{y|5cWSFMvmp z2QU-suk6o+g4=veIGidf34-Os80a+#;dN3R*cksn;bJIAcsk1Ge$gHHVWmr2AY)4e~wwU^KW-M`Y}46zJ)Jrui$i9BH6UUkKo(dS4jbXH*! zIpF=B2djVwsZq@2CZDOWCtq(-R7!%?Su8(PR22nK=nodG@)PlP~ zfFn@f566`Ts0Rjr4qU8pHXbSv`R!y&Y z4*IX}XuB%M|1jgBecPc$v`Fqkn+Jd}lox7o{61~VCY-hPp3HEV8}+zYJI4xw>#R*N zk+te68IyFm&h&H$FdhU-p#odXKS-L(M6x4-4AGTJx3q7OGT(V?aTC7#z4Ai}JOESA zNkt0#H5~qmt>&sJgblyR=zGpSh#1Nbt@V)ZqE#x)h7GUE*UI`jhrT)4^&jg~x_6Sy# z5mWS%YI;;%7_Kx()v;Et+H25NxRHFa)fQPtEt(L0dDwfh2p?pyzHvVQaq&G(NaI{Eb|8W?l}|r!+iOuO$ar`s~hJYCjVD|dVQYO23k!}q<6Rm zXOkPaC>YIws+9C1^P^a>Ho|#|=WKmHgYVUxv zbh?M?S-U4Vdz#GyCXMS7fpU{m4NyN9<_`Me9K{{jHo+rU+^a5P|p%ttdEW$#>h5sypya8JXr*Ji-Civ_f5ir^;t14O|cZD7l-G0)); z?OR}bvRnroaBrSugY!*l#%~1aYV`xH>zug2LA81zK){rYxAs@zuTNiW?9{I6$Z&)F5>)jZ4lUS+W_~A;WHs~V z9nX+T*wV#}8f+-)Qk^g@l6v4VytCsvheV4c`MRHOD5$f$eT~#wANX0?8{)DR@b?}H zM*!Yu&{V(%p%1wGq8~AAKyE!)P(TKRF%`!?IuV)E2uT4RsDx4We+|Q0?B3qTvE2I9X)~|QKVW~0~TR1uY zVTN8d?pDp=AN;{r>V7@;+-IgtWt3nc>njeLGP>Fq_DoMAVd&&6Bif#K%2)#czgyd%9#a?2 zT`)M^a{-q5P?b2kl*b_fW@UL-m5vLh&ot1z_+;)RTOU35S?ZHHFV{&!&{Whs`H+57! z+C``c!%4q4a1oFfaWuOBIJLEY1eFXjGq#+QsxM7{llZ=*RNG-G*U(TgjfKpSm&IJ% zaR`V&`38p44g#Ysx;|oAqAu2BFI4*p$)CT-F2+|FNad5+7F&?mCV;(THQzv0OaWiT z?w}4uCMms^10j3w#OsS<{31~87l>&khJU+xAG_vX8Y?$wk_0T=0{_k*${$nmFXoS$ ze7c&(`!}{aEP1sjY@a{b;(m8l9sDhC!R9z)t8nW&kwWP9kOnEghcgnL+!6lnSm*J< zP{L{T9OG#cj-lCAaJa;t2hGG#GGx4-g%fQ-H-XWI)CR`50vyjQ28&59F+L6vo?r3Q zQ#PR2JTm1k%2o@2rw`L&sMMmi82E<4s&ttzg+5y7qh{fBR1fRB$?E*SgL7@VZ*oEW zi0KPx`>hYYy;)VDjPxga6_q5nDWyI7royO+?bP@jpx^=EH(*fGPzb01F%)Td30D8P z+e@(Nrx9YyT-z~Ms zy!hQb%F6K2zE2;+0G}&;YNY=R@bi0@6W#!fF)qeH3yxuSX|TgIgtU5?6G0uoV>w#*kohhzcY!?xR& zpYJ?o4KxO(Hz8eA_D)=+7r>bh8gN;Tg8TqsxEAvhW(&y&DX>sDP$*Ri_gygPS2Vy! z?|dt!U``%jro-#@ts-ww6-O8E_G_KMPFAX#pGGSHC)$SY0(wjn%G#ft`W63<5G934r>ic11a~a-U84K3Oeye2 zjFOGI1G`Qse^#Cw6MH%}xpard#QNev@Nsj)V zz7qMKiO{4vEjpGNAMjrh#vNt84c&p4mG`=01|_&W7mrSm$+GL319{+8Wm2@YY+V!d zaX9@bnmmW!LgpWPp6s?D63->b%CQQXDKOk@rAYa(zIgo#`r$~$WDZhkUxEFFXkHWB z^-3nvpj=r&Hr;gIK#O z(5(+#-#p1A69abZ3io3*t|I9MsQzoHHrIY^8NJDWKrFZKMFHJX`o+A98+=M-w+>@$ zc5M!Gv@kQyT2{@3H#nf^%XI`49?=bX-FXZlSELdIN8MN&zMva)%;vK153tmD)wxPU ziN}>TB8Zo}2|HX*+5(P`!v&B&n<6mEyQu-Hgji^O3O(9%)dq$_dPd*zbAJu8mW7&S zlt2=&I^$1Zn49p*TZzH|DtgJL_vgj(E-22rEvjoU<0rusleP9%ewf!aq`RZ%?(f0! z$uS@?3>633r4%~x>Zb!=D_WlruY*f+@%#(ocIl+JpS}3JHUj2BL3)U{*Eg7Ty{%kc zQ=o9p5os&mt)`nWKeC;oaqodrJdDIKGQY?8T?0wCz;ZHGi(uCT^NcNSu*&{9npQG( zbiW^jt$$b5XOI67h(o!)Tu!~W`qI-M69dz*r+E~GwdKuSG1$D~HR&z;7>zaP;<|vC zKK{>p$R|VJWHrI!P0q7ex7AcUY)Y^0>;qBf8>4Bz0MTv8M>n=t9=iG*1Z|)$4DMOn zJv&1z*`7SW{JbtLrjEpegVPwai_XUyO_0nG!Em^#=7UNYIg;o1@0Jzq%mZP{L@oD)XtZ#JhmvKpoPF-Wos0&v;d5y<4@E89y>TMn|Pm! zH6jjV>;aMaK>zY8@gUSnm?S2p`e48nhbyEGC9%j9M+_hHb$&|7Wg`)4!<||sijMaWe&9TOEJ&<_JtgpoOG48rnu6l| zac*{_jk2W_409FAsR&(eE8lBGxuYRhj{(L5=)`?vOz7K`@lGLC4}Z^*J^d3p>}GtN z_|tM%rl`+{fURwh)%`l8=;2}46B_vy`q&iYD_K=1vc@;^1{TzruquxEsTNH0C1yUN zAPgU8O#rngjLNwK-rX?g@1tBGR7(;pSHte9lZW*7|2F$+M+g#(U!f24p{Cc7uaQ1p zy-X9cVp+zP?Jugq(fkVBszHJcgxhn^0j^}AfZ`(YCfgUQ_@ZjG-k+5K&d5OXQ%-I} ztzjmwQf9zMkDid3R`xR5!9s$o#cHXFAN5Jk=wp;!&6HVE#LYj!y4X0--c@HHz;KE?jGqLWA2xx4{ubQQ=qVqY;8 zNG1Td)E9AFaXqtI+St-1t5Y#F-2!^#W}7C#96Z@|?myxKIW7{bkv>;G=E(fO-Q%Zq zH)CN`^qOA8#@lq+cdCDUXWqQti=YD<`B0FHtrsJojkTtozXx_)3c|~ckmR$P9r(G# z{TaBL%OY?+WASg#=u!R)kToRp9*V05V62pz!yeoPk(UQ!7)WXICU?ak2rP{4Rbwai zwy=zy7?f)^#g3+bUIBT(J)mC#E_Vsuoc6T*15;YPy)c}gtyPfQ2;Q#9tGwu~9g!9>!c@;L70)T!CTSj|k*nBhdKm6DW@$)ut^I&=%_(vU>K>K}LpD#+F zs9^#D83v944VzA!ix?zT9qK~3z^YRPc z7W4?Zi;Vwx+&6<$5Q^jqRoEIM7nK`7&9qu(Hi^>8U8W_bd$5202pGXfOzOwM10b=d zr90~TXO%XY^H2POm3}}6wMujho)`{`Ll^GzM}V*&@B*VCdc5&Imby>gia2pH@ATb; zx(EsC{d1qi=AeLrB)}O6bVTZNW3Nzr;eXVWbf1656JJlDp~lnl$E%;Zwl__Ipy*Ttv=j7@m|K<~8)||9t|u zg@EC%$j80OMfz(DiYgu@7HOnMbknIfoO+mO9ZjU^!${XNSRBr&PtH8DS?p?)I#6BP|i_fy`s>!9NEAF^ z7DcCM>HQQOV(|B_}Dj$7nX_9t*0gBs`ovc?30ffrE0A^rOyd9K@-o5Xq{b3`} z%L7wHOyBx*8=?N1w{)n<%>T_@_IDAVauMGKH-v-a2O)8IY2G}OG*5IJLX;WI-DnF@ zuUP1>CcV9Ow8^1bnJ)OSTTpXF{uBL-$$$5=2L|;9rGlJe2_V@yrZ#v`YN0q*)uLAH zN zHuJOA*@Q`t?^iOxmXPG%8+z|+DhZmjlgxFGKh2XH884w(@fR)wfHFnlOq7#1Q`!Fa zh?SI~AMq`k$uPxa0?7Uh8WobwVJ) zO#)3FcUEwr1*W*3VhQa;Po9Jjk%ZFGKHD#;ddI~jV~?i}C}%(hhu&8jA^x}bbPZbQ z1%ocveup(Vc{DTpAae63cMboS+3*&ky~ENvM9_BNySX~Kf@14DWRtN{BH_AD5GVMp zW(ean@%3 zBsM^9^AHTngLu{tF>eXiE~T@(X64lie<7RS=&vZb^U;;Ht@F{1-Y%&_Ta_P>TsRb+ z?R6aUb?dhRy~x{R1-~B-6e7sW0}3o6{6aw%6hj^GV z3+fW7>TJe8e8FGdBGS~tHepJFZy>Pw49$gS;#_<%X|a0uI4LA#np*Y9;?W?eqvT5x zQR42Nc{ydkbwD!kLxM>7c|D`Dfr z_e%+!#+M9RM!iW3y{NqxLMg+{V3$bH*_M;K!^f+g`m~JPZ^`{{8}sfE_7k)5eDyq42Lef`e#h&U zPmAh^i5nL;uS;{)=CZXi&8immY^|-a|C)K%3zoZjVZ~pXSA23-O!>n&sE?w|u?v3k z<2bFCE2sZm+uNu?&Kxf3+sJJ@s`@{uA}s1fTjcRaJJ&A+js$VM#9ON6R3>96=+xPe*RJ?eJ zZUZh9#Dy10e)OC31;zIk?!Y@|Li8LNAO605>qpRgqx3#pZUkiWM1i5RnGICw$sM0U zqC8VNKQ0X48TJ?Fy%C=or_(pjI&YLpmBk91#fc?C|*a1=Sb8vR`Kx^3kaL_oO~azvYXGiZ-pOOxcL3a?i*g5Df=Y?kMbU%?#`ibjR9B}iV$os{O;eYhC>H8o?^vSrXLfW@M=2urRF%EIi&4S>$lh| zwWG#+e)j^4Az)jFLr-fm41AQJXSFZ8sa>8kI#PF6mL)Ry3?|r@ZtgOI!~~#IX~Ek2 zypmt>QOB|5D>b*+7sM0fVHWM>J`rWsOPa(zLA>#(byV7O(Zg-s$K*xch)-dXdQzXs z^__|3Qj?!bIPlW9K%R%F=k|LASeo+8`B4o^3fUcwFGK z>`AwoRhDcL$(zJHf0bD^S?sHlvBKTsgedMz-atLe>;bd`^^7yjh+0}%;`$>gg@@+> zE>2b~4Nj`k6c3Av9yv_KgYx#WQL#fT_{eOPa-@G=Ow9HQHPEb;toXlDIF`Mt+=Jt1m<;mW6e z3>M(91eP~Np>m=FHZIE_`vmXY@5O#)BO+PdrZu?6T<;YbFPhg^lN#oY+dW-v1o<@T zNpFE&W0B}vnaG|?@4T&C_Yeefj~~Sr#e?{zg>xi*id&4slGQ%}J3nT}b+-5J6RMAx zeEgLLj?a@p?@S)(T~(J94Rew@DhnGaNtbNm%%YP+luW%Dux}ra8<2TrbRANP18l6f zM>mS|%#s|MJ+>?qg%qm8eWjR=#U0vcY(j{m9)`i?62=8w>v(;q$FZ#So!OmKNgoB0 zG76`}I^pd|f=zZyB1h>4gcaQvr_95 z%^f*bg-|W)u)b~tg7PtqDg}wnv8^>O@eJLgN|*Q5iIO>ag@Rf}gUL}&e81n|T8-0kQvMDo{nEoZLJ7ksr4-y|=Bz`;?YdGIE8kzG`S@G!fwEf1+M{9eWvsg;Usd{Adw>yyo5e zYQ6O0DQ~^$OaM7vjtzKXenvsQdNz!Ig}SFpKVTADlo5;Z@>aWDqmyK8k6c!MqP9`K zwHv5(LTb9&_4BuGzQwU8BMDUMx_Vdb()k=FWGYh~Pz}F8{t~JWQq^UHtuywlXQU_l6OFc?_=$ypT{$?#ia-8jb?=^c4 zVIoQ{7v6|F^gWQ(H!zPyYuAvgJKX8Q89sKT{ehrmr%}JSDkBk2W#u@9f|U|I0x=@$ z*9c7&g9uWg;y?&6%x*UZhyald6}v8+}7HrWM4ezl0C`r)PBcKK&q0P=1CGkPqjfw!l!T& zlCaN^+YAcsZ=f94Mz+*RzJJl8Qz5 zWtr?tYrRN~k+p`vYsELoQhgL>Aj2Mz--w?%Y?S^4lB2E zd+$EY5*u6bratPK_<4XJw7QApH#Oe$|~*B z{zi<>^u!t1YyC25A};2_n9N?Ge@_?n0!o9->%k#1h5cco6e+H$r@s5Us??buhgVAw zv9L3JwwRst6Eqrv8yRE0N*@5*xl3~RmDzs1t_L($lar*TYXjZ&qgo+CO>&a1F8r$q z5srCo2CH)wQj z(FCFoPzxd8v=uURub0q0Qoqr|@(58eL{EFrI`8ubcii%wgZ>+A6KgzyFjLh1jMYd6 z^1yd^rI>frMC&|t#+@0BdkQ7ZhrnbPu73@0j;1bfcLL|c3W89$`GjK_ST1=TRs%`p4H8xbAwHT61!e;ix<&z(OQV_ls1sh~?%+&7# zv<&n&^En~hr+sc}uQSpM`ByYZy!?_hy~RNnwoR+a!3&=#`A*=QerMN%tA6HPAJ`ov zXuhRB{hWO|_pv3M3c}|n0uoTTfl6Kq6ZFh*ZLC$YdQ3*MO*_&-66I^k*O}ftl5eYC z!fIo^w9|WtSu8k4_Mdv)uqLj5@rs-&8xm)&BFXzqn$~^3<41}ZLOss(2@o=^gfOHa z?Dy6D99%dav@8v}ONW(S8HMhF`m%X&lAkQNnb71bAACMr)D@jb{ zU)?`U7n^HYgj0SwW`0}d6)_UpS%LHEvQRQ`yoJ+Qmyj=UxGg>&>s88*y`-F!_$16s z#z_TyeeoRS?Y)2Z8|+nJ(R9qOz&t#={RR+K4PAj^Pd5jzup85anHc%!43vSqKBTVi zii&(rbW6#T7_ti&Zla-kzs(u)F8;h%4T7lf*>FWM|MXVOZytxPyhkcsmk4Mr<1w}N z($|xsJ8xj2^O^Efb6IGi532LvxBalChQqtkFE-td$?Y4hut7*=lSg`lM|8T*_a9Dbir!J73*yQPC9$foQ;eq1 zF!68(bn$RzbbbWWIBg{G?3K%_`poebSXxA#pmZ8ZF?J?r7YQ z()2nyTz)u@`-i2mI7SW*!LQ4Og!6^TvgwDY4pt+s*InIQ5t5vo%0x52KMcIAP51I8 z-E;YLhJ!X1O}?R)Q8IIu|KijsuJofj<%#4Yoh_NDt6%k9>zoueuI&}K>qTd58XDs; zc(i4=j&t_Y>+Q~79Y@CfYe@|K$=%%xMWO<*jxuYQ-BREPVWjzrW@uhq8IVgWB;%6y zcv3g^m?`Hf%0;biEbJ;02bIh|r`<%gfG38>!;i4h6Jr~0403621bEaK6yE4s3Z0S8 z0?U|#sDQVx0j9P*hkKD?4_vU`Y<0FBdc4C);1fqv1ygnn97EEAu$iI#H-W2NsY zDP>ZC83TFx_=}@Qwt$;ai~004_j-Sg zZY7`G7IaYn6D3YzS`7~&4pluJE>S5^i`d`^X>kojjuWbK6nu0m{PMKqua@24NH`B< zJAumJv8@$*4!`JMNCmwnVRx)`o#eVfLm6wkW~X}F*h=rJ-+4)pdJaL@!R9$;9ULCQ zyZR2U*s&zqqF(PODT6(cgkm=$woUoO&NoiFqkB6W=5l!Q)YTSYb^djXs~_swU9OpG z5T{9_(RK(3Y@%jUt;-VB$w~}q`;MS%v4w;N4^vLSK!KT?vOwTulXDCsSyPU34mGa` z!Xx;1)R~PCQB>!)cQg#NC8p# z6^$MtLJ0RJwkMX>!NuVP3>(VdFZiW`gChVNlRfdGA(S66j$ARO>OnLU#8Dh!0TYHP zgP7d{n~2ZRx(|u=pp~P*b1-KO4v(u~zXzNFr-r!h*hVrawUF-(V^S2ygxLiwJ9hbY-4^a{SN;MdL4OBRGFA&sO)k*dFU)wW4_ zK2f;d%R0Q`K1VjhVHW}wpkww8FVsegHiK;XvlrT%_XTn!7vW3^+@{<`z6Qm&lE{pL)ra}% z6JA{vW8XJ#SzKrf%OrYpY%+Q*NqLX0!!}?71+U8XDNKJD$0BWy3OJ5TT}`|dtvn~i zcwerbm>J3NUVa-B5fM7MC~-nTL@3R>ilC}oAS!Cs`^mg?vcF^z)Wb`2{b-s(z!u)KiU z;JXcZ=TU_YI_TURS5l2Z?W5#F@TZ4bvZE6S_-0$&|=v ztuxBP=$#ky%0IB6Wp#B`8z7B&5`_NlUwy>OM=f^9Ichi19INXfB~>Lda5E9vyjf5H z63t;8i`3I3W$V!nG4VXg+iicISgDw$(NmDAR*+(h1mlCSbpt5|JH72UTpxRzwk2lr+qBL=2hc=eh zWQq#f>k4TLPpPpv;BHTfUta{*%oVTPxjn?cI$JvA<(lTOq){s`)TH8?+=5Cogi|wxm5*4a{?hb8a>BzNS zm16$$hq;K|_UG+5*D8X6?r!mUu?ZB=>jPD**Oc74->`Tk_0N5GqRygrGSx~DciAWy1rRS{bax2^>wH0#;XJ#ds1MQ z-+|WuNE+CB5flKtPCLk$RlMM2(=ktAj^iA=kaqk7>7s zc}eGWeQ2A7+A~B@k=t4;*GuziN4|F*NiqymVwJ?x{HX)^Nc_GO3GUhH;VQlxbB@!( ziDT)qw(d#)+LABkLt1Og69gth4<}pF<2mkhUVx~QWnf2Sa6@;x@ok_GIY&@S z=3>+~?tAm|?rp1p+Am#H&q&FgwIo=wagEn>3}3$7^8$yfm;Fm z?8CQ!{`1Z?)+d!r;V+RA_4CTz!x~?!74qu9&T2~g#l`md*x>@j*ke~qn~v`hFPEj9 zTFd(-t041RSJg#+9r>2y=dKxMZGtn?hv)>0hsiyGOtST!)?W zu(3Lkg%1ebF{ltwkim~fzAf0&tciFO$l*O&ee2;}tMD=n0=ELLuIIV9?^y)&)C2#9 z&G5lKNB`FT?yRh=oUG#O&Ni#S>QvRL04Z##77m`J zl5%G^W+s1z=lDDt>Xv2zmV~#1C?lhl+OmR9Toigjf|W+%XqlOB1s%YjybALTw^Q0< zW0%jWu;$yy^oQj=XMZ%*3w-$tX$xh(m3oOojximwaq^1rud;XeZD=j@VE+FAeE*f7ldVFJ?4I$eCxzTLgL=SoB21m zvu`}@ZV&1fUIXrVy$>RjC9@q8x0w_WJROrt@A-{w!Sz|Y|4k-f(c2X zn&$YF^BkU*_l(U9ReVMIw?M>XY^) zc>SHB!jl-tY8Fmo9i0s#7d$;s^Y{lVKn${{#*U|aHr{A+1cT-zxx^V?3Vs}>(US~)P#Gq8MVY{ZB8$aV7w2qcWqT(oF8mo zoaV!%%0nF3!fm6zDAz$q*T~?1g|csnv+ZI;vLCwteZb@*W07WM?Cg~EuLN0W=~vV;HY}%$4q%E^OeuzX90Lv6RtWf_=@ul4LMEB z12_MwYBjEXihIi1=Dg?XSV=_f@1a{_Sz^T4ID`3uh7R`ey<@SMg|7><_`D?)X#NRU zq|=u8|0&xhZD&$quitmF8D13WE&7@&>0C%OV^Qu1qa2W-f#C1&a|ou}!BHHsS((c} zlrCwvs>oNQcPvZNSKprP);%S!bZDBQdX$mig8vQU0Z)0*IA6y-R3daUF}=?9U$N|6 zL${dNu|%~N6OD-buQ=$q>VyO1*FLrOfaT>2LtFI1ys{8jO`mg&p)ir2=<=$t{FVIb zR|4S&^++Om35Q$+S;SG?*2U(3VaSCq?dN;T1v3S7QjiPq9KM!D|AhV;`@gr=)fujy zYdwg;>a++uKegDCq5!yd4usMZz=zq0{+wGIZ&kOs8~8UEcI2fZE#*-q)Xt7+0uEdf zo5(syE}I8!1D;zPqZW1+J_kd7=HWx?Ul#+;K?BF%s4ZFNV=0=_N+lcB{T0R$K^SKM z;|)`6(crleu8szb{wNC{i7viYT4bR6P$^w9BAi$5p)9}J-ZD>pmq4b&;Qx{Q=D)PJ zV)vFWQbZx3w*{&s!rv1NN2~Pq$+MU?Jdzcictjw2kjmD)$(55%F|SZ!G~M3PgARsZ zG|RV|e^X70T2GkCuGDLfvo=rY3RrHNY?lsU`M``X9*wmP#U5d{H-JCnkHnycr>gRy z0>_FP0=aIdt^Si2kggNZr`wuL-zp*&i4zFy%*C7mW2mZO>lH-j=80zae_E89=bAe9+DODmH0q;ExX?mp2HxC82 zs}rY22|E+gwM6*UW?fomi}R~s!qhalm|7SIpE8UZjAI0k>0>*qY=Si|d+dIsW1G%p zJDgQ33qjDu=U$)(uR@tnca;{{M-&(N5x*o=$*aMd3+-JMtavrYx8{oC-rKpzrZZ>T zh5zxBN2s4C!4Ml(cAe{c>RMzP-+C6In+-P$pcEJH2$dpA3LAJzYKga9!4@1BK<5|7 zQ!l3fqrzEg0I^>mwRpde(qBn%v2q|D_6>^&3UJN8CCy7?TQ3h`SRk^?xtGc;|6Stw z{oTc)`OF?ZIT<5)`i5DXk7u)>YjLo$(iy(TD9uvzXf4d|XYZ(Dm6UwanCgzo+}q)G zzMi>XfC})8fN!`Ib{0)JMy@?~s}RZ=k{M?4e`C3(qT1GcQKn=6@g96}D69z#&+~)7 zzu#nU2~s@rC_m_DCP7waS){E;+^ys@?_hZ_a=jzZSsbc^m5FIAX7-}38}Vh4@7YX8 zMTE;2o<)=n9|3cwnQtrU9H_#k;4s7UwPezAUC|N&o~dCtcK?GIaW^VMxy%^P6V%S_&x4qyzY3X{Cfxw z@a#ET@ILi-V)kd-NlN16Exqzbjo%?@ow+|azawy0be{)0+dXypy@0tZDDJ)`O*=D0 z#*hi9t-{Eqcv1#Zm27=q>nBbDSTE9J1Eu@vqdjzKmKO2RRO8u-?)vPX>zvOqX;~?j z6JNd<{dXe5Y1Pm7FyJ}L<^q)^`-&-8vbvz`Bf_Ah@f=e}); zUiM9al1G=A=(aEGJxBdh1IeNczWo#x2(ySS&zt0Z6-JW+8*3>&Gw<<#Pza17uOwNzcETC}k{@8>cI))gFqugf(?+3J5YYqu`#BU@Y7Z4G-%hN?!a0w$XJ@HM* z?K{9NdG?(`g-sPV?3 zU%v-6_w8MMvV&EP+KTS9Ad>DF=-<#Zxy=9TW9xPPW_&>+DjMZt1eMQ9nUbvTqaUfr z4lh|@&6jC{Uh@l(Y0qcAZ$Y@=R{!Jr{sOCGoN%|TBI6?k>%b2I3&qHIm(Yd*J1UU} zp_+wlOGD2!3Ck@^LIbDq3#H@W}b(gxze*Fyzj-d_{ntZ6b^_n;Y zvbKIm3fHRBy$QShuPqFc&Lrv89& zc|3pBigDf+11+$b7eQ6$hCT!3*U{u#VH%cR`V!IHxesM8$^MOrwzJ@?KExyr(n&*0 zb=h^RSQN{-?Z??a4j<9?>5PbwufqZ-KaxwkRrwamep*2huL9^fSXoX?zjR~k)52-8 zJm1Aea_Eilh@=TEUfm4(c1Fw1AS(MBBTEi}t(#PkHJn5Rl9&2} zG$g1Ubh>W-Rw!9`C8qZ-fuky2_)O_)AARrAX8SV$&k`R-u~%<(MHCcsCUC^O6x>=v z1n@usH+*Vap(xXL^&0qeGu;LdJ)FDmZ6W!EopPrz*@mK*g{E9T$LYj^%8OQ*6b*-L7(w0J0{t>W&z$+{Usi== zgnI}NFSk=BwMyG;a=%8|tYH_uZvejWMIggIB>OYjdf!J>1bkR!Ge6Bd(Z8sV4 zV}FO1qp%aWVCueochUHlBKba==RTAYk5T|yf8^e^*ESrNB{Z1x`xb@I^r|u&e%^j; z`*KSRF-H};W-$ZIJG)hxm`k0HK; zcC3A%{$E{EcamEr?nd50F#CMzqUr{;eTI<Lh(^_fkng8`;80q@fliS0dtOKqNFURM!l}fmr!YU ztVp64NpMf3Vb|LpeR2I`jTQI>-^D3BS=fm~+4`-bVsdT&Bu2iz?)j}mhAr$*iKZr1gf;TM<|WPz2kFqQ zo%6xis=*u3NU^^}Xb&#?*ykhWrMx=l{n!Qt3B=c%eyq?$={9ISM>Fda5ONQ7U#|RQ zD|5BT0xgHnwAUpB+Nr#~)jFH^%JHA2%KTDY>fs>2IacAKvR- zQ41Q6Trx_k0onVg+C^magG$EP%C0Mg-Cw9>F=XPR(zx z-6S5yJPxhDhWc8T1X}?}Agu^qt8*L1LNQ$*?ZkYV?|HuYoG}Aj11}luo|{I;e~Dlz zh1)!X<<$^!N^L>Z>4p9%6skrR7S-61+RiML83)& zHH5>rXr?&#B>d}!7SUOGR|ygI3IsCY?HdcZXPC384S*OY<~<_ihRD7SblJ0RYxV?) z5KWsZP}jSmj5=~bz%y@e?_;R32mF8vZr^K-n4zv>rqK5Pm`XuT-K2jIo}J!lvY6I1 zmh6dg+e+Vr#J7PxV%3l8k~Kor^cnT&cR2*7fLeBkf@-0-$=zSKqZw6p<6if*Z;HcJ z%X}psBmup}1z}D~O?9g1cn{I7G}SoERS6!VQa;1=ZH(rh2oYeQ3=YRYfJTAgz`1Z7 z=&NrW(segsA=9S3KP)ZO5npXpaGdfKw_62V;-yq;T62G}^2!nXVC6oKERt>w=sgME z>-tNf=N;{Z=vf{Iw#O@|ZJE!khy~crx5X8jk z`&cJIXU(^XR>nIl4!Z1~$2c=({n07=T>K_vj^91+g**Qv+Jq5bYVe%b*Jw=?j0jt6 zgc|8uU!$_EaBxTw8EMm%>5{aM@Vi@kTgA0HjwPc%??H*H$AeoGr6&6a&Cydh!*8hT z>yF`EgJ0CJI-*|!%9xkmDG9FPxh}}>^a|yf*Pa7&HnuO=a;4uZz?8EOkH5*qPov#@ z{Wn9H)ZVLsKibuC!gUWGqz;|4W$%hjoPw!^I@mQ9kXRyoARfS_RhFO-Pe>@xY}KZ( znKhR|*ea;A5oU%%Wci>DhaRGn0q>`xG?%OV3>eoMuT{LU^(h*8M?}-N+`8lD8oeTlr>Oj&caAa>K#A;VZI^Te~_+_ zvpP^0cM(vMEEcmQWavN9J+g7(@}=(-P3`U9z3{q>Vz2 zL8v*LlreZ;kK`@`;xfY;1_{sm4)hBac6H%WlX=|EK$_x8u()x5doiC$?>-)t0vmJ} z2<^{WKN2hfkD%dc~Zd67Y)7rb%d|aqVdC?+9O2@)S(`dXum~p`3u}Ie=EX5k7aw&NS(C11>{}}C1Ko49R_M)$ z&X09pD99Low1??H55z-@Nd=iCu%><^U~C_pY!84(M0+2`b00^C>&H?%uD$rX|H?^1 z{zfW&g=po#eNF0!VKme6?k`{t%9pu#>;;B9hR%$mI=o6=S>NK?LwJ57+WT4|xJyH2 zCj=$FK?y@tiky8Kiz&*#3h$R~W#ls8tL5L*U9##wq$fL^Xn^oBx`fff*W15ab=?(&x~e)$XIr#diKMgHigaBX!n z1^-L9>&*8(>xMye)?Q}WK~O(kIfMUR&Ym}AKh>N}3*J@$0#AKc-=<0v4fRTxs8Sx5&&E z*6yQ97l52D5G$#2TVA1`qw4K4$+M`}Q8BIf>ajjqO)hgYFrR8LzM_$L6_z;+&COx& zV9=TS5;C6w@v+0^=l#^CJI;MM&xx7eZod%W;>tgJ)decf3XXyDWUuI=Lh&l2eX4k* zV)OP&C6U799umHTA0G3(eRG%4whf0Njuw#ls6v|PNoVe{k4|16!m`&#(O;Uxr{tnM z@R;v1g_YvN3JUD5rZqCh$a`1#7JaEJs-?4bRv-ETt-t+>zCl> zu1s^6$dUY0&k)6XrQ{Le`l^g}Smv`4#1@n*9nI7q3Gwq=?K0}lem|#3X5_KCyHtp~ zb8X(=WpDTJ?7t+@;o}of$f`f%h4>Zd-oH_;gb$GS<#syAcp$11+8IOmA2@9YbxmpY zyPaW3(V*NUB0QLy=Hnfj%%922w`dYAB+5RLOihY1?xTXH;!$Wq$v8wzHLu#gnn6hl z)+s;g+`-DMDC+1*2~CZAsh3Oiw&4EXSzyrq4tNm|bTbJw;%OsUvRIEXf8NOOpU@b$ z2NqAwA&S?7>Ga@p2sw4P@#*GSYtqsz}RGZ=c56qtdblh!54-RoHeovSb=V9uk+d<#@ zO{4hm?^g#9S%|yNV;&B>9@LOo54s1$1^6*i$o?kenpk$M0Q8no+ShX2(unpau0Yv; zmgbU`z{2Y{7;JX>ovo+u&aQiDvFQH$Q#y&(9d*u; zby*PV1U!B#A@hv;61GLP1W`O?E29mM%DzqELsM2BC_r$QrP$MTaEE6n8(&ZZP9Ffa zG!3HO1hebAkBu@W2<a8F)$ zC7%1PqiBD>S>8YORS(Eb0zVa!!e}WlYqO*BcFRY0(R6^rvCufAwdm7yKGMJd_i+ zeZBP~);2}hnt)vl@x_7eA_4K^2JceC!b1bkfn0MV5$GERI73Xy-b`{O0fva{ z@f#~ApXUYQN<$$}X!mEe;gIdEt91UY$uakJ2$FZL@p!l_w@`Nr&Olf$?@q#W(v;o& z{gklwlrDtB_xs!5U#ZUhIgrwg|0v5qrZMn__|PX%8PuUe&+S%_7kIe!MHi+2#~HC< zdf!M1XR6MInNFquhK^!{r*;3=qj0u=^sQ-Yor#ynb~D0&>-S?r^Zq-*ES!e2>!PNY zQ-~~ZhB5%2BXBGKu)rnrL8LLi&u7gI`r2<-S5D-?&F@@;vEq4#{98Zo_<`n)OP@Fzf;sXzj z;lHEs+4>%G^#V@rmP{C(RYu)J{1}$c3JDL*w4*}oZaz!(H`-I>ie_ z_YfZhBxWvX$da8jBpA~CLuxY<-2>5L4+KEZeG?|u?1DfCgFR9d4f{7L+6 zvm=l^XCHOyQfB~mNe`NQuK6I|k$PH(q2D8}M_afGIdMr(T2uA0^S;5NLR6Me(~~2G zh}&w@O_jBd&m;Xw1FNY?s+&i*z~&K}7{u<+ldw>7%SW6p%Q-Gj+^-Ak5lehy{m@9N z=x7NB9((C$ls6T&=|6j|Wl$j#ObMGo#!n|GJ4WNI@o}YW&onx2K?^vTf}J&>?s>Ot zyH%h)GK$MHb@$k_GYK&(cTfDXuT|T4p?@drUcztybT5i)GS}-=7N4t1_=|cs`Edge z7AcE+#=xS!X%2P=U7?#=W!uN-a;N(oPiDQm{8#5hI8C=Zr;KbkW3fC8V4ds|!L9f= z@uFXOm5|uqG|XXC%(euhFQ^&y;=KmGy9?*Q!#GY*nUs<)|Qg5r)eGqcBJGeA_ z?MEJg?#O)`G2M7l%FgPHF|cb5t0Tued#2qPPeRg?*u!tN%5 zx+FL~=whCeK>hDe_4anp$@9ghKCbfNfLY6L3@r!maO5Fsm^X+dO4cd+abL=NUxzrS z^8KLnGlcrCB&$6pgRMr$?)x$7J5^QEsy7U2qqN}R=c#lP$L^a zze9GuE}denxS5IIj(z*(X9|&>&z?szs7hHpLo25o7Z`jtHy$n|*^3c7(fHRKJurXt zA=5C38w4eTXJD+*oz7vqvjURyuf5teZW=x-G)`EkbQZ`<;Mj*MIk!b&&Qsj_$)GFY zCvpZ<@7#%!ndoUP{y1mQvcJLIrG-T=n8}#U^g>%*FnsW7ZDxY%LnfM({y#T0`uX)v zQ1FBq%;9c&WeECOpu`i0Qy9_JbVv8kZhUEq0v!XKxUpHZ{ehg~LLB`K)B26#&8_Ck zOdh(7V9ZheQ^oi)zjd#mt_z!ubMevNS3YR1N-j^xMimNw1}=80K)}!bRCQcbag3 zuD@*UrgNfL^nYW-fcGJ)i3S>_r%(RGo_Ja=r`wu$xTGJtw9U)Oxi7KP(8>M&E6@d| zJ*eHGURMgl^)z~mYq77V`M2kq9qfNI{mn5@e6z;?4|jMlS2_c7u>sC+MkkxY&H5(` zvjU@z49O%f_tEQdf|nqL0WU`DcC+l$^-di)GfT6Y&&+FJ>y0_*F~+qVrTp79tYIcu zU1YBO>q&wbD@l9sK{&95QE8j8Z0oj*f7O2zh;~msZIqdyUEP-A7BcR+foqvHnu!|r z1U-}z^+dkL>Yi-L-%r~4@m$Nwt-KX`6AoG3nI-2*AQEj$FF1Dpz zKbydTk8CK_XT&y1G%Wmz}!*fc3ja;NO znE{k^M}|AKXqq16m#(OLXm!UJ>uDy0Wrrq*>LgWG4rMa8-I!4UcN{G9=%jz>Dj^{s z#d7^v_+c3O`Sa=?zfr872=o!Uv?Rt8%|spZkQ*cX6h&1P1sC(oVf(h;iY*Y|@;< z3qygK&SSaa>bb~fyS zq62R*3^4e*c+|F1DRLy^ZM4^mM-;rn_x3V3SY2H*dnQvOn-do+WfsBN7UQp|&pAvIC;4TQSp8P}-!3-z_O6tFhkr-m z3Tm;LZD}EUReTYQ#h?~4qwbNMiW%6@BOiQW4@No4RI_+>{Ji1U6U8U=l57##EfLV6 zrRhjXUj8<>{-J6#-jpVTo19Yd{ofbMJTKI}5C|uUbU$D`dgnTTqnKE zs*lV~*D}`p-E<4U^U}Tsz;{DDO0&-CvCO!9hy|mS>PzbL%o&?zgnfqe-=1EMA@uAS z4E8$+%#zNp+|RQAY>+aU2yMzwGDv2Ov8G8ff~Ieut`p=iMsl=Xdi*KH2z&KJQkI)v zIKfyA5%uLyhTx4OIu)2sDFU*~e(DsGmMmYRze7Y!4GhBXUwoYrjrf&&2 zJ3WF*RT@vsUre?(t`4#_#<3Z?C39Y${B2to5E&Zm;Y##urs9nNt%Q~z@zOR9-S#|y z;SHQ>rxKONsL(ICT^%VJIjCMHr2R_dPoo<068n1n zg{8soKF|O{Wj9f+3OvK|{-ZU<%5uV@=-5#4xhKOxAxC5UQuCz}GS({q9ysTT1CAGd z?@OP1j7O3?_e4GVD%LJtpe$C37WJ*8)T zHA@Xw_|hxwC8hMOVi{juM|gkKT|I&7&XLz4%mmhx`tOudIQx~$Dg#wEm_M}6#4>)o z&XC?Hs5_tVrYZcj^X2K+fZ&ldZD|JDG~_faS3#=Br7JeI_y;A>emWf*Gpq~RQeiaVxT zvnE1qenvgNofd2BRj1;)3jz$h*K;C6EGT=zQ$9Yk4y0~!odD`*XtUoO z6}$TnW_8cA-|}g=NzLNm2;`PfxtYob#rp4=4sMQ9E!s6ZIcnd2 z2g(=G8z`SYnoqWH^mA8l72SKd9zDS*tA#~pW12vF2+Mk&*Q?hLssf2^ z(1llng`&?Fd2`}LxTPHK$8g%cK`G)d@bmixyaZTDeSvE?I*BUd!q)?q2qHSWe!F5C z!|MxH3r>fyWWH3_9)}VVn2}$j728V7pYmgQ&M}RodyA(8SB^jgHoI1f)9`C)amI{B zi@R9T&A{J;y#GVeS;sZ?K7M<2cO#8}ARjtK7$Bvjprq0*DJT-M(OrUsG>RaobO}1T z5s>cgZW!C{-@ebo-{-Y+Hcs5(JkzhRYihX>-woc7 z7FoNxTT32Mwj;jnyB<~JT(XR{uMUf0_E4!5{scT7NKs{iOVUk#L%fv8_@KRlu@hp- zp{KMOtNABijXW;fQq~W#2Z|SkYQ#nfG0M^#y+n8>dw})?TdS#)<1Z}XqYH?eVI|0 zqIarybnh@O#xx|sWWn(TG%&&4M_89bO6sbSPRfIM{cq31lmwfqA=vyy^BEM*>UMh^ z?FH9Bi+XIT(7~rAc{nMnnw}{OqHfS}@&E+?h6Q3SgAPG>lkA?m{YR*Cj-17B{PFwh zB~&B(0r<}e%;q>xxjv~R%ansTO&YMZ^$A#1SiCv4xl>;Y@%{Ad+}LR$lAucUUB;z! zD89ue!b)l5q@s0w66aK`k#7|jPE;RW)uU6;H38?=!VJG}0ofPeZC`J?ann;sor+*b z{))2Ajqb~r5e$Y+#{REa$hr%>cK)Mv=2ATaQ`m`NC=}KVt2qZ21O^0usXj=DLp(x> zLY8SLsmQ;63X5iT*VO8RU2b6)YbueP7snM)`AT0a3wSH>-@sNW$2VFjn94zIisSf? z>Z;1`>Vc9h8v6A0O_8!WFLf>7gCE4fWG3sG{n5Vz;?brYz#J^a9Fcx}9ikhV9HY_^ ze_t|gTO9eE`JYO(QR=A9vsd4Ms1E9X_$};LeUhp<1Ys@@zN;e>@xYUf(V3QIU98=? zR6#4qx~AP1{8j{fmF^`@7AO48I!gJ7j!P5z%xgIOL4|LO?nMB3hosih}6dNq3`F>S3f#aTDQGAnW4$5 zttmK&SMG`LF;YNBFd8d9HT7-GIW_195 zaAfDb!%y9h`NrJ%HrC#blTbQHIPBAM)DN;JITwwFSuKg0AY?5Vo77KmAGjn z)eX`wO`svbd5!H8e%8InLU++e+l&RLos@IoK6}`1@T)d3jP#7DbV#^S_IJhn9TQ3? z;q|q8aQuR``tlRbbTV>%TIXj3>=b#$jfwg9tA^`c19Pt9DhF#mlkL73(-W=S?|F7nD5mI-JXO{m1*ykxGNl_q!=AHpasO@|%GCOxKust-N^fb&~5EBl#QZ zn}6yu$#a~$e^V{vCD|#>6KC6j-X^C4tbxUKr3Q_9!RRc4%RUqr;=}V@B=8yP-*4|r z?jfXA2KG=zprzMPrn4j6=>FSdlDm-4j8VvguQ#~;OF|S%h1X5@QiRfW`>Rz-VMC1Q zwKyyEjvY=qc@FQP^aGFEFQjUh)$F4z*jXmYD<2S5MvO;}=A_KfQGA(jMj;T5*w=0! zRDS-Cvx<>9Sp(dt^j{8HbdY(LMIFd__v;~Y>$=PjF0w(rhJS%BcDtkDNmSH$EyDC1&SRgKDO*(mB{@q{)u3YEGtlRwZyBI&dY9_T(RmYacvo8u~n|W9U z{fh6$yddJ7S$O8b6AJuJ4p*@rBIwq&>~+$cOV1vd?_a)A@z%0!auNZ9{&Z#|q6{D2 z_7?4P2cfwWYzX5u2HoEUBMy}*OQiXAqF9PtvmcYm(bhFq=O+X_{Z$+ix@y}-h^kYZg$(~1P==12r5k-xUmOX= zi@fLSNza51yJEur$zZl_^nXXqZ!&~Hm{>SfHHrFy4K7q)e6&+t`xDYoexd#xTsY3b zf@ZNRU+$YQ;vd1aeQqS;oS9c#V>dRB3c^3FcVil}-;QfO!igW_!A^SJ#oP&^_utvn z8-G?vKb+e1SQ{z8pf!7FpzKF(R5VD)^H>VUrp^U0jnC8^V_@wEj`LoRFH(6F>NL}#G+meG_H{LbjrYK>JBuk&y;SGN{T%yyD z;yg(Q1^6~$bi&jPktvD|suKHdl%FaF_WzN+tWsoL>x+jZri6WWrl)A!&HuH9Scg>> zPVr4x%PJABuH1WMu8<;W!V_E@raCN6qCsAW<2c^&`xth50josXLtbOK`bKBQck$z? ziNb$&@w{pBX5LpHajd^apbGX7|MTzaZK9T*KvKzA{1%;?R7qOD+#qOXp48$g@)(DN z3WXbqv=2p`c@ZtwAjNd{AncB#ipJzB}E{0WVXi`LyH=`vd+4 zKIkyF1)H~%v{b~+ghkoJ*}9NyZAd##$wyIMS<)Q#&hX3G)hO)4^ncO>)g5p^hpExH zdLWbjf~JuizW+`&OnoBI2H7hslc-*X9(L#jv6b8O`vPw?s=g?h)oZ6tGOjckv_ACz zh%+_Prm?&g3$C!*vd&ZJIxGCOPs713&q)7xuAB7>{t2!Gnp)#al!_f>Bp!@?Q~V&q z(CWB@rFn)HnQiInPZdcXr*klBShm+9>+!i(ZwC)hFsveR58-dJQb1(AEMaxJvd_z$ z9D2tSKSk*hxr=_bC$b3R^*yIfAtX zU2z&iLMBbNwbXxXuHGHArz}kqRfxdjCNm5xd`)ip4s})pE}!PZoLm8?{i~Y2x>|D7 z9oz~&dBW|VIXjKpEu@m5G_^i+n zqW*#w{UM6`qY#tpRVKHp?1vCl%0Qy~M?bOCXJUIW*wZOk{RGgdWojRNoAIwdkvQ~I z6nAGGGpEFPnayU_yPu%K7u%wtG6P0zQ$-u0s%_g;lmj2_y?CcQ35tE%gI+V}VZ@Vh ziiWPg!`zO6=jAOrd&&@~Oz2MjbDuLCgfZqM_z+O1LbQLz^qRhr_!cgwmRiRuvWL-L z!78Y?BECod=vTB(LJ8%2%Mp-k!aWYnhiS}KS(v5kSm`Eiz`w^07*qk=4;xsH0w`-%YOR}QIEOe-K0p{!>Yheh-?M+b{@ z4%TZ#-+`N^8$V^ZcLVZUFg^W_3-A(1_~(O<$PzxN5-NU_V7fmtJQlBu_S2=&Ol${3Rp~1)K*k1<(IZqNiqfyeE5x?6cbpSy|w{?7bQfx!bRou>WI$ z_O#-9v&zm;sojx(JkIdq`G?x1;W1pRV())efW~XY1M0j2gx1rMO+zKF&3e=OVRv4y z$v*pqTjL86?}Pms+yoTG=B5|YDI^`v3PMq)jSa-;b5yWPNFPn1mOWP=M{MJDvbC5L zk}vA--=AUFY`+biTqC5mKsWgEfo>)eexL7;*kr-Rks=$17Y!e65(6$tp=Hb{`11^z zpN*lKYg%7!QK);uu4!BIFs?g`<4)Y=aw!?u>pz0M!ZhTQeTWPX;fL((xJB6=Fg&O$ zc&B}wh?a?v!9;+un_$jiu#!3B+$(O{errbr=@s;RxNmxKb$N)yf<(O0h;692>-&$l z3E}um#c|M5ZMoI}mV+%eh3?a|I?I(J@ZU|oC?iJy2WU8O-;0%dyH#S-S%IZFtHKG> z3$>h!_o9W-o~o#fcn?d#ue*ep69G`gJz(#91 zX;!PHj{wCJNV{R^+p-1c)(<<%a4?Sq6hIS^MU8G?>o%yeWj$%vh=e+aFj)A~&Z_DvwunAnP6!j`jRyym= z31L#r&#Q3q);|FSjmfv!{ebQa(FJhb|2WEVJf|gpuLo%kx8e&b;Gn-wwrL}gl*2uTB2%7ND1G7e_e=k*q2G&xOlO+*25^fsrmstM$OUDuoNO94RLJU=G%AmWvX3fSq zU9i;RLeT9sW;f-8GoZ(!tt}$!JDI6&#w4!f#COO5}XUkCjS)qCZd! z|DAmc2FN(U0IFimjYCGeZ#9|LyydL7BT4sM3-C=4tn1H?5aKBi(g4Kvy*Mv#_f z>diwO+`!BxYrru`VIAf??je+x3NxoLqccYNqc7HO$wIK2DA)tRNQMAQ6W;8Yn=qX$ zi_d;;qqhTMR9vlhuilK*ftTUnVOCL)TK4UIW>_m52kSkzY^Nx+j*ETF>_Hq0HSyp*hfjBbjgza?-pM=yD-7E^b5!Ck400byOZ|PR{b@D9-=Q~8^F{S zNCwoua1&SSY?8ru`!pZY3x4?!AA_Vr>O)YyRdJr*%@G;v%YF1?)lw%hOSgn&1Yvg3 z{?M@hi5EdE+zZ!5xaAhJ@p%+xg5fOGa8ce`B!0@vH8OL@V4zWQXU81R=uDiPrPTCh zVEI-U)o^{2fB3L*Kx?@0DQWt2MOE>OMh1wpNB{H*?W57l58`#NFG1rCQa&C`iPiyW zN^k4RsLXE{UG~(JvtMp1Qike|7-ww!w=VvtMbPIA-GW{L#mYE=nP{yk7Uk$pZ|ajC zE#h+fKoN7FbZh>N2kD4DI&rSG?DpbEfd3#(UD0ncxz+2 zZ=nbih@J`xMp(F% zo-$&r#6bDUk;4+NnO`aWgukYI1rD;!FtT)J%o)lY6F9`pa&CR;LhnfOF=DHx%r6(iGqZ{#XDIs zd@sMq-P^X@us77iYanO?u*G*&S61?*g=X_X-b?QHGm4pFMk_h%RPBc##5C>LSav;_ zE3Q7mLEggu6TMGwS_0vZ`cJaaL>Ot`%(euK&X#_Fh2S0V*?~fup>^PodS3J8UW{RsGa4a$LO9mW3irA@5*)pS)MR87{{@hJ;h07WwIxPdoPgDj%TRe1 zlO-Ikf+;)fu)2xb7I(ignIxSyak1zrah##x)K!{aA(#lx0)%#2;C+;sDIYB2^SzMg zQs);?(@*a-=~!+sduQK4ZVznk&8b*$+-SQ-t)VJOZf*+w>u!#Kc_Jd=4W&e^V1@wH z*&tO}Xu>~b_E)Ni;GtW!aRoB{$LYb9ipCh9PVmUk7$2x-iYMlen&!f8c*oDAckLs z*=7FwT&cMyTmSGwgnQM@C-D_*fSu?>s6T7uP2x8~_dwFOAIS@p$DcQ6ey(B^lSY!go4w(&&hrF{zs?PzT1AJ=KPI)Hm&+|HEFbOle9qwnjl3^E3@N2 zzU|)rv>)us-GF8b*8NI$`6z6|$#e?-RIhgE{^u^*AFo_(whc>ukg5j?BZ27eYpSQ? zq2w=eN4bsyyv;)7Oce+mTFpfvDVnvU{{MNS^7pPaYe(DQDfe*$_0$r%hdjQ?W%h72 zRYj@mUR%wA@ut^XW#4Q0-S1fTm=ahk50_urO0iuC|CFd4vS9deXaYRh!7t3zY8OQ$ zncb-|>Ees+R%hlrWv&Y$I9a*JICE+f_yafh&X3YC=s%NC#|V7FjwkGEk>~0zN6VSG z4|~atWJ%s9`^<~PknUm6-QT;Q5b{obx8u(1bOV)kk=U6@osbR8$Sgcb!cI}=!7A+F z%5zv2?X%L)T}2inRQ{Ka`Vl0AklzqF@IfTnlG0fsl{p9Wn^!q5D+A=jOfX_;53|He zX%|ZH>2(61L{t7<$58oSVIk92=IQ2_J zY|SV5m#5{A&m*P8%^X*VJ-g$v%m^@I;942WRrv;U*%HAKUSe~ZFaN=vbk z7sddhui3ODsh>f~zA_V>NsV--@(?aw+^xx!J5?S8-!V(UdEmF{jehl;lH3=uHaV$9 zC2t4lg9@I!u@}R6M=zPyHWsO9UjC%}6k3k?u>=76`!1rU*yg}%QFrbwz8nPcWI(N= zPLHr=kdml|^sBA}h7a@p1o7<_Mj0Km&B8VGRR4y9q_0;++`H%_?vwpC?PdiVG4qxG z)#uw#Fhm8?WEZjjjr`Z6*%|XrDdEh5ErWBWgfzP`hx@JY8THebtzf|dND?mqYBC~C z2z8KK|K+ekx17D^?8vRI$S76>08L59s~v*2qXFe-+RJ`2sqaTT=l2OHxr`;xYn8jV zyzlZlr|}nEo1HKDqVjW(oX}Gw2`qHH-tv!d$K{#4)hCtO$E%6lKPMO%WZ~RHEcV}G z*?&ttw}_jYM^x+Uc4|HdPob)43StUk=jGlJp7Vqu5JN1_xC0xy0QajvK_$i*#6+f& zav54EOHw6%wAOI*#$|l+ndxf>ub@jM1bjb-Qoj!5ZSw2>C*n*psb1D9QF5bLs_}r| zg)8~%0LRoII7Rxgf;qwA9Y?*oQztb8or5gcEmK<5r>tDUf>?YYIR(we=#RIyh1iZ< z;%IBCAN&8r+4Ah&yhIhHbYWMrO0T1QfBQY=qR9$fiB^NqaI(5JL=5g1poo=o=>l3r zuwqP1O*}|{UtCv1TmCnVFHNyQ@RQS*zq0hEJHp6=&d@v$b3^oiHE_BijMuY^#D_CN ztUI|^#e96EgM30O811UN5GP<75q#sO2-a~+>d>#!L{f{gC};4WwnUw>?IBn}z5WgrO5-=%v2>r zTa2H%GSA&*-h7~W9*)E4xOqP`?fasp2|%4pkqrY)(&_LY%dx}w&c6x?|AUNroG8o& zOS@;%rn2I-AQAl-uziii+uWdTZ)bM!1+v%XoesD+rHKs!J}kgOY9QLqZH z;?N~7Je$W_EJh>ic+AAgiEj4oq?*e0GMst^IGqYEtJvM|oXKG#zWcxWk%SK)>$=95 zgyWg7c0NcZ{A5eTwgvO4y*=AGGx5A612jLX#~mS>DcbeFNg(!+g9(#=kuUJzDL%zY z>Vr41|F>a=XBv1!74oVRx;&8V#Tl+4y(UfVeVmLRFfPMW1|_Ra&xGr_J8F2?st5Xdi8C#Egj9w_l^o$F zbmNHl+>sMEUnU4QAPN})G$%;{&Cq=cE0thR-D(E{yH+1a_ZaJHhD#ZN&gJOV4$DQYP{bRT0zQ!JDcsFq*n>!YQDiuLwK2I!n!+$`K*?jQH>27+-1P z=V`Iv)Dek(^TYmX@Jnh6Or0S>I+BX0>CnTnkL^Si1&pqo=H9Sbq~CL<^CF7TO|c~_ z<=B0+=i@o?<00$r3 z>YKJ^osV|BKgwk&9Uc_K&tKhuZkTBv?9&$Z7vMPE+Dhr6?7?&-_i)U>RwM)xgMBtp z8KJ3TE}w?;9=;FATeVu{>m9YD>H2QlV(dxZ=B6KC?>2q{Lp;SG=B}U$fv4wKG3x6! z_Nw+n)D_mKbOo!E+d>~;?|V1E3dJ-4-oX0M;MM^E#9Ymax8Rq3lbw+pnCM>s{=!xe zP+Tl<7Mu+SjElZMg|hOiFg2Y58I-nQ@$uAf{uLE8cz$7Vaj}{9vR8o+&oAI&w|lvY zJ)q+Jk-amUs6puURz*IP^Y93d8ulQExjEI&}jW0i+dI=WHfbKz}neHFbK0>l+YjVD!qT(N5viadiLp zhp|Qs$DT&vU$AfnRm|4Y)6>_xv|=+pVkzdzw!i3vClTLfDj!;6+ll0L>+TQdgN?B1 zNt8gz+00mp{^l6=vr|($b-kQlX~XdMGNK+b-eh+oM?(`Sl_#SiW<1Gdl((n>C4fDU zJRJc!Mfn7x$K5p)KP(tivhPFiGhXIEm=nh}N?eGiK9(Y{q%rz>dg%-+J5u^3kY|L- zX&=tW;TK93R?Ym73-0FX<&msNb}kN{U1)v5!e6?|UDzMQpJ34T5cEmumI$==?d*_H zzw??U()!a#7F_GZ`N^tMvC$-p9GIXT;DvYWufX({)y1v;*bWKytLp2YlX%L1C#)sY z&v4;~|NH#6WAj%^Q&_5p=@LDs$DhZj)OVOMmj?uTUIDLTJ$x^MiQHfe*^r!oX(_iw zSyLzOmwa}6FhBHO>eyK7>E70`)s-Lir}6!T3R5MP_1$_D)*NBmB`FoGAsI*#HT-UuR`wh`N$0OPf>+q5Ir=ro9Xty%4_;ha0?3dtI=9kT z0#eykdrw+?-@2pZA8KxF(IPc3MLy5PTJvA8>kF4-h9?*S@<#u1Us0f+;qq#;v?0)W zar|rC+R_NmhuHTQ&?V$j>W$lj|Dc?~^8k4={Pv^}Qo}RDY{_~i+|ep7?4Rl$?_%F- zGdMxR!tNjT_@gkV4yt?$`VH*IEBk3K*zZqNKK3tF53pcRR>F6FW8-|8D~+`y-eONT zr;-gUcd(fgiYRyV(Gc{SZQ(_w+HUxK6i6L_h&gk~c>c2_9I5=C`+;i}h>8OR#X#hG zpUce+x;Y>i+YFlhu+?_uU(N}t`>d_%i8L;t8 z3Bu(7F=pC?4mgQkDvF?6{zFSC2RH4XxH2Gu_WW>W^(2dEGYv_PU#TA+CE$c7^96D{ z!x=xgd@f&o0@jcBXHuKyVF>xIt9-vMb$InpmiFpazz1VQzoN0tR)Pzbpy=CsXp&MAM7rb&#-<7z4}b_4%RT!8Pm`6r2wu(G5@A zZ6^2eOTLw2JjwgL-vq|Hhj@rLKiMob*od&jhb11HA2xx7{)M-1<^m zWc!u-MHz-Od)g=j6AEmJ=H(A>U@o(RFc&;IDa-?O)k=xD58{DNw~g;@pVlD{z6GxY z`_=8ySPA0yHM*hW#i1uAPrviZN)jT;>Hk=_tm|Dj%Zr(qNiUS1A7WtA#BdHQ`aWgE~n)~h5JyCKRLN+Jc|%`Xw#g3`;oh} zMrJI*Vk51uGgTu_p?xAw`^QIdCJ*OYe( zEY0Roo&-}S&7kXO?u_%V-sBdOVh1@#+x`(;fW|9$N;&J3y|ZUH(?^KyavSd|7;0{) z2o5y-1Af!DASvhdAhu8EXo}Sy6BV%&^RD#7(_%g!4*Vf>du>4Q1_L}xs!Z%pC$IT%vO<>mlrSmavmAlrL}2{I zJ!!8{z)yHtLDvnsy5^Yl`>!hWGj1Aoad+<$7dt$(`sAC^TJ;d?H09M8b~m#TV&lFQ+Hp(*J4K#D^}^iZ4`bFSzT#&qwoTMA~W zd0;?RG%vlmLSJv_%4$oQgkrRb)>iB1ONO59XU%TTYYvdy%&er}UtjdPXv9HCz_ks{f|u_qzh2-{_=oTxm#62|Yr)s3I-x#9IcM9a9t)7P)CRxUQ`jLfOHh|xFh?u%L`Uwm0_P)8?7<=tk zIgF^6%v>>PywiX(Rius&yyBu)i@vC=UN5V_&i7p*cX!c52jKS9r75tU2JTK#N!r1z z%R%@jj_|gFvA4>Ga$|Mrfu<2NYs9zR{Q9cYK zVI5sAf1Tz{AS1hW;jsTkxeK~`4ZFJB@NfFz{pX@>&xh@A8un7-GI(|8nL4k}rB9Lt z>+B+9gXdB(3zYun6(ITy(AWpcM%Ie z0dq$cOD`>7ylC-%W9cDqg>H})BQl{7?`@v^^zUBx7dKCw_?-8Re_h6wvJN$B0k+Xy zVVRR*rnZ43u;zIPkMSqo{=k_1?|ARLGdLFOV9SF+n5bFjGfXu|Gm8rywj3`R*h-6zkPK z6>O?5e%qz$Q*Pe!r|j9L@ht+4va-Z_CC&1%9Ji}8eZMKXM@R-G%o@yoH!Vp?pcH(| zfVdv3h=bevaiHTxl|tqF%1nF}j~1Gs9$smm0R+J>F2l*t|9)+tu%>N<{{=R|(c$nG zXLysIqBnFBwXhV$YB=F<#m?SU3ZeO=ZeHy(28&&V-D1cfrQ2rLh6D|1f-Lj#O&cz^ zjK_M7I0Y$qBd}Dw)oZI$5Jz!rFi%nGq( zSO%XkGUH6;4-`QWcMz$qC$~SKicZ257u(>09&EbxY#^*yz(y>Z%hrH3vL`Nwcln?H z(=r=(`zBzKk41kp9La!T5{F>^$F70+-|4Xx#o|*sKE`;;oa0a&xg^As57*dkL`iiJ z_)aLNQ$$vO*3c^+?hr5XEH3_UH?bQt7xy5<;PzAzvov>&O?_O*%Fw_`lFpRWeZ*NS zRha2T-wmc1u~@HwU)P8h(=%9~9z^Du@P{QL{}me5d2MINxW{K}!kBGWyjG5hT^ROm zcZzKf*edmMTcR<-5%a$w$IOK#9-jUf? zzAmme8!<-XAbx8h)%?;5RiT7`ffzwd$S6zWb}E_rgi%NP70EBDnvAvy8#XuP>5Jpt_Ib^xMGv$5s*CRw<0tZ?iRU^&F5n!3_dT z^I>tkPP8H7zvl(z1JhE?#Y8~%4fcRDIT)5Ug9Y|wI>y9%rCH?tg&4iq z8=GQ3pCc@UR4hXj*i)$*mM}=>9>g&$hw3ybqsAJNY(Suh3C#tR&Vf8XOI>7dG*=#B z$BgsXxc@5_aBB=hvsojU^ugA7OF$U@88<5tld(*HvL|NzMS~u{4ZU{Y{%6K zPOZNw6K>m$x2NOLImualv&0C>E0Mm*;B_TcJ>lc%yU`;X4JzfTUwh>!BcOrr-(J94 z(lHfGsFQKK0 zy34oAyc08H0D&)~z@H&l14`c?w_cWu-=;X(l{UKjCHsGc)4o`0>Q%2}uiBHL_y)z%KWk7TQTIh%{VVO=b<)Ucf#e z*1spF+jZCR1gSIm{x_C>5mR^f1OW$API~DrAZ{`}(^_ z*0_7@22u$juHx{&>r1_+(!Nt8k8vnJero*t-!-Qsm_=I+6;9rPHB-zTLe9Tbcikyk z)V1qfq}GkJ=vE=7H!ek0{CtD5`iX(AYUZ6guILbY{NU0I!@27w#{C<&=+ckQtnC>X0bl~GgLePQ*8+v@waIL z&=*$Cn5~2It)Eq0zVJqTA%0RGy!~%P^&83v@2HR42g>Nyajt6SHQz7Dild4O4EZXE zc;zO}8EfznaqBN&@v#HHh-4w7G03soTRbY3_s+jhII(^yj7G{hRIo-g@&^461YzD@ zVJTh>CY~Z9VEPF5!|xqGcYzRIWm<=CXXvl{1_^8v)bqgh`*64vHr+OTuK)SkY7bgy z5^9!3+51?o(gP2#N80RKAeG}seD`kUZ+m#m#`blRgj)d!I6J6FI=o4}79}|QI#qhq zdT}%;u}tIHay)(D6*H6YjiN3;t5v1@5^06BLeycBgz$)6jPBs9ut7NRhrD8N%5CZJ zoNgPleiK2OwUoud^eQ-L9)OC7PTfFeDqHq2R&MzXGxmf(@X z4;rJ*WW3JL*g|E9SfXK*ZK7BvBu-ngbu1fA>x9u{WE z-{PB@Op3GNdlWa)uNyE<{{sOq40gB!)=rb&V~#F;e-ZwAL&~D@Wow(2%gQ`Jm>O?v zZ*f&~Zv7DBnQ+aNFe^dAIZeo)ouC`bb=QNsEFz>A@`}KK^>F)Js3DzY`XKb`I*H%6 zhITE{YcyFA+7sl)#R#E!9(ucA{Q7BvJ-^QLXDsMF{Zov^D(nb6l38PMV@lR1nf)z_ z=Ruc!aVLnC>%T@#*O!@1DI|gyDA(6~+W(Funv&9&0KagNM`C zR>&po9kADgs)^ld&i6Zo9;FsUUy6Gq(T?!2+*DLGA?>P_Mz(-Hmf~u~k7*#d&tq@Y z^FkkDR`(&RY>B1Ml;P{|YyE_)OxI1pZ^Q{d`S#Dsv16j$D~BI?4L1^CAa@SPKo*M}T8!^;@R6RvcH)3jZ*Z1($U8A%64@OgO6 zAE0{>FR+6P?1M_ZKvi4N59;|*wewko@u;h%k! zL9$8Ye{xA@S3~o)wjq3<75c<&fOkNh@)AxGjbO zYsU-)A8G7grLyde9nEVQhoWla`)q#^TK{xQ^M7)qNcvgP|3F;OG6p5hSVubn;;s=v zx5%sFWbXI{S|R>g4R|iTUQaKbtiBo-t>!@+o6y=X5yRmZ_!yfkEE-qTHlw=qq}c3ee+f&x-LfT5+E<7w^E zg%?5p5#Jpr<$6J+6iLx^!ujbWKQ3Tb@=&rhC41=wq1eCZLuZ7k)5l}-A7`BGfNAbb zf5wnwf}wjbl#gTi`wv2zYqm-{;ohxTl5*t^N3=H9t!muKRsLNZI<^G#(5*Y(gnX6_ ztx`B_a!ZTucz^I9{)n6W_r%9g$q!}&4^pD3Z7!^)hhf&%z2oyb1kl2+7yPVHTcvJ} zN;*(+g$Cj4N#B`Pg5cZX4uo8BgvpoZX5BBdqu9UhOqdNqv5e9=2aL-VAcoC7AhId6 z(;SwoCW>SVbs&>XnBV$g^p36$^LUN(r?pJc8y7-rn-v?^;u1R*oKIa_ImFX8%(E=5 zr*{C^^i31Pnk0=bsH8C~|GHrJ-#_Bz zQ6p4ttH0kFZ9HMqXExx9+2FZ+quqm$_|&s<@UVa0)*x zo?s?HQq1qVFLQ84yHzCOS7~NGtWd5_95!F^31HL>ILk3d%Yh}`6xpc(VRjv;pJG$T;*-;`|g_#<8KuD3m1))w{)@-mV~Xi*zrs-swQ*d? z+dITVFl-xQHAsZV#1Yrhz>f=CdVV{s!9wk${%HEauhf=G_1_B9uJuCLSTxAEkI{6M zvyjxd7BV$3@Tl`Uy`e{_nzoBPqjYvBiBlrn|0ASk_;Buw3^TQ-4sI{bgbp^{o5Nma zB9llYiANtayWd%pol0ekxumya2{_)r*<6vqzD%S74K6k_LTb#(znGlG>>R!ikb}RJ zJO#~`7vqRR(cT2$u?mvn;umqHw7JGgB8M#7q9bU zZaL*2EQmKseiTy^@)nC=u&?^-w)@-y?o2;fy{>jlC=24SsOy&g<0FbjM@6gz8xnlS zu}Sz@2+HRYh{$rIv}b)XU8>^*1rJJ!xCb?j z=n0Og*Qp^}DJ8v>zLvRfz|#w=3)Ju;_?GDq6 z>O`>}!dYMc#=_p7A^e^IuOMTl2?%wnSZ89-#nrd~o1SV&)fXd|fTDKtHlZ4!S@0a) zY-=vH9_8jlC|%wvhu4VX0in=%6}5bR!Z4Vl64tr^cIRXO={3wCuvE)j;u>?8&ZE>O zsyv2sFW1@-tkG~|0}%1oKaOF)ei>s*>M(WY|J~D=5$cB&12m0mgZCg`p-!jVG}T~_ z{t3!+2F9_O_oAYFw2CWKLWgVpHlz%4Ia6&;{YWNshcT(ENp<5_KC8!4B8=v`xhCl) zA#=}Z^z3d9cOiA2p#(8m(>MGkU zI`fFfXPH=%(O?2fyoSg&qZu2kA+HSB>KAwApZdPvI(jl(v=-L=NNAw$UfQiE6R5d6 z##pC6r~o;9l0`f}M)puvi~&Pqxj}guM!oetZ}I+O6RI_8t^(F{4eYXjVzvR#No}vT z#+q1(Pwej(MafSSlkwC)MyT7{jmN)#u3F|BX0>+(j4lBa6YRyGz$@2ga8Nz_?($zR zX$(2Flk#A6W}EG}juDk3iE?Zdm4XHl(kTW!=Ed+;5|0}2{)JSwaMzv(raTR?BY*mi zgtjmA=`!a53bfB-0|8MM-I~)c(o-e1Qtp0cbSJz2^~rBiRzjbQnG#zUZ|-mDn@_-- zZ?Kmr6HL?xJ=f_Mo}&4g!z>TFh35)bl->v&JMts|@+-z+7hVEaRDc(<=N}M0b*R== zTYd*;;D&GFcxrcKsj8kNQjkz>d~{;U%;Pk2!<%)l^>f2v>WKrIIhLCrPKlgN{-xCoKDr@FJVFT(Bo9yCVM_wdHUC`#P9HpzhN5 zpiQ#9XjPi0Hwfl{TSJ9A{kd7^kyz`dviAzK8>9IbG&)sAFf)|Bf^54pOT>*I5A;nA zXK1SYel>4z%|7N2J99q+$tPGa8iFR!T;WTi`kxGP>R*okd2*HFn0`@n+MjY#qzk)^ z`bPi3lVTDH9z?^?s6lu4e_`Uek_bGeEmI2%rs*=mOSWF_=&a1goWH1_u3ymoA4zB7 z7e)KE@uj=Fmj>xBVF77rm6S$8Qd+`Yx&%SGqy5TfOH@t4lONl%0ks!&z}Ud#|H~7JhlC#UlO^>%P)?4S z*Gkv1yEhy^mrXn+FR664ja5Aa{QNub*b;7mm4memX%3mL!PrU{GhOB_!LKa-A%7`! zj7Az<{~)6}Q4fv%z*;xLdBK4Uq0|1SwO=M}UpQ{b_Gv{pqPSvFK&Va`|0K;H2&B0y z^msUZ{#=GqCb3miGnp_W^ti*jm|P1fBt&`v{~3mUe(769b4pgsxW$ z#w=%d>aFxA)clfc9$B#myo2agSsOP(qpO#x_N<=?b=V5e(qiEfOh({5RR?FLo>CiT z-+DhBU&J2rpbB-ov9Y922wmUe%Apx5AZ{0&51RzjRMvYuucwP*Z5a}w9L%E8n^A^B z%6-JDcoeff4N~n!d;yr*UN{otx5&Rh>qDUz3R3e9QfL?>p)AjKa@3nBlMn?ki6X|?kzGAZxIM9E_fsJO9gEw%AAS7ehceootb&hfjFBlA%oLy8OcDFaN1XxVH^wF$(YH=Cpy!Lg4P>(*u>*sQ=aO+*K_6H3E3k zHU??#9sA@TChBjQ9N%{6~6M}+grao$WpKYL{Zp%?w=$KpRe5kjm zp@*N%nWNH>SKz~i2?xJuDUrBOJw8D~<}i8f>;R}VP3v8%+o=dU-RIhcq^N0mgZ7OBX-*@I~eGF3>CON$Q zRA&8(avAHKXuPu)^=%OJ;c8~^u&V0trf~k=D-p<=ATvkg@qp?K+cCX9bqhoZ)`!QD zi-r3d^pgMZGYTIa{#J>CwroNLP-*m|`o;6AOZgJfZj0X$G-ST`o2n%#AWhfQ4Ut$) zTTQyJ1Hd&>29@{^Q18Gte8d?C&VCV1Fe7IU&1v?sW<-l1==V`-(w9zQAVmN`dv7No zHPw)>w>7uU#jGPyRpVMLDHtNS!AycY#ROpNVM=g*hEIgJQVAucU84M#O`tRm=m(q% zfFfVy!bfKFTe|dm4ttYb!CxeTI98Q*Wset=)h&XvZ7|*c>#q4DPVY@q-^q##dx*Aw zbd?R%<&Pd=F_h;R!X10PVCcWBg3d%Cae6)nML7Pese!H)7Jj-Kz~H)866SxIqY}*| zDw~HTYMhgyY}ok6ZVnq{bs|ZKUNhgWfWe71PbgYe18sX?Q_35C6fvojMjPA$#meHX zeFtRI_4tsRu<0G^}l3@d9HwRGBSNI0_4$WGv1#$F?&T(Sl zlYz=^+bU1koVb3GD@U}fEpk_OqC&i`AlLU3t-Gp1h6;NxUKP3VhPQksbk{MfH6mjw z;Z3@?dj_-}z{4o+cdJl2bT8_s|0sI!;J!1 zXC`yszQFeTu^j&Cf1x26A^LLapBE{{@eltUS9umKXFK=7Y5L{>dTMbAzq!?9$PG;o zBDLMix@b7|>axSk(9pU0hkJlk{PR^+fZ1Pw2%TrwIRk{eQ*s5~k{}3FiS>F{n}{kS z9S*ZZWkS8TkZ}H(mgS0`^;sA6_WCzqHwek0?Q#Ys^mXY*mwfxvRgNqBnP~D7eq`b; z-rvNa73OV-v_T=B^`q@fPg+*U>$KEu=YpvoU32?AxZfnI?lF&Kezf7=kI#!(Asc;8 zuA?T<1B~3(+_%)77_Sf0{qmE=4fskq@STwKs`i2yBrOh`f)m;*qq&>=(9UPOa78Sw ztjlVO(LzW(EbLnkj1&*=J?%q$t4dv71`SZN1zm%plerohN&K{PC9!!h?BuI)<6e*% z=8Es8utHgie)mGqp0U=CXErx%6Mu^cpLE7AJ1Ru0Q|q+x#t9QJW1MJJrto&Op&gN}v4w1Ct|Zo! z!7Hk#2M_4VffkjO3!FSa)u@?1H+>PLj)#RL83)DC)4ZJf7;nfhn?POz8LHe?-(|gA z?wJGb(WAupE%@u|*FK+m{;*r>?+IHqMC#$n7G7C-InyekHl9CW$KG+Ij|=y+w-!}oP;U`a`n|Qq+l4E*jdubexZQL1 zimkFAuY>%^O4NsGXyCiTuZ}LWGWTcnlL&N2vZkim13?5NmQa{8Kf>nwE!`fVV$Q+| zYs59Ik;W^a?8f{d_n2JJI@1_EAV0c?Ktqs)%0_fKSvu=W0Ttzf3Vv@*4$TAOQD z{b7tSH+J^{~pya=&Jk0(m(AG zVfqivX06BvROr^+b2*d;3D7vLxVTg~wg9nSrbc>hdkMo*gEj z_iTb|dK4p*PP>eOme70hXQp5Hz1tPM2_Z5Cl$qHCTY&QpqJHqyEGrK%fMO0_;<~_W zhvw~P3r&zROaS!T1)Sj1GW(G^I4b0L8DGS~-$QrTnhoQ_`S=Mhs1wO?gs?dSte+yG z&GWCl+fkRlRihF;CwfZx>=}F%hdu(SYd6&g8 zbg-@dBJ)o|v^0x4DMktNe|wU`_o&`OA#~P>sDFBd`I=#v1q zRfix$H60dO0J{{(8({OUqEGIkQrO$@1K$=TwDpJa8MN)b@B?t`$)jZ&eNo)_k31-p z2t>KU_vaAO3v|w&trcx;1R28MSeoKpE{yK%GYz-E#iMxO=aS1_htiR~rSwF81}%jm zB+4)3ey~iq&EL8=4$W4CynKnh%cf|e>i0bO^>H?-PqP=ty7@Qi_RlBwU^LR?f|VcH^$L0JLaMLUcol)S7dq!`Oukt!E!{)~zP*^d zmc9^%F&YeNz6#}RxB(*|)WGsFu&6J6Tw}0}$NvR`Ph8oNsAiVd9g7tm<9rM~*@aFd z0Zh4_x>y+#|6)(668aVB9I{(&S*8g3yO^gBAXZjikZU_TZ0P+b)qNpF#RvXnx!+ht zsT%Xb^x;a%3RqDs=Vcgl#OVW^sH!9&(962z6dEA&{GGEUW$xDBf~PiEhVby&3t}wn zF0d;<5`c39mIDx~GiD4>v4w0;f*QL9S$aa^hnDa%6P@qtmhd}6>xgt6E!{cCL&O4l zlD2eEIF!W8%YOisPa|3_XP9?8lby()eOLd5kY=7N4=9ZX0Q3Mz9vC@?SW%`l=t&Ch zMZBJO?;T^_Uivn$c0?@2qY0b-@%aAG{az*p2)XkdggVm$sqv=Soa6+vHPuGPVXaOD z4UIxGISJPBrAx`cy2P%pNb~1{_PCdmpS)-ARqKgT##3K-s(8``V0=FQ(BkRR7=*8b zD4YaR*Pz!w4Lq zcMi~h2QcWzV<*K8ijgqfbS<5cyHrLMkc>Z0X;GPV)Q>{}=sGhH1w;UC|6N@#_K!OK zZ3T}>l3(+r>>pOfF&uGWUDzDcxO)?zALwm{&D;%qZw$)MB#@dVRx2?6{9}YRct^U- z{G9#U^dbhO(sN*;_}2MPOgknBgE83h{ovW!63*j1p;W%T*cY98Ie0FikQTazJE)B- z60Z%onEdY0Q}~3Fy+qIobYr7u+Fj+vA&CPsmg2OC_@RyO-~A&L5THVT56>WjC(Csi zsy*W)j+kZQWCa?}KhunnPve%7Npnj?fZwc{0;Vj0*T6L-*ISt@NjeajRK)#ltKWmt z$rF zbVhxUT6M7;(r(4i>jJpQLTissP&LK=tLUzel8cvba`~PGwlM3o45e$0oyOFqsJ8~n zV8I-r=_`_UDiMbxe=#LbMeXIMG-N9DqG#O<>+3(!RI#h?aPR?8i3({rw0ZAk@gy2| z7&9ZML@?UeDFE+$atJi(%g}ne5b0uA0Nnzb`XN6~etTcU=&`Dl21rRJAA3{|!M>7x z)A^yLDe_*Ag5m9s(0$GQ!zrR>L1cV_Bhyav{F}VhVHulgdQgBq8Ewt*Lc<5K?eg2a@-4u0~-UBRMLp$#wU3sr;1@SS) z%@P`{FI}g~@P=ym&_aBaRGRqsd$_foWE4=Z3GxjyB z=yELbE2d&O76Ep|U9tH%@Bl`&oSyn=JnpSC{UDWLEEpPaLu!rkJ456JiU*e&T473s zbddmQ{ZL_ntvpcX(f7RsuyUgod$|W`t*hF~mtB(C&8KI$Q6@C9zjDp4(9I2-mq+)e zYIK+6$IYFNmpr8>v-^LvTs5Po53nXZBXjkq(_e``$;nm*0tey3XAn^PiB{u-7qmUI3hNz(}B%4FuxOm9HGcDT$HHPq7@LY5&G9U7lKj zv4l{AA4GbgJ;L|LWY1m5i}=6)YBTUkN5F0xH_|oH;hit;>YqdAuDLGDXy)_ZJwl+Q zx4P@oSKjS=z~0?N|4zG1A}UYCLdBT@<1(&DB0W1R=PRiF-`XyZ z3c?iF{UV8MYU1q@y-zKGx_`kiQ?P3yG1e)zRNQs@I$*CVX2$A{vbltABV>$L990VO z%RB}r15X6*&a6sjko&1?);%NvFclfE!2dI-Ox8_-RMbZ!r;Uhuc`eG9S-|8f_Sung z+!mUqwq`ihfPNA{2K{xrc3t>ngYE4NII62_IK9hlZ%@hakt4{O*TBiBL@Y?g%k~0d zaaPEK5)oN-M>rc{m{IazZIH=_;xP$yjUA0QVK6B$Gtvm~)s5?ACgC3SsHNDs{2<;{?fh<&pW*@mz0= zti6@yWuk2%XmVhaLR$ZWjvxZjodUGp6&Pa${F;|Kk9itd-;PF-IKY1<=n2rbd;PVh ztUa1$9?w!m=+A&8exV5<2oV%pAH`MjLq{#loCYx5MEIypU^bDI4fOZ^AaI=dCl6|* z0iiuL4>$5C5ffnRq77LdiOKYxp~GT&zSm38`T#Py5hG{$axme{kS_)aG)+V4PJ!CdigD zK2ZE*D`XbA{guC%U#WZ&CDW6OUU;i+rIhapJy>*Rg8lQPSpG3 z%rMR!WbxnBW%LMXxq|Ez2XH`}{&s=#UCA+{@%Am_-WQ^uaQNM9~yK`AEmEUX$q`gY~W|7a?g4T)}5|rBZQoHu1*z~akq+C0viuplhB1F{a@NBhB zxr7Y%yrerT?fQilVqz2E*aAup03Y2Ua7m~8C&h6x4N-@U>z>9>MsXST+N?Z{>YqM) z?3^s0B|y>oRSy6-@4v%NaByWIwB!a&kssNv;J`Tc8HDD7F~_-KH~K~PuduTpaax-! zLPZk+qVLfDEyLrzcw+|ZT&?ISxablw_&p75iDP)ZlkJ`!bY5|WFX8(f z(02;SLedSx(#2x(Skj7I*@u@BNE&n?KXBI@b8@QqIx(7PvbJe5HS*~q5|vuidV z)Lp<81c3yj7OC6TrrA8k!7t-!eHvRJFDpunPS?rX$3TD(nqcgM7%fEeu1vdVr`Uf_3 z|Hk=ZqUBUr1Teb?(m*b5Mt)=aryi1%C?c~H2sT2oKceXCehhyV;r*C2!L9~HwS>u07Q-H z#01m*S{zH@5iDHgxZRZEfqL?hgIc*lgU8}Ep_9;wL-ovdgU~Ld3tlrZ;hppAxyQ@z ztk^!uSP`-eel8>Bd8z~Vu?yPdgyaIRKKr`?q`GI(3oadD6-QnM?#`iif z$Jg)MIKUnxU6iR(1z}&Ml5&2Kecmv9Pdh<~?jAQA{S zk-U~FqGkVPg@^#4Z0NvYC?>9Wn)`ZOv&@J?DNsk9(LHhoC!`H&7+4 zxNom3tKDhW{Ql0W^-;QPVx*j^24xovJLMHm9N(>tAb2lrDq2%F{xS2#VzGasinXrR zXOGQfXxWTTnF{4$f6NY+$wPg353Id|B2D@s^wM=&v+&6mLunafKk*Ew)?mweIS!av z+J^k$%Aea`?Ooga1PC6W79ym6d~7{MvF3n1+GFu<`7f@vIM_z2=OdF8f> zmxReCP1FovymlQ$>v%R0oYjDWz`LLX2Gx7~XTK9_7QYlnyfV2ecZ?NAodq8yj33Fv z0UP+Dqm0-i+mv1h`DYTzu?wp>V)@wm8^cOA&u;J0FDzP~DeF)=J^kB+0W2dGdCf47 zuX@Vzox+HTwW|-9#-ujK@V34JCCR(v!yWHd+_*APIECNIj|pL4XYS%AIxx^ zFSY>C7XdD`ov+GkxsH7l9dq(Nt2tu!|)&4W}8JfKB-J3E`GG=$Cyp|Ccf$ zTz^>^3FW`c^4g>``ije!Z2w7ltvf<4d`^M6{47@6d6_Tg969xWUzACG_H4I@X72Yt z+rU6j!j=b84iJZTgNH3oQO_R8sG}DW`>8P%xq_ndUvt<|^OLx%{>nV2B-9YG(Z_Vd zoundihv-v?ZqfK}$k zTLE3|0OCorzjM2ggD{MH7%^Qy4z6Uk#9*j`Li3;!*5qvL0kFFXY}#)DgP84&ZV4yL zc5kd1Cs!JOu-eUD{c&*>{v`}^BOy2G%1d_S<2%Vx?O&PTXecrGTTs^mI9EOIS-avOH5|W{kQJ$p?nn#^!eNa(6!JG`a z&$9|agIJRuLa7^tOjl5ft$jPT-$h+s2~e5QS$i$VDfw`FQZA{8OSm{g6uSvFZ;j9Rv-4FWq3$^2Mcgb=~uKEKTdE50}NiKShevDp?6`oe!s8uGoohAN>%)_z*t8mw0?gClaJ z?C(Cza*ax9)g!XhRFhi=t3JdSi<)C-?E|v(KtG=7M$ad$P}P00Axa|mMD>aHrN2`H zmg)i=YWls~*5U35n>kV8wVXK*sQwV9G zeI8K=VFu7ubJ5`Y@{3iUq1wmiOXHQx%-Ak-pyvawgsHTf5IV%xDZ}@oSL@_*C*WGF z*B zV7r#j<`mRRGKGou^Pun6AAm+xYZ7epI7EnyjU?@W=#TVVL$@YdvB;mHGcJJ%fiYV*=P36J=@e(9>}3;y1pVv*WUl@lKZYsv@tnB9~Cp^?1x`k81e`Rpu`r z^tX$UHc6fGWcn@bhS5Tf$^J(J`?%Wm2M#E48e((?JsTK=haJnyY*L)m{2R%MYbaEH zzb(#_gC!`<7|Sr}5Xpj5djToATYG;Q53WA&ZmSNVuV`oYg(WeW{qvEHG-Tr6bFD$k zOb=Y@O~97Ohn<<&Lr4aYIt7F?>iVzO#=N%MgxvbMXdTu{KX$($X<-C$Kf#fRigLUs zJ4A|H*Xe(kxyE7cxOZr#k9i7RfKA|KPRi8duMH962-5(For?!qR3}R6rQrNcFt*a8 zYawG}P}p=`$j&n*fn-oTFtD=3~4x-3^aJk{BD!RHj~*} z!;D>`k$VqoRp{e}O$iX%0#;}NzIyyg-)}B*uE&EOs>HTSChYu`z*58*d7l0{iYO*c z`5xmc>=YGP_I^NjQJ7E63D4OOszJ`2OBb&eyd1={^62LB>fs3(;}9Wpjb7LjlWWBr zj^fp-EVP}vcb_p-v?vR%wZT#xaesKj&oOCl;Z<&QXkq_P*+RX}-pn#rdO*@RzmkCR4yTwg^O8QRWJx%Z-0GOs;FM7e@OfnSUtu&2EDkXB+Pqy529KV) zJ;O8`P_bUB%XnsHysq2tFaa|hd2#e_P6E%z^9k3zK^~CG?u5Pi72_rYE3>lBOwjMu z>HH)Am55nIr7axWLhJ=vTQulse+$|M=(N_IZmjmW<#9eu@x1F=jBU2>N%tyxL6FDZ z*lvoLXt4rh-vcUHRK)gAzTm$4QR_?*d=-E{X*wkwjicw3BH3sOJf;Ak$guW98}&1& zTI}G*Sy0$a)Z&+hI`1u*>QHVRx^cxt&c6=*epYh=wb_ooa{(<5#_+ey`mA22=6xO4rbYd{!<&Q$z6 zN-4Bkmx)vv>M!k{$YBQ!GHcSm0>x1kWnG|i?ihi!d9+OtVqN*UuJl5$xW?r=lUd*T zJB2dRcdA2Blm&Fi*j%hQOt|0k^J^PC327sojtYr+h^H>V8h>edujdv$$4(?`EMiHt zbCCaZ4d?Ig>7SHn~W>e~8lMdQEG5qWcOI)#|N-Sewg8hN88Iv+waT-eGN>{e1iKZ`-(0 zw?~xKI?8f%bD1nB?XaqHt-I1-E(w@k0MbaRM)H(jy7dZ@T2ZGd z-r;eT?4GrWDVz~pkDA#4?-5tE_W*WJMKvZVje%?51$~9o=mhK~e`AU5saMP01l}R= z@8K@rtU;|Kmj1W?8)ck&?(EN;PMEdGzDxXWZU!!2kb8TP9)(LG)H(z)@fGM!Jy=%I zKaY#ZaY+VMuF*eKMMpDmWlIy%Szj}P=O8~)5EXIZ38_|Ks#l}8kZA1~7qMI8K9_8a z*C9VIw)#;b?Z87P*>6tNP{^IwLHMAn*a?MlIaS*j#_(Tg=ls^s@?wq~S}4et8UFfKmzzG!J=KgE4=%7KCVV^>)V=Y67qp zn+Sd9ct?y1&Pp;puGlN{#psZSZcG3T!25k}vL!sh3%lUwwi9M9epbHQvH-#iAt zY$Kj%pb-gW26T-K)R<^CQ$J~~Pm^qLl^qp((}u3v;~Ai{&#{ob*uaAqU~kAFMxz*W zhN}!kP`rZKQnrrvwA`&_B%)Oj6VE-CLRE!+41eNKU6w5>+Z0Ic(>}^B_5yvHC`Sah z-ZdZCxB?e}89+LpWl|vt(eCUTN(^^7S)X|yTiY7eig8^qZa2_)3$fn68$>T@^j2=6 z-qnJuP6cMGQ~orGPW35MALSDTp5$U_X4;4CiY zAB~LzGx(DyjWgSJt7SF;S!W{1)8W0L43gf?EzH=?@5*JVQM&A}`ui2W+tShsuK_ja z*sw#kmoKfYJEpG4rY#}&J)32{B+kd|@W&wwiw zv=Ea7`-2(tgVFM{V6gjR6PQKY67VxElr{uPHASk*$@j2@J-Wh|Z=itP4cz)tIi(0pb|-!7Nii?E{%N)$ z54W0T;XzH;z#~*@X^0%=gYtF9p7)A`r5LW#=<~ZGalzfltHjENUl02S*ENTb8h1p^ zg@}`fhB&#Vj1ry>p}m^&44L`+t*l#>yg<ww~i_NO$89G%G$QJ)ZzT(Dk8YzsB zNM!u&`XQyrliy22*j{qAJI|Qw=?3y`8`5nFn7sv_Tm$^MBr4t-TYI$YURwHD`!c5) zpg)FnyN(%gDWkQq6+0(2Hw$QaAqR?0-bo3oS37~UDDC2az!MdTk1tbuA^Al@BR@lz z^8gik4>|oA+IoM{nE=cI6|{67-%YV&WuAz9ebzmmF}(4l8kEs8{>PtS zs|f=D<8^3uWV(m`Qwb2K81e5%yb7usUX6htzj5Vl4hS8s&jDA3ygLsIXrmD{UUR38+Opnpp`-1k{;Ci@Bb zT!%(JR+n*%r|~I6!TI2h$QG)9hp4}y!Z$5YDDp=? zu#5%~Aq^V+NRq+aeESd@ly2?p?ATZ`$9G_fj`zeV(><`5yjzd|eD?#C)VB?OcYSr( z(k_Go$Zy$1`*LF< zMvS%kFg7D)WuoY=H0go^+`oD7%T;=eXl+N!KcT^DQ0YGlwORp&N0yv z&2ljN5{&)i-x9;2JAiVG8I(5*%%05b)A1d5`d;PnxZar zK00{M=ePL=?y&2q{-6Lon0Ttqi5HEr9@LA)Xm&Q0YJ?5&DOIz&Nh}`sM@wt++|aC z`vX{>yF$}0di_f$(Rw|}Prx54j$9Y{G`b5%q7Dka0WH<4lAsNql(A_qK_SOw((BnS z1iUgC(y5>O1>=?|8XhDL3A^5$TShzTG63eYcS3fly+29c2Tkkh+}d_@>+;d75PiY# zN#W#4jQctPM@RA@KQJFNkc#rN?8cO^x+5eEe`4T2(%f!}vd3HSC_+kZ$?!w`rrDu?nZ~aj_C+Pu&>J4JQ zbdAgRzNcapYT& zfK@(vk1S;U1fUMkn5hAPpJrvnTH5~6i4j-j@qjbfBmU=K&nHEFUxyw%(36q6^=&_; zkYLpE1)YdZrz{^r#08I-*$QjYHJ6F*ay&B*nf$8)Mw?=MRvK77WK5`!zE*I3@!GUw12|B zz7$@{em!GqvJ;v6UuxBW$0 zw6`u1mb_)pLTUqOz7+!7e*ZxtKe5uH9Gs7ESSK#jvv`<`(A9p%mxEx)xB{OLMQsbl z!2={L_?^;K|A_ zygSEt?oNMi`TK97{bJB{P9LW^0yX~fe5HrGs&&WQ!pC9fj`E6fa*71e_a#B<{Ou1P zHItf0lOm-tobVTkq`Jl{>X6b)1_KAl!9G60!9KWSw zsf~D_N}0WT4`r#OH8C7!S%!9wwH(iC!$m9YTAQ*bp#`5lZDR99@6bV!_f3A?2A`C2 z&^$?fgN_Rs%HawW^egEhqcT~+#j2v7rz z{Cq(Qa-zUwDcksx!fU@**`Qf|j1nwvRddv`o{)-iZr}R$`*qV-F^PX-3C|0tT+1fVek z(z~j7W(}+v%>{Gyu&W{l=dEvcL&=Hd`T4oThTg&QdRGq)+d-UEJ@(s)3p$`>NCc%- zw0x;)Z*MoTvWTd!Pq0rtLJr}74{X;I!iOTL((9RFnHpqEGQSS=1!tS!57@LSoq zHtoO|MNSJ>gtAr>cfpuu!vvXxo7>MFL0ozNl7A3=q{G{)iH(s+uO#VVbL)A@4LVZl zDmhW%#2VU#$SJDd?(q}37`RATaY(>%?_+7DPLrgDlkCbr1df;ybBXggRS=oMfy;_ zUjg=$#{h)4Q{s)xlg(JQ z)%~8QKu0bVo%0R5qPOSZsuxK4k1E3W(iIv-3?f31Q9I9 z7X=KV)Ap)ewZPVsS^0S?Ji*oqZI3laC zMX!t0$@z?U?|w{=_mnG5?hW3s zwu>2-EZxVw0*>uZ{&VLUn?(2bPj%z5_V`ByzG!())bMtr5SAaJac?W69!+@hF&g4w z`etA2_`3YV(>Q&lly3Yvum^(Bw}LdZ*3Ms(FdO?QSIiJlbM1*R_bV;ElaH`7$5M>e zN~d`8qPAD99()zFM-e@HXyJS?Q3i8mEz{Hw=7o?5>qU`E3d zEAGZ2*CdD`;(b|okaSONkb^*I=*Vcq?WXhQo+BO67(R?w!-F4eHtbg{-ni-^jmY7p z4j9c&sS*I!tS9&1$Bylh=KY)S!H)G}MzGzzxEbnB z`2(ssQDZwKuPBK%gP9jo!6|b)nMeZk@mfwwaOm!k-n1u9Sc{93nz5B_c~)yKsLobN zH-U*q98?>C!GQ(af2Y`*CJM%wwi|E#4*%FuX6ota*VHP?jk zZ)p+uT3%_X$+HR&Muc^mJ4AOc}4eCND5_Q);P9Lf5XNR8jQ z?!dxeI)_g)uAF&k^p@6^Se*U}^y>F@xFC|ah;Cs$r8$c!l9!Vi`-zZZ&-7K%FTB!K z50vB>A&k2azKPMrzD+abPl=%?wbcY6x#7ej=DrC_(^(z`9X!1ErW9~ zH}hH4Xy)6;a^;iwWYgOastx%eccuEn_Dy<%73Z1Xhjsk#mRGR&Dv~YKV`RX6%v|*H zb&oZ!tG?tfiiX&V-c~ey&`Oe7)DfDAA@~Y)3Lz!eGnU1U5aP$2PIPgxNDqjQ43RQ2 z^Qj%}Nl1uNj}k4!l7zhQ4BcpbR@?dq0T46GVuy*SWNswInzhB>P%d++m&X2 z-6#RFzfCNZhcBI`{BM{s&2XNPFGJ~sVE*sOi533hIK)-)*S-)#M-%fDwL9TlWre!P zpOA{-NAz<|vXcA%w#->V8o?is*A}Z+A*9Vj*#z$R_vj6IFyM zr{x2y6r+4&7U;d>&4#2*f&O}d##m{4=s!Z#lD-I99ee+V1GRIje~AoBix~JT9K|m4 zu{?hwZeIV%P)PbH>e(?d*Sq#0b%&eVQAG(2n!9Eoz`r|te1~M`0Zf>z6Pq}~xniSJ z7Vzrtx+{M4Q{y0*IAgL=xv~=oHwg4p{i-c?&-#fl;*}n!b7a5vU%MbXe( zOzDtFz=4ukCbgluG3Vokp!7T28A34nhL9}3CzX_UMV&?_NxqZ2Ahw6Tj&`uhVs$r!pfeP!xm4+L zQ}h>zq04jGfZ zxFli6M!SHB{YXEe$`~?@9k;tPk3h>%6cCJi;pP=k=JRkD#I*(}q5>Ek(lWHG(gv<9 z&|`pjJxD#{XHha;i5W1%HMc|%P}}UeIwPFPFfyl`goVO% zp7+5kFQ8aZV1TbrQcouWS*)>Ds0@uzOi^^v4mcc`x}uo@5=_|KwcPl39xwv(%QC#f zj0Ptt>1eW)1csFK!S$ycW^hDx+cu!lj4nmZIP!M0@nSf1WW+u6w|CG6w!45C*0DQf zNi$Sx8dJohCP=!WH+P3~qsNCN-NTUq6d7ZZg~?n6anRTWv4KyM?zOQUU=;RdPpN>Dcf6-uthYxWObZ$LI{0CiE(>Rq#~)z-%RQ2@l$RM-iD(Rg%d z)sDbdReuffaVEvGn@e^AATmI2JZ+}{L%cNen(8VG{*OUMO4J!3-U{O}Rq#3o$kiK} zQg!GYz+-Tf<_jESASD}rxK#nO6d*3KjfK#<1G%zek$M>yyP9Lziq|z(%NGspizYw? zD)(WlmmxIWJ%d500n5x0XMoTOyNq-Jjfn9T04aKWwm4#6rkxW~6M|iCSE+{}Q!P6#1E6XphjJSYl?qU!H4|#0 zOb5qtCiU(Q-r1fFI6d3WscIX%aN`mda5)ZpGg2La$NY;?{2LXPBm=B{BexoW?KyxS zRYb*%HIP7LVT2e9Oj*>M-k0lQhPuJof3xL5;cY!sKHQN6oQ%lDiikk05i{+E7K$4P zGjj|Sd|+=m#}9)CwCfrGRwmf4Aodo;3@UZYPk2;HeeGKn49AQCR%QrGPpVH>nHiT4 zx!uAs!$3VP6%{nQ)*->txG@JRrJSkJR7QQOwVCym<=^g?aBE1%@nWD4U4qz_CUb^*7t|Qg2WWmSLx`W$8x5k&$?i&lkm8h zVrL6zki5YJF;oi4ehz)+uyKr76P#uZvkn0zMsYqs2&P+Z)U?I7R0gPsk{X3o_Cimt zI3$<^0|tg#pG5#9R{e5Sov<7=jF0X>@jec7u{OLtma`Tz^H!sx7Lhsy0SJPrB9L*E z1k0m$6nh4clhFP5+3^5Gv-jmAcOj41{xL3PnCkFYVIPAGZ)U(5$?v5wQQq!z^Tby5tzqenw_rNU6?j5-D(W{NNE8Aen?!MhIFn0Jf4q&C_l4Oy8Nn z`-~*Jhq!xJKZ`paAe{sorBn_{V+E8T7@wt{EbfpQT{aV8iy%G0gDqz@NtBZy?(E>z z<1&-&*px3dzr8CF7IzCM{=$rPp|;;E6~gw021i!GkZ%V)x{q-B3G9m@z)T#PePk7CM z@Fw5JAPQ2+e^Uh)sS-xz7hqL8Grb-r1&aj)qM_y->Ov5Hj`8tK&ph% z%z`5gP-Glf_xZ&Cry2Z1d#dLMT)2rzS6`|$Rb=;HH&+Tp!Rj6Dv>_OUBN_&2AsETi zg8d6bh%JbPM@tC)i-ZTnzcl6BV5s{cS<@q*3KAt#v~5X=>CLJcv?JI)vS~3?OMpoi z0NcMdqoox)A@#LE6)0saf28+nXUn|g+pPUNbQ!{P<9hOFQcnKUbZV{E;0xhOb#+y!h4x$D5a||(TCDxo~feF4{-4ih%w`myp>~UNrE!#+o3Z+R4?>2 z4$;sm8n9?|=1u`K3W^z&DD&|M&9KH4FwzEb&NxUke@iGO?gxO4*bvM(Y`Y-XT~Icc zOGR_Ey0QQ!m8_;fnpstot>*x{y2wf$@Dj}8KTeR;1z}w|b=aY5@x_p&r>8b($VIf+ zzC`9ijbNF+$3J)^r`~Dg6mtT^J{U8!K(Sd@FjD1g1aR#a|Q331)Q}^yyffhw!k2 zJ>y9r8T#e{1(>1c*t9}F5i^ruDQy^FGrrqy2gq83)FD1rM%m{e2dqU85|yk1TRO45 zdOUJ43ui4vf!sy-2eM>(9d?@@DHuR2L89!pzB~V&*XIBrk!%-Q1fu3i1vT1RF1Gd- z04eH{Q43%f6_{FD!7>LTp;%@1F;CEF^rwZ_!y&b#wV>|W{tCG8_XRK61q;dZgPf6fazoxk@{zCmgSjaEI?qY zJv|!J|9&tVoJ-3&bt>7~9`Ud z(S`pHBS=cL<{NC;D)+Gm!{vQQfcsEG5$DXOpDvS^zr`F#fD5 z9!6wJna*oSJlbVN?X{yGGk z@sFzVBQ#TyZ3EdArX1AoR5o+zHVgo8mOGor7dD*6Lvk4+>Hv^|0{X=bgCwKJY&?b= zxtxc?gq{wn)u?p82a|UuzO`uLl@y*)=sgzwNU?A%311V_0m&SIt435{lWg|ko#hbI zch(d?Mrl2~*y$S@5Up)Hrc9GzaSl9`TrC{41X?gCc~PRB2)*TH6#peuO-9>4T+K(= z7S?84xSSa?3dG7wm*|Q5Xl^nP7$_@jNoYQ{}y169hES}j%Fs=o>17D zGg_VNrOcBR*lne+z@>y~J8qQC)(~6g0GU>{HmpYR@(yUWyqc`8BF1iUV~Gl74&*b$+s2#6^637)sV zKw@mLLzS>&%#Fqs2sP;1mO(!rTd{g3iAv=xb*Js zKnsSdU>Wvg2^g_+D+Yzp95c-TFuV|!O(Q9`h7)knZuq~Bg3g=?C`-R#me1xG6NWQ21ZCgMeY{jDNTMfP*8F4(aeJl-3|6FW^}_w zwA@J31ho}@-QLO0)uP;lJE~Qv=f10k~JCj08BHPGcCp#m0znt8_%f3J-wL$q5+9H>5&FN zBrG3XF+qVzzXWDp0r+Mq$pBDYK=dnkKPb>=fR13QZLwm3RW_lm13VdBa*9YsnGZmO zh{2KpDk@Vn@)}fEA)siqbv}|F{HHI^PIH?K;oEDxI1SClsj>YFazwU zH6L67bc8$*Q>suX#V|tQU*E@E(rs|rcp+nQhRi(>?dU{##V5EAC506qF6y$M85h6~ zv8i;_n+hRG4h$1KGmm#2ZmybUMYFc-s=2v{-AC860*;0&80)Y=7MjsB_>8NxWB ze7{WwAWCBrk6;&oVH55I0l>`p46cd>ygloWNLz%SoXvwl=J0!RmMRyW7cK+Em z@^$V2o1#tz_JqNjnz6Twe=^PgQ>2eXu~x^bmHL=B5K5RP=0S)ufDsDl0VO=tiUW2w z{)V+Qob?XelECfHTPc=~IfK853}C8<_w^8%Uelk1!BPPglsH_w0&_KbPn{i3%@E7w9X%P^+wCPr=ROkE)Fg z+W?D`5ZD(hHmKQBCNL)t*w7%6=?O_iQm+u-~6(x_&Tqc5onw z4vQpfgV2%=95IU2I=S)&M5GVU_B0IhQ!LoAUl&zV*Dse`f(&FZp@hyz|I+D)fSNR^ zFg2KK4Z%n$(~ajD#6Q@VKyg;kM((D9f*Y?GOq8~Qt8r*%bTU7~+6JIt$pC9T0F-B1 zE!(;l(3=%7Ti3&gBLw&ux-|&>f5DvejzQ}hn*qcm!Mnz3mu}OY5A}%Q_|q*r-4LnG zDhi!wFr>68<#m@`08(bqj4qMa3*j-f2Q=!|NXk^z|JQ%V>Ik?FK*i(~iI2nJj(2u& z>>9;#m;t64_&63QTi9vqt=!v{63ieODRP(5p~QUH<6YJO%imjT04evR3`?AKyxs%m z{RpDb0PR&SmO+R)cS@i%?ED8ZK<*o;Y5>7_U_@Y|lK_i~9~k0mt|OeJH`qvvxH~My zK`+e#Elgn1n80rQCu(5A5wOPc8rm5T&Ruj!0H7Hmc+#CxWcj?FkA^}B_6z`efHKFr z&_upXs9j06a!TMSflzQ89RmkSq&Fj2xK9-kvtds;(COK7$#<5VAMJ^1T{Ukxi&{x2 z;SIQJ1f8`65Q153BuZEm`>imPmWb44Db-eW0MgkA=0Kp5dpBnqK`IC8%N8tNrftCF z3BpGWxwo%ARSZKwXr_d^a)A;P?D$KN6X$1A3Q)`esz#Co>H0!sNFoPLAWgT$jDS## z5>2uc@Bt1dz!*;Nw=SSq$B>m9$jT;RYD<(FEF&%fSUac=_ObN+RO!Q}qa!NAv89p+ z%qU01f2#w~+!x8v@2xS+Lundd@^dHCn81B}?2C!%&_+d~A+v^2<5W63V6=VI-DHg8 zYl1{9PVJ@ZZy`LB5M}@+iaY|*MJxV8Zjj`e!ga=t(X*7yjTb**7ohbK7Ye z5`V$F_5!fEia;uWn)rqdRT$Y39FU^R)Us>YFHpgnr-M+>8_~cuXaPVy;u6JWCZQcV z&6U!EOpLhuu!9FUvo^GGhG9qY)xm%I5@ zbOGoXrrT{jN(tEXNLCUprzHfRnQ@J@;s9dWGL2{9_RA!C`FjpT6R?H!t#_Tzh|&S} zgKa)stxqFprpYM`EXh_p0IE&QUIQ>w%LaR0h&GRpt01&3!Z-?k%;7jh>*{AuBWQWG z3pbEX7ENWuWRT^1AIP!a$fLRWKiFqU53i?|0}?WWw##Cx0ebrAbjDas%ccfdD;TR7 zmNat`77P?P6Jf8*s>L;|;hTqf+~&@2Tud~e1h!{rYbQn51E|g4w16o)27lJaQ!9n) zE`ZPGM^X}rQ32ye0^J+^r=I?!G&ST}3L%sgqFF6cdEx{i@32pB1d^t#3K;DFz?lCd z%0vzg6e|NQzX7#AgzG;8P3FivDdUxde1n$h!sz;aGNSRh%+h*5=EJ||1(b1Z-W(5* zcCKJaaL?Woivvg)eMq%ZsF?#S8Q_@c@5%BE%(ox=uF-t*EHh5$g|SW`wMI}VeBZ?! z#YImi`nxX(CDDxJ_zPcW^4T10pAsd%qbi6ByWW6?1v~CXe&pRVm7|?AQ|CzF-(-M) z{a5eF`S0IWeSR|uq{;w~kIetcx5xkFU&E{fep@<$|4Sc&IQH{CMmPdh2{6=&JdPRn z$c^TI`mv|scYy{!9E}=)W53nt=OB8tZXWXzlEHCfjEwmiHg<5d3qaS7ZTJWiUC)>y zPGm_9d2$9;T3#)fKyG@`3KQzo^p8H_*($PgiulOnc|<{hu6sPBLP(czo)RaKTZtr) zt$&jOCNZFa65L`oi^8lbHGzqOatvWR{4MfgC<72NB#P~`AxBdPb%oFx!bN)dyG&S+ z0Rjkgg|oDIZOC8|A zBMo_|fJX_7qv}KkegUi5<6&;8fwq`j8FdQYIs~POuHz9m&y;%GMw*ktGp4fR>KoWS zW7r--CaIMPKrGDxK`{6B;pj#~$AN3D0pPfSSaovt49GYbW4jXiVK4Q#BjYd8;L)>lNz6!KJgWofhg9~(@bV?o<=*a|0~$R-mDGwiS!L}{&P zxed|#-JrBcH7KjHiG;Gt)coz}hF^Hfc1kL6 z^^4dySq2e?xtmcTZMhIwXhP}W*6W{zsphM4=oo1T3|`B{Cv5C*j?Vcc(75J-{^tyY zepvBS=&U;9JzD7}9?=5WvdpbdVGH6_Col6%kzD}Y#yKHx3>e{3C1O}3wvc_OHK{Wd z>s*09L9T^00ObjYbP&K6sV&+7?G>zU1*{w%d)D8{DqIVAK^Iu24}f6?G1iLSc~gy? zi{X2ks;Uv)+>!o|%{9BDs|UwHDZ_3`v54kii)WHn=xMUk6I28|42MNY^_lH%kOZ>A zQ<{S!?ms*qrf6o2?Sr6v(?Ui`UZx+h3R7LOf_yP`prBrSApoPt#9Kp3LcI#+XDAUW znDP#w7qf~ofHl-KRj_FR=(SK9`|J!s11C^J_@7SSV#^wEe={*APD!gOPb)|wdjqb^ zBEYOv{wbhqBJxv&q?Zxrf+27~odBSwuJ8uj{??iSB7o-DxKWofvKQQ|b$Mu9H5eTU zl)z%GHR4&d z@F+GnB1RBFcEku)e=RpxJq!smpy`6*O?q$+(8WH6RJ#)KVx%@ytcjT^B3D8wTSVFA z2c(!#6aW)~768WdMl44K-G#hB7o1GyvfIb?<`Bp<#0P^_}aAPMZEn*6J3Lb$v z2(ADWUja1g?C>9|7s3=xd5q@_q8DsW0KI>DXz-p9hHP5tApUb0 zemEaQngKR`e1?J;u`YZB+6z|3DbFPhkxVv;Gh@5}2z>+uA&&T!8}w^(<1=QQT1%mh z_FgLy*rCsMSRf2bg+dI1NvOR6P+fbE`_52gxvHSXNc(jBIS~kg1iqswDZQ+?=tL)w zrIhn^5&{m1JKOCx0t+A{Bz8849gK%q$S@8$>_Anst3EIt-5bJybDuZZ;5I>uh`Ypa@ICT{gAAZ=00xs#+=iIQ{g2r}Jjvp% z^bE87_d!>BnI$Tep;Uo8(jsW3s5w^(U2>p2>FgOXJsLM3%UzhlfG}*-DE@OjjA%U5 zr~$Y!WBjf*67VYjitPdIZJg-mAvRKtOgPflpu1&gM*!`ZQLA8>3z{kW4-Ub>QA;P6 zXVAv`vw=!C;SN^-ZiV?7fa)4`E4SgBbE?3&RS$WLgBTL2Q4D#F#PKh4z?8|kgH4Vm zB|Eklt=O%F6R1d;1Q4Zwgy4%L1c@>t>XvAaAZ+-NX*va9gf*d~dgjpL0hSXG0s>1g z62>vyPv&yswC`NBkI-{a_j3z|o67Ze5A-3{aK}~y!Oa1X3;Blt=d^AN`IE8 zZG_7JSq+195{#_|gw&eGhU8&xVUiW45k?zbHd!cztsZ+y*;LN!+Kz^ndS*UI$iGGf zm&4pCx0vYi_7fp7vi0CtJ@1E89IQ=@XO5f%va(|tk#yRpvcmw(pF~)YSPURKmA7IO2DYqzeP4BPM<=O=a<4Ds5p3la0nknEt zb}n)HRcKX#k+2X`U4m*1(n^dirS|1SOy{vy?2d^;!x#urJ&5CL;G+#lP}*v74NRFC zmo)GYTNTJm;@->w@ZTvTLQ|O{Y zjMR;^S@j};os`HeR|APw=gbWW8=-;`nAvseyCDWYns5zW6$O(GG#V)x|E1A0I#jU0 zgi`q?$~0u#UD&I+JNsp0Kg7cJJWzh!iO#rOgJnWFeva&e=`>={P~|b>`6M0mbTO-tEk*J@iUOJ%C%-@%x7}ypSye%H2BS%+G-L))Ff8 z()n4?!;AyO|1k{$0h@<0$^cZ8K>y9z(}7C`IeMwlrooE8MlvvpE<&kJfRwKeF$W+$ zGl+X1*c;`xxrpPkl~g1@MxCU@3bqN@wEL5I0Ke?RV_f{I6#K6*-N%VXArcIwMNq?m zX<^5_?Y>da2sArbb^)-?xzJcXOUNoE8FXhCazAq_YC6TzU3ddV+O(N-cliFNTrHX_ zD$?c6)Xk+r4vt&WYcYhvV8DwKn9!L2nhAln^<(G59{yquzzq@9)-6Hj0t&TYIfnf} zs)T{1&@(P149-qNm)fYr0S*<6Ql8Doeh+kie~yKH=CiW1XR7QLqWd4QDOi9~SN>V3 zeGbA50O=ZrYL*%%ME1N|Fe;!{5I4e3C4)6!YN)8-E7k%F z`QRp?b-N5Z$r)fAS_mPh=AUV}hwmz3kiRs2B2DXmG!a1ip`?s|nrRP8(PWPP2mpQe zw!O)k59F$pxt;)#gfgwz1dljeD>`4eS0HbIfNL#i{-x~^G^i%sP*!Hq{%y4akS2gcb1x|-PIb=I z%mA1SDV(JY3#ElMk=s-02|$YTLVs-RcvzivB)1x%3S}C70I3xm`C-#vKLgrv-p+-o z<6ss(Xp3aIofX4|8A>B4chBaqjKi^d@vt%yaP$IDD0DbTUW@jocE|qtxn)4{-{?7j ztN{`3b`?MXB|pMV$3+i1*SZP@UyZ^g0VHZvwl>yG0*B|!GQjE`Y&O!w6jB5SuGD-v|Lt!+%o{2R;*7!9O8nOp58oj#Z$A}%+QYg>y{yk zrDWSc2#uT!dRGGds$*Auo;8kSpu*y2P{}cXB?wugNIiziT;ovmB(TJK`|Q=lkVu>W zH;vNbZ&R)Tg+=kiScN`1gtT>_sXZ`@8dy7q2pv(#8I0ffP)UCxojj<-!Lo@)GOV5u zIGmt&(K5qqMR@s@9UFB4n^y9KumRv>JOK$`t}{|Rj)%t`R6I+-(x5u2h+cx8g?o*h zlFfbSYd92LfQWhpU9zTudQ!WL7FWjxdDES?s0Gm1ITn>TjC28^D!iA^DNGqu1K4=y zsBTh#YynWDE_{(+7$FBKs#ivxLm?%Ooo2!Idl32ELu&z>@BhI}1RM>vWmH@tc_H;J*OlWqNnQE^fF=d3 z|I}1JtZo44urf|!pAMkg$^g)lAi-FVQAcVg$;K*Bn@0I-0*MGXI{cHpBw;2PYKHIJ z??5vLpzHzy7@~!MVPI2^Xm8GBmVhSz5LPkSQU957F`z5=mSYBUokaTSR7eEgFSBSo z-Ncb1*T{E24kK4*8HIqn)}C_03&iig*r^Q!Z4*OB8aRy;s)`T&Kh3xHwA z;@=zBj=G^*JdlP)&>?Z4*GB$jXq7Nz-qA&{qtUW+s(-AS#i|(2f>iMNwYrOzrZSa} z$D|n4Dv)de=n+xd&xXqYbsLBP%fiS($Z_O3VxyUEPQ6MlC!?}?gdI5%oQZW8ur))X z55(pfBkw%AN+pp&I&yau$UK<(4Sov1BSVit6|g=(9Xpr?6N%V~X}J4uYa%tipS=t* zntGW3x$HB;NEYT=$))-IROO%X0TI#pJnxr*NTS7~!KIqSG6HV%qID8uJV-c1*b<&D z)aaEBiOvF@4UP=$pk)#vtijI#yqcY2l$%l{5GaI{f)x)C1G>-=O!Y0wQKS(BJ0e8$ zLLPAk(_>~FFMva8;YZ^k90H7EX4_-_=*NKWOHU@aP)Rc}a3z143)@?(s#GN0|AneIQfs;0W#=c|;&| zM=#P+z2%b}qdd?!^UxdxFz8E?eT&6Ih+RPJ9NA7{e&Zp>uj+sE$iCq?nAs7)`|aN*ai}_i~1e!a58WEG&S=ldGB54W%#UJ-wg-ru6bC~wdjJYFIl~$Y2cTzy$ zmoVcIox9xJx)e~_K(Q)}%kX#a2(@@vaNX2Kcnq}gAYl!Z%x%n|ejkt}^E$+gug2Yw zwV2kX3MnFt12xdD57Fj@=tPivJ$8S3=RDax`4m@otZTvck%83;*(O2kxhK;TKM`jZ z)7uJvT&R_&0k@SL)aW%qe47cH+oomgt6z}U$rDZD;Y>?J^SGOqY5~J^I~LCf*zy#K z-^H=z1WOTk5Hz;I21zN=c?i4{cJN}#n1UgbLjz&ln1{K;NF)^vS+xr~aDM_xBl~6vPY2DjM3{ z4Iwv4=TTh%%EVBW!Qz5?;qMe8B23^C|D<)y2vBxt7XOi`%63Jhx;1+8*O{zXE8ECjSGXOK`)V5M%R`Bd+gC&-=S#JP1=S;z5EkJ2HHwHB` zlbvzf1z9Qr^Z>WmU3WWkX>QltPDZ3!{b^-S7 zT2c^t%&q1i3GJ^wMI&xV1Z;o`L?kr@r<2#N0a}BXrXGmhfZ44PWEUTIEJI=-;T%A{ zp?&trqD`$?nD`~YWPl?^Y;=++x`t*B)+&+H4tw{%q-{|S=8=p*E6)^K118vpHXSa! z>Q?2oG)I7?w`5ew5N;;`8~?DpO+@8#bV3FcVi24(+7uvPg>dTN*i04-=R;8f5FA`K z<%dxQ24JMQh6S`-BGsGA;HGqY6eM3?=iYA@4ARdDWN|McAQfSE{7u@b`~0~7|eV#{(gn4+=> znS1qjq0y2JJGONWAcz6hHEm9vUwdFdlL45a*|vSUl=x!9OF{^(nP>AR#o^ik!$)Yo59dHyshOt1QH_1`pNBH=!?qqFL z?2KlK(YW7L`e6oUjZbD=w;>RxR6_PBY~dhTDCBvBy6bvwfROmW)Oh#Y(l6m*R*y!ZlBv23MN~C$_tK>Kv0!dBtOf@J)8an zKxS`&g}M8L%q@PlNf5E(58IC=m7UzS@L>q?E<809P~E0eS@Q~ z1gTZdqs=NCjQ1(5EPV+;L}gYqG0Jk=0!HNa|`w$}oZ=4PRFN9@Bg|l=8{upS- z;IV=!)D(dR04Rxnj7*5s`KLt$?Pj8FX3*$kVo=RB{I#cWn%4P+5Va|6pdwq$Amm3; z?)uul8_Fp0J=_d}Ex{v(%5f$GFdD*AsimWn0fG{nV7%$)Zh7f`pxagty|F2v01#d#jnVzT{wth!GtzQrGDtcS75qQ?7gO}ddQ>J?x}|p1 zc$^$nf(JnD0+1d5;0*K8FN`>1kx5BB6a+zC!Mr}E zsWxf&Io;nOQ6<}VA%OMHWt0J02hyp2ZlDeO+o{r|bb$6gOD}{_K(HbAgw1Ik4PI4^r&$04_D{diU?~s$2L`~F;W1? z(Ug|X7Mg&*cRP+|*#=hVJ}A)#7*?D~r=vJZ8YF9xEnU1Z85}Jt6Q#_42H3j0K&!u? z(9x=68D&a0z#LxQV5zfJ^;m60RA-m9f?5$#MXSWEf9HJ8w$4@xp#GUml#jQzBXtyt zx|Sw}I8*^-nx(-^Coyyl{6H5e8R%u{< zh+I zr68OGFrEW74KN23WIX(vby6Hif=s2gE=Q!N8#3|Oj@JDRAl?SXD&OcS*da;N^!wQm z&{f~CZ32w_A#?xvs4}DBA}#*dh_rH7JVcKhM89e?7uo;qIB$+Ybo_yZ!W_&j2GGW5ODbrn3p<~+S1a^(iNOC?_neA)`sBPv$5gq2TEz|;DF(%UV!OR?)4MLR~ z2f*I?U&BPLIBr`V*eggdotd{L_b{X3kHDX3QS?tX2hbP0sepF zVA9`45LK{5*WM{y_>u#ucqPT`;cP<2V^{h(R!4E{%3xw;D{6p8B7#8rIRt|sw#II< z7osm+!Ir&l`>KrikF>;6%C39!OQ2?k?R~KGTt}1wYKP&KncV)4K}F`;fPDFwd&&m} zj7jysXA9$<9V&$~og=f4m(->S#x5@=mmw4dZDM87N7B+dnutDlby$&}LCQPVO4bDg z6szhPfW`)4(nMP^hHb6B04dzGwa0Xj0&`9yXBfee3`exNL&OyHB~%-Mo8D)Ex&Wk; zrvQ@*ajSJ3$Z#7dGeGG`W-_J(1dtt?*?j%ikg|}Vb4rK?hS%yc_dg9iiZJf!au=}H zfcXhkM5c&95FhZM@AvHWsSJ8xu^27>p(0i0-^LxN9V6%n_llztt2g?M9X~%&umdYvt=;ucD4ESzAOkigiV5bzTg%Z&5J4!S2BsCBg7ZzK^S5F?v7zYubxsVq z06V}m%zIUf{}H|L#*eQ*E7gmyN6eJ0#riX%RVgJSJ2ax?c#&0hysd@M+1KXB!Ao5Pl0V*@f2tQ>l*FYyrMF3Au@?@tkwDx)OrnJVePJWXm z)3(Io8MGb;{`&RnyVeLgpJ9((p!uQIUkV~Z+K9_bGmGg_Zb^o07-=`uj=S=I`SWLe zeq?h1BFO;GOH&&70T2!55&o>nCub=Y!ci7SEXQzS%Ck+h3 ztA~|c&)~)kodD2Qj?;`d#JhG~J1&#JqXe~F0UulpZA9)8`&3Lzzfc>xBIgaEJBa=cOhK2eTPQtVR; z*(60t=g`7m_do94^FNzYNk5_F5D!YMWg?^5NC)+YXtSx!%y4JoZBu8rEKT_R*<6?UIbH9oCuEI zuiuoo3H8vA* zH1{a})g~8S!KFHe@ZtME>iZ*gO$(LJE6_-h#8AQku{!Zm+bEnNAXu`s;vb3TIOA$q zeE;wFzgQ=M6z=IIhjTVDC~S?ew(Mz!pqS))AX*AtB?k?Tf(cmWL*f0+muKfspWL1P z`W3N*>E3{^yA}5<5748LKb!RT_uR+1u(;c3U9*v4CUdUFTWk0KDoR9^(&|Wn!Zroi_4|!V3kzoA*&`Jk1&58y*AUv zX+FO{fAVCWr_<9Dl(M>pt#8kJ2C*aN=`ol+;>I9P?kEfZ#*@f=xPSWO$=N)ep6t)> z^zuht&yyp;d^d6ofttx&B`N_}&_5kS!F+!I^vT(o`u?ps$WiK72xavc>I)AvukAem z8XFlEhH}4-`TWPzr%%r2d42v{`F+l37e5T;M-a4k%mrW~A8zDGz~hJfNBt4{7wvk- z{;&Lt|Mee;u29brh@f6~@_);}bQe_4{&)NR{-2#dwUt6}J>$|~H z?r(2zzkk2KfB5+$nxx|R&)Rj)AiBzeY; z{20LllrBW8lMHZFDTl-p2<*)|8k_w4+uOUlA3yB(uUg`GOxa1480rzjXj2hoq^eIH zOdLzYXIlPq_qVrqci(^9-|x>~xq)17Kr~eAVz`qOPKRfIHk4GR%wOSn+8a7nYb>Rd z`z^P^n@bY0(?8%l&-0S46P;%Yfy*3jd$p)t8Ag%7viKL7Rg+dsE=KjQCyI13m- zM*lPfdkxX3n&i^aqwgTsg%+@7Z?OIMzrXqV>9hI%w|NbWaH52GI)r`B7l^k-Y)GXD zln`YRW<;I;v=%=bgGxT!eEsy{{fEzAzWsCe<0o-~!e5PTp#3b`s)U&yAqU!hw3OqS z!5XNX$q{PfHKaxFZ@+&2@czT+FMr=;&mU%h7|%^>)*CarcP31$N#OSz zpT7QccmM0;^z@|s`uWpk05ozh1tLKM`pi=@QB`RhIux2D5GKIRZPAGTPam$Yuik(D z@^|?BEabB}Hy&q#0Iu>IO$W~?V3}Qh;@H@$4Qz;rHU_ZwH{U*gcz=EU;dAo+fr7(X zs{m&}-ONJOBHz>OUNL`&5RGn!x+@paY}}=Zp)Lck-+$MC{`vmXmw#@>^WR1aInbMa z?C5xhPN+*_NtV#A*@dz@5a29Ft8FPOCQVVj`m5l282>k4KYzHsy1IJ*>C4TJUnld~ zbXtBs{J0nF&;>Ia3%jx6xdq;|7{VuLl{`n}0IA2@$(3vD$-4%8`mg%@AHOcoKcAFe z_xtm6--aS?TO$V(A;A|U0Y+F?j!M0$l&Wbpa=G46$kES#`|b0`_g7cf?>~M0C;I-4 zIY6Z3V+2t|9kfL)1MOm~Z31BhU~nRC!ONnE*oe90!j?kOmhW%Be)@2A_2<>~$1ltC z&!^?*{SS!M$+;B=k@jS{h$8!`-|u2bGg4cpHEucu*x;p zqHrL_lsw$!h{W#4mjGUECsM76al>G^Lje&4@8q(;!Dk+fgQG^rq=JyE8gkW(&yxlO96 zC9zGk$E{W!-b`E|U;h2g*H0hbi}-*3^7X6VpPiK-|9m++g;ENIPzzT}K~n)}6YC-P zZfm?o2jpg`w&_+9nW`|F@&EGm>-G7coU}cYGJ7&cB!Ijr5dZ1A6!`8_ruW5U}60wr4p7jpe2;%?c>+So0nof*L9BK`~NSKrj5rX=~7+g$l zWLyy7U#hAJGuui+=&~|agJT7W+}~{V{~rHu#rH3XP9P+mg+o-|)5e$Xln&_-P&x!* zDM1NoK|(sDQIQgs?h;TClv28-bLs9z5EKxQZWh?x^X~We{sDXM-5Y1l%slgaei}Rv zr^(2lW3jDoIq=YF=bHS{t;-X{{!+UZwEV}e^*g!j`_J(;bw(e1d+_0pQqHslFa7B& z2a6Bc6~?-@y9Ard!Jh0dH%|ZXVhD&Mjl$WVjx4jYC6Lq%H}oPBnw%*gf~R#9LxnwL zbwzmF1BP(%QhyGdUo~P@yta<{|IV;=b#Xfr2V` zXLp60rq5kKbBx{^*}hu%j|I4fMt=_irL^W6Q~RDGA2*~X3L~wwuDt8@Gxt4N!XR2a zCe#`>dVg{hZZY<&zE`KOn&rOWYvc(^lRsWHNjVir$Y$W~3As-D;58KnjSNy${XOyl zXQ$!G8Rjh+YaJPZuD1J|9@6d8b1eut#8-c>9Flqap&`K7Ci}zs`E%@QyOtHznsx3y zRMq-_?D4n}A9zmsa!<(#S?@HyLYDZ9pj07tyq(Q@x6&|n?uq_q?fy1lmG+cy(x*%7 zU}A629^Kjt*wT+)%HMB{n#nin{xnN0Pq1goCL&HbPMpK@o|n5KAD#Hk7;lcwH2~)Y z(z4CZ`=$G~Dxb$P9>WFS^H%4WhttKQ!H<^pnhYV3i_1xtPVdfI^Kp0__#Zf{$@g|w z*S&C2+S58x#A%5UAMdANh@n6cQM|^y;7faAtQR)I5{Dje^u-_4Tzx$7p{GwjTJ3lw z+-Ojft+XfVy2I7`b9w5yz)UPPb{>x~4KrTwD=fwU6&jku6Zipa92Rh~_3JNNI=zSG z*YxFwKIHO@MVr+GS-A$r7GFqiE7*VKj&+8#1OC6v>plN{vQv-1SwcU0Y1e+df5*f&{z&Vd z(ESrO;U`qj%((t`_+$NAdvA0C&Kr$A9z6IDJJjLt#s4Pei;7+-(+)$9ipbu~?ieYP z6#M$?l!rvve#j%*Y1F}jjsyB^>sMdj-~TS3{En&r^~9&A8Rv2#Og)4ZgHDvhyb@KA zMoh(SBeE-5RXyB0c<0lCW%D>#I1*lmF?)-7jb-Yx?0->b&Wlur5F#@~JC=0{y%O)& z{{m9~0WRAEue)P^^FM+xwM9?=8<~oQH5Y5fQ(x>iPs*r8-uZ39_W@&O550ea0ld*` zqOR6O=>KMS*h5sA^maTglNPBAOX`T^-3E?BKMZ@gh5nodtr)1?I()PwKq|N=esSih zk>hnAfq*~G)MU$Hv4xaG*N3Z5dPunc+2QHw>H4oS`Y1o6xK*y(ae;zF#?RG!Z!_An z)vtxvRObKo94t(*tyx-NI6xQ3_mM4FZuHf5ko9PTajNHyW*;AZgr=>~-9IE10}0${ z;Wf-fN^P4Dk_KPM2J!By7!#5;y{%JZ)x?wGp&Jop6&QplP4F1$&ZEK4c@Th1PbslK zGRnn%|FFqtF3w}Q%UDJ?|Mm8VZ(f4oA(X``84~EV2Pc;8ssGGtOP}TAuxvgMdp8zi zYlt1Y{^k)UQRfC5JP_?07#Mh=GypIUXoCI}QzM{X{;jQGY7c2#Bq18&n#%p4`w-F~ zin87wD8CxQq-$j0+Y!96>Lm;B(HB7!cJ(~OO`05^|Lx^m8oOCAE>^C+LtYBoH*f0u z5T8BF|K7&jrS*fd?d5!rPmzqfv8fE!$zp3u6or7_)g4OPm~VsVTe6OohFSTPR`vJzPYA@K>!J* zhFoOJVgA{3dEZKH|Nr2eQCG$-z-%3U3G0{EKEocien4zQHLTjZ&#kGs;@oB6Nx`zEl6r1Is8Q&bi)*q4gT1t{SiYVp+uja1`{t4xNFMA> zKB?K#ftyPiUvQf;ZJVbh7Nhp@`^>cA>g<91*eX~?d&Gd7fC8%E3tW+!1FCzS4v?Dl z(6AWQ`;eKhH76@?UOxKVSos^egn*%JB*TVta>k`~9fc#ix3kMvEJ*Aql9_o*?$G{z z(r|GQ37Uhzi6#>$vsFug%M~Q)islg&s^j!U-U$i*FvJtwh)pJpX>-i|1hdq77&T*1Y{_?a~d@R|)!g*z5c1cF^?e zt8}_^e(wQY1oRDn%V<$MN(p8IeH@4eo0V?!XC`{Wckj!1?H}w|&n=PaN^SaU8#$g` zjiA@YVZx1wbj8*Z3p^jwX=kR-<4v>yax2)rl?*d@0i~6E;OHZ;-T+BVKn&Y%>0i#8 zoqu5wWfCQn!ic)_8IxjX!>9Y5iGcnD+#Fz*yw*kxKjf*1mH6-e#2~43fs6iL-i@i) z@xeMzQ5ik02bpRpaMMS8nsWHZvo-h!9bOBG)t3($erDh7U)0n=ToG8yx0V;FqCVb!Ih@6t@w! z>?;&|#ov8A__so%8_O2=*_qRUH4<|N&!W7+)m1kLYF`1uIXOBFo*5h@UP%N`O)`I_ zL5K_^N|9i`a0=QV>FU)&%wdg4Yd#fb~=3BmgG*z@$q-%S1Z0)WTC0}4u z?0(|04gD_;`gWLuSZ7fGqUM7tKE}u=F=T8?w>52^un~>A_oIrEHl(}G>g}&#eP`qd z`snm^4{WmT7_ZF7l?yv_0mkV^LnHhCuz8*^lr-QqTkR&tV}DH7+?&!W zyhQm>1FX1jiZ=P@-0`iB8YG293AyT;R}WroiiVwfV?ftneLYF@A$W5V;YP0vC$oE~ zB~ONaM?t^}?vsBpT%M7$drUO_U|BBJw6f(E`MeEi#i3ghGe|%l~vQ{ zN8iE_BLmP)HlxQ=kVUUdvR|E52w8fN>sy!n=>F7jM z$eLS`evj(Iz%C{{gxA^ve7sH$$42P44K^KO=j3Gk(X6y9Ykf+8g}2-^PVe?wgifB# zmD})SCm*79HgfPK;02Hhd~z5RR^)Q9w_j+Us>KIjD7YC=-EY7ubrE9f=5+ZZHX#Osk( z7!Hv$YS)B`l1MK@`5KCl^mfZ-@VKr3&*uMagHF=BYRh5-cH0?RyjV~zZdCMO}UVU)UFi9j3Y?AgJw`J(E zLYwS|Yn#ezg4i1L!9{&6CJ({*q*qxQe$GM!PV6?8ynzUO6snr%>&&cHbvrM-kq}Oi z=^&!CigrLwc%_EM68}v(!6?N3zVD_AZz3hMTDfK6Nwgl76Qq>1PU-56G6FpdU%UGD z^DaCLo?c1irfX9D6x2N zFudNcO;a3r{o~hLHC;;-eEDQ8^?t%OWJSzMY`0NCNJB914zsUB^n*+2;yFyg2sI&( zuGK#67`p!^rZra{C;YoxU3^-Fhu2PIG?o8HT&ziEk=JLi08BQO&L z-TI>*JJ*(mt4t74eF{}yxAf*~wc5+Zvf73S#XVz>SA;}<51wy{9++HBQk<=VPxVv+ z+jVfD0Hm`8-pxEJ^YC8q)V9U<`#v3`E+R?v0k+Q`d|x+QpRIUzEwJI{ik_YqZKge% zNDwjzxz#a>&V9?GuMr&`H)&>|8q9<~_9Weik_60{mj!%|Or&wR5cE@}+(>+RRSTIq%Z2Dl{%6`S1d!JJG>z_KG9v1ga{|5sSlKxgnOAS=@fBy$0`l}w z_QN%4=Xi~5zq0{;h~3S{e@l06L)z$X*m%*F63j9eD*CE}m-tRQ?lu<9S)Z2j6vP`> zejgU4#}TvYF&w}wkd_$wki`0$b|oI~q=+qXdgC2m2-mJfujfmqKCWgCE#w<^r1$ZU zQ{aR4NC5McmKMryMoC_*Dp`E%94~Xyh14(!V0-*(LDp@A_<#IPY)wmIc_FNY0`ALT z`%txBL;P?MGjm~Ipw8_ z63h!}vJML#*9$2lGdymgpdiZY=K2-3!ng&wl3Xkt=4^7bN4g-_M)1JiG1L{}iy758 zP^MGtmDT7~wvU_D^@9IBzxGV(6ZpftIAR=#7>lUZXt zRMCYt%ep4x=>nxShXEypueFg zO;B)5pvQzk=)_6YQ?Bo#Z0qD!9DOoR_g+uGBB=Rp!oGogG#FSb({O{rK~4;VTa*ab zzbeyOE8fxEyASR*=e1gC7g>>)RuIaq|9p+USir1<3kz%tYRI*C`@6=X8JgiyxyR!? zY8m?+WU^DJp8=6t+>IguY`w8y`UE}!i{QJuHylAuZ~2j z)dHTqY$eVKg?%Qy4KBVN)&d$D10GIGsJo26-0D#dwVlMPuArQ@(Q+cQFZYM2mvsG;t1cEOlJdyCuvULk`3Csg7bMjpG# zVG;IPzQ34+67HCcRW4a&@!Y6fJ6(v@5YkTfI|%c2RL!BU)=z(BrBD1l&E$B($DT## zK*?2MWm0pVmq~~d%NhN)a)ojdx#)z(>^BDfw)#+^`F_qGlI$nYQ{Lct5`{EOlym6nz62LV5H^YWHicn#nfC<0s#r$IjlfSyNK|j-$R|<9Sr3 zK-m0Y+#lGVfZNxGcZ{~4D;8pO#uhycs!y1GTe`@ynL8Q5Dn zHA~6XUeVsm%usy(vB`v|tq$!GzZH`qK%MxQ7RXP7a5gq}k@Z}2+Zq|eYKf{wi%u%5 zXC*tr0smCrPHaBzMM|@QbTcILcQe-snhY&r#$aNqA6kzwK0^8`!H*s;*94G$SU%cf z@JH5nLys}>!cx}n=PuWUWVs}S#0{Unmj1^v15X{WO{v@O=Yekk`XlSz&oi;|Mjl)u{5UL@6%se<)<}5mO(RFx*u8~H9!TMv z&+pt5EW07nX%>&q?{yR4{hPf|d;OSmYfS*X=7xsxf9RL^5#vc~n@QfvsoNR$lt`g1Z|Ik<9lj^>gZluY&MWS}sAl7-EOc_>>>rJTHz~zdjEeZf0$)#vc4k zgXwcVM^5nNf!qra+c~}0quZQS4d2SA{dKA}98^>2gjvy46Q>1F%wBA(cU4>__!t5~|+-F~4@lwt=5mvH}5=3T~ zd3%3CJxQWk2mWavzf+-bApZFTpMUB`2LTH@zE~rTR8GSwZc1QOElRFY2!0#y#NTkK z4E^2ev>R|z`s#=6`(qTS1c4cM7G4Tm#iAZWp*>6Vg_s4bRnzgm(7svU2K1BfI~`F=PVm^pFRQo zCmO1hBz_8U?J**PdKuM{wRAhGhk}qbt}v8c{U}&1I)Mh)IIUxiKP*h8{}Z6Bmn)Wn zr#kVI>S@9aScpTX|AE_cYCXBLSV+}(d3MIXCms*sZ%L*81*F$ucJA~HyWA}WPD2PG{wz|S#I4#J#EAJ#QLu&=;!rSE(!CkcT{lWw5Gk6<%@di!iRT)2DV-Fb`lZdW9+rRE|nxms> zNJW)zbpEh8gpGlAU)$p&E!-Xd=+=7t=&L^?53DS~Q$lW<_4LSFbv{jxr_i|@^3{a; zOww7D<2foT(9z0;gh`iAa|Mq`{cA6AQ(E@`5B9(dTLZA+Vh)6n!oAPSUMK`(IlD)* z^N!bCUZ)zw=H0@H5{6S0nLswEXH(uaoK0ETzC^y=&QVr!GAATb6L)3GzT3$>N_mUE z{rL~p_H3Pv6-uIW^pZ1JRL<9aVEXzaQ*YHanijTb*GQUFY@AoW3aCJ9KU6j(Qa8vH z71aBFJdoVkVut^-XlNVx)$ml<>yLwC_u3oN*FI8=AT4iT(bsIbHP7YZ$VHXAydjg( z>_xQkVxO>vJ+;%HO?_o3?ZzOx8JlK>S``LhY?`sEh2{;}84X(Q?pi{q8Rh&z}5p8ysb)?ySA8g5h0T z?!Zk@Po?t8^3z1$Twi~wGZIlwU~z+O&Q&GflbxY_sdAT+w2|d~6NZVY@c!+I@G*s} z|LyR_V{V0*=I~K=R{?(~C2Mhlf{@6Zc@O5<*MntfF9whauoj~69dLi(25+hHI$sO% zzp1l0yH8+)8I}0dtjvV_JE9f~*v;jfJ%Wi=j9xVnRfuiTEfnv5Rp+as^SpA#r#wsAL{Eeq#eel3J0g}N zDs&v+`h^$0CQhxe#2hb{uOG$&3Qj2C3y3QmQ_a4@85%L<5C0IYZAR%t7h2W!HfzZH z*TwHLclcTYZ1K->TzbIbmZb$V70q^k4TaFGjQdgO;u0PuM`c&8j0iFF(*weERd4t{ zFgx*JJy=|XHOuNc;nKE}ln~|a7d(Gtv{R2Y_-ig6(liVyLn}Q3OBe9jewzCmM3PaV zFJzCLPxAB+C4M;n?6D+RniVmw1^&69^%fR%=Z1~m<0T2|+b4c`TX80xc{68-gK5nj zD_uKx#`#IY%1bS6Klud#VRYl}bs2g&emY4;=W$+#i1eeonX-NlaP}3M=z&OE;>{LL zs~i&rK2Jv1#q|cl@-!#k*DKh1Y_z~d_PjAfv>q!g4AK~6HOnxhcJQZA5}Zs=yQXT5 zUe9%hEtP0XGBT&H@pqjm>N!#JrJ_Nr;C+Xn(VLukRAVbRM=uZ>mzA0{^5^ELjo2AV z>UT>+IOXMCr(0jgnj1(Rq5Bb*&rywYU>oXuSe#hRCBUwlLFA2{+iJskwxy*DG&uO3JLZXfkfV z)X@_y9DQw}tJqg(pfyPN9J<~R z4Rf=?(vQynA)B&N{RS&cDvl@J|9td5vIfZNkCb_8A~?LUF&IC(5ydD0xm!Q_;fY(Kqr_3Zol{ zGjpu&Lg^F*<>$)hvgTJeN5X5n*GW`!Hk(zZOryDva6~?bNhaN){;+>9S9jXD_!F5A z?Dk@23;aP*8dYSa%x3%)uM}@BLb=LHHE{oU^E9xh(nj5xfEz5S{ z@6~Qc^-cbX}2L1lY!-QU4^I z-uBd0TYXPn51XrQ@>IotqEY)8+?X+e?bJN#p}NO-SYzUb;y4&97amr*@AiZQ&5$23 z^Ib)Sl_1pl$K?bSZzDiUTEGPjc z#k0K=HS|~7#(go7Bt7%p^DB29>|*pij?DtXho+`W`QDK{DEJIQ9lJ;1S*G5OOt_T; zVTIFMi;eZKcg&S?c8eJL)MfN6`e3xTa1w98CC%h9As|trxw@{0&%WH6JYMOO!fqmA zN>KCA^!(@?btM}MUQ%GY^?cKF`rs?P#Tl)HCLbLR;MKq{uq4JM-)w`wKnX)dQWmI!kGD&H2&13XsUj-F12Fv4N9C1-TSaT0*=y4<3U z2n4+HxyuU(G|#kg2X(s5h6otYyRe$wHJH>%6*6hc`roG6C23lVi>jzWtQ#fRsWBHa zSh(#`DLYI@G@?BMfOvTg+jk;021B|dral>*P3IGdH2k+xGi2cGK3Wj{?_wABU>oMD z)WJ6NG)%oWI*$4c!HI(`#Bc8NZ*I-0QblEOaG4c{;qJbff!&kYeNf8$W&EeYFZ}$p z|C|s0Zc&SKd5Aopb`Sh0MRQ}U0Wb~U7oIeCu-Y*`Lm%oAMZL&^SA$RBt z=sskCi6wm!mBeX=qVu*csi*H9YyEuEXa|KR6gY|le&p zU8-xMA75frOO|Zf0KNhE9tps`1LLlUu2@?7?~g)otJu`<1h=5KHBq2w2DWhHxS@J% zhfgVY>bIs!{HQm)qv;PNc`gdcjJ}GC2UM5>^|A35)2GW2aAH}8?y2vG9wG-$kIrfF z#s188;$njhHbn$rLZwTSfv67ii(^Z&mypCS;qtrJGYHG`2~RsUC>qJOZlT-tZ5&7- zQZAjnE}&EBBu&(<`2qB1EmOWR*YkCoVOO1Oh*^YOl5Rc+zrl{12EoSKAK2M#1cL!? zjL}yEEtX)iJJG6s3luV%TMce@`MJ9=dXV0Vc05}hvJ>UT!dG8|B(FR{l3aS&%ySad zCLq$0(((G0Sw5=1{)L{*gjJMxlvsOklLvd$m{V!NItuE8?8y~+zZc(p z3(qTHXOaK<7cS}UCE=pDpRM$)pX%GI&b<=dG1w`h3dwfPq)6^0$>n)NY0-%vixG)6 zma9<~_eN+4Iy1?1>WSZ1MPiScSqCE)qp%1V4$g@2+segPoicT_>i!iP5xB%KUH!kcySH0HTjhE zj_XC-{i?%f5kv(Bv3Q|J6WIN)e1Vxkk%FFXLn$n83r3HTbG&o`jo*faOP43^rFE0k zYHd?f*BOR*JBmgr_YTslJSqQSP2X}=fykjU;}?D4@nUiuLi$lVa*Lgi$GkaE4p3~apI3Xoa~r$W7-YxJqgqdJVLI02Ri8y!6qhk zz-;!-0xwJRdy_K&p@2?eY6TrGV8r{Pcue2KKVv6TXjC+ZZm{+ z6TKB?eoGB_$b+Xy+v3x6tE2Bn)(ja-Ql29aN35pzXJ41&oA}!=+IB5efOIq{0M;g` z8|qHQu+X*`#WDQQ;n8dMhh%hjWb=GY+nN10(ZwOD-1X={IuMB%_<~UWW}1KqT;vQc zC3)ga{jXkhhr%cVn@_^@MJd-$m>Z@&+AP%8^1S=u`eXax&c|iS852CkBTjubp;qQi zn@kx{DHh2~!P<2gH2lQFN{VcX$T9T8N3j1eQlwV}VFB^sCV%(P$7y*jqlBw+*9|cD z1M5(~J_*m3`hJ{mOD;m$l*NVa;QI=Gcd;Gq!}v}FW7;e(iBSp))StsjZiEk`YD5Wh zL+@r3u-VMDQF~+I1(EAJ^b{7P*H4K=w*}cZv+kSyKdWDR0yM1yRC%tCnI={`ANg|gi3PpS3+e---Ld^VW zBN8s!9iQ-8Zsj~c*P~%&g(dTiFH0VX?kQ#}+tcGl5m#yCx%Jbk$P#PNpzTlK$eB|h ztc+|8d#QsS8&i!{Nf1igLP~%dB{8%Y~k?(MKdmMJZI*u*ocru!b$RAMJ!Tn zhn4~@gti&Bp5N&7JCJv*+3HyDXic(0{HGG@EY0{F{}aLlN+5F@1<-#lk}9=X^2ZEOul}oxh>}1b)atitFYG56`q#&M^4fcq*e|TGY&y;1Wv(Z+0DwfJ z=5okTK5XjpXM(M66axlQ^!j5V)n{DoK6Pp$oy{G^#JT@O$ooJY(((m(bM)#9ib5?y z^j<@VKQ5u@#CU0^w_$wfaFRNsNZ+8=$fp}v7VJHOW)w!Ldw`Fht15nJD`-0F>~1uq z{1k;O5-m6)-{9PjTIsJ&}rLgZSi6K&&@JUa2-eUzyr{M9>|YWC3NG05^98+FH8wJy&YD(Uik6*oCEQ)Vue=1)8gat zIU~iACz=&){Q`Iz8{leTz#CX2V66aWJ;rVB_H^f6Rr_qJcK$_?W^)M1U2LW87`?*S zV_3{NTo~Pyd;=Rz|3{|(L{;gTo>;b7>pTRnMq^c?+Ti8yxY6RCl}m7lG)CQ#ty-9O z?88*e`kL&Nz=EXecFIb~9=8@f#EBeT3wK7b^}?Dwe{c#68aXlRKeSe}m3oqN-qfi1KgvHFhDZ@ukt5E!N1?Ddun7>#bYm6)O>wP|4Zh9;M$e@q{Eh@l&Xmb?H8x5ov8IUIO`oPkn|r z6blQ_z@^Oi_+(JrtdtpSCt7!Yo?Dca48+oE-kq{!a1^dE`s@L!Zki&EWb%I|% zA@@wf`UJjpkBB28%Zw6s57`z4&M>@!ThRIkn6F2+RmN9aQB{Frfuh3 z9{%rYDOGYZBEj}^a5(WftU_LT1~a34Oxce0Oo51SYCS;jqes{aUj;t65lYEGmiE{A zi+RMtRZc9Jx+K%W@6i1eNiJH7J6y7P)+L5RC1Cg#>&d$@@>H7ac8s#F0R8x(hD9jg zQEGpILl_7l=IE5gE>3vJqpJFE?fzL#5R%Wmy#E{dc&(_(Em=nw4^CYU_;9bWBk?h~ z6v%>!Hztb7KqUS376t8Ad2q5}895x8VaeRDP9WKgqLL7EH88bn3CB>Qxx_i{+47qC zWiRK5&!(k8U1if*;K__3=;{~F*(0?k8?XiF(0UHyifTMW)Hem|Hx&n^X)`uK^+ZPT zU1{VJMHaBM1p_v98}$+@pjyMT1!hgd++J|1dv9x&kvLoPZ zx4GHMn{^0lf|jZH-RY>))MJ~06X!)(eu*!1_Ia9^ZBuGov?;_WI;oraNn^~3cRiq#a*oH5TA zqq-UY#AdJ9Pc1vZm4IOBT;$bC#Bi;QDW7_cVX5gO|BjI8C45niPgx-E4EFPUqCaA` z4mXRbZo2e|Ly`KkzP|{mS>gxwHPeD`mIEA#MFrDtw%NjNq^H4T#ofC(c&e^@q>7C~ zCIZ#CRCU>-jO4`Old`&;Q6|?ON=VBLM7m)>V%8CFDship_rQQu)y3IhEVpl$RsL9Rr7iiDhElR>6CzTL()58SG3C&0mPgJb@OS<79BjN! z{WJP!DRyp3!lcMh=T%Ul6NO%9@lvwt0O*} zoGhHxRxcO7@6{_oI|#qy2`;;Ng@BkR6G#E2Zwv6Y>NkT7R2M#KwiDQ9|L#(1b}{V3 zq0v|hOmv#pyJ35WfZb=L=ucfx{4JbQsVszVW(zUjKruP`bmFljyBOs3!5`HvT09~IZkiolQAukuTdEyG>X>UZPTPbDg#PjRq1C{7fA~U0@fHB@3nw5@&^g> zKFCmRohZ8Ngk(nL5CZluB4qkRkP_JG&<@+-?b`bbP05v{b1J_yQwIXoW?N1(MhMQJ z2UY%62zqYW2q51(GmkS@mi_B~x*ZF_njQbP$~!MF7kT(+-yuA&JCn%`=uknI$hQ-+-4 zMO@y~uHW~^za-6l_9!#{tPk|wK;3St#`=Fsrb)!nNeSd;hr71D-Q&n-(qylu0;S*@ zb&(G{7TX7smLX(bFAH-s5R59K;%*YFXL0l8EREIu`! zVl`QsN-z@^9Yt_~NM&UG>znZBG1Ny;Lu3?RO+ot*KJRSGnUPV1uF;I>Zvv5!5CQ8i z^=$6&K+|K(uCu6<9Ls7?@Y#Zln-?S6CvNS1tEgngS4CQV#&iOo1JgjT8T~oyz0(NK z4z-maBpS`G#FP*gX9=-gcvSJJ3UX>7jb2l~VIyo|F)8PuE-i2)PZr)1JlC;i=TOI@ z+1JHrr02sX6B#eJ@x70CLCLPA7kY-yXwoe)Ie&hJ?i934#fFTW0us#?-h%PE*1olKpROzvB`jC|b!2goc0 z+;`>@M$BRqMoXfyqAf5j_>c73q@e6mb^=v#QE)d9jt0`g^hh=&0ilsnQ_{c(VYv$J zW>#F0dsX~IKQbs*G$0PLA$|IrMfyRo7OYmm z-=r4NoDVzgd!UQA&;!t(;lE$GY>sZGC9E)X{8p^&Laxs$F6-LkhU}I>uUT-TCrHmX zcR0V0Eo{3oQM&8=lJMM%EDDpDAUB+To6GqLw`Ux7b(^$^_%v!Tpt;-)2i>h|NI7FE5FlTUurf{gT0FN~b_cQqP5JbwSxgO(2 z$xH3B;U1;SvqoQGwIv&K>^pexrotF)ECXb7n7@h3uuc}YKZ{R}XR@06nDVzHHEm%e zoROKT9P)|T*rCpTr19nkeVH`?tGqbc0)4(Ab!8fR_aI>opOjei9R2aVCA1;dzsXmQ z^}`#2Y+C?=rWaszxg==eqN69Tayvhgy_+9$MGQDWsyRNNtu`&=)e~{J_F_~A2P0{s z_RSW=59vr4PWW8sAvJD1rI|TOIP+vJISpqD3(3ytWIzEy?K-B<6V&^^`HoAk7EDuE z5WLxQ0geKZBuF+fZ?cA_z(JR_2EokPaXXuG_AT}&4SCz*rlpcJw`(%!tJ|C%2yNW_ z2E9d2>B|@R{5s$K18`rWtkXpf3n$a*K(*#g+$R z7XIDUq531!<6HLbO#?^H{|fSmOs>H|Kh%ahM=pCu zS10xQY-u1L%d9cqznouV0wo%_rs2*4hqH9!B?=Lv)@K z8lGB$XXo%hFR^h{R11&9Wzg5XT2=92YQ`wu!zD#JEM*?z2rL04$5+t)388-3tuTI* z))S~vqWBO(T8I6uBJ66>#3fe^UnWOz8)N+9%BJ9Pk@Odyg}9m@@-BdU<=__RG)?UG z%$qSiitd%eG2#~VM|;+*MF6*cxaPr5F-iXSOF5o=+^7BcY6sp|3$4Ct_Ss74(6JMN@DOrpG|L19 zZH^E!ru`eAzKgo)w{3Qql7QhnmE_;AR5y4|PuDR}U>Rb1@=7#R4@rWmH9vi-Y0eIT zO|ftOBMHXUbth{xb=A{~VnuIWV2wL^Jgw~mZtsCG+DCEb#r?=qk!(a}KxOoCQiJ?X z;#c;ymF&`;cUYQal=nTGZ-B!Iw2?73fQQAXh@7Nw!ZnaJl%%Q|J4jSl*^?^c+Tn0k z#v9FdZA6DU+H*;Q1yr01*R@(N(^0?GtsYe-c-w^)@@Ab%*8f_8A%N^R^Wqqgj|TKc zcCb$D$Nw0^5tL;VZn}!`2gHo;*pyE^xyZDmLPw<&TWxV^9{QX>-`@BLpvA-K$DS3m zbCm8pu+1p<{H9RF#O$Ef=vE$CLfk z{P30FSOu`#W>)ZTJM6{3ClS@2PIj+?%rF)X)1*sm9gtDJsYQ(rR@^7;DyxuZtCHc^OUTi z2bP{Xx%-;7Sg9zdQd3yeX3V|QUPUU4gZ&*649tN%QLx)36JzDSx571fvQ;)!Rrq-B ze(;c@eHJ+nF-M+q@3hT6YR!8c%)L{5bNtMr&u={WYLodSl@zJp7hZvW5T};B7h39) zros_{oL~Ys>g-m9jGw%le#{-a&r!!PG8o`pAF4uZm>CXxT*$1>qv=qXtXq%`Xrd_I zMV)G2|5%ApC2BZg4Hkm*_V-_C&C4|e^PdS24<~Ave#I)kkzV+N7MurL(OKVou10oz z`%5&^$r%~5IlSL5V7)Od>6X-(3h{$6&jV(Vj`fJ_bMgDHPJL&Ovg*{j*Zw`3?H7=c z&;0NXD`H!Gv*zawJOf<&_kmVwNG6KfCWxD{nrcw_CWKC4v%FArl2|B_gVdY)ib-)- z1T#Cxj}Az24-9%Dh^AFhrA?kUNpr@A2vfFNq!0euFit4Hk5z6Fw*vclozo2N3qXI* z7fU9{GhAPVDQgfrQrgl|Pxp_u5Po)?+{(au(Jht(JZ>C2KG2`FL{rcS%CrzsCoPxs z)&=z5K=SnYjSY+KN)FJ_Y;a-v52kJaDOlN33+5o3mlj!fk@FPee2F0=w{N_pz=+Ye z!^6DBX%Ol|pfwF0we)+*W2-o1jfd`a7(Hp!FvMGp1b$hhrY?R-Vl|%l5^F6Ge{vxnUc-=ePN#S8-RNocF545`q} z{Yv=`pU3u4S}X#5ApcF3J#yA0IK$@-tF^h>+p zQ?dqgCH>W?9Z6_D!M2)!LWH8(UFWSR!jQ%#XT<(vGpqDHgN~19%<$f3`9=nPY4s8M z?x?{^JHdD1*MPeE_zxzI6gOxRPGEBkvib4=dP_Fm6w{z4Us&uE0?1F;JiSAtg?v~;wxEM=>ao-Snw|9U0mfdH(_FK;ydg%76O;m+pzeiBcw+ghQ^JKv5#o$R* z@{lW6&PQLE#kpg`Q9M?gIlPpj5~f3G#a1P-0%X!^`9u|*#b8Xi2%norp^nz%I|p_N zTWIc0kOBTW!3-W1w#p(_I+{k3*%#v?TJo@@Gn{ab*W>L%Zop z&Nw=7()9+HZXUC_cf7yx47@0c293iYiD)%-cvDK3(A@&@wL$Yp|5WTp zq~kWsGvho%^o=R)KgM|{au$ExILfs9YG(3c-hq`99$&;s_N0=t&_*mk?^}wjg}62B zUA=WlFAi!QD)LdMX!0W4{Fr@tA=a?F@Bj@bzl47P28NKd6naS5mT1^z_3b@dpJG#1 zacr%{Pi)UFueXi(&@(ICOiiBT8MqH>*WHpYL!+nY93W7lvTP;1h!OOaDFQsWS(^jq zR??e5`qJ|A_xAhPi&K9IjC76NA^5j!B|bFy`O+`Y-pozAIWGxr;8d8B*2yL#H0_bn zceYGL*c2j_a%4zoByCaHS}<^1zwkb~>H-;X0TQfh8Irv(W)$L)8(Nd$Q_nuz?oURu zHh+RlIACUq-d~?%MpYU$(VB4B;`_-bxZ>kZdio(pmE&Aj!yy&TW!Cz9VG)lCP)VzkM)T?Kp7KQru7576`ggP(Q z5@9Ecxa4L2__uyz_nFwpr3g0xznMbV#kMa}dMPv6I=K3o?PBwgK&Jp!e=>Eveeio` zY?du=^wsv<+{M5IC)yYZr$3GMcIDXi7HW~SQVA}kI1t8iQ&2Kxw^RF|gqt=dhp?W5 z26}8jGr*3sA}6d#bB0WlUV{5>egiSjUVuSTM0aSP+@hUg?DpC%pqK}Xzd?*$RM-PI z=v+h$wK_Q>!s~)T=I-1=U z*}oJC_-)Dcb7F8bt$RJRz{4RwSv;`atqbF1C<8+&_G{z0a67>o*n>DuAjy`a}N*IWCTCd5K-w5>bMG<*A(Z8~G$<^g@ zcsK(cKCon~s;as`HJ-u$7_k`e8mHtjgt9;U*>{l|Xo^$4junnyGEdMX+$e{(=!I8- zfp?M&d05gYYY{0@hdL+wrcjI7(^Tf)K`kA(dx4a|jzS3>_c!C`7)*XfXgvRF_HLs-d)R5HIQ^{VFvrZMAy7=}w{%FTOv#g7U#_(M4onfX523>p<}J*4bhou;dTBFI>S^ypX)6 zllX$z=I__Ob|v)Y;QxpaI_#ik(duc-lVfPkQdjz)rbpJ`&>QPPRp}fe`s&mY{SEbL z$H+|xv$=f?VAMYP@=P5&Oxx8L$ZAk8y-H)i(!^b0&Y;g=BV6xXm_(#PV#dINE=+yL zZ-F-*6f&je`wr))9xkf3;#n?fBSC>Q71LCjSe)F8$F_>TCPkYnV4@ zXh9fziK~i)_CbvJcLy;k5+D19@QB}LDOXfRJ(5L|m%|h#$|Tzd2=fjCTd%|Umx~Cz zz6pi=gNW6w^{!)mtA2T7YlrBU9O2QtUDJz+JH`Fy!`~^pf3*JaZ;>dyz3*;?6hA{k z0hgyZ>uVs}Pg7=b?>vLl{F`?pt5Pddx?j-azY1RTQ)PEXCBvGtr?R~uG`h!^6XcuV z_ywNPdS2|@T-0td*S=R!UD8Zv7HwWU;7DsDTo};wwpFxw5!nV=uYykVwMR4gEX3=R~i!<1OCppap0jmhmc1ZX3-rn3@e0u2ydes zriGj+J)cnQg^WKY7-*45zVnHwBZIp3mHPO}A^aEHc3!->s11lKo0^X_9~-wn_ZzNY z?0u5y`2E+O^qngT?r|)fLWu_BXzOZ#S$lN~CN(Nm6*S`J3H`0!)!)@hYy8yUX`(38 z2j$;vAII%i**5uMy?uS9&LHj`j6&*;qBZqR#(HPOY#QzXPn1b1|8u+Lp5YgXz6rht z36K@-`Z9cQy3h%_gL9^rMLk|{zN%&U{X3l@>u*py|A~t)K0@%>H&RT;`33kZg|49h zHrDEIE(x2b%(DnS&5M@t%RF}B{g2Mc^MPGrkg>V9MPL63`2M5@$^-qI?MHvTbzL^w zjd@4QXluuar8V^gr=1&V0{k%O;h5#k)ehXjy+aVu!l(Xcr2u#N^GEvgnSK)Ch};nU zgTPVBa7AKpd;dSO^%n|OpvPdY6xN{$e0Z``=o|5;WB!8CCjOc6O~tO!aGc>^UmBgG zDy(@m)ad_V!oR`OOnl`b&o8Fm$fe0h%6iPSa-TpI^-{k7#D?7#v?MjsqXg^J-cQ z3(NSNj3$3v?{AIMCfG}iv&`$uAxw`yMWMEb#}8ik8kX(muYgxa$VYpjB}CL^H7F*Y zq8nc+W6|+9rR5ULxg6#Tz9>~#rQJEl^dbzJ%OlP&$}t&e1Qjec^IvN=Xxd!?@r3diSK1e z1_lODo{xbPZMeVcXg3s7&$>j5Uf2FuCb5B&w3J~^o-KY>pfzms@V9tE^W}WU_(7C` z$m$7%t)JINAz$!I<7hjV7^jy$%c{{dsFd;z*{;zV&5;t7rk2m+4veh87htgg6`l;u zZWM*b{FYPus>$&xm3oN!kGAU1Z%N?+jjEuW48jS|?Qi!bgAtkZWI+Or64#RK8|%i( z&z!`Fn5w<0Ey-@oxbzViG+$nB;~@2T6fzC=kg4+hE5K7By+lVX*l%fJ?fs7I-}Ty3 z!OD&Rx&iL(u9(nJ;@kTNhlku;`PvqW<>z4Y>;5+CvJd>?kC_p^mvm9UuSCS~t8ytW z)w%s+kn2*9x`^T%)XEY;0^Ql!net1VF_sEg+b&Y#SfjlxD?=ZTFh(T}w_TfiS|Ufm zF(3Yr6`}a%rDsri#N}n~5BMx!8#~MfP^ObLtj>ql0hKP80wEvOx!J<9!YKxRk@ycX zR?yqo{q$YP=y!~%2O`In6)Y;!8aW3p{z`n(a`Sn>@sjDNu!_~S`yKDg0(|kvtKgo? zW;_w_GNPxhI~4^EwgfFERMy5)T?2n)HpG8)W{mddz4cwNTCd}04Ds!S#rXvs!xY%) zKy7c&_%vhktLn`|NP>vovK=EoZqazJCHK|xhH(kX>$3kNFgWw|#TR@ZV})mE+}WbB zS6;oP09f$N9)hb zYQSK`YRdQwV4P22dB-8?zuy6NN_W4@v(ft2z;D0TO(8p>Yln-B9gz%Icex+H(kidQ zFA+zcH^im?n@*iyMo1}SFL%Wdr#_@5fpHkiP+*-0B;ox8B<^TiIDZwOPuEs2?}C2_ zKVG1)P}Lgv34Le?P>}r)j7QGxV*oddg4Rs9?!wJEz}Q0UB+>9Le9}-GaY6sz(mq$f zD6+Y=_0k(c`oHljiUzCkXGpT;oQ8(JL$`T~+aN6}osO_nw#>*4nl&sc&B+_}LgrucW1!F&D;hP4n;?(t$Eo+q+j5J zu|E<${fvt*|7wp~L!X99%7CnJE;(s#Kxt%<;tSq+5;!`SxwThFK&z3nBX zILxU;XQOOyzEQJdcJ>hpX!;JLR;Q4=XzShlMxAQ}lTjY|E9{_+Hb~_G-_`35WcMUs zk3byTB0$h37zCi44Y3A7JMY~-6IZMwST^Fcbc#l$M%O*MDGL~1e{?6~XU*6i_w>Eo zygCQ^1Q%LTz3in@PUraD*{H-;jF8AM7!i^fT-eXAk0=IcGm_N_& zh`ZEBLpJRqq$RgulI6$O-WR1T;QBBIv(`S@g~}tJtz?G`1}5nK&chd|lbzi&YcRVD z3Q&gwWodh~p*cz&v$w=5W%=K|KA|+%e)mdK9~L%Ugr0)7Ym2~k0?CcDeptbBzw+tq zj$LM3Ur760W~ShXvi6Q_f3E8(fjtvydE;Vkvg`bO{VX3W=wXoZFoP`~kwHoz%P;+_ zncTLfgXhuRh0WTZMbNvKFHdk&hssfqviaqrjfa>w!TSiOzPk3^IAUT(vHke5mTTf} zPne=uy#GB9;!_g?M>5T0}u?NYawKmo7c1_mlwgE_hm zpXRMa492Mh%V=9`ODIO39R9f4emFJf3#F$)OOKKVjwc0-X2kT@Al(lhW0Blvi`0AR z>$d$qiCsw9LTur|7rdUw<(#IBZ*F9B_h4hH0(yOpyV!vH{vb>59I~jfE-z=h`iGhF zkf5TBFcH){x)@GEp2&qvftM-k9b1s+%eN=Ej32N8C5VnCp0fbtEO+G?Z9|bl88t6j zHFU_;BXJs;pa+9MVo$vT03v|n+e7fXxN>tKp#?*Ez>>_tb5+)*NPBzUph5dT#7>YkD8qvu*)R9^wv94I(*yDz-Hj;ycs|NiE%86;Mqt|x0-B}egUuP+Y&UmgRQk4K zi<=o%tHgv_5oBiEZR)Sd`QTD`c(VaG5sZJeAhEgO^jUo|k2J!e*zmLEcy;RCSh7bh zBERLB@%jQ7F9dZtu96JWlQ9?^E^P?a0sdsSuF1{vUfp|8zGA%v-NPBI*GjzXBKvQLUUzTn_9JW!?AwvG3^0v8df@VaCo>&!|A5^{e&C^(=~I)=f3ozZ z%CLW#pDT5QibR3eje^y#=g+r0h8}J?=g$Ct6sWr}tpW)>vv`R@oUuK!OiI>?dGM26 z)OlHqN0@qlSj)@p$A2P$z#(5ljg?$t=>2Z*3sW$0D@6IBc$Ch_1@HXL0shHeAs8La#X7s+QEquF0&<@b)I zB0DH#VRlVWxQVS9{>KCW5hba(w#vG4!8Wa9a) z;^eCTwngvrP_g*!yyUKW+uga+P#w1d;SIYNWLq;Oni3+-wR3TmuR9ynX*{@2Mf$uH zW5L(2U-zY#!HBzD=GWB`S$ehH)(}nov?K)+-N>-`z-scKK2XmHVrl$uB<{E7h=)i7 zjZNrky-ODw*e#=KCL+~@WulT$*) zX`-&npIeimpohz#>QG>|r~!@^YGd~Q6#fb=TMGrUF}lzp98l2BPbVHeUw(WGw>WR8 zbcn?aM%wEA-VC#vm7suo6C7~K&2zgR*5w#rx&w0KF$&$xA|*@m#P>dwJpElJ%3|W^ z_&S@Dfa4yy8PH1-FtE29yUmXj9+B?i@vay3eV!277W_Nj-6P6o84edI`Yg?%b3#jG)!Y_In(+r0F!5_d3DF zFh;n06Uz9nS#$r6VIfAk8=QA_O&x-T-39W{`mG?+s;`o*FmtV&k<|(#JSMwu^!Jd8ulbWXOE*8fd9-qK z7NXv|P~FMe#Zp+&hTr(o$T+7oq=*;?nRnwsbM9NLxTAT z)l^U2m(@s_CuB*Z=GTF^UvXH`S;a<@sxDl;o-xTIj>vgEHZT_j8WntbJ>KlxW;anO z96eUEWqtSSF~Rz>&o4wD2BDn2ir}d0B9J7Of9V-0k}^Q*Gd;JcHNs}Y z+@d89ksES}dSlWO>W@8$6}1|B#MEi9Qe zVc&n#HQKFzoPd|^W|!6!-5X8=!>{0|m{x#vH*nl&lMy?R|U zDixadp61c6m0=~SCSq=v`x&g%jT}e5|HZ+qeD4dnigtv|1*PioX1C*=ILR!l&=Br3 z?ok*zf;J43{yyIjdBwn9JVV0l$JM4*Qva$Zv18|tJo=;=?+O8uXojeFI?mUuI3F2K^s}`sWBO~WU<&^ivBQLZ zjn@*qMs5)D%_X$2t!G(fxwn`Vhbbp6cMR5eyu=zDf)S~k&$-p?5@J8TePg|4C_p67 zTwmY*hrr<%$l1k!zat3yG0&fw-?eLE4?Kyfym_m*+h>w^rHN_Na|nfIo0`SpstfB! z3(o4vj$L7O*obH+=5X_}a9s*kt6@+j1Q%ALqCZ|Xa*~KFX)m4h)o4{dwXOd&HA!}Io&F%WDwOTk$y|rdyTt=c1X+lb4fnfK={wEdi$-3S zHib8ELk@yV&9Ywj($DJdWLH;dCY0me=(K}9d(QOj_k3Polc(#pnFHUC5}Wh58*gt% zL0v#eWiIdY#jxYj!gn^tj)jN1jczWE!&~ima9C)TjhPR*@A3=SI}p#DJ6`o~0JQh8 zs>Dt53`DWHPMh~+73A->Sp5HrghXT(~&^HW%DPBU&N16RbOWzrb zocF#qt(v=SUL(ORnBDRD8*=3h*!L5tIpnf#`LGG$*D7u|$^a`Zw~BBZExm?oluEA0 zT1+jC=}cocIG2O%?)8^u`UMSFffFpn<9~NF62B6ET1^;nc-VHsaikQ9Pe)@n*v-`q26@H36qUT z`O`b2qN=+u$Lue+L`^(h-b^JM#w6`A16?g@*>_@Lr7z!$O)}S)I$w+mhpkS9P1V7euwEj$!B}ASV zw7>HlW9nOVuY6MK_Utrh14eX!QKRs%TkBq=vhK)GPU_WDmUn*%s1x3? zE8&Y&W%gR*)Iq~n307ml@QSq+ptscZQ@B18Ue_w2J9NPNhT+aRUH#1+YU#DYN}*@i zCrrR87-Z}biUzIq@kxyYn1>PL^@;IB*MBeCV+99Ed37du7?z%Q^q)h`rnq5J`o3qO z(<2imbeAp`rAJg=Z|(?39&AvcvAe%k7ejZ2`6_)Rmhy7wzE;>5v zg(ZJ1qvmt6&3*G@?v)3$ocYKFh*Ju)NQjI1@m$QN$1(0D-`B9V!_I3(1p>z(39AaO zvfygaJaB>_V7~uElom>Vr&qWaPj0nv&m*|YHo0YoVdiP%;Kk^f-ECjjr-_(^iqgjh)8s{r4yAmuf@ICk7O$zUThuzT6t@SMP< z99cHl+h&nRTDPq6slaAjL2)1X2%A3yBZ-TQUITdMobYv#iQLb`%eQ`W9hwXLv#inm zXj2q;t2_wp?alsqYSl%xx5`E0W-XLLOjQxXBa#>Vu?)J`Iy66c()b;gy+kYm>as6m z^40nO@$QDw1CskzCIXBEuE>~P|FyKiB8jYk^W@nL-!I;H`r2hqpv6KG6TYYau<+w zj`h>P%NOT05!VWSzrLaWxvw_;Y`k*LK1h2)t7JU*+JiNyVijtZ-?Y|f>0qB_+2gke z=g1TazGluCiTn0EGW`KOuAl%E(HJsH0UM!kRu;R4bLAA)>}47?9>Paj07-yO=UQ?3CYFo&tWhZ6bQ<@T;b}$wz6P5#z5=&zwP5= z*HiF0cT(JKN`R ziZYU5EQ`c>4Kvsk<{U{@_e3R7>Z&2Dj<2wOVSd5Klx;AocFS_U6h^jO)OCVez&@fz zCozuxPf=`_ z?-Kn%1{P3*wtnL3AXxT7_D4Xy;ccsu*CJps~%N`hv<$z+a*jxwe9GI<$){YFRXIRw%|Xs z_sLd~Kf&Dfm{Y`Eq%4g8;2|sSS`>J3f7{$XF^oB}>HO_W!<~=O=kB-Ru-?XlhgWpB z5E#tjv9IvY&ovv)4mT`4KMpdtio0;#PoUK&a)$NpfcuvC&bwBnjNC*VABW9!(vOZ} zM)2HZzA&ySqsm18!~YI)aq*YszP&M9F=@-fw~Zxg zO5-TbnOWR(DH}5gAw{{Vn6z90_G=F8h2kXV6@#p=bFkl~+1dzH&f~ zYJ=H}f}FU5@q*j>5w7t9s8ig1lq@S2vZ+UF#RLYru6L-r82|h48_@@h!A*D*mZ||W zLK;5IwA8HR+kw|dy>ybJ3aAJsPWlAEz2$s;{ox^11B?Nw8_6;wK|oFL8;^+IzRV~! z>9LlAtiYrB2#2(=uWCw?`8eyY6R8X9B$y`YzWJAuJlcyeM$GhKaLugM=Q#5yOHgSLsYpdBy(R28I`!Bs7X3BC z0_y>5{~n2a^j~?NrUYyxeAS3Gb8>@w2=q-N@`bVaHyw%xPKn>+>c;FfjA8CVN$*>z zMwco|56w%-P?~M{1q>#45oS2 zO2aI%q29PNtsdlU;JqiMA#uBt(wA4GTD?66Qv`)ze6RxS;`_&|%^iA8wjDQ#d7SeZhJ>zH6RH(t zMut0)dp9c@zLk@B-EctM+1weCy~}NC%K8mfn#&6a@Nc^J#M(rCq1Tk~f+sECgLhqP zFaCW)IglCZu5saLCx9Li`7GV*nB-SQV&H-axzmgFY7>EeIqMKzc1;nj~0O! z8T(c`$ssc1f4#{BeccRDOmuih??**U{;<8T)HK?Mr}9KHjw6JIT+z zj#QcLJT|G84YFAoZMP5mae6P=#h-LkpXmMl{O&1%_w8Fy`7!qdHl;8?U|wQ9?>DhJ zsmP<=rC4%ZWlz-6W;B8}l<-a`NJT+jJI9Dvik}M~o=!>@l!&Uq`AW@#f$EueS$U74 zQFP64Alwm<+diEv2+3k{X6`QLqgHtC5`2_}FQ*zsKyDm%Q&;O{E}>j8f{UPYBfV1I z@V(Lyc|h^E-Q-spo#5QD1l){dtvt|js(sOmV_*+*63%wzyU=>9xU*UA$KVtuBpCF? z_NK1_0%JXallv z6|NiO4@&aZ>;AhkI{V_4uB^?;BhBW!#?Qrg{`>-|=tEI>aTu>+%_|>Pd4$m&ct&R! zo$va!oZdz8tNwLU3`fLeQ6#7Y$Ye~!Fcq9KJ23stgLeW@4$1szQk>!I0<^Ts9yf=x>>`AN~XwxQYk0+ zY!C8wyhNTYpAU}($q8O}(zX`SH=KUbMWwY%{JgUKgrvAM{iQE@d<~h9|7}hN7REl1 zF3D2eRljp9yD}rqg00^J?XV&TD=)3K51fa|jCy|C&0IzNLtMvkN2sOWIEP(p-UtxN z+K{o9hox!`bQb4s*NZDz=TUsRJ_c}?ljrzrJuMoba1n2EEDx7{#l(5(zo6&y3MTt= ztoj3KQv?Cr&I&dr4zPm;x+|0VluhN~4E@$G_@+mxUR4k#`Kud(0ZgM2xLL!gNAz4_ zw|kmHT~70yhjc=gZF5FSVDZQK(V%Z8#sgo^S)>N2H=~fL!MF>dr_@`HErSw-Gvg(p zdj=nKtzMnMrttr!gXHiqGRHa=1u3rNr{WPKb#SpMlj;-1nF|N3a;mj=(2d(T2qF^5k*~5f$#ZS8w`LmR$dwB)XF3 zp<1e)gi_K3p8I^*dIp82X^3}NwJp(xg-m4Et~;GwCuUOI)?)`>LChG!qWigCjEPUh zKb~(#-5=4dAt98Gq-tw>zu)v`v!AGQwS`3jDI;vcrL|c_7PG{(SY}d~_>&d)*gMT> zUUnW{HQ-hCKLg?NLSTI5@Hg$4lvq9Z`MmVJYm;}U1*u@|a;5G+8} z8cAB&M0YE-v!~z}Ld~HtOywJSfYqv8&t&wM2dmIuNCP#!^Oeo#S^M;LhbVATBZ%q$ z(j9p|FX!dCmmT`@2+@F&LRsrFG>8OKTZ)$UvEBK2+>MtV*Le5vUKbxO=3G98eLIoW zq9yOSi3@**8>W=;rO%pNtyHf6@=#;Xm>11N{CeFHK%zAOJ#%`DnIEN(&0r8E>gr~G zArh7b4WU2hOa9fcolJ4#TEQ&ljaM?Z!- zK5@1wuGs|wVO)n;ITWbKcB>MP4VMtCenR?QD_$+BFr(_7pYFHBEwfl*T|c`?WWp+v z)FS@!YLwcpq4HpmC(Cq@L-wbAqS)P-4zZuR;(C~xN#HjReK~^oaMP zZE$sEbwtcrV_^aPKG}DCb!2%3YMysDP;FDWZW41^(yQ&AGO6@Kt+EpoakvM(uk`5) zrfoX?l(1s&i$>2)evb$5ft>E|SDHfn@9W?8#}Eh*CmjT5t@xVUsGGnmzYvxa!=Sgi zl(ah^UMPlHv$xZk=9t8k#L4b@uRIZuR^5eE@yz73F2ddbdAZk=E3e(`U8w2I2NbMw zsjhyCKe2xyy93Q(rY;O#-E9u(rTBB_S6xO+pEE7r&wgi89&spcQ|Wl|K^i#~?}*F> z!~i@Hx)+=LaIT(~MD15Hx^4o}rrkT?g6@PI9F^Y}W2my<0^g)upy^pxL2p3QDw_cl zi0LYR71oHTEZ3h%|3=Ye_2g!I$KdecEf^7a>*S*GNRnLTSTu$@a73JaYvzcZCeZ7@ z>HWu#sk;6%)Vvau1u{AziX6pToBFx3FQ4D}m~+;*jjZqd!(y2F{N|teidFb*Zt^?> zjgG0}UOflP4$UjpkN+fac$82GXRK>V_CA_rdNNTFxqmxm8th=ldXQ_-%?XvbDNhD; zD$CL8K=x-YQbzTLH^;F{ol(~hW0JEw6oJ7Cc+THT-5Fc_eg$h38G@(hMyJ&!QOKq4 z_yxtGZ6#4vJ)=(_4P&KP!BtpC^0(KLx z&0Y07K`1I`A4_QtMrfX8Pu2-*2t9JN|HOXNy!+KG?Pt|0WqAEOV#O)o%6U6-xo?JI zNLon99LvwDH8Gh+_^CPwQwfivhqo^_;l&k~_a^fLg+4_EFGwR>gP$0Cm!#O5v`!{( zTPIx^>G-@065hiNW0dh3p?BeM!P2i z?-@ zRsc^|7WaYHHqkpin=3@@o|G2&Z0w(!#s-~~GP-O8+Xwe@@>d_IoPf=nFrKrd-eMK_ zIasoSY?M?-hH@hx$3LMoJy4gF&9#c327BXXj{;AD7MG|4B0{ktW?UP|V%;4-Q_s^Iks2<0;dF*(cpv^7>*6cvZ$y%Xan3L1hZ zmbPZf-Y_&%TU>S80mT@E`<8W9Dd&S1JAu4Eytf)jyq{<&?Vzf9(`tj`(TFkd(VJye zlKaCNyK>0mMUFiKw=rX4z1kYppAiM{qR$fd6(0AH8XZHM*}VFNqs(}A!7?~pQ-s9A zW~SHYomVly!{xXQuxBq|x9%z~8>7eX-1KSnlAW$C-PLqKkv&G!Yl65!EDan3*>}dAFZb~W&sqCzYT@oqIH#jp?x!YA@MIdMfp(>yC3PT zqQ^%{Y499UAFxCbypfI;<>gfL^EP_%*3PJ)lk=e*k(H!CBWJ`!EcY4mJ|1Bg2~Fil z|G27m>~~IwsmisRE)C6SS-X!PIi-x?6P5Dl8No*8S&62=X*(|6MkR&h_|@;`H!x5- zfCTmE+S^(`_!Bs6^PCiGUpgU-5wV%ezodw)(&3B-S2^Q zwm}Wb;~2_+aC^t8t6*_;3GqcY?;NptIR*O2dAeOwDxd28Q#LfVxEppQedyYyxa0Tc z${c{VPoYd!euC+_v5tCoTvQhLez91M0`N)M`k)FIU4V2Qgd#B9 zLcx!La4dxNcuNkRg*Y|yo)Vzy*a-S5u9CcmHCPR6p2NQv>0lSnoUsaR-e)VEm%CdNI*Sv7nfn7YaQ>(!INwFeMum5^)T~i^nGeC7s6+y%)0z;dJ4WrNOOF>2r@~7 z#O%8I2DUxuZgtLnXCDZE-kEoX_=;(4m6aN$-?<@!sPwulPz=;F9RK1~G(tcPD)4cH zd@vo^9^!|}2!|)W4c@rhe6KG$cJ|)b^M$vS=vU_^{w(4x9$my>nG@ViWMRna zBA0z37M^VWMoKB@sPq{MZvXb9VKgcoj+#V%$DQC$4J-XxOhAKT%VdkaMrl~(+m`#| zULx5Pf_mYA3g&G`*6qb=44|dR|43xxY89^5NAu1&FRLS-&CT)eav1$|AV^b z@6}x(u;Wl#>%B1DeH+KWFI)Sg$Zb*K)cdzg)1GEQACq%-f%j){dpUox>fE63fN%Ng z5q$x%m{Qo?!HEWT+6S*m>krwo(C{U;UrQQXTSYXq_f2FKcyT?bd?k&_tys04iHXs8 z_`dJL^BAA_aJu*b#Dufx)VqDCU>|zrdpKyrL)H5XGMdg86T%@6PcTkym$Pr*|IO~t z3Z0*#Y?pDiIa!*-AR}$ty%dTu`51&O2$sm|U)tw5edIrkcko7^wH)WA8ZmlK?}#VI0}y&bTQsrzjwW?jK~o)}r;eVdf`SD#o{C(+UH>mw|yYtw0s-@M&57oTf#C zHYKzA?-rZ<^ya_^7TQa}#5L^KaloHoSF9KVNJ`Z+UW#z?6Bl@v#D3GIDbRK3n?Ga# zsP&uC5ci9%fZ=dmqU7mek~^)p?k~k}cal55G0|%oKj+>9-58zPZ+4t*r44Qs4fAFQ zl_T09qfY+Ro&{q2UlDWG`11IBu$$cljJA`<2ypzU8t2~BrIy@ivU+1l4+>8`mU6ifQfbC^tW}M{}|GKc*X1Zd&gc#0xH1S$~1I z0kAh#Hn;DILJkLG{BuPFf(S&3nldFXANA5>B8kaDMPSGoR~hSNyUB$tJ04 zbZ(y-asKft8HRypZ%S!d=UJoV5@9rs9If-%mR|7{TtfPWQuo+5hi4V>vI?n(Usj~!YnyG;4Q(C^;U z(o~fs)i&Fg+4(8(gKl8V*jDw5^s3O7-&91WcO)iT=&rcz&S*l>Jou%)ya!zh#z1mi z{`M(fnf|?q-e6ZJaKr8KM~l4^Eb>z>;Iw$~IGQx6W*5s@rsJ{o z&&lP_=0mkVA+dP*P&}Xq;T#_Wk<=rjR3moLBql?9XrgmhKP|uU_&6LW0rn`wL%>Ay zVth-Rnwi&+?_ry5Y(}|!v$00(yN|Nk7gHI}V1+{vPuh7kP&>Wnx!ckU|7UzAEqAPB z{(ROy8IO=76^)YV(cj{>62qbG68!`_TsxVKy z|7tJo5>0?|JT%1>%fn8%VZB(?xPR*bmraAUC5=k6au9(Ik%eh=d&1SK=33^^^-s{h zlOycPuXBVxoG(aN$M+-muSdtd<60+^LVk+p9t>pD)&B{DltWY2FTe;!o)}~@RGR>>>e!pjh)Q2LwFBb9mmp{9Z%oBuk zF#ng+yGiYLN!%p5@gWBTXUkFEj%ukkKzSG}N=_vNOmUQDsnw`-Rb-^=QCf&c>Ozv) ziX(eSq#lf1Y=CX$fc?#7hK4A(LZeO0t%hK4T6^hPKPB}ZPZ%J3tl_|&AQXT#-FdT z(-f@Ud9HT*!|oG;he>alY9f?SEf&h^DH^zwnhWk5fLBDK!ogIfpS_YNP>x7fN$rHD z$5~KPKoD;gog|7(klo3z!em@bxfYAFyREZ%qhtdz_9>^okjfgXmGo0|$iJL%S^y?1 z`H`G0vnaX?j!$+3nUoYdXibL+5M4na&rwmEI*&gFt7H4m$t)QUhu-nClgnIwAD#;O ziQxTAJy)L`kgR>N%L+rp9Jwm7YIL{Ab9Zd{9#72p5)imf-_uloWb?=?K`&*2kS8%A zT`I8%oq%Y}zJPq;d`j5B19NFnz%tI{`FQ#wzs(~33c;=xx9dpBNG>}lKCvDi`i2*y z{6<^TWZUU8gjk0Z-_QP*rLtgn%-%ln)@*;(kdwY*qnt@q^AP+3rVqYRsQ#7TXBf7x zB^uS-AX!NLQTS;=oGMx6fnuiMMG;h913k$GzcO#NervX- zpTn*ewUITm1NR!J69r`cfV&kxNNtzXZqzfikX&#mDlhkN2YaQg7s(#^OMJH#CxQDr zfsg+;72I)MIG7}}rD_xw4oTkn~3dStBmsAbI(}ueRM)x_O#)O2d9-0sn*Z#q!@kLQW=KUfjgIwil`R>^=KB zJo@X(=rYH^P0^ZPn%C@WxarbKxZVH4hXO~C{aSmWaOkTJ4$?c}>a)JC(Ip{vO(Zrn zXIt01KHs^=2xvj{-51#(27@oI(yfM5ye1qXnre|Ak(Uz?Waj;H=Us5t$QKME46_;C zB-1L+WZ}T`n_b~U`rwOs{r%UU-^+j8ad3wbE8Kjzhy+Stb=!?_$MVi=(vjh#JKuyARbduEovaR@loG~Qm^8(N<}X* zY-#34Api7c(o)3T{wZ=4UVmlII+4VkBG|P<5lmd^Q*d&}mAYz_Ck_21jJTI0I7ov; z23$I?0@q`R6&8;2d-nOol~o+wUN57Yi0|*ln?Bq(qpHg$ji!fGrb?iq(?xIK&2|a# zq*=V?+@(I_$SbBA{3A}V@un`+VJ*d}8oT)th6+~z`r8iR(6_NYx^szl$Fk3SoAAIuU6C=&Q6sdec6Vdr~Wsim(z zEz3=+OnXc6NDDltCO%L{r5m_sQFL@whM}=^4QT7I>Tq zcr0+|i=TMp-`C0xVRo?A{P2vLwK&P!H_EZWk)~4%)-ZVY{f+Ba%?l@Q9QxE#Z6cxQ z#+Nvo<|p{rZo^+tokp!{o7v-VhmQeQ#c1bVnz4Wx?BX?ppLc?9z%JzFuL#+K63N7r zM})`3)ujHsGS{9A5WhseftgWYM)XzRjc1>x?;U+Sy+3I>I>2Mb{f2bY(2Y=WJO62p zwKxiq3-VQCHxajn5Vb{(zYPncY)Nfi5ac}QAI)@2w21nml25sQnhn%NZTLEzTq8N zA^83fk-TF}FK`A+O(C`cALn39Q|~y#>}nwDkE8+327PURn}!n3Yvt zxR9$&xGjxX#8t4`C19pL`%EuX4gME0Kz!{ZZ4L*96+?fdfchp)WB45L8i^pc z(@0&FRtQ_vMf{c$if#xd%V6Eikc1I!xp{~r2ZAzBS!}5;n|G2DhiTKuVaOL=7PMMi zj#)J{0cGH2Wrz?_0)EF8jzrE*g- zLv#0e;ajP}hZ$27bf)k1GKlZ604lhd1H{&Ivk#M?^=>iL4J14DB|iQ4NhLQH#E3C~ z^8Yz1BN}x=@2YS_g-t)K#irHOCBCd`dzq{pe>*k0n^0VX^}G7IJN|4Cg5gHdjTL@r zB~_E>_xh)}({^6uLnc+n_3GaW`EKaHyu%P&L^qfz`fTibCD)jzR*2qFVQ-MXp{Sa% z_2$kw*gZiOnYOHpTKn26U{&j#7rHt~MxOzq=qj;h5St2=F`fu>TQ#R?*F6ctiz&wx-h(ScXvsO zq_nVrG)hPaigbh0h}4ow$4`_}8tD>|X6cZYMvw+2qFC z|L3PF5`YXx(f~TW7pcJC*WLj8`^wk^ce0uUS6-}?#N0v8^ff1i3=M`W4rqgqKYc|P zgShEgG>vi;^Uj6D`JL6w)ByR8LDcC?$waI(5XOsHld4L$y5((J1e`xAz{sC#7V7?LUdX7sNaN^#+U_ zf&sfBV7x%O0J)zcX)g3 z9^Q?vGFW*di2DMjb=QAb&dJs2dnn_z-eYv`EF$xQBOdd)=Qt)sMG=hXr*(@!yK*(Yk&Z zeN;LXNLBpVq+69UD)?f=r_`DLSNA#J6Q^ZmYHY(d4t<~ zPZ5RK)|SEfJ{JTFUzJ^7;>J+Zpoch9ev2y`$@8H$nmZ9~a3w`Zly2}to%lYFb(BUg z{alAd*Tm~jPo}JsC&40Qk6?HvCV?B1z9`T>xgxLMtSOh< zv@DrvIC_fo0`>jC|L$3h`FkT0r3NRg{@TQ_dVQzoB8COtBxdCnLeoF^>#DwlKO9(@s`ua0o^NzL1bQ!2bqAohn%rvw%lKn z=7XPM*}gs6N{V@&5YD4`H=?0tqSASke<2YwuoDj!eo^qpVpMPo_*qlru4^$5Dvibe zlQWs8LrWP56_S4cg9vW@d5Y98ar#w%z+VA+DT9A#bV!77yCs#7t@TRb$kJjjx-N1> z$8Vr+PuADJGJ`Tf^|yM(Udn#`GQ0bh2FwO`UkzRptkb*anm_XG|F?v4+J+BarQ0=N zvtgdF$y(p3;>0VuSiS3Oc$d*tyQsSQSxeEwYS=wdZ`4Tz=CE&O9U(D*Xve%<3YHdr z_&kNKzQJCVch@V9aFS^1?j;mI`n3cWE`jI9+e|W|^#E=T@n!+>PAvh8@7-CF$^o6C z7E+t%X@`UC+t7Gg9}-fUk*Q!AvkCnA%R*YCQTl;P(r0&h*dRZm{}OQqZ!7JCtCQ5u z3MGsT+1j(RF(Zx)s10AlQaFe9EiJYL zkIGVT{U2-k{eWwE-}?<-pWu8;`l96Xzg!uw1x8-&KV$I0&Zs^{gfi{J@g+A4$IjQF zLp@ec*FL4Pwv9)+2UeihOJX@k$*?99BQ)3=zV4ezmcl$mTNRwGKdPwvxly!k_;Q|x zpa?gNo0H2ONAdtUi{8oZg|;OuYVT)$);-pmpQoz{)o>n?;flLD?l%+>8$*vL35>o5 zs^%OF4tto^S5+RMV@Q}DXz6_Cp~LkFr|b~I_b;rNBRTsxSwuJ;YY{ucksnU)j& z#TBcx>RAc=@nb_tGnF98`IBs(C;Q)%z@8wcae0S>vCVkxPYn+y@i&JN6|Vm%PS<^p zD)+A?%LkCsr7aazljG1$oIqIjqjFdD89QgoMOEGLdvD`MU$FH|PHlFT*{72e{-YtRDl` zgJ+PzJg9N}d-u#~LafM`T4KV_0>_@QWzktHVDsmejyRPKZmCWKhMgUTrz;ld)-f>Q zGtP8jPW?N2^Taq|s`x(rbNsx17jl{PW5?P%=oSA~^tT)2lM3(r$WYUJVMKRHs)d5( zvuUMAy!JwlC5#gAVTGV94!n;A7Z=VOE1KIU#eW{Z9dXI8n#$RFGsxZi)`;xrKJdSJ z`l}LD@hKy5Dg^MQzf5`I$Cz-Ns{#|JuYVkDwok2A;mPUOJFusJxwbv~oQ+jt#C`t6 zAnPVX$Ca0B|tynH7X&T?WmCTjsKdNTnvC!hT|l{rgiBKt6Q zX>ML}KE!opwTnjZT(8VO|3_c>dA!ap!>CGaY7{!0x-wpEoOJ75y~_aKr$KGI+%rPj z_9b5dD#e(8gIE>k>`$IN?Qm2fCOJlVBqGSgmJCoqAIf3_xplw8EcLo{aR!Kg_Q%~b zE~^o0`f}`n<~W|4Oy0qGI|;z|^hM8eW5FAY16NYjfrgON%cMkuO;^0dVNb!@0kbUH z6hr}MVC^(q4|pp9vI5F{J-oY9kKYKUvODcRXY3BvCALlWRvMiwH}kaBrR@VlC#bdM zPh9zbsavgTovKDsNqfs%ZL9jJHmtMXWctner_%NVr+h^CPPu~YGwI1UQ3uE^54P05 zxl}irRxbhYKY=Sp+Qo#BJNj3;C&U%+E=-8lIn+vV8Sy28gDXt^S;i0g;df;VZ}ZC5 z>C2@UBG#S=29BNiyX4@H(}B>n4a6Ek_SQs`NA17Gav9QY#;9}qG0$q^)-lOleD@)e z(bJkcnB*09vLOuaQXx|e+2VpsE6w)rI(sdVXiHu8+GD``2du=N=B73TkiFA5T@TAr zc!ng=VUSNDtlY*LzT=?Y;?NHd^}1=~=lIP=UIpX5aMk5xraa@tF?qlJQ@V#ALt#BGF#v7v(`@U^KgUcL+vMu#;z`wx1?#hli*V>;@bPzFA>y> zW2C@bTYSuQWgQ&G9l>sd93&;dkgh*y_%D{@PmnmV^gXh zztKNs^I&2nrce^%2T)pg7{gM@`MtA8S=OJ~F-#2O&2x-uxq!9IPoZLk1d1K%qrMKIDP zE4*bm=59(WoOS-UldrT~I7wY__@k7|uCM)rU66p{fZB>in5np%53DJANSy*(aWa=) zunBg~#EDLr)cQL4Sn+u!*S}W`C`flf1)U^K1IFl;*i-T~=Ld9fScaYNyp{d~@roWh z6d~G1klC0<3B0fn+-PP=IVS#j#XMKZREwOdF+s^rZ=om*Lp|u?w7$)J#FC}c@t0G~ zI7W3mg=t2 zlhKFQ&2iwM63B7>W^L4gpQ%J#YVrQ}#=I-|yjYMaA{7k%0rcAlD+*-+Q*jiQ0ZEy!|dn* zfh2?m-OO+AMqkdYCpv4CJv3B88WZg#k3M+|yc^^SZes%h%v| zS%3acl|*&(*@$>vAkHAo#P7$q9%^U0W+LI0uy1b7!UwHgBm&oOU99GdU6V(|cGk87 zPmuK(iVaMpJdQ6`ZGD=}gTI(Ujw4AJzOQN>W7?z9A#kPo2gyo;(qHzGyw!GzAQMH< zO@4W|e)r8cGCu1DFUH%gNt5)cf$R-1Obcp$2g@$xd=B}YGkA6A`#{75ZInyhL{Xrb z(nXV@I}~EK|HHp5%fAthwEA(0_zAKut$xEt?qmwFIc9!7tMKDl{OSDZZdMTi&-A~8 zg7lT0T_EFzir*2PVB~r_Pyq|7&|A1?GQ$TWQ;>LY~1G=GYS1Ci-9D~QD$2TbEF z*JWf#o}stLi5yQNgCpOBWsW+0FQ(1o2KpvV9q_^b_osUx57*1Z?+8sNkxShDM`rpD zf;X){)qlED$0PmUY9>KuV7l8q*DY z&)}$wjg{5`=m&eib-?NTNv+#35s81Pl%7n94XTHwyaYGzkcYK;n5)j?-L3xl{ zA>m5P{FoqrWj?6P$Lw$RkgosKnQ>xh5gczvs4 z&SQlm*Iggpp|GfS&amF#%J9#wonLD_p!UfxV(r+hyP7!Kyc6PYL8Aq77NNm;8yD{koRv+WezAm3srRy~m@pSi~E&9fkrfkrgvDxWW>S@6tJ}Sz5uySZUf&>`3To zk5zL_VT3}UuMd}bEZs43fv5jAN*XZ9zUB(#D$^ya;jaYVEw*-H-pk<447P7xUH7Ar zF(8s58)3=T{^k}_cn0tkbH!}HrJaDOYHDk88AMg1#Jwddh*SMa? zTn>fE8M4D_Md@QiB!j4Qb~-?v?*2J)kZ+;9M{wDX`|VN*t%E&C{V09)50rg#L#0PV zMl$sHqH(&1HzXlBL4E4$xQdD_p4jp6VlH;H$N0|G+#FJc>{o+K9?49x*zKVacAkca zB$LRl_TrT&9sxm5?VIb9+^6r#Fka8v>niS!oK6{FL_S>t@eW8i35L;or-)kA;kEbx zxTXfc2kQnN{CNDQbJo$}&WT`I=A)L*^Q(?TK1^( zV|sf;;B_nmXvC2zm=2kqY}0m(!-h=-{72z~RyYSRGtVR2({XqW2I5^oU#qEQpAV4& zS7%67g%-R9${?YKY1n!D@fvUc0SDB;T&_DGZVDM5o^$$l!2ozw>G>67m`hQ*g1q8S zKjZS>Ji`sqP|+@44l}ohxc*^@P-^O8A2Ni8G^O7;{JTpm;=FZv$eh_3lwCPZ3LkO zaty#?U0v9`*eV{EGGBJI8Gpj*#Q+SNvAJ5EZ>z_+Nejd+VvbwdR8kb(utnrr` ze3+F@5ON!EwBD!kCNMa7ahY7y>Q%oClJ#Y+aGm1~ZV;~C-HU!=v0M^|OVbGV*unnp z)V?DyyN6_v-Z-yM`&vefp!#4=k4@2JF4bsFSoS3kKwEvP+1~!W$IK@Wg!ns&$shpp3&^Vz}dC+ z5iEx?XhvMd2Gt!q-gt$84s`_C>DdfhUayfsEkH3nlvOf}e#2FnhjGK6jg~-a;9sv- zSWqv*;Zh+7AQ2%*i%To$6+Q7MSDw%-ka7Wa{Y$Wx5eg=Zc&&JY{H1+nZGr7$ZnyT5 z{B9o#vs{Wo%vK*O*u5$FQIaN6rP@9|$sY5dVdhbB;%ZlRX*8lQpU)4M8k@!uAZi!f z$$w0}Y7A-_=;|`IDjbx9;k=k}4SE!S!lB_$c*02={sf`${X2IZFR2tCNde~b8Pbxy z<7P+w4TOIT0s;KPA9!)h?gu11h`qY1mHp8-_PyuPmk^BP?_d+juqhCD?b?EAy&X`v z-o~>vE4#Ap2y(tF}@I$6{bdhaa*_J3e$8IB46GtVzRAN4n;bVvE+L!#}88ZMc1 z&zSo~%IN|PKd2;((FnPI06h(o$lslK21%kig4#axF-k|g)6712vP@rKnoIcFxt~wu z1+$Lfcw!Q>^&~Jm^GLEphDxO^=dSdOE^mD1PA2zz_j_F(voJ?y?B@3c_d4XY2cZsh zvf@VR_wON^rsj~oDp+qnr;5c|{*%>o^2LMmZl-?Ld}yDT8h7tx?c!#jkq+Fh=N8Us z*1SFOe0_?IfVhoTo?h7fBrS`SJiF+r=tTqV8zHy4H3K1kDLP2hVCiaZBjO?i9*yV#)tPcFrnC z6P1tOX6}d>Ef8ENzwe(*`hU>hN>o%-gaPz4?wvv^lZh9~!WxP8y0ChRhd;BjabX1i zK4ztKIY#BXeoC)+7C7?;g9wEvU@NHrV$Jnrr*C0aI7F@T21yO#KH>W5sv4Gxa5It$ z5xtMI_ntqg57=APQYtn9B}mL1AG15hv#zzAe&P2cM3%YObdj zG+SH)4tNJV(EOjr*{RhIr**YJN%KAG(8NWh6$+ZS*4H@qUQtM26imyrd`vJCyM{xY ziyG*W4q++!2)&lkQ8!DCjH~VrCvFg4H5aKS>FH~e5^pw=skCz-;eQ3?CA@AhG zAsw`z&rA$!{<%u|7z=e&ZRgK+o6{TH5e|%Q=6)S4l)SF_v0Q`bANBCWH&#~o=*C#V zSlftejrSy`_g>q;viinq0O};2p(uW2VSQ{BS^*RIzl>pDAHZzK%BAI=Q!967n0;e3jPv#L zezJp=%>1ujwkC3h3AF@oBXpxW_ZU9!8U{$N!i3+7^&%wk@yOi#`9h{<4pWw z`>Ow{?*F~2`uZsf=vUukn0jscLOB?Jdc@{J;3}vs^%1XTyk@0AI5#Q?!c3HKkPjbB zMg<$r=}Qt=+u_%GEGM@YZ@1)G%ae(M3$YDECFbz<>DIq<>s%0sZE6^Eq!C|g>y+Nr zR!T$=&n%sitW5Ixl}A!owb1;P5gOu5pYDu1l?-TZIevb)OWJAaLb1yZUD65!lRlX?HyPU zudT3|Vugfxthiq3_K%WNrH46EeHx8RZdfDfB9|0U%!{+4*J@=Q@>Y3BvBN={C{ z-fvuLik|Z{g|vN`WEfSgZ+uf{|KE1ZUDJAr)*{~rWHVLsQnq@=KmUJab2>(Ro75l5 z!O9lssJcfJG+r)5+)3=xEXXOfWC0B**t+=hJc$RJH!l3)BhpeK%oKlCuqsFCCVwiw zgle~L01@hS{TRwUIH6ywFHDYeynjGkP=L=(pfELb=Q#EP5lV;OOHF_NUp{QyMJ1DBcNkyWPM@!k;+j7b>1MIz>gMeCS+~ zAj~+118?uV#|ai0W1G-vV>|m<*f2Uo`ZDg#MT3dtDzmVI%qe2_D)$DMgY)7C!{_IP ztS>*+{?pl2BeK5d8EhkzthlS=X`ra?oTBm+#|$*b@te~FRlsILCYIZxnN3_qRcRLJ zX|fB2%Lb`OQ_b4aWk0J2Ch~o|LPQo4lsz9g`-5mUc9QbCJ1~w7Sp8p%c{|#vA8o1G&kMwUBG|U4}s4{ zNt#=@gm{#B33}}@(Fx81PPT>g3TiRyGt}`^+;a`~!`q{d;cqZtOBI^ChXRA>NqnQv z)hl7m_!+;K3dx@EcqXgFCTp2CSbWA~*L4mqm&7^u7G6Ux%cEABfc*8VIG{pIecqs_afDKU>L*sRaHpU~ zWy2v<+00mO+1Q1o@!&`EUr_+K9$OUxC>K78YI&8k$?`%DOD74J{vk82BxONqG3Gz~ zx7KAOb$FDLgtY|0F;+VT_-*g4YS>7Yl=NMq}O`t{vq)GXO zHMVlebp)QTBkJ?2#7jj4vT*EMmiE0MS$COaTXIaS))5_STj78|ffOpm{sb$PJW+x; zdJQ=T*!TIdPKWIM?jGY4U~w{uQyG^X62MEa-QQ**V6ngwD2zt9xnf= zYo=b(%RKR!-i}Gh5!4(Gc91Z(~HDap}=S#2LVZV{f ziDi$K*BDNNx%>&eGcAZb=#|!dvJpH26E@`vKMs^GhT$1#JG~Y9O-x2s`W6vH*4}!3 zjy^eo!qd*g#>s^^jBgN294$o30f37Ib97;XDi_wjF#IXZkL0aBikIRDEU*&gj9>rye zwpL~unrPF}!Ay1cR9@1qwTa;fYcb6eD7S`&bN$teo4vGa0@Ny`XlVip#=>C}*xK~(c|tI#H+%}9PkM0hYgx7R?|y0%oCCsYbg6$*4TGz* ziK&^~4P78YmfxHaU>O7HH~Ql)1(KdZ4rb&sZ2QHyE$?iFB^SEsP|BgCfC4LTz@yB{}5$RvVXroi0@uD=)^M>c` z!|S?lIGmFFgu^X92zkx8`q5Q-I_9FY8NRKf>TYY`rnE@ zp4h|i<6I_NDoNndhJkKgb3h(58Z4xN`*`KRb4umh`ab^iyH4Es_o+2rL|n-9xphud z%EW_lUCis{%d599r_)~tz?>cGsgwDboENz7XX_Dt^HWRM2u>!VNzEY?4!HVzwtsqpi6n}-pM@v+1NL_{`C_H?mSuYK z&`;M=E$5KfU4E920t2xMKs(jV-%pX#`eEf|FE9$F6*BiEar1hGdtOKF2Y%{!prIQ6d9I7X2ZE^}dJ#+-)UAB^cSb)^@_xykeY)*-J>%dr zj&=$UCfq}AU5hfZinnDumacAV)drh}z!K^^6BjqjMM|7~ztoWzTqjGBWzfU?V%=xo zac^iVoO1AkVL{AebmPynM=xEyj#G8azBk$|OKN>OBK_F;%sJgWdc+@ddbM(WEqaJ( z3a_VvRZ)I*3}b?acZ?;J%G=dCk^b9;#Yl}+><1u!*d~FH+awih%pa;Dfz79i>$!#=^nh+)^ePEWU*Lk5N60OiK6_+-?#=z4z5oL z*a|~l65Hc)e#XJAFZ;9ckL3Ni@2e?`#YXjec@R0U6hM=<&@E*O^ST{}aI9oBwQ`b` z?`!_Q=n&tDESqt?QRN;`*^5m1^7chxLeO-2m?!>=G*!)yh%_upF_@%@Csss`L}#9J z$bTjwi z#$yQFZy}-Hp6$eOr!1uyE$1imS1_ECCch2cBi&zBrkB21Ja z`~|P7xwm(64Cc}xY--Oz4M!jl+(XEUWvAO@W~vzCckZ)&VyKj4?NiodDDtL;HPCZD z9jm@Z^j;&yCdDZe62);it>4n7WvZcN6e~1k_i`{vz<-?B@agHs3I%ouY>!2tPhRLY1$*m| z&d>d!cG~6T-NNk)*E4w9jVOmYAi!zMtwV3mmi+3hlU?k54LfuF(vfWb()NJX^F?)C znkUix$8}oFJ1cI~cqzavKyzWTb&iG2sSboR+lai2?J1=;gtuHXq{l2xGo?hZ#q0JC`e zH+%uSFax4UySB0Vv^zNV_l_cJ1=;jG7ThSu5&PE!Q$fnLsREKPoo=!v<9M35Uz8*( zj`iK&NNQPW-eWncPCy*}#^Lx=(H({p6m&G~NvsOEtFtrDDv^mRF$!a*XNcj55GTft z{U@qGg0};fVt})6*+_+3VrsO=n#qIC;V*l-=4}!q-}a2a+=Md68H#%knto%34^nPoyYhsxdE) ztlnie;TBTa=0>{Myp3AwB>Qi|$yKiitM~8UT<_4wRAN2Tcxl@%nIQYh>PVEVIrL4s zj8tL~_fS!+8g&*y``dwUpWsVWdx#zMXw6|d1A~=F_Z5ZAd-;+dr}t2)MHMVW5EHRT=O(o4M-8;qb;r8S7t zi$h-y0!TCw3%;p5jc)(FRflg~kg_&E8E&e1AODVy(O{jK3GYYtJeVCQ;l=pSDnx%> zJee{ggH7QjBaA#FUoqi2`3!JbLw~QkXe@yEI5C|8IC%vyz3ct%N=SHRs$JHA5 zw@ZSsr?PE&FymK@hiCssF+bh0>VW7%m_u$11BN0kv=!my;u`j!qq4rh$ybfwq!v+~ zyz=d~K}8OQH6V*}6T>l+_lY?^F~%7IibybpBX)@Vj{fSGy@8{f0w^7~^*98}WbgMu z(DVVEqsAiR zoxjzybpMuj4O#vTdQ`H31_~RxHvi}-BG}Q$U(n(skI7!j<%BaP5Bx(ENx)i^1qW`GYtnl~S}c_D++#p@3sL(A8U4uE^ek|TO}U(w`D>H%FT$|!4EhlK zG3S*(T?jqQ>rl+);y<{-z5BHh|D8ytY`Rux3eJpJN2Ko3^I9}7tg9S_9(Ym)k~ToM zsgO)CdKi%MH!Msmxdy{C*qQtvWNcJ<7yNQU<@9Pe6UN^1XX^1ui&VHYbVAPbR=Q7>x zvIvOPCRbkx7sP3M)MovYTA58M=1JI<*l0B22QcZ72e>!j5>pows#v7C60kYeJX(oC zzXtoFtRGw=&KT%!4Kf_`s7#Q~89pl8gOd=t(kE zBsuaFxH}{Mbo0@cBK?k<@>2Nqc*jU^cGt+b7W{=kjkOKcqMXW?33ydMyj<2zyijm4 zqTiq7E#2M`Tl{ag z55}7N9s8|GgLJ7{7$1 z2Q#O``tKN=@9ZOjy}z?Tju6y9*@>8?(XF<%c60c5Eg1`bCSAi!ZG}bkAj&Tqn(GMeh|yVwE9(^AUXP5RJlmn`R09~z;Fz|08V$ohsBZ~ zYL;py=CU`dcQq~wZ244Ng34wQjuKGvP3qQ477dP3{M64y7hhzZT#?MLF;hLI z4=^czPk*T1*Ut2lUCS+O3ok4NaDXw)4m2}9rN4148vIZlAHp137yU61li(2+wZ6U= zN**>dBB0>Bd&&V%uokdY691tch)Y9lEH48Z$nkiO zsp>~QzRooaPYNS=JYW+x0xJ!0%-<#}h-ZHuN-iZ;<=kMuRtHh;=dK~|v4d3uZ-S8a z%Q><58bhL*&-jdz?vx&(=|i{1fd80d$EH;?+P_S!(aA^cV~bB|B3P%JV7y}AJVG?< ze*Fd;yx;k*48Y;g%!Pd>cu7h6I@prjhDGYLTIzdgfn)mY&%~B>ni7eO&JT*WcY!l{ zWe%O&e(JfE@^O#Mk9v*LYwtEU3hp|3o&}51!Rt+iU47R zlwY&z2)-^@xB3KZTM`F980|I9TWw@ueSOJEUu7{!$TuG_*dP*Ky;S_|htL?f7YF{L zt=_ii6f7lU8=kN>`C}2fTDu!8yx^ITeIiQyPPxF+7(GUq;SVwgz*63rF~63o>*{FY z{=uWBa9uh^d)QTY9>a!pb@ZrGz(^^gW(V$tURe`1+3MeVUpwi@$%>m6!po*pbY;WT zX>ePU>xcoz_P1~PkYCMynMf50TAH^7B=}5HS+~8fJI;{*AxYY7NvY$8zq(}SaUqY6z6)3Q=Ja&vc%bRcy zz7tHUZew6HGOZAh89mOdJEA;%5`cB7-YL$UDV_}d$(rZSmH#1VtE(#!bo9(S#tJ04 z3HX9Toy^_yO?u8LxwSw#pW+4K$T0n3@Mc^>M$eGMb>NSbsw{u$#PjCwDNAeh5sh1YQiRdEk`I|EXh!dIka@IYp|7s(D&GUf+Y=i9qdS@@p z-IZ)o$N%gbeJUBMW)?ni3mlK~?P-&r}otk4Dz2f#kidfC?c&*<_iw*?w9+}3I_P(-`92+cg0J0G2=X*|I$tpUk6(x zj-`)RNW;g;$Z~I4&(*{{zl&iY#Vg*WSN9a(VeE7aGvxozohzbLt^4_*=`4ruMam+gZ+{eblSX}^UkXs)Or<06q zZmCvyL!xoHAE&BQL#Q>vT$y?Aodo6oH+M(SIt_Hut)`!(qRKKJi?ZCkk>P*x<3$Z{ zFh0$%vcE6J*%bRX_r*Q0GEl9FIyIpOj#0!OZi0h?YIxo47vZ$I6FbVrZf>UhL29x* zjuB)zx5#)OI*_Y~;n%@xo-c2k(_0f=c&C5TjDubLHl!-iS_<*t(&Hz>;sU{kXsEI{LYzv`jKB{;xI9V2TgSt^jli4UI#s%;9G^zg%*C9${07^+ji1;7;4` ztId09ST23Q77*8kv?QIk zQKkoAZUK^5&VVcjj<8Ukr=wD^U8%70n{`rayyWao&SVCQE2~-H3)MQTy13Vo{+A|L zVb)Y-Zh3#vxsu=WJ`d0&$r6y(J$(9A?$i09|9jx%i{U80UU5aRf3q4<5U0mgzmAEq zc}e|Wn|6?LilX~(^t3FHr8uV`$3e?{XBjJkO|&@J;b)|F+2MLR60p6B6J@_7z~N%Y z*MdzjgB@bPvx|`Kd`(C2Y@ukE>z*`q4y#jG6K>x2vux_H57c3)L>Er)TuUbO!cLM?sDIJF2oO)*cGG6Cn#b=`%r|TSR z-moqm!8lOQi&<9U`8cKZ{424pjcTvigUet znptS+!SYO+#ag#{U%>iNF^cmB4S&TCV%4e;UduG#Sxm*=VDzqj0ZxdI0LdmDRoyPG z1ng<;>7)G$Fn}5Vlgoc))!XEBTVp+o%knFp%YU@Mcf7wouk#1iBT*PBl^A3k8_ym8 zK5&?9dGV|)I#Nv9V1DRMiltz~Py9c`@0yE@+axWnt!zQi*UxB=Y3V2G5A6PsteG3W zvXXGBo)#^nW|3}hm7UUC7^yI zI(n7-DWpe6M6O?aI!&U4H1C?+naBEah~gA8euTi(9HS8C@>LC)+L!c)EL%(Y%XcP5 zuqN&XCP~xu@Rol7&1Z-Key39u|FM0%(5UCxhsWZr-`D1?Dcq8G4w5xNZhU-v`#M10 z+I+KB@WlqdIA*TktUv18i;H_+ACOsG9qVwZH9G;P>P9RvpBdnd1|^qh&52X^q=BD4 z2WB7#K63i>=BX33*QJ1sL&O-VkZfS{PWV&&Z${A%Os+f?ZV-R2R`^Z<`abivgN+ov z*e#yf=WoB2Y8||860Y>mIRYbV<|;Jn?5Jj8dK1Ab)dsPR@-At@7$}9D)LM#3`%vXY zv+$SH{v*}{x+46Wt{j@FJDBnB&s#zLmmfjhE4%!wH|HbLwQWn1IAq1$Y!lTE0<7nV z4$Eh#@ggA3iM?5fw3 zB-xdWF3+4<<|k04BQj~OG!~=s*9iO_aB>9^PGM2+yC&ZAIF1=F92ft_o6xg<_l=@(zEE`552`IZjR#UV zO@na_$Zq-vd3a-(W4cGvj!4>gS02|;3|A6_CE&3?UB0LBg&raGCYx)sfAm);meeUn z5s#kTR8pV8(|NoE)XFLFWA6p2SwP;WpY%8F&E!29tP3(1$FSm3x*;5_Ev_^yy${c! zeiy{O`s7IDa~iv?BztUMCa|geSB+1X4~LN56<<40PVNSQ`6CN(8o6JW_PR{ac$rxz zxk~eKX+FH8d;!(*gXAb=8m<~$DqbJ;Z!hK8aF8}q?BtYMyWXQMT&Xl+WdL_8fYboc zs6tVJD~hZbJ~h$4~BcaB#5uA(mW$&<4>+W zNVYk--At1+d#eBcHdWp@;zH3U5oPo1v)|G?w?|8*YldvXE1M?+k}brW2bZAXbcImZ z^>c);Q1K{tOT$<5%&2Svai+*T|38&$$TP45X>BAtzLL5}7KtaORYv>#S@FeEv||cD zA?Fb0m++{34EGbv{sHQ4%HkR!mt;&aZT~Q%ZzbN~1pem=E4rFm7 zVkT}N*07jdI)>4)%e^(2gV~@8IoFWlva*F&R=MD~ED0gIna6Lm567L(?Pwv{n8vG} z4upw2vl%QfR%iVaUWJ@--e@VPvem6?#DMcbFwBRhbX&R9{Ai^YPR3L1kI}y#*9DYp zB{gX;ZEezn?`62-%yD}}Ucfk;cD}&OY${pLLC0NCa~~tAAfS~ksH*cp%ZVb10 zF;#%WG1PmPvi(x8Pz8mMuRHcoYIihl=ySprq*Z{WRJ-Ksu#;Gk$XA#9&#vYoytJI` zjK>QfXrn7QF@`cW>O6S+WE?s>uuq>PD+>CH_ryLHT=C8z2G8J=W+G3t3$~x}nKO7Y z=0&8_+eWJK(2u3p1)k2ucFzCb=#zWOVK>Y+TXfxbmUqLIs;5fAw8mxd3n!|=tXVT= z!)hE*j~GyMTRAbZX|L2QOy5Ljg_#zzZD98H1`(ia16;k_M2MtuOci=D9Y&gXlv7v4I?1I( z8{Zx&OK{BANdLyDtXd%;^49qkg{Y@~(ZDmMNV?}V3wQ=5UCNmj_1q7M-#=#*kZfh& z|7#-$kgh23w*@@ZCi~*2CRpZp_*29LH#p>*WNt;z#SG0bQE9Gnue^66mah68zq0p zqS7H<9ASCuo66W%w@%g93nn(u57*(T!3Vj&?vks>tg9W#FRSrZL^w-s(6C3vm|x8- z_jF{~fOCuC$%xvi`x7P=v073mkQ4c4cx1jmLD|zcL{ipW?^PzE!6CHO>*>CZsHAuj z5w?|k?pG^AB`$2oaEJACLjFmirYj${1okozZ;D(nfbou(*jVJKy^N zvR@!Oy}Vm?s)S=0YKJ-G0tkjhQ5hC8B2*^{#uda#$@qX3{GLq|xbu_6bAR4OP;Dck z_Mm8@-iIGmu^DTcpON?DEzrlfPfJA!8B`Q(2H1`>C3G zjpzA;xaaOIU;3fuCtB#w;cR}7y6B&*0P(VA>KcC~uJT1sC6qi<%CXlr@>$*U5s`cE z@5wB|G0+Qg4j1`cWCG`pb{{#^r2%5DO?(`&zMj3138w#o5vkdVfZ4i5PXG7vmDNvX zNgfEb(zpzVbdda$2#N`>gTruC(2}d6%6FQ|ATo!8D>_#sNEqWW%}xOvUBI~w8IgF} zhY?dXNtPIiA`nlAoZ?IyluV=vqwc0?y$Q@H^Vd!h2Z*P28Lc0e&GWE!=m zJ;>K(Y>x-CY$Gd0i-$(|K)@<81p_b*3Lt>K@a)Lm>b4kB@64^_QJCuk!skk*ISm)^ z_%pEY`UdzJ%v_klKdpERp~{>Y6&qu3End>3W75*>UC+@v_@Q#3j0R72Ju`{Pb!cq_ zTT^eTY^)_$fj)^+N_ivUAn$4U%o^i6w(nql`3_?H9*!x5H)(N!?#~flWA+s5?Dd-XDM(R&>}QKGUF;L#|j3td@#G zuNYz~{no*cXvU{&F#K>A&ye#1+=I zE7E(Okn&idhQZ-7^jA9&K1B#$qm^@u$tk*hzth-t{G%ElkA}ugu{nA+FB5{P&lz^B z;Hi(yN`V39_rxPq=*5O}$b-h)8{P>Pq=I*eM!qE$b9Qa!%NoP<=nVg1P#VBOzA%oC z|BKD-k|%$w$oc6p?eQL(!M5*aRIbAEG`1oQ{HVA7<+r+kD=KYAFIe@2`FPA(++EGW zsA22;q81_RU-J%SIjZ>JO!f+J4#u+-IlHb{PCVncNG_x5h`FDT12^}3SUN@W>=geN zP_4tg=;F#s$|g^xYPc5ZvTw%kMe6P4wOU}I1b2#XwA?@ z!`a-HZpxG&9Sz}&jFEeH+Sq69`(P1Xi)ZewC>L_7cVxvI4_3|#x{ziQ*D5{Jbu&Nh z50evDmDsvsj*>^Oh!KZuIZ(^1?b@{=HSuxZBs4upo!)W%>SV}t_Aag)i&fW{6?#!f zP_!T-%v2!<(B~J<+B4~eWKTn28@cV+ZzP_yGy`(falJA%4IZ|Ibeo;9w`UOTw{{K4 z5`v~SB)`whr)%-Sh0FF%$23wFy|7~CoKM^%i9-_!2J_4NXX%vv{2pXA=~dL1vs9MuO(n@hvRE+(s7f)c17yOA8- z<^ri9My!%}k$B(-?yo|9k%({KY@=Q}pL!?g7PYoH%{LmKGu7sXLuTrg%>%Vci zVb2dwG?sv5NsDM$#=|ZRKRp+1HA2kO>n{ezWUjW7YPsbV%Zy7)%bfs(7XCv#atXwK ztF!B(W)>zPR-nwHR?*Iwuc1n6{wyNhN>6V7v<+P_xeRA`mOzfBH+kB&kT)iEd?2r+ zzr`UdmE@XDa)%k1LLGd8419JDQv8RN_~hOK6J1A~gYDxw$Jl!blq|{booSV9AJH2* zUEi$)pS}Y)!UKU+o`Ggp+DBxQU*2_4jWQNl-L88OvmOTLty-+7qVKt#DBTvD)OQ=N z3y}W)5Sa5UoPbsGLph-nk5cUmx9Q>7mQuNw6`l)%>t-vgjLxu^b`_d}BJFH|e;IUc z0JiU9_K2%Q+vVLdma9cS4TF{_~A>)*Xpr|?5<%&eZylL3d4N= zN~-+%6Zer@iko+e#K@*5Xi0e&v4h`QdABn?JzWXnaxkALgU2LQPBhXiNRqx@lL(g{ zEK(_DQiQUCsePixlLNm?fnNp2%N@)2x@gSK+B1zxDan2??{kTS)9I~sQr=2}8w&dh zsfE7~=l=35=(UTXL!?1Y(QJ}d^uj8ydCSZ7JMII%F!mR(D}LT#SK$A!c7fPOoE>32 zIzc}dd@eunAZc};^Uo7iiCCQ&uKNRYcQ^IK4-my?Hpd8t9Z$Ol1V+IhjP4lDP$g-qX#Ufw(B^ygD_5-+n1zJOq|C26SVm~| z5%NNQ3H|Xq2)WaR`~lPtpogwN75r0t97lYi(^3Rc`J4L!l zjj|wRjl`{%LdUvT`y|jI3tz`0pCjc0ZfUF|nz@xPf8)yLRKKfXg#6SretPq^`0@)k zK*^1c0V(ha-vc3NxkM+2-u}5ZqVpy8hgyHRcyf5vI}W0usr|vZI(L!CreUCS2H_AKxmY%&(n?B|Qq#zbKBCJ2QAuE@4!lw)kCjb*znr;g=I%uU`^arik>l+9Ba6G` z?;l^|UmFKV9D5glP8&!-NR{?5qPqM?Do)l!%eZP*Sn@HOlj5v35(JlF=w?2<;?Wh- z3iSDKP@(59!9i{-m_=QFxp*~28iO3iLa>08y|r_JxwDL_udPWE?(6I)%Ux~7?FLI- zg*3#!aojkYA+e9)H!(>6FLU{!fwIF}2AlTClT?+Z1t^u@DSNj{j8Nt-ry*?XXHHTg zOAq+c4jj4$v`vK*u9S$pb{S3&}0-4}|Vi zOChN_r-&J@rzxle(A;&$R(}t&xMdMJVK)!N@h<>Ni1nk4H8fl&Id>=qxMy91W?yznvp_ohKSs zh(9}U4-I!ozz>QOF7Y?%D*R%gCQd2b`Xt<8wj_+vtY%iXn?>NyFoG*%;R3 zoC=LzVeMt_>;>@j^9foNJDJa3=g+Zpb+Y2B%O<@bIeTb;u96<10b>}c{tj--%64rMGyh>V?3c}lz%_6-XQ5UC;}!(eiOn)< zpW@ji)N-1AYeE2t#=^O~0HnUX8@d^ARk`wG3v058OC13t+nd{U-R!8V$qe^Y$#Rus zU*g3QKu24&d(3C4s7*698?9~_T}h(ATyC+nS-jxlESVESjYL%B>62X`MuPX=2FZ7^ zhHJ;#nPbt#nUq8IZHbgIpVGc|YFNQl<+^J`@Q6^xV>&fHey1EWC9wtd#N4nyFo)W? z1dG7o3)Zp+DusS{%wb+GD0rP8mXE1^LR+f;o@yXI(Sobb;&t6vd_2vUcho#xc9R}F zSL*-fhAD{?HM7)@@QgElbB6*RQJSkCMf|th|B;|8;?i zVRVxC&JP={jnf(W(>`qUvo4Jf#pEDjnOZ!ILS$8K1B7^-F9>*JONg2xf@xY`=0C2x z3d5?d+5W)Heep%j_*J8%Xj5W`B#=DR{T zADxHng;15~0_E=>IwBQcoJ~UOA0r+S{7smU$;sr&5lSe>7~Qnfj+qfZ^GLlt9~S?Q zp({Vz+CL%)5z1+OM+9cIaV9?Bo4g2~Bt18|ac}yn#JIKsry5JmFW~+jc^4anY-Dj1 zJ`r{IDK)iON@2Hiir{jqjnxsz(5zWmVv3MkfuJ1U&c3cAMuOqFZaQ7dd@n2Sg}$C( zMm@!#AEO3}P{cQ8#qZa?Ikpkg#6sMVN4LR;@*X)mrjD>;-u&3Xt{StTm-J?d=_egN zjPZ=c_5vE2Q6vw~_k0GB8KA2a`RaZ4`$`orqE`EQCv|q0=f_d3kKP%&GE6%%Mr|2}tLF z%^!@Io8c+srxt{=mNf|!QC-0D;sT0e9Pwon zlW9~psf*w|t_BejC^n?C>ryHZ->Cd}_1mD47S=rwuXl*7qvn`?VRd(gX!5Tto3Y)D zhEPG-O^R7@3X5)!w2GD%krgs=<4nl^$YH0ox@oOYhEY4L6~UlTIS7vEoz z@+atjUM?Q7jDz2>#6l@mR!9Zdkq3DmofGl$bHpQaN$gsLG;ysT8^xO&6Y{PaV)GVB z{wlL_v%Kj+w}Rg~vM-7~38@^d37*~T9FF>^y3LiSd4-%EF<%ggX_p}LX!dA zq=kS#=6uF(kJBZi3>^$)1_KHBw39~0N>I?ID1w9%rpoWUo8=iyURu z=#v{sU*feUb$R7uP%#xOuIr!3VKfL&@`{j0_LZ7MvF41x)i?4ymdtsSC`Y-{1ya#+ z{o#or{JH_C-beqWYfCs}{i&R1toTT2D32g~{(=3G?h*Fv{N<1SVAdh@KZin;D_Bpp z`YCIY+njs$^CfF8)^4Kc*AsINkK|CAx=sxScAd|_%Lr6o4&j%X-BzQT>ybA=(#@_K?R+xeRcx)uuD#!iL8U2f6%+sS8W zIXvB)(n_ISM*{s8TILzzBgI|6&g-#r6~)CHghr-|bD!6zXLli3!1#DPkiH-r0hHt8 zTs{NJ(IckgXJYxE;l~6^jH#aRhq4q#Y`+enC?Dc?;h&wWX%0l@>SjV$o=Nf>%<&cn z)~7Mh-QIL~uolrS?X^P~D~%t@I6I|(cqvo(q0#1|-00=}V+}2_sZHypXGax4-hR0u znSjuVzJC^2J7R2N@~7l zTEmxF8MH4c8E>T@OU8DIrWyiDN)-^WL2}`8Nu#?5GVA;jdO1NW>vhA$Y1D!H^}4NL zAC**OEVLpQJp`w>@uP!L!4=BnVo1$Kp1u8;UG7@i`>xi^lf;=X6l7L8S~sXGcR?&= zHr($|WNX?;7VM~0j`$v{PpVTb0Y&pC@+!-*P$E3hKabaf~BzTMRJHE1` zC=3l3a+No9mJ#ZT4W}q^_yMV3ph)gI4zcVPP`dKrc#&-n@u`u9@^T5aJN{p+q&IlmZd89oB0|x!{d1-kJXkEXJs1w-2VVK6{;c&t?hjN>-Q>zu{Qny1MToNEeO= z8;_qO&FJFFuh+7g3;&9Q+tv;%PkNUY-YRy}R4LE8Z$R|oFy%fR6ih%}-6CxBL2BAg zkJ+xlyxD+!(UJ1#YUxrcq1NKpwi4jT4T`!QCv}|H`E_M}u}_j;n>Uvrg$|Zf9a|qg zBBD9{YKW!64e{e;Yd7OP2@1<^=APJfDfZD@@lwI-cMLv@oBXE|r!{DodsGeCgO1Vh z&s@o3Vvk2ve#=*I5VfM-x&}YgCChib^C2PR0v>gAUq3#l_GZ#D^3EpO@mS2@Gt;5e zmNqfP&)6GMhFlFwJT2FoW@&~}B5etdZqC~C_Iofx_FK5qcEsCYa|PZ?hGKBUq1FKE!#d)c~6=t0+INxd=dGI zsKbAa2Q=&|lbTvrB-+NiA0<6*OFjSN`T(auUTWTNLXexzbuWXUXAAwUp`rQW!q5^- zH-qSGd)qJ(;V34%rTGSgPFWUWkqK{qr1Lhz-e1>2pJ=ylCxr)(xC8cDH$8@~kTGf>|A)Tw?Ysd0g&N+%hm7Uz=csEFOqa~p^0{)} zO1E`ox6gg>IX&F`uNz=`+mY>?ADwaAzhYKCntL^VtS|o5SvdZpCi|B=>fH6o4NBC9 zh`tW!`|2AY%<@Rj1a~)DU985QVwS6vhTJUVk~M(!)7|bh{p%`88Lbo`P!5YmhFiZO z!+`mCvIu3oI{ThN(OVfSbEiMGdbCFbt&F*4wb8Dber<}>aRy934gwGL&JVXftN}6% z{E^(>>-xpau=8_4>%h*0bZu?F{Q(nd zt|Y^L;OUxUBf#Od#=jN};|(N^Z5qyk@L)L0*RtZP8NZFk0)f^?jw>0siqU#jOJOKL(bH%6ocAOIU4d5T7c zUY@8QfWe0=7wDsq##~J&^&K-C*g%?R+*ZVO9`*g)%G=VX&Y?v4{<77wj;ELFr+Zr9 zRGlAx>pxe8tLB{iwDoZV$7}I4@7hDVrWh{hRb1IUG^}6&vUef&$UrrG0h(->F37rp zSe&9g*F{a&UP@^7R2BV8*|Z1byFY!GedkFWT}=DInYh$@ z&TZM96!wF|FxW^6zUTFi`g6H=T)hA-H&%}xUOMQh(nDPi)N03v_s*)MSey{}4@x7aEK+6>juVa;6C;LkqV zKLCvc-$O#Z&rUjePo5+}r?9^9{*Nn(nXY>EjLAfV)I@h#$X(HT8pTWayo*q*vYEINvVv~^8S--I?KU2Z z4=aJl4@FSR;6_KsNCh!#q@$ZPTX_g5?Y_G^U0M}2MTG4UY;GU5=~wL?mdJ}Tf64CB!S`&w3#LGw;L{KIb$D@hHj%x#`ag-w z`>>O$ZnmF=zE24a$Pr}0!Doj)f^?&YP*!^C0LdV%q)(Xs*Y!YGUOb~SYrq7(cW?NI zc(ukGH-4&7jb!|Nrs=hbc#5mv)v=34VnkzXfZbO!}&dZreXF4q7%*bh0PC z^)?8i8KOf*FleFS!6xDc!#Ii|6^SQ4%?#5DK(j@X|cLf@3 zA>S?v92eicuiQ%bPpcw>V~p4qTA8G!Sm+U@(-?e%(!1vS)N#u`I6+Q`G}MzZ3h5l) znS;+nnaMS7?>HVA7?YNZ%;fs?>M5Gv(h1$FjcgQf8cT9-2$@xCF@r)Ln_x~M%+)_{ zYbPO-Xv>P8(4HZ4l=XtqC5ONsjlz)5D(~j|lXWN+1lGtymv9C(gr|G|BHup^aMK_Y zYcJLao?exVvy~P$NqzLJ=`f$$@r@l}xg_d0DTX3OPjRX_7zbSlG=hdz%y``|n z*%c=f>gSeo4mAG};#9Tl?-VPm2=LP-HbA)EeIrt4&gLnsZ{X3|&L!P%px>YK$42=U z>vT=}V5V0j@@Ps&a~y4@Go_<1L_#JeBL<**vo?3RiFFtH8UNDHkW2h7*R9=?^NIxa zF8c`h`C6K)OL_HmchvDk+yEl(ZuP?Vf)gauW~}lndWyiaJav(0=EJmU?~{jar+R6& zx~p7}{2T(~OIu&}5TX~z;;%8mf=RKn%jOV&n0WD|TO6j!6v0WMx}sxYI%#G+@>aBv zdW9?S_7iLT;pV>JKH|^!qd3v^+2li{mt-&ZcZ!?5F5mD2Ykv@+SwWGL(O=y`JZio9 zg;Z!{unECwfWZzGo&+=JUNDFDUrYu<$#(9xX$f1aQ_{TnF02RD4BquaZ#3B6_LH@}wqbW^2s|1*T;Be3f zw{`akuUox3f-|azOIxZ3r-IjcshQ1G#-pR)?d?^PX95#3R@v-&%$VCo+EE`l_|W5M z-ueg(#{+bxs@iNikBDsr!#-@YJ|IxLRrmI(-+2!jo5>5obLg zzR>sa7UxN*TxPna?Wk7rD@OKjL)F^npP#-S^~Jbozdc!gAv{C0`-^{c^CvFrs_H6) zl_C6a%dv}PIN^&iH+%7^l>5V4gjj0(kbut6JYqnm_#NY@%zAdgvXVqf)ugHHdH_DG zYLWPCBiToHT)k+2elJ1IEdGr*LvvMR1#V7F-)MNmIuK$w3{T~Nxo%W$eY?8UT`nBH ziDry}8&nT|fca@8-=d86J|Jif^k>AH{aLZOD>dM6CYrFYPBbxUY?iyMr|um4^6|%> z9slMRkipsl_f2~hw1m6Ix`(?vsSdF%NHTV3Cn}`!D}@W4x$!mYy%QHgmA)#fz&N3* zM?3kAfoxwh9U2$(p=k?ps_lsAqjbsMlbb7ZLvaFn=1-Vwce%M}f-j{4kFVb!>rt*h z4w)tmip7r_Peaut(a)Q$R znfX=vXA7@8YIc@#2&r_#tsjkTA29siRdHMt8T^}Vm@=VHYw3= zs8Y>Le7+m>id`kxl>O*~dEU^gFnF8q;>`Wvk^$>`5&INT#sj{xcuzLYcb$sXgnLX4}j5xb#59I}o3Xj%nN zyfRx<>T1m{ZvCO{v$%u1q<431sh0C4XD()BrjU;9hJl=prJN20R-xN)K{;sr{S#L) zNFpN0SFV2ViTB3Q^2X59KyQ5Dcyjgm-{NwR1`{$mTkqX)X6pybLpd^FByp#4;~7Hh z>kA_za_bVR4fTFhnq|$F53MXE*t_km42`g&o*!T2;Ph?tS6!I(tnR*itNAC7Q-^ZH z$&Y8RM}@09WKYcdG{zT(Jj5Q(JuG4OiR-o$R};g za3Ep1&VST=p&@L&Kr>)m))boB>>upuiGafceN_vW>(I*X<`$}~{BX-m1pXWUZL?wW z?)3cj+U90UTicw+!g{b`v%G3B6_^KJ!me8C$KwOW=7He;%HE^F4hGdPaDsfJ(bLZiyEhusIA%2aBGC@&HcvU7Q2$K?t2z+&G&%No(Hw1gNMvnVnpI6ML z$ZJe%zO1j|pIKa;a|lYQj4ahMnRTyyf~vKcsNe0uI=bJW?1IhWYn6QA+X&g>Djhry z{s3-Wvvxc*t|pZ&W<;*0VmCuno5Wq=MZ{MZqusmu8@61Tr$F35_mo6lm4FYm^=WYO z>2T!BqV;O3s=HQ6wSe*sz1~uKW(VWHlTTl|cl|dj2Oh9zQ0>a z%(mPX=WP$F)AiTa9C*D2?fG&-ILho#X3ySg9Sb?^OcEFHPX!CY-?zq8w6I_UQWQyV zD|-wXMu~>qlso-Hlj~b!fYLKthobRPOL>XvH+9?gYvgQ;9)gX-0>xdWqPx~YJV1Y^ zF1Fe}=Gy6UQLK7WnEb+cb>N`a%xrP48h@F=t#=t=NooyM)oNtih~`VMBhN{BB)@{* zzGK#^UeRB0%IWejp(!idJ-Qs9{4i4Re$AHB+rX}4GIW#8EK4sdGx*bAh*lWyyq?N7-Kwqhdjz429d;#Ms{5oP_f4yMtm2U=JhvE2h=9P0XE5TQGPtC_CFYprA9eWas7M_vFd@ z+^RLJSloEdr!EUU)LkYBOP&x;GiNX&lFx>nX}!40iI|rdcbd5G&Vq>jeyd&c!`{|@ zMC^&wzGAcd5(H_S?wQglzqe;I^v3BB zoTonz;CYcdKL%x+gagWax*+;VH7h79 z-=P2ek+V1+BCvkwvd19k(fB6>!+}d~xhsh5Rez>5FoTpj=1d?IFfpwwwF~&mA|Iu> zLwF;=$Q`HFTox5fLF2uU zHn=eCHz}bic1$)7qr^?051c|Z1PRETkJRLHXMa%Atj;oOh# za2GUCz0s?ps2D`vTUkY$1V7u~1BGL*p!1~;eO9%{oUa6~IlxB`XKg-yS6t__D&m-S zMBSAh8;Z*hhsdfsx2RXh{&=4sku=h@3RNi@|9US+L38ZwTAw2p?Z-qJJy}V3L6>pc znt$3`(i@dzEglfFUzbtMxA8*KIeAS-u%jc?F}K5mn9>+X-qZ822F3F1tbEjrJbk7S z$0HuWzf!yjIa0brSpz72jMR&8W{iUZmv1yWX>F;?Fj|lsYPl@#O8zl>-ZqXa6?%cX zBCy#7#oY5_^|X_h;dWYkrN!zS_s~vxEHto-?_|P{!ecw>n7eJPr)0trs&g$d3PA>Y zY4^TPg$b60pYsh)wNNVSm0N5Wsbd+FB0F}Bc@P_(?avQ`P)#-fKPlc&n$R2KkDB3+ z%2=E_&cRvC)BL0m`RmpdK$MaPliZKLa zB#+(?r=sb%f8YH5Qw5d_$+2$Iq8dgYI2ER6%{C}Ai!oT<(EYl!G(a+O{MGRm=-8?m zbgXa|kQpc(4DP#^^Gpfn-2S+; z9ds|5hG}^0=5;6T^qL5|v%?ntHeV(~$Zu3fmfJ~I&!HRhnk4YoNlw$j#FO7bpF)s% z^%!Lx;Ed>w0I8{=GikazexEgLiF$~XJpS4K`*uxremwf=B-GBimT#1fG1_#&qAO_g zr6cszzW>aC5HfRTip&n?PUvIKAO*58j!)Rfk|}Lc7y?hu7n=X$5zCWDqbJ<{>X4`|Ang>t3VjnClEynH@1H_Lq~ zm$z!1oV;SfMOENh;y2Pi$l7CU^4WY<)&*A&2nu9D zNX+T?cz#Sz7kbxu-%dz|HQBv7;LWT4$Rb8b;YyD4AN8OCPirco+XJNZbK{~{$8Y82 zoN?p}O|Hzi>Fv~HyK#Z7E+MSXF}lLKQ9+ruekaoIwR>`3%xt+9mhpY+?8wq~tqKOA zX1MqaqS9;$Jz2y`Dydm(a$!`ySyKuYht(?7;mR9+;d3S=n;$M+O%n~kEik6EGy-z@ zbtXQ|myfN}qdXHid!wgDHzZ?f|u zf*zdchZe01a4FrdE9xB5Po?B z|34j>G(L4qijt#6dBDPypC*sZtjuJ#EcwLdASDZrvpdMDYdq#u)92P8$oF$NS~0Qh z4gL%uuZ75b$$7ug$>rV?*U01bn9jRN&s9q<1qYK~<@F~<(PH)$%v`XbLp4qfb;)bm z6vHXn_71l9I8W+xU)7K2-!N$xU}CH5MQu5h=L)hYr!M+r#l8Id^)T4hKt%uYNCne1 z)=FiXOhR`?H|w|A$%H0nK8LjEe0#>vg*7Hk6m5_d zX$})U^%k)@r8~P$o!mQ`Kd+xIacN_B43h`^(b(vmkbUb>5S3#6C)?X$#8S;q(@RiW zGsxa{VNFBMGFj_qnn8VCTHvl z>gcPu@7Htcq8_vEZH6BsK}gj~Kd|)Ef6lAsqilVx-*5F_{yo5Zo(-76va#E!Hao<~ zT{s*i(4mh!@O_``j_GDG<5oyz;>7Z?IO*fvtXXMmW&?0R^ zlwjT1#h^R&c;zyMt}))ZdP~mqC}7-q25wMRS!)(Uxy-14Q`8RftM(R@lKI2kpC%fIMiya{c<4qi;r~@no%w>NQy;h<>B# z9L(ErnEx**sHB*Jvg;35OJ>+OJtj~kTF}NPF z{~~GRawL5qGORC61R_aRc!%j!9c@^3Gh93i4Nw8SbXdhsD$CZQK?nlNjuwb9oal+0 ztqXcENU+p%%As@IA&Ne5(?0n@ zk~~fU9pD;P%|Qoh0Y|aTJ*rZ&wHCn422(0oLNh|AL}Z$5VibEESoP`Tw`?$0HfTtR z^fY;-_)^C05ilJ|>@?RCkq0wa{I*6&)05T(i9k(=uumoj$$DoN#_?R`*dGI%AMm?lNVw#*7I1YBUCDaxtdIbbt z!lE)=E#zqW&L1G9DU}P(^U&`SDGes1W!G6n1}l*#)Q!m zd|DSA+66*+*~Ytnw%`wJ;1OQ(1uZz!?XhgsBHOrJ44r}@Dsoa}g^9d=A~e8e9&Yj! z;wvXyDF=+ah~{=ObefdF*}9caGNm;?$;(>MPYM)*jNwjNAL1 z8_1*)%ouW_DzBeMJ;2@0&C66j{Z6fEbXixbM!?aRV+~DUx}=|c*Tiif9#du=2GR^pXQjaRcMZx*kJA7&dH_!Hp}=Mr~4M7aX|H?)bI%Fg@^w z*4{2=;%5X#2M-igp zD<9i0X5x$77%)^I&vMeR(4-h~qp6 zqq7OXG!4VWkxS3TfN5)a{j_2_uO9{q;?F8F!ak@gDmC)LJ|mz?$LK7(MgW7d@DJ4a zkK&*|Ys1BViYRb3)+6#1a_SOW-C5uv4z{{(4pOM1Y%Q3REnt!492>0)>stl0=bz#e zxO$6d)1Cbz^R}<(w6|i$ZO^ixceZi$>>D>)^ZNBJJ-3Y7;dTdofKC;JeSEK|gesB4 zF`bXqL)P3|7v5#kDumg_DROPx_>k9cbo_Z(-wX8+$h3EuetOsr_w+vog@^U=sfSG4 z9A4-UVA{A*lz0EI@2JZa8o(ea%*vE@CxTwBb>XI?RT&HD_x`V!f$j3LV7mbom#I_K zqf1O_f1~KH@_~27h2xW4QDJ>k*oJBt3e!2I783r)wD%MJbm+r`52mUOWnFd77icEk zKcpTsH{Nbt_?B@SUky3|Wn+H>c;UN)Rbn@{Vhm`L%@x=LKI^)=N>CQZF}k`9qSCPJ z!2XAZc}Kwq)_OnR?=LTsAYPgFex#pX9EVS#wVyOnU+)hBDVSS&}fc*!&slNjG{8`gdoZQ__bH9jirORB&AK)+d`K|3Wo}d1BQ}@eZ z!+CV1&61dTDpTejXS-!HevNJq(CaPjzm8=CdGLnO7k1zVzwX=W* z-y-Y<*7NZDdIrj=j|9CgfZt*gIH>_UsUY~%&E?@g*PvU$Zn4lW1?9N%GbS)ua7~?! z%&$lAI*n`Z`bFr>9?Q~kQN!uOx_neGp9)Zz0zVw}5Xs-==S4F*-w)(LWv?-@a?!JD zyo!BZalibodJO*y(}i_<#6i~|r*-wBsRw;T9$sD^p0)M$Rlt*EbUt4*lz(Kgk1EGw z!@-$v+<*`|LL;urPl-p8Odzbx=m{HZGOt|DTk}ug7uJ$LH}+^GnB{D)OlWwd{;kE9 zehns#vS^z@^W?oH5u=dw)HU=5G&@PlfUQF~LjJU=^T8##Yx&$SGp=*B-0FNhn`x?6 zcK)^}P8G4MrvGDacq#302|FA*J6K;)Lk22)H4B9DO(rE|B zH%dH_4V|m2heN!>&JNoQKC;Hn1+j)|W%PGG8_ueyR=YP_b1~dzQx}e~Wc6mfksV;E z9HQ;LjwC_2VDuH0 zi)8*O`ENH_Tsl)lt+=mMG)v5l)k${`j+FLBMKhG^vumW|gfwIkqZoeA3nx6e?VXuV ze6lGEIMRq*5~q6?w$gEw#g`2?(Ya)c34B@@{>DJlMc@IOV4=~S8-sE4VyBBvFLXPS zdN=%jfP;pqPeBRU`m8o1!EtG_#Y6mO{{BOAT1KZ5N2%B)N84Xm+%R>tWw_H`63i&x z_-An|C&O(i^KqPVjjEf>s$+nZaV96a6P+f;DC1mJz<=cqZs5A~K*3v?zOR^Lp(mT` znnY7=N{R!#9j|J}$so%V2T!#xis}@@Crud~aIacU7vka=xs#r&t z3qz9k$Xd*#3Z3)EtzrU~iFBm1rVJnVY$V^MH69E4{U_r4|H1+P2RQCO12O*FrvDS^ z<-eN#kJ;wG-u*uYjQ-=z|AUz8a|tHq1LnO7&gWadi=KKESjO3}H(x=mARAUgT3-mX z?Hs!8+^C3h5h*@w*Lx;muP>ZEE%eoV#-zp!S62Z3G4C$d8$eUu8#ZUrjWZXvK%V2$@l$h8;BdKYR4h-OJNXeM>QZZd9|jO2I}^Ld z-zmA{fUK4SvPuatr5r3s^r3t?)=4?UNjzG&-8uQ?oPIL@W6o>IHIV`1DTT`d*7Fs+)7#re z^Yhgu=mp;HYB(xHKKIvFI(V|Mx-2ovy9t(65@1#*MbSB2aefZ>NkoN}n)9^9zhb?J z+bM_nu-qth*6%8cR}(wx%GKfJy{z%-&Yj)|w)tgW70Oc^1o>xODF3wq9`0JP}7ymonshIFGPG!GO;s#PIyf@tC{_!1fpC81E=9f`Am zqfufjapkhYr;*4a7RvVzNI|9`KkezOwYXd?!kNpOt;w`*js{m%z%gr|_oU&V%yf}) z{D~qqGrh=6T+FJlCLf=TLx`HizJS;8m-3wb+wKXQkNFb4Yg4+h1_@QH=Z6!13`~{9 z87)nkI&-so#GT44{_n^Uhu4infJY_fWD^?_a4|k?js!!aq03puMwTNwfN#MtWJqe# zn?d;%t3J=C`tcMBnM6VZ%mpYqbrrD6N%7vv#^w(n8tUqr>eGzitM#jz>#gQx2}Nx9 zSopNqZm8^Y#8$)93ha(r766o9YYAt;T!TTCeA`e}(?vzb=dpNoUkKX#%jX5{nw0vs z9_;#Rz&yKmqC`Ndwq=Yje<4e4K|#G7xK)D_Ds}MYqy`Qq|xU4J( z^NMIn%<@{r=JCsYKn7I31{B|HFpy78q&H4Q z;2$Cdamdthg$-5135M_oCWU<#Msfs|M1Y$0AU#FbqkgmcE?-}?yc^)@?eB}&odTG@ zhD>S3Sf`{l2m5*=z)O4cPi3v+?VT~U#t=yFz@5uu!|{aNs5+NT&sQ(XT)4y_g$CoV z8syBSW2hfooQQi>EYAP1o9@IW(X9No8v+6jj<6vaIOSClsQ_pVN_ZIV6;}{W_-1@5Pr=TQ$oT@aEiNt&Nbe2^>j9yyt-iavkc39#cWum!-jF|QA^kkprrJhT z0`E-E!mz#>0hasaPj)|xu|JjcW(+>?nnF#CuvMK$$~BBPxm~^QEqy@}AtQnYVtMAaUuRO*j3aK*CXVnpyxpoaQjUhV`p8*8~>!6{H*qJgMx4Q7@ zkh>{0)v)C1z0s3j;g+^;Udp~11qD{3334?Q6O)m3aq+(TLF&OY0eGmJ@n4|U!d3H6 zwwJ=;Z$H#0++pnF0C?kxV8MUO=MR@XEqX0e_X}R<@U62xuoFU=iLmpLx=Oy>Ehv_8 zuS!2Ci;W*PMjE^oo17K!VpW^jxJxym&IPB_SX-MyneKZ(l2X1iH=B;H!5u)@%{zxD ztO0GmC09E!SDu&U)`b`}&vjP9+>2shWMJ5?#Px_m0I)368-HHldh!~%?$gZ5KX6w4 zW#{v)J`0UdL`p z!`oa0C~*=Eet;}f_tK8y{cYhdqfqi|>!_lNzRJcxL)?v5I#9t{tDbYaY ztHN|BrnBYm!=bp$ky2fNFaNrBO45gM;RMPr(AA0834L`OZy1_W0)yBcj!p{W4--N0?*n;PnI5=+n=n^vjb)HfEJ- za~sjON!4pQvlSIH0D}GqrcNH#=(C1AzQ9TTkiXL4fqArn3sCfjt6fR_B{6^=_y}&U z40oYN96bQ=QdW+d87>cWBc=quJ^i`IRW+(&2I_c4I(f0H_1<=m-RH-7m5+;eJrha_ zy#NvjopH;amQU4f*9bk8o2vnofQ<<9@+x{{sLO8a{qO39!`hpVA*ZqUSH2MBA{pW( zb$v(~6KU#-OMc^!T^A>}K{Q8|by-ay4a`H*qga5Ooow1404%!!O?n;~G!E!MxvFo5 z%meOzq4~pjMvY~^BHi3&pNzrR$6H>kjnhGF(v2AV6$v=A=r{ip3q9&;qZ1{LyE{&@mFH!T!l9MI~1AbOiUr^ z9tVqJV`jx&aL{M2)2^U{g#iM@PA%iFaR(b`Lz6}#c^swT(lP&H@pY6Ll4>JjYksS6 z$%p$F5mQk{ut*To5%^JEeq?Y{Sjq~Zn7J3xHP!Y_>sTVv0GggII_;dnpb?-nA4g+!fC97hLKx16Cf{j!;ZV%e z{$X}X=p!GjTm#+t3(-j*HXjbFM$Z5|Cm)p|6rKTqvt$k$u>~M_nIEYcbGV{A`|nVr z#C4Ct7WbY8_icH9qrLSc09-p1uyw)TwLviS+uyG-)U$yNz&Ax~DlVz>zk>GMn$pQ8 z#Mbig(o)xkpHjF$k<8p5J0G5;$pYLp(TNdaKXd&mQNgt*(a?Ua5W*|1NxS0 z0Se^34I4Uk56hYgK;i6r$C#ZHAx&nn|X)|RC-1&|16=7hAbV5?Bm!i&vCPxTlYD!9$aclx+8aM`3QBg?I z72LBvSf^_Li@14GVn_vDeGUNQ)%D`X`qrR4JS&kMnL^>PCObM;jyY`eu zXyuIs9%X~|XsKo+f7T}8Yoxv`Eg+|ync8eiW%e}a}T*Gf-Z+*{>lLPp2SvNjarO2ET8({-Ln)aqAYPK5- z_OcO{jT_;95=Nm(3OwYcmwGA*M-6G4Li6&EGCLQCiNSU|8+vjKUv*CWX{F=ECtS&k z?sVonxmQYEmUv_E;!K8~sz)DasRQOZ$rY)t&#IE}4CpORX9(@h{~XDMjonDd=Hu^w zCz(0p=KXa6h^m`c)OS!C&Mec?P|!8J;IrNs`tsAd3-Ow4ZB$qv6K^C~hq*)U`)L54 zzuw@V;-CFC)!hya|FfT)li|GDt2)nj9kL&lYB`_h^+$R@d_Armjs$NdLR;^d7hZc< zbxe~x*q%ngTtEC=jq9!;5ALaZ=kczeQgxv9CV}qklc7^pAcq69 zK^%beP9pGIQ!@g;k=G9g3{lVWFR;-=1A=5(cCVnekJEg92*{?Y74&Gf3m*me+yN$! z*IyItWPn%CYKESMHABDxiM>aE2W27f#d-ZYypF+>G(V9*EUq>utg9mM0D{pX zL%UM>c@;TZR0NvFC z6&}j`BAq7H$LcS$jb|$A0}g2qK4@_rjK!X1_tYgi@Cj7_NL9S*FR_i2XWV)kwgsoX zuLybouvG`R%K4NAn3wY-;h%ziNq*XURnP;^1RDO?HEj_7nro&2D9XkJmmeF&;yT~=^s#<{V3R^sFeh_R|%4E6r;_XPd)!wAq@ zNE@Ih414nazK&M=96xIT&blo*?R_Zd0m#i|B1Q<@{Ye4lt9t@wZ|IZ;>N_2OUko;N zfY4e)+5l&C@Alj-!8U9w{H%2cNxuqjT^QFRzG7xmSX9)nSCk!F2VPfVsuTm=PC}2a zpZ_Qey1h!HkQPBHjllRjNE%T5i5u(Q_+~tXxp`e+N(-aEJT0&ZnC&Z`e<hAublod~$wth_(o!73C`(QW`# zfzbp&0h?H=u(olutFgTPPcTUH-Q(Aa08k9up#fNoVlOcvY2=?PG2qv-y#C9zELCvZ zI1A>DT367}1T^T=$#+!!bUbj|S^f0-fE4A$q!BT|BH#Q%k^oQ=>RGC;f;#LQwO0T% zB|w8VPQFV6buMqzw&|zK_wvm4khTHtg&Qu)Y6Q4@8ZNF8k(V}qQb^iWzg*170NoG3 zKsg{PH4?(C9b+vi2m+X0tAK!mq8LTrhRM4XrLzVgP67bg9Rizsm!7LYL)Oe&7r<1S zmiK=rT?&9>?PXx(ui^vC_5QLhO!&@3GB9-vYUO+UeSTg)jYawx3Pn|f%9HCV0Mm@3 zCrknG1i+&1*PB4#AUo~d4P*f$AfG;hM0I zh+K>uj;=`X9}3y#xeX{JfLi~BLi%RBxF!c*udl$ zXRJK`vyJg;u{G%VCvJoL1-~F1$^q2Y=_}BTnh`pEk5$7gIYJ{7z#mLRZd{k0R1?o6 zae_!7VCPY4`tdqL@x?yMH^=Vkds~sqr|q!_tpA@}Vw8{x4zI*+OTG$&`Rv*2Z;DMd zZgZay(45|Is;rR%@;FrmC3zJUc@+gE@Q3^;IQ;CG5sk97J!chd9jyQA@uLW2#x)vw z;?h$GZLNlq&?FL`gw{KS#~wj3S8;g+ns8Bgq|3JsFY-9&n03R5Zus|RHaObSZCF{tHIow2hpY$kkS zp;jazjaox=&HgO(rQAp1A~;@UWTZq#F+@i*M1lV^P{6;OIL}{9hl&>SjcvVkq17`! zyUR%?`JVdFQkMTjyIf6;oUbp1+-9R6Z-lRIa`vlosVWe$9;qt{Fj}e{bXJF!bXvyj zhf=qUmPqDqR|tpP#be-9!Z0Nd39=Vf=tR{xIbK)(qWYm++R>=HvvzWfG!6*a-8owiWYa~MZWXcftE zp9y9q^UpWNZ$BV*e+~LF^iuFlBqZuMgG~AlYJ0ov0}3kq+|^@_=@F2NYEt3$@!ITP zz%jEL;tpH{7gv~FW>{q?6D!vckjI~ReLdUlGJ(U(1f<7M)F_i*W`etWEa#*zMpG!c zK=r7haSGfKcW9gn4Lx=0lt0kDaoKyax)kqkqoe{KD*X$$i2`B1o76?U+?gdGrKE_C z89Xf;X@A$IRbK2F@=@w$vKU=(Km(3x>aHs) zD9D3b)=6}fV5W@bdlJHW^m z_wP@d{m0caONMZ!(d&VpTo`+2&R3eT5#=BuOQ$_^$!7xo+svf0;%3NvWSC0H_?=@~ zIQlapZ70=cCDwlHJfXD|`j?b}{%pqE%u$Upl$W?ykl#~t(!*3aod*eXUb{Q7DSZwo zQSDL>_xi&aJf1e4ddLL@81Gh~i#)nJQEZ-55u+uIu@lym4>x+A-@@eV*akyD zt6#4w8h@^J6hn`nmS=b@LB2gE)2ewx=5jx%G32YLs_5_?e z1WinxIq^bL;B3n^%^CfK$PPlzrk%oNJ;9kitAJwfX6uHVWZQaU83PM1f~F3ujbOPT z7G2d{=N=v^F#-0Rd+H!pt+;{t7YiE-a8;Y{sva1ZuTpGU`e|d#^MNUqr z^7!e#R8=nmH-cM@z~L?xv>Cz`t)jSBR1{q2r{2{N+mJJ^MKYe$UV&~GBMM1%FnmD) zD#KKqEUI1KD9-KZ@yOuDX*JiQ4+Ct4LQg%5Hb+BZB+dqvyI!rLLAAgKg>}sMAg`2S zL)NxTQH-wO{{E02F*M;|w!H--2Kcj8G)FttTsP9KRoM?OXTU#{Ct-{Rbcgw+l4q(|oD`G>vG8ZgNk4-Ml zg%O~#csa7U%%E7R@mDzfw%vP{vGw_4(L(q6>rt8IqMj+rl}RIOcJJ8MS-m;cj$@En z1bGA*PPpy?{kjTeJ|lt^g6u!rbinv?qtCTm>%iotN9uSzDE4A)FoC{E=l`bP8uzj; zJXm$^Fh2KBJ!5HAZkq`zM-x9AoO;iT`#!T5&hktcufmJr%F}EhF#)GJljqX4TMP`V zjFc`duEL#>Ml6k2od$XvC*r1(ME92)p))Hlunh`=kb7T;-+eHhq{hiD6j7qWpf7hl zCwSHDO7)0!l`_SZaB%-pS*Gc#_EPhwOd zI;}famJCq=>;r+H^fUr5Y2%Qcv?T^hMQlk;;KwQpIoR<)Fyp!X&A|%usckPE(=ZHQ z4dGOq?bjumJ9-Mb_}RwwUet+6mznMGq;?>w?h~pgAt^VRS7z2vAwp7wX3m?MH_g6U zp1V(Q{%Cot;`RJhcw(SM4Hn($AmV5RGV209G%;&iv44pz@ikyoRF6mTtAjk zQN%?7bFiC6T>^q3aU5lPRxE0!Ja4C~R{j(?u7akAt*m!hTrgjF7!!P;2?NJ(x%&!S zQ@VzF=$dN~0qX=k)xU;b5?Av7W`d~fbFp1=vX%l(c{wdC!0yak8g z9$56yxZH4sn9rldYcrW;UoLEnRpj;e-zUwEC8kO4eS1U&i7Z2nT~FO0Xdn>u}UJ)cQ#lJU5;*hp>nQaY$_e!IKW*G9)|ZaBFE^iS$$ zmri_=J)k~AF?c^J>|1{1gnE&1p3Xy4^mjKx2CVD1nGY9eQk6%c|0Hz(|N4^uOzF!1 zhQ#}SJ!{~9L(~5*r~iNUoC2Wb33~lVtma~Z?{gHN>2t7WY3VJHD7y1g<7nrwia z0mGM4v%E{dFNdNA%1(XLl2PVAS^vdS`mN@ZvVR$o+CC#Ds|Tw$BkkjIoXHJN$sR}i z4f6jr>EHUlB>h+35QJU91(L4_!AN-H6B1oKqG(EC=pxBJA}MU z5)NwEGhujptFQ2i-_vBJ7a@b@7DmH1F_V1`U~8(W@!hWV&8C!_iuO%5mKK1ddWF=8 zLH;u>&yzMNL5C^cMuge!09oq^B5`tJ0tY7_(AN}Oh_>DUU#;OroUyW|RgqQWy}Q*h zb({7}Unb4^#Y)PNB!e=It~EU9C;;h)mhinIU~i z43VIB#yeWa7H6X$rtCO>&J2rveY@onsycu5uAY$yLJSqYcD9SOAa}C}4#pZ@E7aWx zZuHkwU=APj&_|@^z3vz6G4@8_#}OJ2U@#*GM`AOknWl@FJ{)$FwH7C52&wUFPV+~l zkd2H+bOv%DItR#0Io6o2fkL6YT}C zjIg5rIM2VY{mF&bujRqFcg^f&plWEn*Hgra#hcSKQI~+m!?dpLv)KGo&-Hn4llegz zAYQ@f$De5JDUQP#%#13O2UU?K#4e>)!+Q_pfxEkbvun-1goZp6oAnZ}sRhRYV#5jX z1~jo#e>Lpis6W7i5{`}9F(HD2Jdf56_W_4jndsrrb}bI`TPM}!|4Qz!ao#vt!;-Yn zc6hk5+=AVwn5+|N4nEx7ZQI?YC5xMw$WbZ~Lo&U)?yTt|8m;E#Plr$XNq}?tFji^X zVO%-OShaykQ|F=A6uURb4c_gcJ?p@C_j6+QXf+87bY5Ej`?=QqsA#$N0i;I8c3{F^CE?$!5DHukc4zv09A&VQ zFk@_DWC_;6xonhJ>f=qCAvWsh_%4N0k>hk6iT-}vAWpC;NEl~ z<%1^WgEFiZWHhHi%0+2{0f>$9^r6d({s!0&UGT_{ls-{2tzh1U!pci!f=;-QJE9M+)M ztKvg|Uv2=upeOOWEdx%e+D2OVFiOuafy~-T;o^PMJvrk`8bD7rFav%42(oDJ5LA>| z@J3C|H)KN8r2fdn8wgFd4BWkcX)2g)K*p}kVDjA*ay5*UlV4a@kX%r}6W+Dj19FkxzRBi#Q|tkFNK3gW#+#M{ zO;bu#%e9XO0LT`-X3;h|UW9 zBc-tVfQRBVGb)%u`6Q)d!fJ5UFpCka%bnW&kLS00ElL4X5r%}^O}eRu)Ro3WZuw;l zMO5)+SHm}|odvyOzkdpgRu7OjNaxGED+&L-kpfvOJO8nVH17*B3;3QDrgSNP2C4Pg zeb^X__8v4x#nq@xAnU->Bt}SV|E)ZtU6KoHUMzerrD3Zf>}M)FT-Kqu*574ks*?e0 z!Mg@!3z2zas(l+grvSiqn$V(h?PNtA5&O)=B=^|nhVgTMDW!RWEKV36<6- z@z$=jA965eQ)AcY&Au8`*@f(M36#?k)iW}}fRY24M8TK+R(cV0MyPX#>_Sz zSu!CMjB;7tyaE{YN5{Ml?V%64#Ain!*lzrBzW{;jfAWZI0Yn#_rKZFr8J%uL_hQ z9-+Yt#ekDiIAUSFEm^l}uiqbI(cz}NZVD_O?aVgy;>GSl+k z`k&<0*S&;ONQoV*nm}XWAZWm(YynNx-Ld;@_O*C?0gL7+$6c34WJX#RG0I$Jj5Gfw zdFWd9MDsT+*QmmXPd0IV5Cgqj2dUvP(_fkl*&gQWgn+<*#0YIfHQ$FW|0`2| z%@J$id#PW+h4#bJ-9pu14MjwBq@oSnMSEjms4~t9On_@S8AYg490dZOZQ>@#-ym{q zJ)tS@l>Fl!3!3g z$qDR|&!_abiAR@jr*7yI{Ua+Gsl$ilz|>J4-03cU2~|mF1RQ3SEH@P> zb0f86mLk9JV&KyP5~*>+^q_F~o(JPrGwZkHH#|ZP^-76zG6#N~g%)>8t7KWMWQ6D} z%A$0y^xQdLT7aok77M44VYZcdJoWB@6+2%aH4C_2u5ytyQ_o;Iuu&j4ej7YyYju4^ z$9k>Tx|WvWDEN|@Up~u=PYJM`r)wu=zNkyz`E)R!%68kMwm|9KV>G>+_rq!Mg^(Jm zPr32NX4p%X1bGX}ka+s|r!U|`u9vuIO*kAY^mgpBvXRuCB?1P^Y;_iH%z9u zjd0vR_fuFVwDpzmGto&nNEKfwaOtit38$?LZeAePO9$6gf;`V&uw=SDaS3mdmDTdS zqG^JVI=<)tL%U)F5y4o8QYh}m4Uh3LO&dzqkeX1Q$?u&R=*8Z%v6V=26I>c6(j_)i z)M;AmbWEXitt07d$#(8FOk47Yb1=+^sNY)i`Spvo>UU?=?aJ^)s#ARrm~Y8;Jq8`c zOX{fR?7Y=&l4WGjxp>D6j8(Wn*J1ya#jF6|9CUpPJ`2eW?F}WfW_{lk#Zo^^rMXymr#?#c`$tOW z-#xHK6)rhQGH9Hnon6ypWaF4QHMDM<2gBw)(CI#S-Py2dym9RwSg&lk=|;H`NowF8 zX)FxlGm}}FPT;S}-qq2){@FjHB&jC7BTZ_(F?bp3 z6Pw{+R|HW`3@6_yf_cK4)hV0Cq1qi$rycv(hw-Y*61!c+%M^kR)#sr*X=i;c`X4L{ zy#mL>!XBKhn90BW9=WwJg%VERvvy>Y1bXzW@$e)@n2d)SXlozA1I9SR&p}=lSKs?J z3Ifd=Vf-mfz(I^^FK&Qm6gS`3VnBguPN^nV0D#St)bTSw@yu@rLhLUi|3smUC(t@r z5eyCU8!F6Kxk$irED6K5CeqG2E?11oOsbBc@gX@~E1CQS1!z2|&!pIc=;+C=9#-Rw zeY#*FxXMFNnU@~_Fy3@oLbwNqveo)#!80&1iNa&#Ku4EIvcA!dr;+u8q33+I-GHKp z)z|Sr5ySv67~&X4fPnC5w`YTnW4r^`@A?ueK!d)4(+!KPoWJ@Tl%7$twIx(sQJ577uUSUy5z~U zLa5jL8gEH^AvmHczL{<>B;)4qQ0{Tkik4DZ-yVT>FNj(WpnG)b4lLs$3q> z8aElPqw%8%r)JROBRb0NgS!CC425Maizu=RNVNHwCgtwN=JXdWO(py`m?l-ZZn{mA zf=dmg2Vj06lYbuCwFguWPyPd_Rs*{9=ZA5CrWfRZNd~(6L!>ZOc>Qzo(Q%h7ZmJh+bC;b{n!Ds=4$goyd@*8OvPhl73E?EUInBX%!mZdDwOE&l0z+lSe^oo|o zonzeDb`PI^!REk@IC7r4s(ME0DQTxbFlQruNIEDmFf6Gw=I2KHE z!Oh?UQ&D)J zvwx@%q}50?bJV#RrQ6TRQS=CjfLNj2UE|$)-vH8TM)(Top}+iO7W3e7J2-F*uR24+ zMp_beF$gEv4q*d(Q?wT->sY{+_ts6^ol1dyJizG0tTd8f>A4SSBGb!|jsY<7LwENu z%u2l&+LjJ_8BlD?QK!c)S>XC1&_075W-2E@Q0wk${mMM3;qC3fU%>;PNeJ5vA&x4U zy#o({G+VBMW>TnD%#47h{GNv(@sXg4jPJcAP(`hU?i@;`xJ)f`6*NF^s%rLW#5DG& zD|dx;fis#QpsW|uYF@0%Wl3zb69VL0FUIK0h>`IZ_&XSMGI}9+1ax_YFMT}~<4Fj<1i*m&N zKy&TM-vU3QHtJv0eKBLtV(sAp;c^EXL!DZzi(&BqC^CpOnX$YNI?)2Q%}d{gC;$3O^^a(^cZik}gF53{=H) z)$H1ib7?HH;XG;CohtVmMXmFrxM@#v+v;nVOz^{!wUMFHu%V0*qcOY0f zCrz$VRcHP_ON&Xf)n^~8p{Vg|QHAK^w?h%2=!YU<+-%MbOmJpPN$)BsT$=39h!5 z*ch}j?+b2@9nlIsc_^4^HUcSlUYX_yWs_=IU%d2_H9jdVmk~~czirxvv_rY?NZqNWM*8f z*WL1(sE@qnhS?!`f;?d5x2$ou!v}mUSaogGPY)bC$(SD$<7 z{fG>q*f1nEsn6y0(7yHkd^FPDNY9ok+}n!q(%GDIRFYOrNTcuNATH;P&DYu&XnBf7 zjqi7S7&+wa5Dq11Vuv=eAD$y3(SY)7coo0wq}}Y#`)zXi<`i#iWJHQk0OhfX_WJKB zz>_ouz-NCicHl>vRASS#Vg~<7h$g6TF+L5;zzme_xdjv3$T+nOIt(UYLtQ(j4h@(W zGjh5%ydJ|0f+0u23~~VGEN>)CrSRoho^B2^-B_mb_azUof5F_PDGYNL!B1wk;rgEY zs|+;HeaSdI_zJ<0&tE-wd2hX#3nbeXtoF42{;$&Mrv~DS3~q>kbJR8h!Of2a9R`kt zH^^CD&?d4npR;7J&|-(aAjKbt;rmYVIM_ z3nXVr8xCHfvJsWrAW52J?TMx($rltAx&_82cwxTm#$8t}XQ9+~%$L1?;|}HZM`8}l zecr=S$8;RGx_mJ~*eNt$(C}FOt*(OekZ>T(A#k+7xv-hZ zm#@p^+V~(?GN?dyj2DXZYf!>yNDL$eXCJE>(mZlGgMURB#oASCH951Hb*U}^JHgu% zpHr!x>Ugn$SozCr>^a=s#V_Q#z$BcU)&5MKbG-GJynCTbLVV4Eq>%8AzRt4qp@{6L z*LkW{ICi1lZDgLmOO{cVatSXA@501QoEc|%D2|kep7-H4I(?>;2V=<@$2>U!4q|ph zu7i5&9Yul%Vq;tNu>UE3i3@orWXw7fD*qNCwFQ*dT}njrZZ%l?CRB=Cq^xTHx-mM< zE)*H0zz0JSazHGMqdRx+{pRoCFSdkcD{F(b5gknVz>r*6Hb)byQ(c0v&|{HBkDYa2 zl#hF` z= zzKC!b6o2tr3yQ6v#*@bsP#m955-5M;pnU-`uvtO5xTHAyeqzcSudp>AAeRsOnC`mv z(`1N|F|CtRp4c)xkZHhPT+k3i@*>*GG^`)8AK(7g2;bhup@$(wWwX-T^YZkNQMuaruCU)>lqww+<6W%Gd~kTm9ut|CyyasuscAPXkT z0XpPiex1Uj_%d7XN}E=>KLpw)rs?`!vaZ)HR|c8kpXJfU8qyVN zkX#~G1o-~5MIVYugzyf#t2va)P;`-Se{*lZj1okDThAN>=l8Q%&@ zTK$)S-Aork_b<4G( z>QpV4_nI{^`L{AUYSaJn@)GlhQGpWN#HcTJPOI_~Y)k*{z2 zsILzy*9ej1CBqdmLu=C;Xpq6AgVkg0N&;~St$WIePM_{_a$w75zv+#gTf~7pL`mLd zhZsK2T(5I^#8K>ZEVm#{I~{8EMZy2Q9;MMoao-g&Ir6=FwWx+=^i-@kBoPe3C>`FY zfwTgrvE|G@mh1Q%D&vNgMm+{Wmr&sgAM!6|a9&O+ypT}2V~5}Y z4U^dF$FppouaffeWuWu;OVUyPP%^`>V_jqb(XWiFZmcnIwR2A4@z#?w#FPe0^XsYp z5sQ>7g*V>Z2&yvcm6`Ouh0*^WgL|PpX;$ugHyu6ePWg5ZMMh{dQ-m2*nnB1!pS6K3 zk`vT)^0#_EcBQXrD3aW+$lNY4a5WpXn8ZeTt-tGLLp>7r8u}44a+h+C#x6kg3|*&gwmq%QkSlS?;lv2YE=poF9!RRt>37i<1j9X4Y1;8qR|fzyo(&7IGfec}O9N>6TKftYJ{&=7QE>ga z)@e6O;k@`~*n&slpW+9l_Wi z23R@8+S7~oEGU-)+7=fqMV0y)lAFKzLni`i3}A zn$-Msdpj~R9l~ubErH%Wtk0Q=?w1vTMniHp+(g6m9!Y_d;&(%K5zx`#`eHTSaQa;#ZRnU!Ivtf;p`JjqvjwGS0_2J|vk0gXx6lhcX zpVjYYp`oP_){_>!RKR}B>emk+@%a25=1WHsx=E)-ATk%OWkpO1^lVAgxlDs+F??Ly zi+!{;)D?*FEt?4SNZr%KN?4~=4%vCUT4bcHn|Ld$J+8n`YEoNRGMZOQ6 zNRe8J!mkOn=*->h-K7*3M|v6cbAGqZy&^7jHzrKO1AY^+W)a|2$xq%8cgpL)qv63f zq*yUhDD?y{+df%;IL0jCp#d_MDdyWpmrml!i_>;p02ik#STn6T zYx;LSyIdG6V3@tR@&90_|0v^AuTslBeX4@;>ioVf!UFlNQ%Txf}lukYgI`j3Bb@H@Tu zK?lOincexVD(i~jwV%pe848)U|XwOC!9pj!~}-cI&^RJ4(U9S zaP5C!(hZLE05CT?KI^EKrL|4wMC?gM{`80Prsv1Pw{XNe!)*{R+MdlH&PH)0eRON- znkKN0ftPQGCDFmx25q=#yB2-kMYFRHP8ad!+4P(n@Q*gtZs3w$7NLALg)*E`5^ec6 z%o~z`rTt7Qb&ahFRYGK-u6+sea&>#?1Ou*4m@yiU1BDJPavQAe0!?i$^G6nq~p8*!KadEhicd< zWO1>8C$Bs93+$al`e*q9obAKnxjP4hvbJuAC4OoE8P8c%(_Q{HNZfpac@qi6z~0_5 z^t?07e!rH0qfC?Xgz^&}ZzSWNPl)i{2{1V=V?r;RSI|;;GVnto^R0XGzO0n1)v5;X zrcy5+TUm5_Xn_x9dy?Gqzv7y_-5-5dd;am7X9$;1QL_kXq0II6I*Na zF_CLwgG#`(Pr0z(h)-C8{PW*xU)T1IZ>8OrFC=Y^Uo5s}dU2)~vUBl1LijwCVQ05#H`+Te9B zd)8$-WvYbAebnJ8RIDV0GJ%_y$sqw`hng8B$Y0+Res2#Ldn@k9kyNmiMW7N{a%sUH$;n_wc2)t*wmP zO5N)dz|uFACf9!TKzCpRe~o69=!L|3-9Q}%K`W{w0|e!8zV~ZBdbQQ06O{l0TdkG; z?6ni=?9%t-CD|xzE9zGm_B6UQ+F99jhyv-{=}ERG>q4(ho3@ATWYyM@hCC^ld0T0c zq5v)dNqXc0N9h3c`tR=UWf=VR4Z96u5D^CEaL})0B!3eeQ6(JF@YrUmn1dK2^`;c< zZ28XkmmeTXe8X)$D4v@3@|~_Jh4V_ev@xf|oKRW7g^}(97V}ZLx64VjGxeHkdy!K^p);rF*Np%YTwgbTRpE zK6<9Z)1cR4fMyA>=_7ySJ@0lrcwOutuq|inoP5mU{d@Nn0B%EI^S|s~Z@3LomAi5C z_b4GkJ0yEgQv}5(WKnu2HFmP&aLfs_Gy6b$gRp4+lke=^2%zf`dy>{@GTQBg#?Q9n zRcOMiNde3*St|k#24|>;p^-rrLc--G05#kM?Xc;fTUyNEFr#GDg!9?!sT>fw8{VvD!#=HD7t+P(a{nIs7J zhN`8$0Z|B&av?)`=Tp!t?vq?Yq4Lj?V3SLYn;;L`*-qbhgAtTVobhd7OQ>YvLXe_= z*=LjhHC6z=TbyUK6N+_rZ_~`@Hy1R&z?}|hh zxuH#s*8F(T)oj~Aw|Wd`)>yPx1(aOK;a!mNTUcQJ013hY9>qI{=pOBJAyhjSTeS9A zRPcaAngFUBC{UwQE?Fp0(JDaX4I*!3{%{&r@GRbGqcTiC)1fTLp)|6U>7 zC5yx!lO(@WT<@7bfC6^Jr`U(#7%Qd! zIckzd3bF8Zg~X=Dgj-qdb9NAo{C9g~M0-g)A)(2SS>mM6fx78tnE&PGfufxbI$B%* zI3b7#z9a;iAv|FYcFBTX0w z&Ay8A4}=Hg4O0brj@$(o+WfyfQrlGY3 zQ_6&RJ10CIe~mU6*jrJ*9G-UH)5>v&2k`gq1+5|dd;F+fl$bUHTtx5elZ)a90bPxX5!LaCHv4v63WJJ zY64`XabcOHgQYiHQjXG!zc$m8ACl7%(`O5bF8P|4YOdTG93#kW`^olG8gjVPdts6M zEzh?Z-&sE@O*{5uXnM$ztpW4Ve5QEl6$~hOYV0z#{o9=1Me!o9m?!fMN_6jpKS#8X z$48gsJFQgJc3Df9N$1Y$z=75B_ZU|ao0Uhf#p>=;Z31-6_c#gic; zNFZ=lk5cx3;G!X@W(W6uX8SV*=+d8b1%wA9XdCGpC4vp3oWq@wn^iP;-D$(i1(C3e zjW=ek!Le5sGnJ%a+tf~j1^SnW3d;#LoQ5-<`o+hPRJrRjSaY~ijM#fZC1CeIp^Qrg z@G&)K@taxd%G|E=vkE%VogAZP^v`P9?-u`*A$U(D?^OOL0+Iww12(-Eat74dTU z&29RYCY;!RUx~)wB%&#u3+ir=fbe)FfOE~fyq!Hg!JFG2i#9@*I}ZSEyeDPlAvymO zZtShMj*ZBov6aV}eiz!2HtkIb#=v1aY?zFVGVQ2sZe8duM*&}+uWxVWeawUqz_hm2r2s8bZAMp@D+~Jilzsbfy*kP zp3Sk-No6s=EYptGiRybPBhHh;gNSDrh&m`9>=B_>sk?Or!c~-^s1^G-^W^EpkLAJ+ zn1oLN_+Fq@xpM0EOpnEf7wK}1#hCjfh}=v4P00`B6=m)U)XQBB;vMNx>?kcCkF}oS zkb-TlSb;k0U)MAo(;~Gpb5&$Hw%*uq1P>j|cVG5dJud8EM(1dh;Gg83!^&w4_i!91 z48tXjr8%f&me>`uds}vPet!G$ic=N_0Ng39!F5%nV*;zmISw34oo9cQ`lLv3kHGnm zk{0aAbEG5y4;N==cXv4Nv}hv%<%!xZTrTsH>xvhpyqN%?;*`o~l35|2$ftrsE-O!! z&d*MsaRvL;l@G$O&eF#o#6KJe~8@~QJg+`1#un`ygy zL&BOqEhY*h9F>nlM*zi4A^Ww{Q;%hPhQxCnA+K9n8E( zBA$(AhuonI`Qs*__`-!=om76gwar@6EgG7x9;2Kc`SfXxs^~mCW8zbY%$RnpvcQNr zXn&HV_rnmC&ZGs-8BQw~T%HDluEPG7*)W-m)7CP3Jm=cP1ZRn-qy?pne@N4oks5xa9Z9rdENncT=mTIcl1OVL*SdjMRY;*@zTot z{OI=fwpVdm9t5g?ikxbf^(#BcN-0!QPYoIAl0L)Qhvb>{;J?x*Q`9%?L^eAUgqL<^ zNKaIrLw&&PNZ{yyLYR}FvAc^0smu}Foj)(-3P~&4{kto^JF@mb=(p>%a&+c^B8o-e zZe<=qgI6Xlh+c}QUC}2>6;4-UaRK7bed&3_K<&Pr^%q& zppbV_jbt`~zzY6o*I0q7i3D9g$|L(Y=a?K`q0dfFj&;!4ax0g@!Pe`ST|4odSDngj zD`KK0(z|7LY31AMg1r!FUQ{0}_q8It$<>aBtATG@Ny3gDkP(Pm6bh%-Jm+tHChX(H zHVpQ$y}{l+wzM=hHs-Td7IMch`^NLwjMVvgoZor)p6*9$cB_pBNA}x^S|E<};;t_} zK{!p6vLxJIxn|7xFX<1G@w5&}?nZ%C6Bjbu>K&CMzQecP%uf@IJOe>Ecod;QEcXUK z#;#vwl8o(%k9bjoJ|$c@Jy^iW58}d`;j%&)KZ5oQLpVLK=I;pahMqGbf{Tjun?Ax6 zr1}E%<)v=7dFfLty{q>hvv9Lg9(JEDaK(ES6YR{Vy&+?@ekH^s#osH=dpQn}S=6+5 z>=wXa**AN!$a*ES#l*^L$kgo2>9A?a89&OwOd%xfAx!W9}puE?Pxx1AmP3@vc&)ooyd* zAy`LvglA{&_NKHUMB{%5!12HC5Idz?YOJ+9r+Q}}Q)s`5e|$4R&>=MyYo29E6Z*Hv zeFqjSJO5kc{>A?AH}u;RzuEsc^jopeR`PQtuXG~)fV9^tcMjw6DqE41XUv5ZhtkH{wR?Q&(bB zCYlO@cFI&c#;erNBK`W&;-xR?W#Mr4mhy=2F^ zgD7b7ExRks9v4dmHrT-TM>i>z%|6j1_|E}XeQqE_o z^ZmtGTC#h>U&0~y`{|)sJ%L*Xy21p{=FVEg)1vS`+iW#zLmH@-iLtr&krp?iLj)G+ z!XntP+|dOhVaE8W>#5l2s_oU=%mecHKNZT3z1cdC zJpW|nWj)(GtUlNB*3cXzzpt)#NURl7rQ)*k-U`Gt6`N6h+;=26ZfVemU&icyrk#~u zYkt(0l>XK))q#Q$E}W~lya$!~8K)e1lYXI}K6Vgy&t)4QE5Mm)m+ZS6EMo z>bGw!B~cO6jn$YnSIvK_O5&ktKglxV{qHrsc@W&Alj9AlIw?YXDKP#VEd!eU3P!DI zX9S!ZzoSi zpA-z0gOus~C`fA#E}ewXW{SK!`R7_IrVu5l%t9!{`IUs+N;nha4X$Psg6v%(-N1V9 zeuHO;c0S8=v*$7eo4sy%dCI_x(%M4sKX{GN90wsAl;UZyGg@b$Rx)jG*{0HPA~udpbIKAZy;& z7rwLu5;sh(3#h~G@g)^KBG~zC)T0+ic_THR&qn~5`3G3k>+2Ccqbl9^&?a91%yUzy z^dz@6pM1$$CZ6fM@4NMo#}OZ`WuV89_>gTN7}cm>Vo32v*relAOy4iXU6%T`~b4dh@L)Wq#&sVgdzMBM&T=fa>jUAXe)36 z40f&vb7Hwqm|b%zbZt4V{_-f>Zz zP3Rwf1L=nR{8ALD`gXsvVLwtQsZUu`7{dQ;lh&9&wCSg#4d5p*-0rlov9S;sU~fs3 z(TnLo7lhEx)S9}kS(oN@-d%Gwn14XY8*b_ag>Ql$K&gX6qmz&~m!{u-nVpr=x&W2n zwvu14Isfpu3gK`sneOcW(=>d2=X1FisSr>ou6!Yq!5ki6L##ZMu@*38b&~2hn^1YO zL;9a_!;NdJrJ{wo8CJ~?HAq@|{PU3R@kTK=CeKH!#piXAk%UO`jRun;D9&z^{vl5J zCSrUWN`X(I=ED+cp-RC&mzxky8TvZHOL*>F5D;8g&6s0GT48LRsuzr zARV`tn~q59QllyE=8_)RAThqPm6el|D6*4})@uK?SUrw&yOW0i^C9lnkFlv$A0eJk zCFK=#(%PgN$HDhW^_G{CxkMKUG~r6&sp<`$xxs&qBPm>Rc=!n5v+ef zwt=4Heh~fqMe@4D^K?1KwR|*MUqQl-yO-n(nY%J^wlW@};m*^-uHW9{DtiR-m%Z*z zymYX+avYogIOb&ppaB|_bn>_HZG|S&)KS8vYC!bTexfOOObzeQxkaO zv8T@?AFNamr3T<`7I@WBGOroab{K_3chYCbd;Nj4yKmjyDHQ%c>Ke~3NoSw8y{({L zdCHLSu;`)@bvWGd=X>_t0U?>>6*%9Yx&46lT=4x~i8g5hRP?Ssp?2!Y#nztfni;su z8X6)6x9cU4tOoIV(zr#|C))UyjCXUg0^b-p)R6ice>L#9v+ zGfJq5ybrXBm3KcwSq;m7pwMr!NTnexeity8y?i72yIq?;aYBZja@r1G=GyC09TB!$ ztxQW}trmF{amN2WyCOVnQ3<8!G)ltxxF$3dAA7EyM=JKRRVH`}By7Q;YoP!Kq=$>f z=Wrp?b=_-9Ul1X}RtN~?G6)sG2*MLrOk)Z50#6TvWgasmE|Yv5Hd&scbj82xDO2)d z0-J3r4koSnW90aPBVMqDsSfKVsq}a2%J`Eq(6>+bfB!nhP6ouUC8By_?sr*sH@9EE zt8RnbEqKA)gzB)#X{&-LpU;|ZQ1zf9?@G8(ZvdhPDk$?I<+0CAfU}(XINkm_?PV9P zfH$e-D&xb7y;|P#Idqo&;FN*7;8jTebY2L77l%7gj6pw3yS9-o{U_!p+PhHFqVQ_ zDc-lfgAp8foBeU9#H}!jk-9(d^!+`d z3sx4FtXbN2J^AC!Seceq18$W4xELAVq7<5&E)h6@k zufMRFo8)c0Nr6;v=Au<^I6wVSH>u>&=ctUYp zq8S|*$bPYaT`Rx;YzzLn&1u4b*jf1olXDXGmMJMoULulP6ZJzI8!eEH-`&lw+{-?o zo^iRL{8arlt;ZU9K^L9!Hk>|Nh-ykt&9k0&JRSI#Pk`3)RwKXMT@itV0{_ai46v{G#=__swm^iLppU!bq{Dm4@z z6+DucKCd&@?rC`f-q!Fs{CIOrxi5es+t*9PR=5Rns)NQKus_W%#nTpD|3i zN|xflk15pC$fL4;=gh^GL@>kQ2YdEe_Zul|Jt-gW_R&zYOXJ;3T6;W~6E;y`06qQEB+ zmr8Dk87H>5t-_QH=XvbC)iw4AW^IMT99^PbUO1WX0rmCzHcqY7q$`Meq^y>_5h~)l zLF-RA&#-Ab%T33Zf;?skEh3cq$Ek}>bA7Eh-DbtzqOM|WoThL{8}8Yi`#Cwe{=lV3 zl6_euG%Dg2%w*|Sj`= z;Gn!68mBeq=hB~c6A0H7Au4e=?QwhtmC2jneU3lhLFrl;5Ve80UHSNU^TdLFj$^+b z+MU66t$dF6IcFIy&ykkHy**U(>Ytp5t&gY z=WG}X-I|TVl%=Sv-pfA|m{jxGl0Os~L9Z~d&$?0cUd_YY^{Ao@No-fhp+|MO>%U~g zsz;f8f8Ekyd#Tk~p$wgmuTQ9dxu$v5H1zhV$+LCLMB4~qt<=Tbb+3}7g7Te>Q%~cg zBScT&F@DXvj-AA+Jrr)74s!rH ze9N<*tF`RZMlMoQ!O!H?Y#arFa{vsJJWSp4m zw(6jI!AAy9h3#7N)#VSo-cOIU{IoR4;*cZt<<`=MnV5mXMk3RT_@_7hOwM^rQvD>7 zh6f!FqRyZqHUzq2bHOc(sY%awt8?cow5Q8m!)xakD@SlyRNZlPsX*FLQg?;RKQs2S z%{Vw3#$E|c@M9U(@hcP~!^K1C<@q^F4R@$LmfUeml}>+ z8l?hgOt~?dxs3V93a6V*thaL_pXd6p(dHEFvQnG0%kM0b!+A~c_GmNnhs6aJ>VRcE z6Zvdp>g;hchrIP)-eOxDXBWPrdPlYWYQNMc4BD%{Rv@VP)n<+pE1?l}<*}VOXzLha znFlhpaU4Gl=af{#N^4C#@Q%oDcn%3)pR+4iB-{QCd{#A<+u2_{#?!89#!zj2@c*EMLUucvC z`E8hS;8(PaCvuqpzieHR_%iA2xzRRTm@6fwE4|}a++wKdS6i7hvv`#b29B23&W>{5 ztPp$%#l(YtBrZLZz)F4LCBs#Y6AK;zgiT>fxPf-4=J;7do#OD;M$KET+W$@z*6{i;U-XZy=eP(PCj_eh=_XJtdrN(JG#s^|p^7-ca1Xt!o zZVf+s>7@;Swvj@t)RvAkoES&3-r*1Um|Q90v~{o2bd(M<)jMAL8?>1Fz&n3xz`YY! zjj4y<2M8`E;VN&!3XRhS$q%YCAe5f+Ep+d~=lFrOse)Z>S33vP?iPA`2-_v0g7~RJ zy(zTsl~{0Rdztc9F8Ta=eZrawVH@e^CJnLp@q;^9smhGN5kaIC?Yz?Lafci5jx4-W z+yd`--U#ZFMSlBol5AAV%l;K=SQu$#%s6n9dKh+p$HK@HrnM@BlPt`s75oL9xD^%Z z;aiQEO;$7=}aj2@L9ftNKfC>#<#<+z8JC^3hXby)e=|3)V@axKJ74q6ZlXWJN5& zoeY|O_Dl5%+}eKjA;6z&r2<`-Lp~}Y)_XNas`55iRP7d7h5YOO#K$-tyYio2_I;XM;NnH2oIl>JyCeq}<_zdSlgqY18ssNC0z+R;pOq z`UoEqZly&F$-NO|a|uwZTPxK=w--)o;T4I>JJi95l33`J3jP%x>MC2YW}2UT#V4^~QDdR2p(TuhS*nX2Zss!km&|I8;>>g(?A?)sNk>vOOeh3#ezx!>M; zdu{&E(I<;!r%Hk5!&bwvY8!RCuXm4ArPOHpo#AC1_^CqusV(~Pc&RT|&51_FY*#Cl zP~XufhGeJYiG_WrQtKNiA%3b-bL@VB6_4z#cp)G_a9h()R5kKUaOY00l(!+sNJ$)h zbO*#V7qwEG;u7&<>=H0qlaL^*rEqBN;On;8_Wn|E0`{p&J*?}?|N9TFtt`RKu#LnE zZdov}^GbHS(1#i=VKeT>KQK$d&t;PH{N&z_uO@DVorFV7Coec**a=6R!qLY)1t&9` zyg=b|{KIT=L#~*4Ph;3s26@5!cDFwaW9Xo|E%)71!JV*uT0!sk^$E4PQmy{A9ltkl z6*d5EbwwWAYdxlMytk2sDrH;4Put&q>h}R8CnrlS4T&MYr5X9z!3hcHD6{)D{{%Jw zhs_Nt+BT35?h(j%2Dp4kw>AgKfQlIvDbiBe+w8Yw~^oSpY3(JPV`>$ zQ8vGuf3>xG=^UKvTq#b)QgYDHU{!IZo1}eP-Oq89hIM(6IjO_Y8c8Jk`KO4I0IV8x zJDfL&nS&FiOl^}R^t;guVW+w~7NCKiVF7~B27Or$7*UW|+jM3^S&IUhdd_k_m7`A# zBdiYZUa;jQ%}Y7rBI0M%xrqDc=ZwmfTRa^uEMGl_j#@Ga4G_E`N08P!??7+Oz{n5^ zalTz03p;IjQE7#VCHaAVfoJ1sp(A*OW|D|aMQoHZa9JPfIKcQ!czII!b_a{2my64T zhYz0%tB*1zQb}o<8zRdi1eDgVePouRc*dgfqFC#^28x8wYVY#N%e)L^rx7)!E^6=_ zIDAS(l+r7#ME+HRf* zI^0Jgck!IR;~8`7glRqX$){;Y>+cCAO70{gPJF$m`qfVt6%h{C>idr853_D&-ujkl zmML=ddY_7{lq~Df8o`C`g^vm?IJKv#a(wY4OsIKm>bW85S!KB~JhC^nmvUC_S=ADO zyUyEQA-Dc%x8%;!N0N2I%E7@wgnb#RSf6s7t=o|~UVrmSrfVECR7F6CQ_C1l#z#4c z$4d~H`P;J(IKEiAHg>B`uR&f;pLcU;R+ij`Nnd(juAoz)fHtrGR9Rc zhE}hOVVI>CD&&E)?<0|`l*d;(>2c~UPv{b!Blhyy^>SEsLL6A zK)a{YR(8^NCs98@RyF_%-^nD)sAoid7_4}Iwy>r>xJ)HNB-jPnKc0J{T`EvSz44|r zM2erXi-@>gT*ZED|HO?>OVy8u7;@82J92z@(bct};c?kQ=W5EYL`N!;*PDsKOz(nB zxQ5&?o!y}aR5KUVM0>2y%&PIww6fJ*X0hh^nts=e(z;P$oE#lFaqKbk6>Z*|oP060 zeBikJu&Ab{a~`LM6y2Vg;cB)e|Di zgfAg%o!mu}tHB3d#`P)@rZ(R#C#1HD$m{fEKhXI-DcJUYW-oKoI^te(`B~Y-D0%wp z(+$q8lP=f!`giyr-1>3`Kp{{Lcr_)E8uvNk}jqyV-oIW@!GMsP7)%w+R zW`v(f{l3-EdK2QR%U@PRt@HXqgdMH$oR299c`C1YL}#A?g?}xI@2aqs;;WY+ebz>daE@ zx8Ci~t9XkjvA%2gw!{T$A%nX`^=eU`0)M>R{o(YGVdVX}2)d1tm%>eh<#`Z@UxvAU zo;dmCZp_!l>AWoK)m4A^?&y`CRXh5e+iudQVbHZkm+NEX3`Dy?!i!g}2cIIHVx{w9 z4JTa(LP4r0m<5`j#D71R&&Q~{?B_&giZ;KGk^b? zeb6BEgDqbzA3t5oVDZh{Wa^3{R}PGZXM_kJywX&Jz6+hm5;3|oXz4L5Sa}e zMn9eP2$&BRoax@Umi=-DYUUV#@W44nuNVIIPUsUJ&ux!(M#Ud z@(*Q`1A`7iP_U7-P%SiaVTxZY>GRBIrG*i@acob2Ib1U&KYfJkx>{NZ_4+!{)7q6C zPvX_%^4}WXH#FS%`T-ux3l;YkCym6&sK%@t#3?ZV|a`UA=%lkA_uEa9(*9?$@BDKjr%RS ze4#JdzA&PmHz?aCOS98S=6$mtdu_SX+Gnit>uZB?=db)_OajMkCjn8rHCeu<#3qB= z6{DO8VPjT{wIx2^+Rw(a; z8lBCs>Vmr76YX~wq*lKxa@KiU>uCC3U(z|d>pJBy<)fj2(vGjPS3+u=Bcsic$bN5f ztLMc8G~$fZ(Z`)AhWR;jJ!RJ3*+hJ6D-SJgtS?0TO@G9MF-iJXy#ayA$=kVFmn)=L zI{78B$-@@MOAz>Jcml3N+C;{Mbfi_9+(e6sxwgXcmN(GL$@zv!wrhh1M;+)cSi08c z8oqr1(ZI^>E_Q79`QMiI1|=y&!S6g4z1sxOv?}XQB^<3KTzMrh9zymt>CYxv`g~?y zs;}i~`ux@DI-_c>(HEcEs;W0{ei0`cQm^JxV zHB&bEu7v6e>PHr9TjiyB*yQ#pVPSV2<65J;cYS{K#q7{BNqX;iZG3HworM|0tM*#c zQTth6**u1x^{v@6d;?;)7bFhn>pMIzZczpS{_PLfuLac7Pg=y*)HW~UP);w)t-X*{ zjU(+@Zlb)AOW(Eg+U+oYuN56mc=E(QcO_LrA6*Ec*e%6=&oWxsk6Yu?C4a2EyiJgf zHWy6lD0EmTEUbn8{q;F!2YTAO3eNrV*Uc$=aXqfs5Z<;AXui+|+cUn#F>3djMinbU zgx|M)MhuFAyt|~++wVGA_7juv{IY3`?DVcQ!E>6?l@dWTh18>DeiAG-8rCsaQW1O|D_v~k}S$D^LOHmB;9*XnI6`%b5 z*gc$SsqNxItpgq=RT5Tk;uxUlp~~tCm%T{lC?Te-$=J-lsjiuAeRHvov_Z2b<59v0 z2vYLwB;z#y3PUhFRsTA@ZCWnWR9%!dX>@F%s7(EE)cImRTJwe+g((Nsm`Ay^<{TQ^ zkwG;PmUEjOHOKB&+lk_arphy-bhNpYFLOuS|FFHit*O${HMg^GZZKrBu_yG3^+Z1B zh`{FZ54`QliSqNR&Cd#6AdyZqSHrNTZ~n62LTC)Ew?3*XEm{ej-HtdaTvl1@xdMU* zD{;Qgk5?I$BIE@CXkDe!lWAU^%rKh^g*uV;G^t?6bY-ogKlsK-vCY5)2A6sdC07Y| zpQPH`OE#R3QvDt9LV2Z_C8J}nK2O>ZkO7;&*2l+}zJH(Xo`ZWEUnkCIlpzjr!z%5C z_S*(tjtn@itAlY#S-7=UO{aBm)2_x>hAZ!%4G*C+=g!v8OO@QXxP#{4Jo-VvbYp}T zmuF&ct9rI_Kp`#`iY>)b_1uUBRsOw8nktlM!#b{_i$zJpU`gn zt6lL{X;>Gf!7=2i@)KS69!bS{1zw@~^;O%|$l!dFx5Up3t-;dvro7KJC4PVym`g!M*2J*$IaqKhj}$=a|s3N$omRc&gokeP=yr zO%Grqv1Qz6rm+h4r!a$sJBg#?jwUl}42e(oYDlMNZY_@eYFYpG;e$DyLLAjH?}2CN zld?VJ^BWFauV|bTjgN6yOZvoJwiwanufb3c*#YO;d5Pi;t>HY!Jz|%(>oaTS&dUitF3tA)P{$@^w)2A~*iXnV zL}J|tS1Z*AM|v0#5l>~~*rHZqW2t*?X!P`I$fRcmh{;GvNL;-tjT>ca)uO|S#~?~P zo`Gk2FHgurwMkZ~W7n2U@hxW%ztw4c9uZ9uPajqyfLPKMt^^yQ9z60HQIfzL76qCh z|B%6-MID)M@LRb;tz7lmNLG4_L>HPPCHj1D$c9o`3CL(6>8|x>06=4pO7pL=!$^Jl zWaMln71a05scH@{@7aN&cr?`p@k#CgGk4bB#n+Ixy8Nca}F*CGHAbj}n`df#K(eE9X zK;J-5UteE;_e(fDla1XO1#u|w{m~^STgd^y>V23P*nd=GGW|YzvfMVZUAC*hrsQiD zw?RK=#JnYi`LxGYL@&gGw**%_YmDb$*%WAaEz*~PaJ@Jt$>7osNbdxp&f3=3`=p0X z*u*%Vt2Q%2wo7^K6IVEtWap9oHiRH?G~CWt);tX%-z-zOq)J=pn0!f#SOipYw_s5a|46d;%F}aKr5(P<>#iOF^+lAh+7JK9 z%IrNYr#&nqdTk@x?h*wjLXa}+uPe-RjMgPXA=7$<^#TdQ&aTbZ; z=5GpYSnkx@|K%&)VD9Kj`c{b8*w4Se)6?fu;UmY+cF)pD^Xhb%_O9ZQR!DE>*E2Y) zS0ti?y*6E|CQJj6*g$1HHfT%KCUAXRmi{*(qBkL-g;IZ@ z#Uf%U>-Med-m~c$-#%ct1>bqkH+AIwWXln3MnLEo#DIoz1`=@?0s`tF_E z5$LzX16`^-h_%%nmifT?ZbSLP>GN_9S@%}tAMUIT2KmjA8eSUFOCeOy{=@fq$ZBi~Q)ew%;s13VHb#-l-YdwAes&jjx{9%u`pF8sJ zSLW1}=X@=%ynf#aAgY(1f2pq1vxp7l5^h@FFN!(qfU^o=t%@!G*-ya$UP+{2xEA`fFRd(GdRW*uqrR4frr^GD;v5CCLH(a*abtHY4q zvrjUK0?_d*VLp%(Cx%CFuu=+-UIFh*xHO6(L6f_RR!cD zdIuTef-aCo&JcojtVG^3PZ9mpv1m4b4#1OQQb!Y znhp3`1Cza|uV-Klqm&;o&{jB)uZEQcLThtja#U;u1+P;!%=3c5r%r*iqJB(J@o}-RvEDO z)r~?q(V{hfD9FG9P*NAZ*`wnItZTej3)_SdV#)}1cY?U3?VHb5v2>Z})B8M?>FaJ_ zXCw~Zg;hMS!z!7>14VN6*=ujR9M}L=9zjc0cRMQ3)cTE;xOs9#scUD#ifaP5Hh@>d ze)>^-_C?)31UtHZ>6R)gi$%xZV!A3eqBlCYrfJBgx+H8kZehS$;v`h~&Vjat?lA%a zmN@Pvwda-uD$7LIziEuI#=7u*442X)EGY|G%qt?kX%3m8k08o@i+RJL%qmwSP-U0(gpU#EqD$l$*R?M(CcIL)2x%z^& zA4rO?#k}s3^qBxvM=Y>24808`idf_%47B4k!Si)bWdK0$;J|sL?eH!rKG4ylcWfw<)c+!BS z^VfcOQs_sLP$T@Awf1A8i;pf-s%p0t_cIDYEfWAR>CnUAu%+Jn3Tk3EhK^Tw6>1_L zj90`8m!VALfm7j+T@6K)2aQD^2A~%WsU-%L$)1#jNhcOb;Loy1yEE5KB_4P+~$=`QadxM^WAWM(?ME*WQqadks z0Qy9TxJPAXWzgOXr&c|HiG&bQ^sygmjZB#0+3D|{daw+TNd5vi7gC;x^LkUtUF77$ z%C={DiqndhYjI4+9j`JHcxhZ$x1>Iiv7AI~UCVRvnEI}wo6Dzaki?&x(fb2p)sE^3 z7#sYqTN-${s`8Heqd^+gLG1SS!mY}nJI{l>FNuqNW}BOzJATL~j?*Q|XlJl;FR+pm z+$`TNhZFhvmi#pVC~-$@It03reAyC@)$&0bS~_bzfj{=VcC}N*1BI98NFynF*K8dYo;Z)<;S zc=~RwRa|ed!LUk2C$~s9A9Gl~{!#tui!6&*k(-k z`2@Rq<)6n7C4BqUnSv=B6cr_9B_w=vtTs+ox0Iw&<-GT>iopdjf#fJq6OPLPILUmJ z#qCu2o01&|h8s#$2lzPey${LiOY@j?@6wu_jx*_5|4`GXZ<|pD_LU3vX?mB7Hj5_> zIHQ(p+=A*CVD^`PtKq5jL^P-3Te%4K6Il#e{NqTj0;QX&RtPfsq|Jg{$g}n9Cky8E zCDGAt$?9&+db*{&1}ZiBpQqj=sha5#E)%ZuAfI`paotCZGErU6ZoGdVfa3P+*I#c{ zLd|q$I-fKy!_b$@JA5ADK(|6=Pqb;JariuDP>?<~d0*C)&gb#Y%f+GZV+ll}FFi$G zF5AV;oL{Sl&eG6Cw0o7PR@!fjKrrCS=&(ZRg`^pK5 z(lQu6trH#Y(q<5Not|*d9kQ{H!c%ddm_wtT@y*COR}@cYd>FXE1sYsV)G5^4j|#lm z9~Vz|DBYgdg7qytRGQZP{(7)zgNBk`wHW6YDrz}R^f4LioN^U0a*FqBd2J=MF^1^! z8C^w`6NyJ6f)hub6RO_F^~dQWbroYTN~a6vE=CiR7CX}zuk`Yzp-NwDJIltdXuCcR z3(<^_zwlJ)ZFp33Ntzlh4OULE2sOLFS&mD?p0iB1`)q(dqc`}pRR6FZj8vbSezW_* z_^Zr3<S~?bz7u%Q%PmEs~X4iO1hAXts&te}cuQjxEW?1wjCKZ7oym z`HJ-h{-Rb~TZaUdKT}gmH2RniA{^7c{E1V|3AK9XO;wu_!@A?ozB{OtE{DQ1qnD!v z^T21|dKGhlE8&}~fpl>}RkhSbEu<%Z!_~o0jwPZ_R}4tU1+H$ofAOU>6&m04b4c}P zS2~pb;Ma5IYKh5(zyvk9k9+bEgJ%fe>qJ@;=eVvy70Q?yBGy|nwVM%zx;kTswH=LY zH_f2{p1!m#XK=KnBt~EHhN5n-kKoLP;Rk(Nhf)3Jx|HJ2V!o2$*WTtN9+t~GN7Gg$ zp#D+77%`6gsuX;7c`TqBmxnzEy|o#DPbBH152+|icj zXjWr9zsvksCP2Boy)h(y-1*Uhe&5p5_DIQ%vFewXOVy!FPNAKLA;+W3&WJ&y6mD6e z$!l>`+*CaM>4dhs?my>~0crHbCs&n4l@%7+rIi;?M=F>MI_ZUW9O6buFcYFGeg-=J zJ;GijHkIOGx*~@j3l|$kLFx%~k%*dDEVq-WvSi87ug{ZK;tp42TTj_bEgFHE#ulM; z=hW{3k<$-~tHKaW3UZ3Nvut?^_OrO1c)Ov89!1LgHM+E)Q-%y6d>icNa&!?-J++HO zchn!mNbL=@L_OP8&d9?Z36wHdtWmP0J784JL9*7P<59KA{m^A8P?Dfb2Ilg8PcBK<~S`k^MJ6Jj_E`VtUmEbA;P;|2Hr znhXln1u;SB9&vYZ3zl&1YTs!F+=HVMnBUm+j(j^;SAVfHb1;QMO}$D@eI17G*4Ega zOYKC@VKeg}D;gr5d~4mCN@$J!fo9(Tv-^ z*Q1xUHH~6k^?ceo6zO%e?(v7uz6ce0=ZVv-174#dZo>JPTCe2F@f8`j7_yq9A!B1U zRWwFPiKfOk6DjQ-IIi@0#N)zk*koXU-35 zE_2wV39vcE-&0^d)DrN4unIp^6!2C^?ca624Nbf94y?#f>ZcWc(-WuWeWd-$e|8an zZ2qvSY(9jw&DY6Nrd8#cZ%sJAlSk#97^wvcFE?ric?Ih}s`$;yd?lRfIya+1FrwUO zOr5M(=+iiQ{*ha^;#EdaNH~YoaNn}=s`;f+ie_Fi@9{eV{V34D^KGUoJ6}XuyD)%) z0&6?tn4ogn;qi}_=O++1bOcXmHL!ScRdGfts;O`JkEBAB;TW4gXx+5AJVrLRkJ47s zVxAcFvEZYK+e6Kg`{-x! zi>8`&cb>Uad$fsk5N`53ZhSN8sqb(!=E2+$|7%aS2PDo-#ea8M;Sca;|MTJxF26?c zO5^YM{?CWuyAA#C+xg#ra_!H4v;TSVrvoVd7c?7p|E%dn ztQff>lg{!q=<>g~jcN5Qj0RBHJ9jra%X*^9f-`)v#ffgF;DwF|p>Aei@I(3HW3h)E z&mA$Pf>F8f6I8Of2_=6o;F-Qfzetm^cwto8v07VIMhAZrtrO25u8oM8D#c%EvTK*A zS2Y~{gxqsfx$7_m#z@#Upzmd!tMxZXmNxcYeLH@2255SlMrLO@j)`gsp*k*hY@dx| zGUhW|$%P_cX-w?-J`ARQb{11u2#P7xL|J~1zJNu~W$f9|XR0D5$R1u9gOA?dQkMIM zy7lWa{N_!D?l5T4(!RmBMz;GX^nHv?2{iOs{Zw~#K0m2BP1~9~mb*WAp?VSFs?}Ev znuBH|Z_7;6QR}&8wEzfn=jo$6a2Sml=5SE$tQ+Cj@*XRiW)$UY{k$I4u>hE)WQ%=-I?RBY3up7}qN)ZYde!6(pWUzh>i3G`%zpyeU+cZG zjG58_O-;2K-P6SNH>XZ2MeOCu!)Zd%QY-$e8ct(xy6zn*A{oFPH!E}SA&|B=ySC3 z_a7Q~JG7rFA23qw`LOk&HOj9;fyHHE=3$#*da^V1v5HXBtb)?3Nwm4xnj z<%cdQ;%hd3x!%>oP3&w zyuow2y(eWI^egiOL7w{4*?)QJH*uy&BU5^3eUaD}3jjQ@Y0clm!y`cSG5nA(KKHa$ z%gNs;u2P|+rta-Ynj^2f#KuH)k*4Zf(0h+J7UOv*@Zs#U&xCX^{W~z@YDiOSSao{j z!sd7&qTRP{=e@rZ4MAKFS)GX=OoblwnPO6X+AmJ29G9E-N_dKkR-Ba|dHMqq|KBe7LNTdP;44}$L!}+z%P2~y_ z&NLfVT{wc?qNE@g&0ugzkl#Ku^hvD^7EO=mrd7W$8Pmld{1*9M!aT}EAZ224a&~!chFm3FM#gk=eQ_K_CXpH`po1wZLv!^R z7$g(?c#=E_`lFBSlNPH?=o3dR?wUCz$$bgN>(ltuFZ8`jIc}g4uQ#P?iFe&UR#LC9 zi{`VdanDOl&c!YaOd(u%8T8xY9HD`cAO|=D{ZIDy3`VDH`))h+3;dg$q5QG_;rYgz z_mY6$x&=F`z<%#xrcG)YI% zWknKxG@4al-wfu8UOy3H{wP;Do`c}$;M50@x)Uw1$1fr> zX4nsZysTXDX+sgi@dp0@D)D;w$;g@6Zrg)!>8I;>S)RMpYK@!Bg{|_lvtjyS>?aI` zg?(l+j7Hc-Q{o9{%0aj@;A6`skVLaj#5%8sX3g3k|XylBUIig?ziu&c&wZ9 z?l79AF+34*%$lzAvJpLJgZODUb&QZIJP90U=&-ME2L%Oj=Ndc34^$n6%lVk7R_sqj zF0%SyGFR2dngF_y3q_PJ?-#*(!PcE}&)1ao!rElXz zPz`Nw65%B31#Od_o*CL1oew9`0Mhhd$>(B6N7jHj`;@%5pV%00SA*viSA60Vu}Q>u zVO~KDy@q&*H&(%*tG1h4v4G(?J>Ri*Vo*MgZ z3D=un-VHg?dwSN<-=fD38}@RR)4~MED4UaFh|(3F*X7GtZ`a2SZh?HDUD)^PqM5Bm zE9ml^^79HCuH5?;idfp*1PDJg_F8EdW-iY)y(P#Td-iS#-}$A`o<(+!cxxtn|IxR1 zo1d&1C-++kkqU-^&I^)NX4?9(JNc2Fpag?yxmK72sI43T)n1Mm=^M6bU?t12^e385T|7WUDcgv<5LpW$5JVt&sUtWJr9U5CYCyz zv=4hZt&LK>LAEfdp7@_&8>MyU6lH z;KXsssTwHC5Yp5xwzw;pSK6I@O0(LW0>GG_e;^1GLG;rL7bE}Lrbeckr@d`+KmLJa zQ4&|BRpPnr_2%jq9;6Neh;x@Y3F;i~iI((ON@)S`HfP_v*_}16>H19UAwTx}s@G%I z?i7%4ZF7}h*g^?vVz0Fx;p^Yd3atx%)GncTaLmb#dbU5?Wguaqg-8VKYYU!SaDe-| zyF=l=oR*CW^#CXV{dqEvZQv;VVHOyfc7#A@G98~ja3~G+O67%4tOVLALxnnba$v}& zE-+ke8!zV2dgwG8qrc<{a2)fB^%eJjH+F~>d0nI|Fymj=V$`G&WM8QqDbQC=j$rNQjAa97e91TSEscp{CyfnIo$Q)}x3@3F zJY8|`GYl0!_J#taUOxI4$B{O*?mEk zX|zVY+x{ftK?N4SQDmV%dxbGaYLo>{aPi(O_(zqfxkt4_yZj+P(yx1} z5%>(a6wkLA?0_;wMnXzT3Y;W!bH^(#H_G7LnZJGAF5PdLVB)QRu|jTY-_=vkEIbx3 z_NldozyFZ^U1kk6(=7q)I)BX{6oXO|*H2TQ>_NorrJ}D=#OTuTQqqr5*OwAtzV9El zZIB0am8;Ea@)WdaI0>VE!^qK{r0hg8t4E{R2*{ zw=G|_+*cs0JfL;UzetzQJC!nBM73GZgkx1ydJ#np=T&#Icqvn-U?QCE z2+7sWsfvXGvQZM23VDy`A@eiOvP~0Gr$iHw_F4BrCqh#VsynlFm7jD|IN2?2k@jlTXmNc~Z1(G| zAk9e^W@R3|Or{EL-edb%zUj}3#eje-E_Au@)Vrr}H%L%x6+Cn{a>FFvn||NV=JHYZ zzdha^E(5Y%cI{c4E0L63oL3X6qko2)53-FmEUVJj)lw|1lEo3RG;;gF!IVI@ZwJU= zXD;G~Ao59OC%%ky29n!m&f?o5|3!m!?dw;4b-s3q1S6P7|qmj6*zl23bMwJ}jIM6|1+DPf%0 zma2qZs+kfas*-H;&R@Bf80&LRa3tyXbG|NPgWP44}s zLn0&K%)ai72=bCze|jjg9xR+jaBXQb$TYbUrS80GzBTLBk>W|}PdDrg8v0WTXSzLI z0oPFI*TLBAGy8=1*T}wC5+qyp=&xB^ z9@vlr&SPhe!X|(++f6TT0q|ccYAAH7lPG*oHG4alT;ejT(M7YIGj#|2OT+Z#WSI!M~4b3a(Mf2&W9`ageJQuy8jHo9~h zzYaPw>EfWl9y|lAkOv!ODg`s+s5C{f8HgIphI>F80R~$5p1jmSkdcgDI`|`xw^Bwl z4u+ln;(BtNpI#eRJ@NT=qXgOr$bRrR(J8hX%*dlc6pp%}zNr4jKo%hkBfar(laB`9 z^Qg!O73%hmGXgGemE$KZ6O4X6-wu#Keb+nJ%VMzsEx zN2ph+`+pEFMG8FDN7Hd)WTQ3<_^!cl9SRO@8`B!-NznL=G=~4E z;B}V({e*}}Z9nyFjRAcfEC`P0CUy$PjBz)J0KkLR0!;B2Zu%!uNB~iHn~nOQniOX+gIEVt`W0 zEo+L;2e30M1#(`pVAB>Z``eoBjDSNrLi!|H{pV84Z)xyxgV4YX_4VYQTOhC?4GU0$ zcy=}X1oO6oPS(qC@J^GY7kELDZ~!*Y7il1A0K@jaS5#!f_J}p8Eo@Cmj%^1NHrA#78^X`+j z0e^3Z5n%xh)NVUS`imI#aq|FpgRFGm1qtw`2KlARV?aV9U3|iNK$w6XF@<7C=IIR3 zVFXzwAfrN;p>us5$i8Hq?Zx?>ePrk)qP`C-)i$OX`N~JIw%`?xJR=$h)?yLTw?wNO zDkjiU&w@%~xrvvgqu|wfyrZ{sWmkBS(vP*>m0A45vz@_zL^KX8!vbfZ?V=(EBqfGq z@jQn2Q!SR6OunW%cBXbZtG5Wz@O0f{;A1kVuOoZOoA2Sh2ci$l1Z#P- zJa7F`3DmO}6{pEteU~k>y@$Md#X9L5yBm^^2W0H7Joop?e7h@mdM3PW`E6Pt*C^;L z4Lu6F5}!Nndv{kJ+FV)!5zAJZ&jqHSdUmoDgyrIH>wEaft*Sm^%-N>W75^dDA zyZbRK*U8;o2M@H+mt$SCyKe3G1|j7BPB{)usejS26iC{(G%!3D6m%@?t!rfs03&$e zfda^!7lN1%(TE@Qon*2%!ybzZ+P&-v#Ibdz) z>+0(5>l*Cox!S@+1CAq6b#6PEvHQ%zaZuLI(@nmVV;sj%N_PqN;O>i}|8{B5vxiT> zvk*M5QC;5vS2aAJJcji5v4Nm-t58Difb0ncarJTj&Ikn4v53p+Q7_Dz2$GxK?1$mO za%VkL8pOe%S7;o&qdBQe8*0vd=dXs9=<>fU#+@G#y%C6BVpJ+jw!{vk91_f+#s;>w z1RoN#P5axlyi#32%OOui_YDLo+T8^!sVjfEN8)4%+6t%My?*@eXj~5B26=*-G(*oR zdZ~ap*i=O~;{HSVId=|st9$Q_CRhU$4QDI*CW3xXY<{^F^oi9;A^EMK^OmSfrOO{{ z@{ED*FO`d1 zl$4w(?B*=UW z1pM#>u7z}F&b1KKx)OeHAs9Fwuw*{gKL z!1EdRxB5NqeOxR-l$4N>I6XI?YxmS6%xFh_fK!{xh54W?6o%WSo%zk3?4*pU^p=M% zv!j2l?>`%&2VI!>OWiV4yecuLF)`LPPJtxi$#zN*HU1a7_Q{f;)t_S7^0ns}!beMw zni-!uC>K`FA0(sCzrRg6{Vk2NVjV3Z{_UxaF4s#{J0W!Y=VH|M+bK-i8G?B8&IzG- zPrV1KbTl__%9wdUqZHS=>rT|(zowk}$%8l?hjn@-|M92V%!NAy86&&>5#tn$J1pY3 zN8YCEY&&HOv+hA<{w}x{hN0ZA$E&?RJa1YXrWjz1+~b>ZeBtCS&-$P76Z`H56cqoz zK;N}LYybJ0yMNxe_U8rrpBMjc1G_zO_fLmh`}2bR&x`*(UG$Gb`G5Qdo;!H&j5jN@ z%IMi9AD4Npd-2QwVU1C-++4lPP4h#K{s?G)7x~r z2zk(i@#7x6i)+;yTJxC#``v}j2H!g3bcS1Jh&hA$xu;6OY4`6QK&(Ds=rI_*Tv}>u zPzTAnjD*_r^NIj6{sXr%J2AaBG=fNL5jKrH@PG~uilR_5TP7mW88;fMV0JVHc`6<$ zTV9*vRN`O{D5bWtv$m+{{5)rLF1~h^kaXOZhV!f)mUhTVzWdC-ySc^jlVf9A%x#9D zt&ae87Lsht(Gc+5)+MJjoKjpH#@}f#o?$XNY+@3y0GcSJ6)4DefRjOY!3F;V$Bu5#IhznQT8s;hU2&#;E*V?O6XS6n(x+Anq_9%`AOGQZ*YSoZ;by zu`3i~uNs{jn%zBd8?W>crU$>{KX_hUOgR}OMXY#t(Z29l$A_=44E0bRV6g`1iPH6` zN1d@OcAJzERt>jzwN#jFi_Me?{Ou1t2JYStE8OMi)gC`HJr9Ecr*rgUP8D-M{%i9) zSc02gzHF%b?dvBhQ1tGqA`*yb(?;Jvaqlg}?c*ESW+u^5By?Bu3!w5vVQAO{=LIJ8 zR=}b~R81?>gu47FmI9o+U&4$A)vf;B{oe5zx>nkQSX-vzn!H(fu10+D9e8X1o!eq2 zeOnFS6d4&=``)=c)2jz)e498i6HD@BZ<2QG`}g0@k_EFk!@(8L_R7(;vzEGoudW8| z)W+SiDaM^ibLnT>)Pqq%)ugOAqut;%1W zAdu$aJbBf@4z&e{E$^&lqwwTU%U8UOf@hmb7P&sE{RB0F?M;bH2!9w%s!0wHA1aNh z{+k0YHJRHNy9|_kr#<^Z`D1-_g+NN@qW9AB?-gQyKNdSh=2s-(fW_u_b{3Vv)gs|r zalmEH+@&5LheneC>(!m3;^^5PN2}=n7BsRa5u2oN_NpF!l_QxBqcPPY^+#-48P;Pa zivL3=Kr&u$kzu(Vgf^U3yr;a~xgJNfg5uI`f=?V`iUvf`HO<0!w++<2L$>f4x7G}k}W5+%-CWvp|t z3AtvUKIvPKoxh=fEfFLf*syo=oDNGSa!oQz9eX0&R8=*K6kuo)XcU|b&z;naWdDT( z+e%uU^E)yl)5P&OivbQE+UG1V!DqPC(Gd#kz^FkEjXpFbM*`PBy zkKo3fyG5Q?QLmH83t}xFV^e8|!C54ofC(%};Sv6qDcz*0>tMjb}|1}Yw`!KDW$s~?;2-G*LceP`5lfXd8#0^r& z#CZsrcv;<{iz1wPom16AbPPhzf4xIzci~ZZgGZ14sJ{JAjmmeLPLTVWFj4GvG)=e3 zzTHRr-uT1FuvP8kov~&~t@vF13->*&q7?9zstQ&uU32P_{}m8*Q^&uO_0BG>tjKWt zD+gff^cFlxNVkXR=5QS0;_`f)p52<|RJWem{>Klkt-`4+#7q|&n!Ss{Bztw9eByOY za2vp{^wv1Ps!_I<)HmKx&+$r54WSHEU$a@I7m5z?LU>LGKf8T{fd9IrUbctU!N^jHT{ zR7XEeru$wFzXOl)fu2ibjAxgen?-g1lJ{YBDSha8x6z?igYd8BwB``W)}wfUjgfyR zbEl<1mzkG}ZnTX8VWDAF>;TCRztkotFzx@zE9Dz`)mS;v4tQq@*lFC*WYtqO;w74F z9p-6&I#IFZQK>hHJgP2Xh{OV?PXnxKyQSHfIDVL1Kx0#EQPx?6HWFY$#sc zmLWRdP>(+F<@NY)G@?U-K|44r$|BW9h)+lQA;eohC_mNCVNHLSzEbdRj$^0%d}GDx zg_|(V?wh_01^RYP!3h)D9N`FZZZvYHM1p#DY76r$R5|!x?W#-sPAPMt3)AVs17i?{ z-fGa8&&EX)L+N51HX+AMFSi}EoiEJtoC>nL-=XZ5G*iShTJ<4=S?uKc8rRal14`}z z7l_afx{Z@N{F>xX&mH9qK$SG*J6Na}oZh=W% z^km0tjkzDxY`RnJP+)5DgZX3I4-qR48>PethazX2!+-S%9;Mljh=7sLAEfi2S=;=a zGF=~i&?R9#tj5>S?M_}_sON5)yy88E3zo`)cXO>;m7F74smMK z*E|~p+bEKhn9ULeSw#kE*W+Tcxg!8knsLtO{Y5rdw^2J#3ArSeNmpdAsH$Swr*$fv zWhyshG`t9;yz86(+uymC{-iUHZVy&vE7d4;l!>FgpTE3;JzsR~t5R!0mryFTj^IND zqb(?RXcgZ9ToE!%nD;x;-pbYIjgMzzJt#(v;&yURUpAug5yOo#O&e7IemCB~C$t9- z5v($e;l7WMVgORFiZe&AL?B`(xCfw`-e_zidp|v|f7fXHQvGcCzK7gjl)K6_|OF?wGHej%dQYy=6GTX-r-}9L=aX&L0emiSI_89|_^%Od}1~DIbdKK_``)@vSCzv{e@S3ST5%{LwJ>dVGUd8a2DlbC8z};_H>Uvi0}NI&7@_jOnW+ z&+$J5eZuRlA_`6z3BSZTZm+BDrHPjpQgH74NQ=mTz>v-tfXKc5&)K7||ne_a(18K+fFq20~ zN1fzz@8;Mmb((_JOyj}Qw(rs^X&490q_$_G5kV#?eo@&SV(FF zxV1AI!#Ax>mdbmWSoxbG$A$ldt1o{Mnfz7oVeDIk~gEzJXo3HDa%kM5{_2Dr!MOo1Ems`Nh zdyq{xmk58eQAZ%1CS_nke{~;A!kl*!z^i*aFVZ@Ge)gkh&ahbSOt8*W$v z9e(wY7On->buiw;u7P9yTZ7ZkeXKgk;XdMtqQ_0S=(-ccbJvW6yzxThEzQ$Xc;18$ zQrlNm2wQr)uJeonn0A=nQTDQke!P5Vd4P^m-km zsLzHE)F4a(a;clFscjDd=>!0pLmZem6qx}H0{^#ek=%w@PehNA7uiU^1xe<>b${-; zRS((P>EX~ZaG+`ePCZWoM9nL`cErs zjzzqcgTiqVMU-2!KLKoBBqdY(6|TL#kKIM3KV1e`zBMe%LA>>C0DH4k6ammHqiT~eupHTK z9!uUv0m+s=$7(w=-eWXR^0SkIy8ohh53rD-7wP;08b^ksB*%FtHNytjyiHJF1)?gP z&1Xt_$sc*DEY`lDcz+Sz?m{|e?&44g@je5dbSeE|TI@^=5K5m+lEXlX|OZN&ja$9-L3EdZim2a* z6k|_9I-e?;ADVn>)bU^8f&6}6cM-E&q?O+n7R#8KLgOJJdW4h;J#t^cENK@Z8tXD< z0z0ed!PE@&*plB9H_(Q5UzGK@v&eCi?hd;$WbfXPCwrv6;pe^66<=O6Th`AiG633)i+eMi_)zo6XlUEyN_h{kLSYz-iEh~pH%-ur43eK^C zovDnPYw>i0706X4YXM<@iPWG}L82JKYbJPp#*` zf#^Kec2tH3EYje3cHfQctIv}T{tFk}qXw`e%_b@Pe zTP>$Ex(TF|7B&@&$)Y zaH}S~e)1X5AP*CrRnkh>H;o?G-R}4vB=` zLK(e`9wtV8&npNp$E{$pUP!W4YrlO_q#~M$$3kVa(!cWy=)1DuOBj8cYD7=X%~ut9 zdOw#rGdDj^eMsT4jU7;e7s^eS8xH)X&3nA%(v7G5>csv+`unml4A`17AO_Ok>6zng zt-gk*22-}2y}ahd;HbfeuwC4;`Zhxk^P2oV2u_mcI8jkD%;(X9={rA&C4is+MFn(TVajqqy!(ALQY4hwFR{!EC5$o4M zN_y-3t}!zl2(*iE$y^>Z++$d^&l{xP_9|Db4XxsOE1ON;i{*60vx+oXSDeW(9thN0 zY#6ueu%4J_2rSPrnA&x8=jzZY_#{(>Vz&tkfRLAKAypiYlj$87gJ=M=BGoq#p=ZZZ z?V)TcS*Y4HZB{XVQCe-Hcsb8S{la&YmK@S^DEVA&86UdqT{b)ZqYSN<(eHerZe;vB zF?yFSaoX2CDuLT8Z;re+|7wJ~4W=PPgd^P%7K4%z3-c5RqzEwaf%ztLV>fwC&Jsm3C*?*-B;l)|_5tXFh z+Qd8O`hl{FY$tyvB|zk7qKe$wYJ~j5v9awjQkFn-vkWuUK_3DkmfPQHo`s+?ab|{Z zBIU|O`^OM$U~IWHa*x$4_^QS|9&NhNbVcL0l2Oe`+bzMwFl`89kgeHqlP{b&Es&VA zKQtoyRhzj{ie?^<(ff?N`UC38{$_VxPPqT#-JN4O(v$i#7CCJGUe(lR1gs5{yQo|{02H2!5yKgB~Wyh{+$b~66 z3?n^Dh|_6lCChnE2Xv6X*0|!s40u7-TNQTl^!f3uUmDspZE|hQ4~naIz@PpTZ?&;k zpO;FG#-8T7t|B6$`03D$FhkDKop z#y6?5l!G`;V5mYDqhqUmu&t2s8;OJXj%=qi@Xs-DT~?TFyOR;DL{%SAjwY-wQ$bf-cmQ zuOgYxsTmrtkk>WTgWDBodd|$2fZ_!1Nw{CICTh=@szS|)2De~LI{_wsqT*p;4f+U7 zyR;{v;qMX*J>WmLoH-6Un-Eb;W?=;*@$mQg6br7XXC6AI5wr?%nU9$>9+O+1?vWr$WRv@#1H*)`e=<5}D-8V9<>94}G z!!_LMf+D%sn~!RI=q)vWBmZyNwO_zJ9rOQL96vnkoL$73&1Wj>i;KU%y}xw9-j?(2 zd*%b%BcY)y%A78&`P6@@NrI=V{@-(Z;A-#>8}@DH7h9|mrRP<*b@cKi0nc? zS8Fz?@HF4d`S&AV4R|Erk;Hq_N1vOqH_Q)R80c{C^4UA~k>$@Sfmg#B-MSB4z7xFa zf=cJoDdoUR=j(&nZB8B407gskstW;uH@B>3s8iv&0z9kZ=grK(Cjm8OUZS&(Of>xV z=+Uc}k|N-f`&qiq=dE;Vo-pHX(Z?si?Q2D4RYesW`js|iZ=0#W0i4;q>^_^XzmE@i zOj(x=@Nk~shq|Dn`TgB8G&2M~7jBgL@eVUcS!;WRRz;pUXO@geCyV2D`uj literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Honeycomb.png b/resources/textures/displacement/Honeycomb.png new file mode 100644 index 0000000000000000000000000000000000000000..d428a5d2b3d3a7a6d92cb24060e2e9dc34428f03 GIT binary patch literal 29854 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 Qod@SkbEiJow(sJ<0Zn&PP5=M^ literal 0 HcmV?d00001 diff --git a/resources/textures/displacement/Leather.png b/resources/textures/displacement/Leather.png new file mode 100644 index 0000000000000000000000000000000000000000..0c16daac63e8ab6e946207a8ba9ab8947f8bb774 GIT binary patch literal 534739 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