Update scene preview when 2D gradient settings are being changed

This commit is contained in:
sentientstardust
2026-05-22 09:19:00 +01:00
parent 14e61db633
commit c6035a7c91
7 changed files with 208 additions and 63 deletions

View File

@@ -18,6 +18,7 @@ uniform bool invalid_texture_mapping;
uniform PrintVolumeDetection print_volume;
uniform int gradient_component_count;
uniform vec3 gradient_base_color;
uniform vec3 gradient_component_colors[MAX_GRADIENT_COMPONENTS];
uniform float gradient_distances_mm[MAX_GRADIENT_COMPONENTS];
uniform float gradient_angles_deg[MAX_GRADIENT_COMPONENTS];
@@ -96,7 +97,7 @@ float offset_fade_factor(int fade_mode, float progress01)
if (fade_mode == 4)
return abs(2.0 * p - 1.0);
if (fade_mode == 5)
return 2.0 * p - 1.0;
return 1.0 - 2.0 * p;
return 1.0;
}
@@ -196,17 +197,19 @@ vec3 surface_gradient_color()
direction_vec = vec2(1.0, 0.0);
float theta_deg = normalize_angle(degrees(atan(direction_vec.y, direction_vec.x)) - rotation_deg);
float fade_factor = abs(offset_fade_factor(gradient_fade_mode, z_progress));
float signed_fade_factor = offset_fade_factor(gradient_fade_mode, z_progress);
float fade_factor = abs(signed_fade_factor);
float sample_theta_deg = signed_fade_factor < 0.0 ? normalize_angle(theta_deg + 180.0) : theta_deg;
float influences[MAX_GRADIENT_COMPONENTS];
float edge_reaches[MAX_GRADIENT_COMPONENTS];
float min_reach = 1000000.0;
float max_reach = -1000000.0;
float visibility_weights[MAX_GRADIENT_COMPONENTS];
float min_visibility = 1.0;
float max_visibility = 0.0;
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
influences[i] = 0.0;
edge_reaches[i] = 0.0;
visibility_weights[i] = 0.0;
if (i < count)
influences[i] = component_angular_influence(i, theta_deg);
influences[i] = component_angular_influence(i, sample_theta_deg);
}
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
@@ -223,24 +226,25 @@ vec3 surface_gradient_color()
gradient_max_width_delta_limit_mm,
gradient_minimum_offset_factors[i],
gradient_strength_factors[i]);
edge_reaches[i] = clamp(gradient_max_width_delta_limit_mm - width_delta_mm, 0.0, gradient_max_width_delta_limit_mm);
min_reach = min(min_reach, edge_reaches[i]);
max_reach = max(max_reach, edge_reaches[i]);
visibility_weights[i] = 1.0 - clamp(width_delta_mm / max(gradient_max_width_delta_limit_mm, EPSILON), 0.0, 1.0);
min_visibility = min(min_visibility, visibility_weights[i]);
max_visibility = max(max_visibility, visibility_weights[i]);
}
vec3 mixed_color = vec3(0.0);
float total_weight = 0.0;
float reach_span = max_reach - min_reach;
vec3 target_color = vec3(0.0);
float total_excess_weight = 0.0;
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
if (i >= count)
continue;
float weight = reach_span > EPSILON ? clamp((edge_reaches[i] - min_reach) / reach_span, 0.0, 1.0) : 1.0;
mixed_color += gradient_component_colors[i] * weight;
total_weight += weight;
float weight = max(0.0, visibility_weights[i] - min_visibility);
target_color += gradient_component_colors[i] * weight;
total_excess_weight += weight;
}
if (total_weight <= EPSILON)
return gradient_component_colors[0];
return clamp(mixed_color / total_weight, 0.0, 1.0);
if (total_excess_weight <= EPSILON)
return clamp(gradient_base_color, 0.0, 1.0);
float contrast = clamp(max_visibility - min_visibility, 0.0, 1.0);
target_color = clamp(target_color / total_excess_weight, 0.0, 1.0);
return mix(clamp(gradient_base_color, 0.0, 1.0), target_color, contrast);
}
float invalid_texture_mapping_checker()

View File

@@ -18,6 +18,7 @@ uniform bool invalid_texture_mapping;
uniform PrintVolumeDetection print_volume;
uniform int gradient_component_count;
uniform vec3 gradient_base_color;
uniform vec3 gradient_component_colors[MAX_GRADIENT_COMPONENTS];
uniform float gradient_distances_mm[MAX_GRADIENT_COMPONENTS];
uniform float gradient_angles_deg[MAX_GRADIENT_COMPONENTS];
@@ -98,7 +99,7 @@ float offset_fade_factor(int fade_mode, float progress01)
if (fade_mode == 4)
return abs(2.0 * p - 1.0);
if (fade_mode == 5)
return 2.0 * p - 1.0;
return 1.0 - 2.0 * p;
return 1.0;
}
@@ -198,17 +199,19 @@ vec3 surface_gradient_color()
direction_vec = vec2(1.0, 0.0);
float theta_deg = normalize_angle(degrees(atan(direction_vec.y, direction_vec.x)) - rotation_deg);
float fade_factor = abs(offset_fade_factor(gradient_fade_mode, z_progress));
float signed_fade_factor = offset_fade_factor(gradient_fade_mode, z_progress);
float fade_factor = abs(signed_fade_factor);
float sample_theta_deg = signed_fade_factor < 0.0 ? normalize_angle(theta_deg + 180.0) : theta_deg;
float influences[MAX_GRADIENT_COMPONENTS];
float edge_reaches[MAX_GRADIENT_COMPONENTS];
float min_reach = 1000000.0;
float max_reach = -1000000.0;
float visibility_weights[MAX_GRADIENT_COMPONENTS];
float min_visibility = 1.0;
float max_visibility = 0.0;
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
influences[i] = 0.0;
edge_reaches[i] = 0.0;
visibility_weights[i] = 0.0;
if (i < count)
influences[i] = component_angular_influence(i, theta_deg);
influences[i] = component_angular_influence(i, sample_theta_deg);
}
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
@@ -225,24 +228,25 @@ vec3 surface_gradient_color()
gradient_max_width_delta_limit_mm,
gradient_minimum_offset_factors[i],
gradient_strength_factors[i]);
edge_reaches[i] = clamp(gradient_max_width_delta_limit_mm - width_delta_mm, 0.0, gradient_max_width_delta_limit_mm);
min_reach = min(min_reach, edge_reaches[i]);
max_reach = max(max_reach, edge_reaches[i]);
visibility_weights[i] = 1.0 - clamp(width_delta_mm / max(gradient_max_width_delta_limit_mm, EPSILON), 0.0, 1.0);
min_visibility = min(min_visibility, visibility_weights[i]);
max_visibility = max(max_visibility, visibility_weights[i]);
}
vec3 mixed_color = vec3(0.0);
float total_weight = 0.0;
float reach_span = max_reach - min_reach;
vec3 target_color = vec3(0.0);
float total_excess_weight = 0.0;
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
if (i >= count)
continue;
float weight = reach_span > EPSILON ? clamp((edge_reaches[i] - min_reach) / reach_span, 0.0, 1.0) : 1.0;
mixed_color += gradient_component_colors[i] * weight;
total_weight += weight;
float weight = max(0.0, visibility_weights[i] - min_visibility);
target_color += gradient_component_colors[i] * weight;
total_excess_weight += weight;
}
if (total_weight <= EPSILON)
return gradient_component_colors[0];
return clamp(mixed_color / total_weight, 0.0, 1.0);
if (total_excess_weight <= EPSILON)
return clamp(gradient_base_color, 0.0, 1.0);
float contrast = clamp(max_visibility - min_visibility, 0.0, 1.0);
target_color = clamp(target_color / total_excess_weight, 0.0, 1.0);
return mix(clamp(gradient_base_color, 0.0, 1.0), target_color, contrast);
}
float invalid_texture_mapping_checker()

View File

@@ -9800,12 +9800,15 @@ std::optional<PreferredSeamPoint> GCode::texture_mapping_seam_hiding_hint(const
const float theta_deg =
normalize_angle_deg_for_gcode(float(Geometry::rad2deg(std::atan2(theta_direction_y, theta_direction_x))));
const float sample_theta_deg = state.signed_fade_factor < 0.f ?
normalize_angle_deg_for_gcode(theta_deg + 180.f) :
theta_deg;
float raw_inset_mm = 0.f;
for (size_t i = 0; i < component_ids.size(); ++i) {
if (i == state.active_component_idx)
continue;
const float influence = component_angular_influence_for_gcode(component_ids[i],
theta_deg,
sample_theta_deg,
component_ids,
rotated_angles);
raw_inset_mm += distances_mm[i] * influence;
@@ -9839,7 +9842,7 @@ std::optional<PreferredSeamPoint> GCode::texture_mapping_seam_hiding_hint(const
return std::nullopt;
const float centerline_shift_mm = base_centerline_shift_mm + 0.5f * width_delta_mm;
const float centerline_outward_shift_mm = state.signed_fade_factor >= 0.f ? -centerline_shift_mm : centerline_shift_mm;
const float centerline_outward_shift_mm = -centerline_shift_mm;
const float outer_offset_mm = centerline_outward_shift_mm + 0.5f * target_width_mm;
if (!std::isfinite(outer_offset_mm))
return std::nullopt;
@@ -10765,7 +10768,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
bool object_center_mode { false };
Point object_center;
float inset_strength_reference_mm { 0.f };
float signed_fade_factor { 1.f };
float max_width_delta_mm { 0.f };
float base_outer_width_mm { 0.4f };
float dither_pitch_mm { 0.08f };
@@ -11002,7 +11004,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
outer_wall_gradient_dynamic_ctx.object_center_mode = object_center_mode;
outer_wall_gradient_dynamic_ctx.object_center = object_center;
outer_wall_gradient_dynamic_ctx.inset_strength_reference_mm = max_allowed_distance_mm;
outer_wall_gradient_dynamic_ctx.signed_fade_factor = signed_fade_factor;
outer_wall_gradient_dynamic_ctx.max_width_delta_mm = effective_max_width_delta_mm;
outer_wall_gradient_dynamic_ctx.base_outer_width_mm = base_outer_width_mm;
outer_wall_gradient_dynamic_ctx.dither_pitch_mm = offset_context->dither_pitch_mm;
@@ -11027,7 +11028,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (std::abs(centerline_shift_mm) <= EPSILON)
return true;
const bool reverse_shift = (signed_fade_factor < 0.f) != (centerline_shift_mm < 0.f);
const bool reverse_shift = centerline_shift_mm < 0.f;
const double shift_x = reverse_shift ? -mod.shift_unit_x : mod.shift_unit_x;
const double shift_y = reverse_shift ? -mod.shift_unit_y : mod.shift_unit_y;
const double shift_scaled = scale_(double(std::abs(centerline_shift_mm)));
@@ -11805,8 +11806,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (std::abs(centerline_shift_mm) > EPSILON) {
const double inward_x = -outward_x;
const double inward_y = -outward_y;
const bool reverse_shift =
(outer_wall_gradient_dynamic_ctx.signed_fade_factor < 0.f) != (centerline_shift_mm < 0.f);
const bool reverse_shift = centerline_shift_mm < 0.f;
const double shift_x = reverse_shift ? -inward_x : inward_x;
const double shift_y = reverse_shift ? -inward_y : inward_y;
const double shift_scaled = scale_(double(std::abs(centerline_shift_mm)));

View File

@@ -2512,7 +2512,7 @@ float texture_mapping_offset_fade_factor(int fade_mode, float progress01)
case int(TextureMappingZone::OffsetFadeOutUp): return 1.f - p;
case int(TextureMappingZone::OffsetFadeInOut): return 1.f - std::abs(2.f * p - 1.f);
case int(TextureMappingZone::OffsetFadeOutIn): return std::abs(2.f * p - 1.f);
case int(TextureMappingZone::OffsetFadeOutInReversed): return 2.f * p - 1.f;
case int(TextureMappingZone::OffsetFadeOutInReversed): return 1.f - 2.f * p;
default: return 1.f;
}
}
@@ -2789,7 +2789,8 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
for (float &a : rotated_angles)
a = normalize_texture_mapping_offset_angle_deg(a + rotation_deg);
const float fade_factor = std::abs(texture_mapping_offset_fade_factor(zone.offset_fade_mode, z_progress));
const float signed_fade_factor = texture_mapping_offset_fade_factor(zone.offset_fade_mode, z_progress);
const float fade_factor = std::abs(signed_fade_factor);
if (fade_factor <= EPSILON)
return std::nullopt;
@@ -2812,6 +2813,7 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
context.rotated_angles = std::move(rotated_angles);
context.weight_field = std::move(weight_field);
context.inset_strength_reference_mm = max_allowed_distance_mm;
context.signed_fade_factor = signed_fade_factor;
context.fade_factor = fade_factor;
context.max_width_delta_mm = max_width_delta_limit_mm;
context.dither_pitch_mm = dither_pitch_mm;
@@ -2882,6 +2884,9 @@ float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext
const float theta_deg =
normalize_texture_mapping_offset_angle_deg(float(Geometry::rad2deg(std::atan2(theta_direction_y, theta_direction_x))));
const float sample_theta_deg = context.signed_fade_factor < 0.f ?
normalize_texture_mapping_offset_angle_deg(theta_deg + 180.f) :
theta_deg;
float raw_inset_mm = 0.f;
const size_t component_count = std::min(context.component_ids.size(), context.component_distances_mm.size());
for (size_t i = 0; i < component_count; ++i) {
@@ -2889,7 +2894,7 @@ float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext
continue;
const float influence =
component_angular_influence(context.component_ids[i],
theta_deg,
sample_theta_deg,
context.component_ids,
context.rotated_angles);
raw_inset_mm += context.component_distances_mm[i] * influence;

View File

@@ -68,6 +68,7 @@ struct TextureMappingOffsetContext {
std::vector<float> rotated_angles;
TextureMappingOffsetWeightField weight_field;
float inset_strength_reference_mm { 0.f };
float signed_fade_factor { 1.f };
float fade_factor { 1.f };
float max_width_delta_mm { 0.f };
float dither_pitch_mm { TextureMappingZone::DefaultDitheringResolutionMm };

View File

@@ -99,6 +99,7 @@ struct SurfaceGradientPreviewSettings
{
std::vector<unsigned int> component_ids;
std::vector<std::array<float, 3>> component_colors;
std::array<float, 3> base_color { 0.f, 0.f, 0.f };
std::vector<float> distances_mm;
std::vector<float> angles_deg;
std::vector<float> strength_factors;
@@ -4145,17 +4146,16 @@ std::optional<SurfaceGradientPreviewSettings> surface_gradient_preview_settings_
settings.strength_factors.emplace_back(std::clamp((std::isfinite(strength_pct) ? strength_pct : 100.f) / 100.f, 0.f, 1.f));
settings.minimum_offset_factors.emplace_back(std::clamp((std::isfinite(minimum_offset_pct) ? minimum_offset_pct : 0.f) / 100.f, 0.f, 1.f));
}
const std::vector<float> base_weights(settings.component_colors.size(), 1.f);
const ColorRGBA base_color = blend_component_colors(settings.component_colors, base_weights);
settings.base_color = { base_color.r(), base_color.g(), base_color.b() };
const float max_distance_mm = TextureMappingManager::max_component_surface_offset_mm();
settings.max_component_distance_mm = max_distance_mm;
settings.distances_mm = TextureMappingManager::effective_offset_distances(zone, settings.component_ids.size());
bool has_nonzero_distance = false;
for (float &distance_mm : settings.distances_mm) {
distance_mm = std::clamp(distance_mm, 0.f, max_distance_mm);
has_nonzero_distance = has_nonzero_distance || distance_mm > k_epsilon;
}
if (!has_nonzero_distance)
return std::nullopt;
settings.angles_deg = TextureMappingManager::effective_offset_angles(zone, settings.component_ids.size());
settings.angle_mode = std::clamp(zone.offset_angle_mode,
@@ -4177,8 +4177,9 @@ std::optional<SurfaceGradientPreviewSettings> surface_gradient_preview_settings_
const float global_strength_factor =
std::clamp(surface_gradient_preview_config_float("texture_mapping_outer_wall_gradient_global_strength", 100.f) / 100.f, 0.f, 1.f);
settings.max_width_delta_limit_mm = std::min((base_outer_width_mm - min_outer_width_mm) * global_strength_factor, 2.f * max_distance_mm);
if (!std::isfinite(settings.max_width_delta_limit_mm) || settings.max_width_delta_limit_mm <= k_epsilon)
if (!std::isfinite(settings.max_width_delta_limit_mm))
return std::nullopt;
settings.max_width_delta_limit_mm = std::max(0.f, settings.max_width_delta_limit_mm);
const indexed_triangle_set &its = model_volume.mesh().its;
if (its.vertices.empty())
@@ -4325,6 +4326,7 @@ void set_surface_gradient_preview_uniforms(GLShaderProgram &shader, const Surfac
const size_t count = settings == nullptr ? size_t(0) :
std::min(settings->component_colors.size(), k_surface_gradient_preview_max_components);
shader.set_uniform("gradient_component_count", int(count));
shader.set_uniform("gradient_base_color", settings != nullptr ? settings->base_color : std::array<float, 3>{ 0.f, 0.f, 0.f });
shader.set_uniform("gradient_max_component_distance_mm", settings != nullptr ? settings->max_component_distance_mm : 0.f);
shader.set_uniform("gradient_max_width_delta_limit_mm", settings != nullptr ? settings->max_width_delta_limit_mm : 0.f);
shader.set_uniform("gradient_angle_mode", settings != nullptr ? settings->angle_mode : int(TextureMappingZone::OffsetAngleObjectCenter));
@@ -4840,6 +4842,17 @@ static bool texture_preview_zone_uses_halftone_model(const TextureMappingZone &z
method == int(TextureMappingZone::DitheringHalftoneIncreasedDetail));
}
static bool texture_preview_gradient_zone_uses_model(const TextureMappingZone &zone, size_t num_physical)
{
if (!zone.enabled || zone.deleted || !zone.is_2d_gradient())
return false;
if (TextureMappingManager::selected_component_ids(zone, num_physical).size() < 2)
return true;
const std::vector<unsigned int> component_ids = decode_surface_gradient_component_ids(zone, num_physical);
return component_ids.size() >= 2;
}
static void texture_preview_mix_zone_baked_model_settings(size_t &signature,
const TextureMappingZone &zone,
size_t num_physical)
@@ -4958,7 +4971,9 @@ size_t texture_preview_model_settings_signature(size_t num_physical,
signature_mix(std::hash<int>{}(halftone_model ? 1 : 0));
signature_mix(std::hash<int>{}(simulated_vertex_color_model ? 1 : 0));
if (gradient_zone || halftone_model || simulated_vertex_color_model)
if (gradient_zone)
signature_mix(std::hash<int>{}(texture_preview_gradient_zone_uses_model(zone, num_physical) ? 1 : 0));
else if (halftone_model || simulated_vertex_color_model)
texture_preview_mix_zone_baked_model_settings(signature, zone, num_physical);
}
return signature;

View File

@@ -16,6 +16,7 @@
#include <vector>
#include <string>
#include <regex>
#include <functional>
#include <future>
#include <boost/algorithm/string.hpp>
#include <boost/iterator/counting_iterator.hpp>
@@ -50,6 +51,7 @@
#include <wx/simplebook.h>
#include <wx/slider.h>
#include <wx/spinctrl.h>
#include <wx/timer.h>
#ifdef _WIN32
#include <wx/richtooltip.h>
#include <wx/custombgwin.h>
@@ -1092,9 +1094,12 @@ public:
const TextureMappingZone &zone,
size_t num_physical,
const std::vector<double> &nozzle_diameters,
const std::vector<wxColour> &palette)
const std::vector<wxColour> &palette,
std::function<void(const TextureMappingZone &)> live_preview = {})
: wxDialog(parent, wxID_ANY, _L("Offset Gradient"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, m_zone(zone)
, m_live_preview(std::move(live_preview))
, m_live_preview_timer(this)
{
const int gap = FromDIP(8);
const int compact_gap = std::max(FromDIP(3), gap / 2);
@@ -1133,7 +1138,7 @@ public:
mode_row->Add(m_basic_angle_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap);
m_basic_angle_spin = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(84), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER, 0.0, 360.0,
initial_angles.empty() ? 0.0 : std::clamp(double(initial_angles.front()), 0.0, 360.0), 1.0);
initial_angles.empty() ? 0.0 : std::clamp(double(initial_angles.front()), 0.0, 360.0), 10.0);
m_basic_angle_spin->SetDigits(1);
mode_row->Add(m_basic_angle_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap);
m_basic_angle_units = new wxStaticText(this, wxID_ANY, _L("deg"));
@@ -1211,7 +1216,7 @@ public:
row->Add(new wxStaticText(this, wxID_ANY, _L("%")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
wxSpinCtrlDouble *angle_spin = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(84), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER, 0.0, 360.0,
i < initial_angles.size() ? std::clamp(double(initial_angles[i]), 0.0, 360.0) : 0.0, 1.0);
i < initial_angles.size() ? std::clamp(double(initial_angles[i]), 0.0, 360.0) : 0.0, 10.0);
angle_spin->SetDigits(1);
row->Add(angle_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap);
row->Add(new wxStaticText(this, wxID_ANY, _L("deg")), 0, wxALIGN_CENTER_VERTICAL);
@@ -1234,10 +1239,18 @@ public:
EndModal(wxID_OK);
});
m_mode_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) { update_advanced_visibility(); });
m_mode_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) {
update_advanced_visibility();
emit_live_preview();
});
bind_live_preview_events();
SetSizerAndFit(root);
SetMinSize(wxSize(FromDIP(560), std::max(GetSize().GetHeight(), FromDIP(420))));
update_advanced_visibility();
if (m_live_preview) {
Bind(wxEVT_TIMER, [this](wxTimerEvent &) { emit_live_preview(); });
m_live_preview_timer.Start(100);
}
}
bool apply_to(TextureMappingZone &out)
@@ -1303,7 +1316,67 @@ private:
Fit();
}
void bind_live_preview_events()
{
auto bind_spin = [this](wxSpinCtrlDouble *spin, double step, double min_value, double max_value) {
if (spin == nullptr)
return;
spin->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent &) { emit_live_preview(); });
spin->Bind(wxEVT_TEXT, [this](wxCommandEvent &) { emit_live_preview(); });
spin->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent &) { emit_live_preview(); });
spin->Bind(wxEVT_CHAR_HOOK, [this, spin, step, min_value, max_value](wxKeyEvent &evt) {
const int key = evt.GetKeyCode();
if (key != WXK_UP && key != WXK_NUMPAD_UP && key != WXK_DOWN && key != WXK_NUMPAD_DOWN) {
evt.Skip();
return;
}
const double direction = (key == WXK_UP || key == WXK_NUMPAD_UP) ? 1.0 : -1.0;
spin->SetValue(std::clamp(spin->GetValue() + direction * step, min_value, max_value));
emit_live_preview();
});
spin->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) {
emit_live_preview();
evt.Skip();
});
};
auto bind_check = [this](wxCheckBox *check) {
if (check != nullptr)
check->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) { emit_live_preview(); });
};
auto bind_choice = [this](wxChoice *choice) {
if (choice != nullptr)
choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) { emit_live_preview(); });
};
bind_spin(m_basic_distance_spin, 1.0, 0.0, 100.0);
bind_spin(m_basic_angle_spin, 10.0, 0.0, 360.0);
bind_spin(m_rotations_spin, 0.1, -64.0, 64.0);
bind_spin(m_repeats_spin, 0.1, 1.0, 64.0);
for (wxSpinCtrlDouble *spin : m_distance_spins)
bind_spin(spin, 1.0, 0.0, 100.0);
for (wxSpinCtrlDouble *spin : m_angle_spins)
bind_spin(spin, 10.0, 0.0, 360.0);
bind_check(m_rotation_enabled);
bind_check(m_reverse_repeats);
bind_check(m_clockwise);
bind_choice(m_fade_choice);
bind_choice(m_angle_mode_choice);
}
void emit_live_preview()
{
if (!m_live_preview)
return;
TextureMappingZone preview;
if (!apply_to(preview))
return;
preview.surface_pattern = int(TextureMappingZone::Gradient2D);
m_live_preview(preview);
}
TextureMappingZone m_zone;
std::function<void(const TextureMappingZone &)> m_live_preview;
wxTimer m_live_preview_timer;
std::vector<unsigned int> m_component_ids;
wxChoice *m_mode_choice {nullptr};
wxSpinCtrlDouble *m_basic_distance_spin {nullptr};
@@ -6316,7 +6389,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
gap);
auto *button_row = new wxBoxSizer(wxHORIZONTAL);
auto *offset_btn = new wxButton(editor, wxID_ANY, _L("Offset Gradient Settings"));
auto *offset_btn = new wxButton(editor, wxID_ANY, _L("2D Gradient Settings"));
auto *advanced_btn = new wxButton(editor, wxID_ANY, _L("Advanced Options"));
button_row->Add(offset_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
button_row->Add(advanced_btn, 0, wxALIGN_CENTER_VERTICAL);
@@ -6466,13 +6539,56 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
apply_controls();
evt.Skip();
});
offset_btn->Bind(wxEVT_BUTTON, [this, zone_index, mgr_ptr, palette, nozzle_diameters, apply_zone](wxCommandEvent &) {
offset_btn->Bind(wxEVT_BUTTON, [this,
zone_index,
mgr_ptr,
palette,
nozzle_diameters,
apply_zone,
set_config_string](wxCommandEvent &) {
if (zone_index >= mgr_ptr->zones().size())
return;
TextureMappingZone updated = mgr_ptr->zones()[zone_index];
TextureMappingOffsetGradientDialog dlg(this, updated, palette.size(), nozzle_diameters, palette);
if (dlg.ShowModal() != wxID_OK || !dlg.apply_to(updated))
const TextureMappingZone original = mgr_ptr->zones()[zone_index];
const std::string original_serialized = mgr_ptr->serialize_entries();
TextureMappingZone updated = original;
bool live_preview_applied = false;
auto redraw_live_preview = [this]() {
if (p->plater == nullptr)
return;
auto mark_canvas_dirty = [](GLCanvas3D *canvas) {
if (canvas == nullptr)
return;
canvas->set_as_dirty();
canvas->request_extra_frame();
if (wxGLCanvas *wx_canvas = canvas->get_wxglcanvas(); wx_canvas != nullptr && wx_canvas->IsShownOnScreen())
canvas->render();
};
mark_canvas_dirty(p->plater->get_view3D_canvas3D());
mark_canvas_dirty(p->plater->get_assmeble_canvas3D());
};
auto live_preview = [zone_index, mgr_ptr, &live_preview_applied, redraw_live_preview, set_config_string](const TextureMappingZone &preview) {
if (zone_index >= mgr_ptr->zones().size())
return;
if (mgr_ptr->zones()[zone_index] == preview)
return;
mgr_ptr->zones()[zone_index] = preview;
set_config_string("texture_mapping_definitions", mgr_ptr->serialize_entries());
live_preview_applied = true;
redraw_live_preview();
};
TextureMappingOffsetGradientDialog dlg(this, updated, palette.size(), nozzle_diameters, palette, live_preview);
const int dialog_result = dlg.ShowModal();
if (zone_index >= mgr_ptr->zones().size())
return;
if (live_preview_applied)
mgr_ptr->zones()[zone_index] = original;
if (live_preview_applied)
set_config_string("texture_mapping_definitions", original_serialized);
if (dialog_result != wxID_OK || !dlg.apply_to(updated)) {
if (live_preview_applied)
redraw_live_preview();
return;
}
updated.surface_pattern = int(TextureMappingZone::Gradient2D);
apply_zone(std::move(updated));
CallAfter([this]() { update_texture_mapping_panel(false); });