diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 561bd43ae7..c2d52a26e4 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1440,6 +1440,7 @@ static void prime_tower_textured_path(WipeTowerWriter &writer, const float v = texture.z_max > texture.z_min + EPSILON ? std::clamp((print_z - texture.z_min) / (texture.z_max - texture.z_min), 0.f, 1.f) : 0.f; + const float texture_height = texture.z_max > texture.z_min + EPSILON ? texture.z_max - texture.z_min : 0.f; const float base_width = std::max(0.05f, texture.max_line_width); const float config_min_width = std::clamp(texture.min_line_width, 0.05f, base_width); const float min_width_for_positive_spacing = layer_height * float(1. - 0.25 * PI) + 1e-4f; @@ -1474,7 +1475,7 @@ static void prime_tower_textured_path(WipeTowerWriter &writer, const float t1 = float(step + 1) / float(steps); const float mid_distance = travelled + sample_len * (0.5f * (t0 + t1)); const float u = (mid_distance - texture_path.anchor_distance) / total_length; - const float visibility = texture.sample_tool_visibility(current_tool, u, v, normalization_tools); + const float visibility = texture.sample_tool_visibility(current_tool, u, v, normalization_tools, total_length, texture_height); const float target_width = base_width - (1.f - visibility) * width_range; const float flow_scale = prime_tower_flow_scale_for_width(base_width, target_width, layer_height); const float centerline_shift = 0.5f * (base_width - reference_width) + 0.5f * (base_width - target_width); diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 571d624604..a57aab03aa 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -44,6 +44,7 @@ struct PrimeTowerTextureRenderSettings bool enabled = false; float angle_offset_deg = 0.f; + bool preserve_aspect_ratio = false; int color_mode = Auto; bool generic_fallback_for_missing_channels = false; bool compact_offset_mode = true; @@ -74,37 +75,48 @@ struct PrimeTowerTextureRenderSettings return enabled && (image_valid(false) || image_valid(true)); } - float sample_tool_visibility(size_t tool, float u, float v) const + float sample_tool_visibility(size_t tool, float u, float v, float wrap_width_mm = 0.f, float height_mm = 0.f) const { - const float raw_visibility = sample_tool_visibility_raw(tool, u, v); + const float raw_visibility = sample_tool_visibility_raw(tool, u, v, wrap_width_mm, height_mm); if (!compact_offset_mode) return adjusted_tool_visibility(tool, raw_visibility); float max_visibility = std::clamp(raw_visibility, 0.f, 1.f); if (!tool_indices.empty()) { for (const size_t candidate_tool : tool_indices) { - if (candidate_tool != tool) - max_visibility = std::max(max_visibility, std::clamp(sample_tool_visibility_raw(candidate_tool, u, v), 0.f, 1.f)); + if (candidate_tool != tool) { + const float candidate_visibility = sample_tool_visibility_raw(candidate_tool, u, v, wrap_width_mm, height_mm); + max_visibility = std::max(max_visibility, std::clamp(candidate_visibility, 0.f, 1.f)); + } } } else { for (size_t candidate_tool = 0; candidate_tool < filament_colours.size(); ++candidate_tool) { - if (candidate_tool != tool) - max_visibility = std::max(max_visibility, std::clamp(sample_tool_visibility_raw(candidate_tool, u, v), 0.f, 1.f)); + if (candidate_tool != tool) { + const float candidate_visibility = sample_tool_visibility_raw(candidate_tool, u, v, wrap_width_mm, height_mm); + max_visibility = std::max(max_visibility, std::clamp(candidate_visibility, 0.f, 1.f)); + } } } return max_visibility > 1e-6f ? adjusted_tool_visibility(tool, raw_visibility / max_visibility) : adjusted_tool_visibility(tool, raw_visibility); } - float sample_tool_visibility(size_t tool, float u, float v, const std::vector &normalization_tools) const + float sample_tool_visibility(size_t tool, + float u, + float v, + const std::vector &normalization_tools, + float wrap_width_mm = 0.f, + float height_mm = 0.f) const { - const float raw_visibility = sample_tool_visibility_raw(tool, u, v); + const float raw_visibility = sample_tool_visibility_raw(tool, u, v, wrap_width_mm, height_mm); if (!compact_offset_mode || normalization_tools.empty()) return adjusted_tool_visibility(tool, raw_visibility); float max_visibility = std::clamp(raw_visibility, 0.f, 1.f); - for (const size_t normalization_tool : normalization_tools) - max_visibility = std::max(max_visibility, std::clamp(sample_tool_visibility_raw(normalization_tool, u, v), 0.f, 1.f)); + for (const size_t normalization_tool : normalization_tools) { + const float normalization_visibility = sample_tool_visibility_raw(normalization_tool, u, v, wrap_width_mm, height_mm); + max_visibility = std::max(max_visibility, std::clamp(normalization_visibility, 0.f, 1.f)); + } return max_visibility > 1e-6f ? adjusted_tool_visibility(tool, raw_visibility / max_visibility) : adjusted_tool_visibility(tool, raw_visibility); @@ -224,7 +236,7 @@ private: } } - float sample_tool_visibility_raw(size_t tool, float u, float v) const + float sample_tool_visibility_raw(size_t tool, float u, float v, float wrap_width_mm = 0.f, float height_mm = 0.f) const { if (!valid()) return 1.f; @@ -239,11 +251,36 @@ private: } else { use_back = back_valid; } + const float side_width_mm = front_valid && back_valid ? 0.5f * wrap_width_mm : wrap_width_mm; + apply_preserved_aspect_ratio(u, v, use_back, side_width_mm, height_mm); if (settings_zone_enabled) return sample_settings_zone_tool_visibility(tool, u, v, use_back); return sample_image_tool_visibility(tool, u, v, use_back); } + void apply_preserved_aspect_ratio(float &u, float &v, bool back, float side_width_mm, float height_mm) const + { + if (!preserve_aspect_ratio || side_width_mm <= 1e-6f || height_mm <= 1e-6f) + return; + const unsigned int width = back ? image_width_back : image_width; + const unsigned int height = back ? image_height_back : image_height; + if (width == 0 || height == 0) + return; + + const float image_aspect = float(width) / float(height); + const float target_aspect = side_width_mm / height_mm; + if (!std::isfinite(image_aspect) || !std::isfinite(target_aspect) || image_aspect <= 1e-6f || target_aspect <= 1e-6f) + return; + + if (target_aspect > image_aspect + 1e-6f) { + const float visible_height = std::clamp(image_aspect / target_aspect, 0.f, 1.f); + v = std::clamp(v * visible_height, 0.f, 1.f); + } else if (image_aspect > target_aspect + 1e-6f) { + const float visible_width = std::clamp(target_aspect / image_aspect, 0.f, 1.f); + u = std::clamp(0.5f * (1.f - visible_width) + u * visible_width, 0.f, 1.f); + } + } + bool image_valid(bool back) const { return back ? diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index 608294b2f2..4e06d4c481 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -1419,6 +1419,7 @@ static void prime_tower_textured_path(WipeTowerWriter2& writer, const float v = texture.z_max > texture.z_min + EPSILON ? std::clamp((print_z - texture.z_min) / (texture.z_max - texture.z_min), 0.f, 1.f) : 0.f; + const float texture_height = texture.z_max > texture.z_min + EPSILON ? texture.z_max - texture.z_min : 0.f; const float base_width = std::max(0.05f, texture.max_line_width); const float config_min_width = std::clamp(texture.min_line_width, 0.05f, base_width); const float min_width_for_positive_spacing = layer_height * float(1. - 0.25 * PI) + 1e-4f; @@ -1453,7 +1454,7 @@ static void prime_tower_textured_path(WipeTowerWriter2& writer, const float t1 = float(step + 1) / float(steps); const float mid_distance = travelled + sample_len * (0.5f * (t0 + t1)); const float u = (mid_distance - texture_path.anchor_distance) / total_length; - const float visibility = texture.sample_tool_visibility(current_tool, u, v, normalization_tools); + const float visibility = texture.sample_tool_visibility(current_tool, u, v, normalization_tools, total_length, texture_height); const float target_width = base_width - (1.f - visibility) * width_range; const float flow_scale = prime_tower_flow_scale_for_width(base_width, target_width, layer_height); const float centerline_shift = 0.5f * (base_width - reference_width) + 0.5f * (base_width - target_width); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8f34b1d4ad..a6debe57e4 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -3489,6 +3489,7 @@ void Print::_make_wipe_tower() texture.enabled = true; texture.generic_fallback_for_missing_channels = auto_mode; texture.angle_offset_deg = m_texture_mapping_global_settings.angle_offset_deg; + texture.preserve_aspect_ratio = m_texture_mapping_global_settings.preserve_aspect_ratio; if (have_settings_zone) { texture.settings_zone_enabled = true; texture.texture_mapping_mode = settings_zone->texture_mapping_mode; diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index b4bf28cc37..c5aff6a144 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -635,6 +635,7 @@ std::string TextureMappingGlobalSettings::serialize() const const std::string normalized_mode = normalize_color_mode_name(prime_tower_color_mode); if (!enabled && std::abs((std::isfinite(angle_offset_deg) ? angle_offset_deg : 0.f)) <= 1e-6f && + !preserve_aspect_ratio && normalized_mode == "auto" && prime_tower_settings_zone_uid == 0 && image_file.empty() && @@ -652,6 +653,7 @@ std::string TextureMappingGlobalSettings::serialize() const nlohmann::json prime_tower_texture_mapping; prime_tower_texture_mapping["enabled"] = enabled; prime_tower_texture_mapping["angle_offset_deg"] = std::clamp(std::isfinite(angle_offset_deg) ? angle_offset_deg : 0.f, 0.f, 360.f); + prime_tower_texture_mapping["preserve_aspect_ratio"] = preserve_aspect_ratio; prime_tower_texture_mapping["prime_tower_color_mode"] = normalized_mode; prime_tower_texture_mapping["settings_zone_uid"] = prime_tower_settings_zone_uid; prime_tower_texture_mapping["image_file"] = image_file; @@ -689,6 +691,7 @@ void TextureMappingGlobalSettings::load(const std::string &serialized) const nlohmann::json &prime_tower_texture_mapping = *prime_tower_texture_mapping_it; enabled = prime_tower_texture_mapping.value("enabled", false); angle_offset_deg = std::clamp(prime_tower_texture_mapping.value("angle_offset_deg", 0.f), 0.f, 360.f); + preserve_aspect_ratio = prime_tower_texture_mapping.value("preserve_aspect_ratio", false); prime_tower_color_mode = normalize_color_mode_name(prime_tower_texture_mapping.value("prime_tower_color_mode", std::string("auto"))); prime_tower_settings_zone_uid = prime_tower_texture_mapping.value("settings_zone_uid", uint64_t(0)); image_file = prime_tower_texture_mapping.value("image_file", std::string()); diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 4952db2308..b20840660d 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -242,6 +242,7 @@ struct TextureMappingGlobalSettings { bool enabled = false; float angle_offset_deg = 0.f; + bool preserve_aspect_ratio = false; std::string prime_tower_color_mode = "auto"; uint64_t prime_tower_settings_zone_uid = 0; std::string image_file; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 1467630b9d..31acb1a578 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1098,12 +1098,66 @@ float prime_tower_preview_anchor_distance(const Points &points, const Vec2f &cen return best_projection > -std::numeric_limits::max() ? best_distance : fallback_distance; } -float prime_tower_preview_texture_v(float z, float texture_z_min, float texture_z_max) +float prime_tower_preview_preserved_texture_u(float u, + bool preserve_aspect_ratio, + unsigned int image_width, + unsigned int image_height, + float surface_width, + float surface_height) +{ + if (!preserve_aspect_ratio || image_width == 0 || image_height == 0 || surface_width <= prime_tower_preview_epsilon || + surface_height <= prime_tower_preview_epsilon) + return u; + + const float image_aspect = float(image_width) / float(image_height); + const float target_aspect = surface_width / surface_height; + if (!std::isfinite(image_aspect) || !std::isfinite(target_aspect) || image_aspect <= prime_tower_preview_epsilon || + target_aspect <= prime_tower_preview_epsilon || image_aspect <= target_aspect + prime_tower_preview_epsilon) + return u; + + const float visible_width = std::clamp(target_aspect / image_aspect, 0.f, 1.f); + return std::clamp(0.5f * (1.f - visible_width) + u * visible_width, 0.f, 1.f); +} + +float prime_tower_preview_preserved_source_v(float source_v, + bool preserve_aspect_ratio, + unsigned int image_width, + unsigned int image_height, + float surface_width, + float surface_height) +{ + if (!preserve_aspect_ratio || image_width == 0 || image_height == 0 || surface_width <= prime_tower_preview_epsilon || + surface_height <= prime_tower_preview_epsilon) + return source_v; + + const float image_aspect = float(image_width) / float(image_height); + const float target_aspect = surface_width / surface_height; + if (!std::isfinite(image_aspect) || !std::isfinite(target_aspect) || image_aspect <= prime_tower_preview_epsilon || + target_aspect <= prime_tower_preview_epsilon || target_aspect <= image_aspect + prime_tower_preview_epsilon) + return source_v; + + const float visible_height = std::clamp(image_aspect / target_aspect, 0.f, 1.f); + return std::clamp(source_v * visible_height, 0.f, 1.f); +} + +float prime_tower_preview_texture_v(float z, + float texture_z_min, + float texture_z_max, + bool preserve_aspect_ratio, + unsigned int image_width, + unsigned int image_height, + float surface_width) { const float v = texture_z_max > texture_z_min + prime_tower_preview_epsilon ? std::clamp((z - texture_z_min) / (texture_z_max - texture_z_min), 0.f, 1.f) : 0.f; - return 1.f - v; + const float surface_height = texture_z_max > texture_z_min + prime_tower_preview_epsilon ? texture_z_max - texture_z_min : 0.f; + return 1.f - prime_tower_preview_preserved_source_v(v, + preserve_aspect_ratio, + image_width, + image_height, + surface_width, + surface_height); } template @@ -1129,7 +1183,10 @@ GUI::GLModel::Geometry prime_tower_image_preview_geometry(float width, float angle_offset_deg, float texture_z_min, float texture_z_max, - int image_slot) + int image_slot, + bool preserve_aspect_ratio, + unsigned int image_width, + unsigned int image_height) { GUI::GLModel::Geometry data; data.format = {GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3T2}; @@ -1140,6 +1197,9 @@ GUI::GLModel::Geometry prime_tower_image_preview_geometry(float width, const std::array points = {Vec2f(0.f, 0.f), Vec2f(width, 0.f), Vec2f(width, depth), Vec2f(0.f, depth)}; const std::array distances = {0.f, width, width + depth, 2.f * width + depth}; const float total_length = 2.f * (width + depth); + const float texture_surface_width = image_slot == 0 ? total_length : 0.5f * total_length; + const float texture_surface_height = + texture_z_max > texture_z_min + prime_tower_preview_epsilon ? texture_z_max - texture_z_min : 0.f; const float anchor_distance = prime_tower_preview_anchor_distance(points, Vec2f(width * 0.5f, depth * 0.5f), prime_tower_preview_anchor_angle(points, angle_offset_deg)); @@ -1157,15 +1217,33 @@ GUI::GLModel::Geometry prime_tower_image_preview_geometry(float width, data.reserve_vertices(16 * points.size() * (z_levels.size() - 1)); data.reserve_indices(24 * points.size() * (z_levels.size() - 1)); - auto texture_u = [anchor_distance, total_length, image_slot](float distance, float mid_distance) { + auto texture_u = [anchor_distance, + total_length, + image_slot, + preserve_aspect_ratio, + image_width, + image_height, + texture_surface_width, + texture_surface_height](float distance, float mid_distance) { const float raw_u = (distance - anchor_distance) / total_length; const float mid_raw_u = (mid_distance - anchor_distance) / total_length; const float base = std::floor(mid_raw_u); if (image_slot == 0) - return std::clamp(raw_u - base, 0.f, 1.f); - return image_slot == 1 ? + return prime_tower_preview_preserved_texture_u(std::clamp(raw_u - base, 0.f, 1.f), + preserve_aspect_ratio, + image_width, + image_height, + texture_surface_width, + texture_surface_height); + const float u = image_slot == 1 ? std::clamp(2.f * (raw_u - base), 0.f, 1.f) : std::clamp(2.f * (raw_u - base - 0.5f), 0.f, 1.f); + return prime_tower_preview_preserved_texture_u(u, + preserve_aspect_ratio, + image_width, + image_height, + texture_surface_width, + texture_surface_height); }; for (size_t side_idx = 0; side_idx < points.size(); ++side_idx) { @@ -1225,8 +1303,20 @@ GUI::GLModel::Geometry prime_tower_image_preview_geometry(float width, for (size_t z_idx = 0; z_idx + 1 < z_levels.size(); ++z_idx) { const float z0 = z_levels[z_idx]; const float z1 = z_levels[z_idx + 1]; - const float v0 = prime_tower_preview_texture_v(z0, texture_z_min, texture_z_max); - const float v1 = prime_tower_preview_texture_v(z1, texture_z_min, texture_z_max); + const float v0 = prime_tower_preview_texture_v(z0, + texture_z_min, + texture_z_max, + preserve_aspect_ratio, + image_width, + image_height, + texture_surface_width); + const float v1 = prime_tower_preview_texture_v(z1, + texture_z_min, + texture_z_max, + preserve_aspect_ratio, + image_width, + image_height, + texture_surface_width); const unsigned int base = unsigned(data.vertices_count()); data.add_vertex(Vec3f(p0.x(), p0.y(), z0), normal, Vec2f(u0, v0)); @@ -1516,7 +1606,10 @@ GUI::GLModel::Geometry prime_tower_mesh_image_preview_geometry(const TriangleMes float angle_offset_deg, float texture_z_min, float texture_z_max, - int image_slot) + int image_slot, + bool preserve_aspect_ratio, + unsigned int image_width, + unsigned int image_height) { GUI::GLModel::Geometry data; data.format = {GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3T2}; @@ -1554,6 +1647,10 @@ GUI::GLModel::Geometry prime_tower_mesh_image_preview_geometry(const TriangleMes const float lower_raw1 = (lower.distances[point_idx] + lower_len - lower.anchor_distance) / lower.total_length; const float upper_raw0 = (upper.distances[point_idx] - upper.anchor_distance) / upper.total_length; const float upper_raw1 = (upper.distances[point_idx] + upper_len - upper.anchor_distance) / upper.total_length; + const float lower_surface_width = image_slot == 0 ? lower.total_length : 0.5f * lower.total_length; + const float upper_surface_width = image_slot == 0 ? upper.total_length : 0.5f * upper.total_length; + const float texture_surface_height = + texture_z_max > texture_z_min + prime_tower_preview_epsilon ? texture_z_max - texture_z_min : 0.f; std::vector cuts = {0.f, 1.f}; prime_tower_preview_add_raw_u_cuts(cuts, lower_raw0, lower_raw1, image_slot); @@ -1602,12 +1699,52 @@ GUI::GLModel::Geometry prime_tower_mesh_image_preview_geometry(const TriangleMes const float lower_raw_t1 = lower_raw0 + (lower_raw1 - lower_raw0) * t1; const float upper_raw_t0 = upper_raw0 + (upper_raw1 - upper_raw0) * t0; const float upper_raw_t1 = upper_raw0 + (upper_raw1 - upper_raw0) * t1; - const float u0 = prime_tower_preview_texture_u_from_raw(lower_raw_t0, mid_raw, image_slot); - const float u1 = prime_tower_preview_texture_u_from_raw(lower_raw_t1, mid_raw, image_slot); - const float u2 = prime_tower_preview_texture_u_from_raw(upper_raw_t1, mid_raw, image_slot); - const float u3 = prime_tower_preview_texture_u_from_raw(upper_raw_t0, mid_raw, image_slot); - const float v0 = prime_tower_preview_texture_v(lower.z, texture_z_min, texture_z_max); - const float v1 = prime_tower_preview_texture_v(upper.z, texture_z_min, texture_z_max); + const float u0 = prime_tower_preview_preserved_texture_u(prime_tower_preview_texture_u_from_raw(lower_raw_t0, + mid_raw, + image_slot), + preserve_aspect_ratio, + image_width, + image_height, + lower_surface_width, + texture_surface_height); + const float u1 = prime_tower_preview_preserved_texture_u(prime_tower_preview_texture_u_from_raw(lower_raw_t1, + mid_raw, + image_slot), + preserve_aspect_ratio, + image_width, + image_height, + lower_surface_width, + texture_surface_height); + const float u2 = prime_tower_preview_preserved_texture_u(prime_tower_preview_texture_u_from_raw(upper_raw_t1, + mid_raw, + image_slot), + preserve_aspect_ratio, + image_width, + image_height, + upper_surface_width, + texture_surface_height); + const float u3 = prime_tower_preview_preserved_texture_u(prime_tower_preview_texture_u_from_raw(upper_raw_t0, + mid_raw, + image_slot), + preserve_aspect_ratio, + image_width, + image_height, + upper_surface_width, + texture_surface_height); + const float v0 = prime_tower_preview_texture_v(lower.z, + texture_z_min, + texture_z_max, + preserve_aspect_ratio, + image_width, + image_height, + lower_surface_width); + const float v1 = prime_tower_preview_texture_v(upper.z, + texture_z_min, + texture_z_max, + preserve_aspect_ratio, + image_width, + image_height, + upper_surface_width); const unsigned int base = unsigned(data.vertices_count()); data.add_vertex(p0, normal, Vec2f(u0, v0)); @@ -1661,6 +1798,7 @@ void GLWipeTowerVolume::set_prime_tower_image_preview(std::vector unsigned int image_width_back, unsigned int image_height_back, float angle_offset_deg, + bool preserve_aspect_ratio, float width, float depth, float height, @@ -1686,7 +1824,16 @@ void GLWipeTowerVolume::set_prime_tower_image_preview(std::vector return; GUI::GLModel::Geometry image_geometry = - prime_tower_image_preview_geometry(width, depth, height, angle_offset_deg, texture_z_min, texture_z_max, image_slot); + prime_tower_image_preview_geometry(width, + depth, + height, + angle_offset_deg, + texture_z_min, + texture_z_max, + image_slot, + preserve_aspect_ratio, + image_w, + image_h); if (image_geometry.is_empty()) return; @@ -1718,6 +1865,7 @@ void GLWipeTowerVolume::set_prime_tower_image_preview(std::vector unsigned int image_width_back, unsigned int image_height_back, float angle_offset_deg, + bool preserve_aspect_ratio, const TriangleMesh &mesh, float texture_z_min, float texture_z_max) @@ -1741,7 +1889,14 @@ void GLWipeTowerVolume::set_prime_tower_image_preview(std::vector return; GUI::GLModel::Geometry image_geometry = - prime_tower_mesh_image_preview_geometry(mesh, angle_offset_deg, texture_z_min, texture_z_max, image_slot); + prime_tower_mesh_image_preview_geometry(mesh, + angle_offset_deg, + texture_z_min, + texture_z_max, + image_slot, + preserve_aspect_ratio, + image_w, + image_h); if (image_geometry.is_empty()) return; @@ -2082,6 +2237,7 @@ int GLVolumeCollection::load_wipe_tower_preview( prime_tower_image_back.width, prime_tower_image_back.height, texture_mapping_global_settings->angle_offset_deg, + texture_mapping_global_settings->preserve_aspect_ratio, width, depth, height, @@ -2157,6 +2313,7 @@ int GLVolumeCollection::load_real_wipe_tower_preview(int obj_idx prime_tower_image_back.width, prime_tower_image_back.height, texture_mapping_global_settings->angle_offset_deg, + texture_mapping_global_settings->preserve_aspect_ratio, wt_mesh, resolved_texture_z_min, resolved_texture_z_max); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 39827a689f..8a6c94f7e8 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -409,6 +409,7 @@ public: unsigned int image_width_back, unsigned int image_height_back, float angle_offset_deg, + bool preserve_aspect_ratio, float width, float depth, float height, @@ -421,6 +422,7 @@ public: unsigned int image_width_back, unsigned int image_height_back, float angle_offset_deg, + bool preserve_aspect_ratio, const TriangleMesh &mesh, float texture_z_min, float texture_z_max); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1de123ac58..9303eaf721 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1355,6 +1355,12 @@ public: m_prime_tower_mapping_enabled_checkbox->SetValue(m_global_settings.enabled); global_root->Add(m_prime_tower_mapping_enabled_checkbox, 0, wxEXPAND | wxALL, gap); + m_prime_tower_preserve_aspect_ratio_checkbox = new wxCheckBox(global_page, wxID_ANY, _L("Preserve aspect ratio")); + m_prime_tower_preserve_aspect_ratio_checkbox->SetValue(m_global_settings.preserve_aspect_ratio); + m_prime_tower_preserve_aspect_ratio_checkbox->SetToolTip( + _L("Scale the prime tower image without distortion, anchoring its bottom edge at the tower base.")); + global_root->Add(m_prime_tower_preserve_aspect_ratio_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap); + auto *prime_image_box = new wxStaticBoxSizer(wxVERTICAL, global_page, _L("Prime Tower Images")); wxButton *load_prime_image_button = nullptr; wxButton *clear_prime_image_button = nullptr; @@ -1552,6 +1558,8 @@ public: { TextureMappingGlobalSettings settings = m_global_settings; settings.enabled = m_prime_tower_mapping_enabled_checkbox != nullptr && m_prime_tower_mapping_enabled_checkbox->GetValue(); + settings.preserve_aspect_ratio = + m_prime_tower_preserve_aspect_ratio_checkbox != nullptr && m_prime_tower_preserve_aspect_ratio_checkbox->GetValue(); settings.angle_offset_deg = float(std::clamp(m_prime_tower_angle_spin ? m_prime_tower_angle_spin->GetValue() : 0.0, 0.0, 360.0)); settings.prime_tower_color_mode = prime_tower_color_mode_name(m_prime_tower_color_mode_choice ? m_prime_tower_color_mode_choice->GetSelection() : 0); @@ -1805,6 +1813,7 @@ private: wxChoice *m_generic_solver_mode_choice {nullptr}; wxChoice *m_generic_solver_mix_model_choice {nullptr}; wxCheckBox *m_prime_tower_mapping_enabled_checkbox {nullptr}; + wxCheckBox *m_prime_tower_preserve_aspect_ratio_checkbox {nullptr}; wxStaticText *m_prime_tower_image_label {nullptr}; wxStaticText *m_prime_tower_image_back_label {nullptr}; wxSpinCtrlDouble *m_prime_tower_angle_spin {nullptr};