Add texture mapping halftone mode
This commit is contained in:
@@ -7059,7 +7059,9 @@ static float ordered_bayer_threshold_for_gcode(int x, int y)
|
||||
|
||||
static float halftone_threshold_for_gcode(float x_mm, float y_mm, float dot_size_mm)
|
||||
{
|
||||
const float period = std::clamp(dot_size_mm, 0.08f, 2.f);
|
||||
const float period = std::clamp(dot_size_mm,
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm);
|
||||
const float u = x_mm / period - std::floor(x_mm / period);
|
||||
const float v = y_mm / period - std::floor(y_mm / period);
|
||||
const float dx = u - 0.5f;
|
||||
@@ -7068,6 +7070,66 @@ static float halftone_threshold_for_gcode(float x_mm, float y_mm, float dot_size
|
||||
return std::clamp(radius_area, 0.f, 1.f) - 0.5f;
|
||||
}
|
||||
|
||||
static float halftone_screen_angle_deg_for_gcode(int filament_color_mode, size_t component_idx)
|
||||
{
|
||||
const int clamped_mode =
|
||||
std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW));
|
||||
if (component_idx < 3) {
|
||||
static constexpr float primary_angles[3] = {15.f, 75.f, 0.f};
|
||||
return primary_angles[component_idx];
|
||||
}
|
||||
|
||||
switch (clamped_mode) {
|
||||
case int(TextureMappingZone::FilamentColorCMYK):
|
||||
case int(TextureMappingZone::FilamentColorRGBK):
|
||||
case int(TextureMappingZone::FilamentColorCMYKW):
|
||||
case int(TextureMappingZone::FilamentColorRGBKW):
|
||||
if (component_idx == 3)
|
||||
return 45.f;
|
||||
if (component_idx == 4)
|
||||
return 90.f;
|
||||
break;
|
||||
case int(TextureMappingZone::FilamentColorCMYW):
|
||||
case int(TextureMappingZone::FilamentColorRGBW):
|
||||
if (component_idx == 3)
|
||||
return 90.f;
|
||||
break;
|
||||
case int(TextureMappingZone::FilamentColorBW):
|
||||
if (component_idx == 0)
|
||||
return 45.f;
|
||||
if (component_idx == 1)
|
||||
return 90.f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
static constexpr float fallback_angles[] = {15.f, 75.f, 0.f, 45.f, 90.f, 30.f, 60.f, 105.f, 120.f, 150.f};
|
||||
return fallback_angles[component_idx % (sizeof(fallback_angles) / sizeof(fallback_angles[0]))];
|
||||
}
|
||||
|
||||
static bool halftone_screen_on_for_gcode(float coverage,
|
||||
float surface_u_mm,
|
||||
float surface_v_mm,
|
||||
float dot_size_mm,
|
||||
float angle_deg)
|
||||
{
|
||||
const float safe_coverage = clamp01f_for_gcode(coverage);
|
||||
if (safe_coverage <= EPSILON)
|
||||
return false;
|
||||
if (safe_coverage >= 1.f - EPSILON)
|
||||
return true;
|
||||
if (!std::isfinite(surface_u_mm) || !std::isfinite(surface_v_mm))
|
||||
return safe_coverage >= 0.5f;
|
||||
|
||||
const float radians = angle_deg * float(PI) / 180.f;
|
||||
const float c = std::cos(radians);
|
||||
const float s = std::sin(radians);
|
||||
const float x = c * surface_u_mm + s * surface_v_mm;
|
||||
const float y = -s * surface_u_mm + c * surface_v_mm;
|
||||
return halftone_threshold_for_gcode(x, y, dot_size_mm) + 0.5f < safe_coverage;
|
||||
}
|
||||
|
||||
static float dither_pitch_for_gcode(float base_outer_width_mm,
|
||||
int dithering_method,
|
||||
float dithering_resolution_mm,
|
||||
@@ -7078,10 +7140,26 @@ static float dither_pitch_for_gcode(float base_outer_width_mm,
|
||||
int(TextureMappingZone::DitheringClosest),
|
||||
int(TextureMappingZone::DitheringHalftone));
|
||||
if (clamped_method == int(TextureMappingZone::DitheringHalftone)) {
|
||||
const float dot_sample_step_mm = std::clamp(std::clamp(halftone_dot_size_mm, 0.08f, 2.f) * 0.25f, 0.04f, 0.08f);
|
||||
const float dot_sample_step_mm =
|
||||
std::clamp(std::clamp(halftone_dot_size_mm,
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm) *
|
||||
0.25f,
|
||||
0.04f,
|
||||
0.08f);
|
||||
return std::min(high_res_step_mm, dot_sample_step_mm);
|
||||
}
|
||||
return std::min(high_res_step_mm, std::clamp(dithering_resolution_mm, 0.04f, 0.25f));
|
||||
return std::min(high_res_step_mm,
|
||||
std::clamp(dithering_resolution_mm,
|
||||
TextureMappingZone::MinDitheringResolutionMm,
|
||||
TextureMappingZone::MaxDitheringResolutionMm));
|
||||
}
|
||||
|
||||
static float dither_cell_size_for_gcode(float dithering_resolution_mm)
|
||||
{
|
||||
return std::clamp(dithering_resolution_mm,
|
||||
TextureMappingZone::MinDitheringResolutionMm,
|
||||
TextureMappingZone::MaxDitheringResolutionMm);
|
||||
}
|
||||
|
||||
static float wrap_repeat01_for_gcode(float uv)
|
||||
@@ -8210,6 +8288,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
bool dithering_enabled,
|
||||
int dithering_method,
|
||||
float dither_pitch_mm,
|
||||
float dither_cell_size_mm,
|
||||
float halftone_dot_size_mm,
|
||||
const std::vector<float> &component_strength_factors,
|
||||
const std::vector<float> &component_minimum_offset_factors,
|
||||
@@ -8224,6 +8303,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
bool high_speed_image_texture_sampling)
|
||||
{
|
||||
VertexColorOverhangWeightField weight_field;
|
||||
(void) halftone_dot_size_mm;
|
||||
if (component_colors.empty())
|
||||
return weight_field;
|
||||
const size_t component_count = component_colors.size();
|
||||
@@ -8734,13 +8814,16 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
if (samples.empty())
|
||||
return VertexColorOverhangWeightField{};
|
||||
|
||||
const int clamped_binary_dither_method =
|
||||
std::clamp(dithering_method, int(TextureMappingZone::DitheringClosest), int(TextureMappingZone::DitheringHalftone));
|
||||
std::vector<uint32_t> binary_dither_masks;
|
||||
const bool can_binary_dither =
|
||||
dithering_enabled &&
|
||||
!raw_values_mode &&
|
||||
clamped_binary_dither_method != int(TextureMappingZone::DitheringHalftone) &&
|
||||
component_count > 0 &&
|
||||
std::isfinite(dither_pitch_mm) &&
|
||||
dither_pitch_mm > EPSILON;
|
||||
std::isfinite(dither_cell_size_mm) &&
|
||||
dither_cell_size_mm > EPSILON;
|
||||
if (can_binary_dither) {
|
||||
bool has_raw_samples = false;
|
||||
for (const WeightedTextureSample &sample : samples) {
|
||||
@@ -8788,8 +8871,8 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
if (sample.weight <= EPSILON)
|
||||
continue;
|
||||
|
||||
const int cell_x = int(std::floor((sample.x_mm - min_x_mm) / dither_pitch_mm));
|
||||
const int cell_y = int(std::floor((sample.y_mm - min_y_mm) / dither_pitch_mm));
|
||||
const int cell_x = int(std::floor((sample.x_mm - min_x_mm) / dither_cell_size_mm));
|
||||
const int cell_y = int(std::floor((sample.y_mm - min_y_mm) / dither_cell_size_mm));
|
||||
const std::pair<int, int> key(cell_x, cell_y);
|
||||
auto cell_it = cell_index_by_coord.find(key);
|
||||
if (cell_it == cell_index_by_coord.end()) {
|
||||
@@ -8824,15 +8907,11 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
});
|
||||
|
||||
binary_dither_masks.assign(samples.size(), 0);
|
||||
const int clamped_dither_method =
|
||||
std::clamp(dithering_method,
|
||||
int(TextureMappingZone::DitheringClosest),
|
||||
int(TextureMappingZone::DitheringHalftone));
|
||||
std::map<std::pair<int, int>, std::array<float, 3>> floyd_error;
|
||||
for (const size_t cell_idx : order) {
|
||||
BinaryDitherCell &cell = cells[cell_idx];
|
||||
std::array<float, 3> target_oklab = cell.target_oklab;
|
||||
if (clamped_dither_method == int(TextureMappingZone::DitheringFloydSteinberg)) {
|
||||
if (clamped_binary_dither_method == int(TextureMappingZone::DitheringFloydSteinberg)) {
|
||||
const auto error_it = floyd_error.find({cell.x, cell.y});
|
||||
if (error_it != floyd_error.end())
|
||||
for (size_t axis = 0; axis < 3; ++axis)
|
||||
@@ -8841,14 +8920,9 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
|
||||
bool thresholded_dither = false;
|
||||
float threshold = 0.f;
|
||||
if (clamped_dither_method == int(TextureMappingZone::DitheringOrderedBayer)) {
|
||||
if (clamped_binary_dither_method == int(TextureMappingZone::DitheringOrderedBayer)) {
|
||||
thresholded_dither = true;
|
||||
threshold = ordered_bayer_threshold_for_gcode(cell.x, cell.y) + 0.5f;
|
||||
} else if (clamped_dither_method == int(TextureMappingZone::DitheringHalftone)) {
|
||||
const float x_mm = min_x_mm + (float(cell.x) + 0.5f) * dither_pitch_mm;
|
||||
const float y_mm = min_y_mm + (float(cell.y) + 0.5f) * dither_pitch_mm;
|
||||
thresholded_dither = true;
|
||||
threshold = halftone_threshold_for_gcode(x_mm, y_mm, halftone_dot_size_mm) + 0.5f;
|
||||
}
|
||||
|
||||
const size_t candidate_idx =
|
||||
@@ -8863,7 +8937,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode(
|
||||
if (sample_idx < binary_dither_masks.size())
|
||||
binary_dither_masks[sample_idx] = candidate.mask;
|
||||
|
||||
if (clamped_dither_method == int(TextureMappingZone::DitheringFloydSteinberg)) {
|
||||
if (clamped_binary_dither_method == int(TextureMappingZone::DitheringFloydSteinberg)) {
|
||||
std::array<float, 3> error = {
|
||||
target_oklab[0] - candidate.oklab[0],
|
||||
target_oklab[1] - candidate.oklab[1],
|
||||
@@ -9613,6 +9687,8 @@ std::optional<PreferredSeamPoint> GCode::texture_mapping_seam_hiding_hint(const
|
||||
const int dithering_method = std::clamp(zone->dithering_method,
|
||||
int(TextureMappingZone::DitheringClosest),
|
||||
int(TextureMappingZone::DitheringHalftone));
|
||||
const bool halftone_dithering_enabled =
|
||||
dithering_enabled && dithering_method == int(TextureMappingZone::DitheringHalftone);
|
||||
const float seam_texture_base_width_mm =
|
||||
std::max(0.05f, float(m_config.texture_mapping_outer_wall_gradient_max_line_width.value));
|
||||
const float dither_pitch_mm =
|
||||
@@ -9620,8 +9696,11 @@ std::optional<PreferredSeamPoint> GCode::texture_mapping_seam_hiding_hint(const
|
||||
dithering_method,
|
||||
zone->dithering_resolution_mm,
|
||||
zone->halftone_dot_size_mm);
|
||||
const float halftone_dot_size_mm = std::clamp(zone->halftone_dot_size_mm, 0.08f, 2.f);
|
||||
const bool compact_offset_mode = zone->compact_offset_mode || dithering_enabled;
|
||||
const float dither_cell_size_mm = dither_cell_size_for_gcode(zone->dithering_resolution_mm);
|
||||
const float halftone_dot_size_mm = std::clamp(zone->halftone_dot_size_mm,
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm);
|
||||
const bool compact_offset_mode = halftone_dithering_enabled ? false : zone->compact_offset_mode || dithering_enabled;
|
||||
const bool nonlinear_offset_adjustment = zone->nonlinear_offset_adjustment;
|
||||
const bool use_legacy_fixed_color_mode = zone->use_legacy_fixed_color_mode;
|
||||
const float texture_contrast_pct = std::clamp(zone->contrast_pct, 25.f, 300.f);
|
||||
@@ -9684,7 +9763,7 @@ std::optional<PreferredSeamPoint> GCode::texture_mapping_seam_hiding_hint(const
|
||||
if (dithering_method == int(TextureMappingZone::DitheringHalftone))
|
||||
component_key_stream << "|hdt" << int(std::lround(halftone_dot_size_mm * 1000.f));
|
||||
else
|
||||
component_key_stream << "|hrz" << int(std::lround(std::clamp(zone->dithering_resolution_mm, 0.04f, 0.25f) * 1000.f));
|
||||
component_key_stream << "|hrz" << int(std::lround(dither_cell_size_mm * 1000.f));
|
||||
for (const float strength_factor : component_strength_factors)
|
||||
component_key_stream << "|st" << int(std::lround(std::clamp(strength_factor, 0.f, 1.f) * 1000.f));
|
||||
for (const float minimum_offset_factor : component_minimum_offset_factors)
|
||||
@@ -9760,6 +9839,7 @@ std::optional<PreferredSeamPoint> GCode::texture_mapping_seam_hiding_hint(const
|
||||
dithering_enabled,
|
||||
dithering_method,
|
||||
dither_pitch_mm,
|
||||
dither_cell_size_mm,
|
||||
halftone_dot_size_mm,
|
||||
component_strength_factors,
|
||||
component_minimum_offset_factors,
|
||||
@@ -10916,11 +10996,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
float balance_weight { 0.f };
|
||||
};
|
||||
|
||||
struct OuterWallGradientSurfaceCoord {
|
||||
float u_mm { std::numeric_limits<float>::quiet_NaN() };
|
||||
float v_mm { std::numeric_limits<float>::quiet_NaN() };
|
||||
};
|
||||
|
||||
struct OuterWallGradientDynamicContext {
|
||||
bool enabled { false };
|
||||
bool vertex_color_match_mode { false };
|
||||
bool high_resolution_texture_sampling { false };
|
||||
bool dithering_enabled { false };
|
||||
bool halftone_dithering_enabled { false };
|
||||
bool nonlinear_offset_adjustment { false };
|
||||
bool compact_offset_mode { false };
|
||||
bool object_center_mode { false };
|
||||
@@ -10940,6 +11026,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
float active_component_td_width_factor { 1.f };
|
||||
float base_outer_width_mm { 0.4f };
|
||||
float dither_pitch_mm { 0.08f };
|
||||
float halftone_dot_size_mm { TextureMappingZone::DefaultHalftoneDotSizeMm };
|
||||
float active_halftone_angle_deg { 0.f };
|
||||
float flow_reference_width_mm { 0.4f };
|
||||
float base_centerline_shift_mm { 0.f };
|
||||
float centerline_shift_balance_mm { 0.f };
|
||||
@@ -10967,6 +11055,53 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
return dest.allFinite() && std::isfinite(dE);
|
||||
};
|
||||
|
||||
const bool halftone_path_closed = path.polyline.points.size() > 2 && path.polyline.is_closed();
|
||||
std::vector<double> halftone_source_cumulative_mm(path.polyline.points.size(), 0.0);
|
||||
for (size_t point_idx = 1; point_idx < path.polyline.points.size(); ++point_idx)
|
||||
halftone_source_cumulative_mm[point_idx] =
|
||||
halftone_source_cumulative_mm[point_idx - 1] +
|
||||
Line(path.polyline.points[point_idx - 1], path.polyline.points[point_idx]).length() * SCALING_FACTOR;
|
||||
const double halftone_total_path_mm = halftone_source_cumulative_mm.empty() ? 0.0 : halftone_source_cumulative_mm.back();
|
||||
double halftone_anchor_mm = 0.0;
|
||||
if (halftone_path_closed && path.polyline.points.size() > 1 && halftone_total_path_mm > EPSILON) {
|
||||
const size_t end_idx = path.polyline.points.size() - 1;
|
||||
size_t best_idx = 0;
|
||||
for (size_t point_idx = 1; point_idx < end_idx; ++point_idx) {
|
||||
const Point &candidate = path.polyline.points[point_idx];
|
||||
const Point &best = path.polyline.points[best_idx];
|
||||
if (candidate.x() < best.x() || (candidate.x() == best.x() && candidate.y() < best.y()))
|
||||
best_idx = point_idx;
|
||||
}
|
||||
halftone_anchor_mm = halftone_source_cumulative_mm[best_idx];
|
||||
}
|
||||
float halftone_surface_v_mm = m_layer != nullptr ? float(m_layer->print_z) : float(m_nominal_z);
|
||||
if (!std::isfinite(halftone_surface_v_mm))
|
||||
halftone_surface_v_mm = 0.f;
|
||||
auto halftone_surface_u_for_source_distance = [&](double source_distance_mm) {
|
||||
if (!std::isfinite(source_distance_mm))
|
||||
source_distance_mm = 0.0;
|
||||
if (halftone_path_closed && halftone_total_path_mm > EPSILON) {
|
||||
double u = std::fmod(source_distance_mm - halftone_anchor_mm, halftone_total_path_mm);
|
||||
if (u < 0.0)
|
||||
u += halftone_total_path_mm;
|
||||
return float(u);
|
||||
}
|
||||
return float(std::max(0.0, source_distance_mm));
|
||||
};
|
||||
auto halftone_surface_coord_for_source_distance = [&](double source_distance_mm) {
|
||||
OuterWallGradientSurfaceCoord coord;
|
||||
coord.u_mm = halftone_surface_u_for_source_distance(source_distance_mm);
|
||||
coord.v_mm = halftone_surface_v_mm;
|
||||
return coord;
|
||||
};
|
||||
auto halftone_source_distance_for_segment_fraction = [&](size_t segment_idx, double fraction) {
|
||||
if (segment_idx + 1 >= halftone_source_cumulative_mm.size())
|
||||
return 0.0;
|
||||
const double start_mm = halftone_source_cumulative_mm[segment_idx];
|
||||
const double end_mm = halftone_source_cumulative_mm[segment_idx + 1];
|
||||
return start_mm + (end_mm - start_mm) * std::clamp(fraction, 0.0, 1.0);
|
||||
};
|
||||
|
||||
std::vector<OuterWallGradientSegmentMod> outer_wall_gradient_segment_mods;
|
||||
OuterWallGradientDynamicContext outer_wall_gradient_dynamic_ctx;
|
||||
bool outer_wall_gradient_modulated_path = false;
|
||||
@@ -11131,16 +11266,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
const int dithering_method = std::clamp(zone->dithering_method,
|
||||
int(TextureMappingZone::DitheringClosest),
|
||||
int(TextureMappingZone::DitheringHalftone));
|
||||
const bool halftone_dithering_enabled =
|
||||
dithering_enabled && dithering_method == int(TextureMappingZone::DitheringHalftone);
|
||||
const float dither_pitch_mm =
|
||||
dither_pitch_for_gcode(base_outer_width_mm,
|
||||
dithering_method,
|
||||
zone->dithering_resolution_mm,
|
||||
zone->halftone_dot_size_mm);
|
||||
const float halftone_dot_size_mm = std::clamp(zone->halftone_dot_size_mm, 0.08f, 2.f);
|
||||
const float dither_cell_size_mm = dither_cell_size_for_gcode(zone->dithering_resolution_mm);
|
||||
const float halftone_dot_size_mm = std::clamp(zone->halftone_dot_size_mm,
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm);
|
||||
const bool high_resolution_texture_sampling = zone->high_resolution_sampling || dithering_enabled;
|
||||
const bool high_speed_image_texture_sampling = zone->high_speed_image_texture_sampling;
|
||||
const bool nonlinear_offset_adjustment = zone->nonlinear_offset_adjustment;
|
||||
const bool compact_offset_mode = zone->compact_offset_mode || dithering_enabled;
|
||||
const bool compact_offset_mode =
|
||||
halftone_dithering_enabled ? false : zone->compact_offset_mode || dithering_enabled;
|
||||
const float layer_sample_z_mm = use_layer_aware_weighting ? float(m_layer->print_z) : 0.f;
|
||||
const float layer_sample_falloff_mm = high_resolution_texture_sampling ?
|
||||
std::max(0.03f, layer_height_mm * 0.5f) :
|
||||
@@ -11232,7 +11373,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if (dithering_method == int(TextureMappingZone::DitheringHalftone))
|
||||
component_key_stream << "|hdt" << int(std::lround(halftone_dot_size_mm * 1000.f));
|
||||
else
|
||||
component_key_stream << "|hrz" << int(std::lround(std::clamp(zone->dithering_resolution_mm, 0.04f, 0.25f) * 1000.f));
|
||||
component_key_stream << "|hrz" << int(std::lround(dither_cell_size_mm * 1000.f));
|
||||
for (const float strength_factor : component_strength_factors)
|
||||
component_key_stream << "|st" << int(std::lround(std::clamp(strength_factor, 0.f, 1.f) * 1000.f));
|
||||
for (const float minimum_offset_factor : component_minimum_offset_factors)
|
||||
@@ -11261,6 +11402,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
dithering_enabled,
|
||||
dithering_method,
|
||||
dither_pitch_mm,
|
||||
dither_cell_size_mm,
|
||||
halftone_dot_size_mm,
|
||||
component_strength_factors,
|
||||
component_minimum_offset_factors,
|
||||
@@ -11293,6 +11435,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
outer_wall_gradient_dynamic_ctx.vertex_color_match_mode = vertex_color_match_mode;
|
||||
outer_wall_gradient_dynamic_ctx.high_resolution_texture_sampling = high_resolution_texture_sampling;
|
||||
outer_wall_gradient_dynamic_ctx.dithering_enabled = dithering_enabled;
|
||||
outer_wall_gradient_dynamic_ctx.halftone_dithering_enabled = halftone_dithering_enabled;
|
||||
outer_wall_gradient_dynamic_ctx.nonlinear_offset_adjustment = nonlinear_offset_adjustment;
|
||||
outer_wall_gradient_dynamic_ctx.compact_offset_mode = compact_offset_mode;
|
||||
outer_wall_gradient_dynamic_ctx.object_center_mode = object_center_mode;
|
||||
@@ -11312,6 +11455,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
outer_wall_gradient_dynamic_ctx.active_component_td_width_factor = active_component_td_width_factor;
|
||||
outer_wall_gradient_dynamic_ctx.base_outer_width_mm = base_outer_width_mm;
|
||||
outer_wall_gradient_dynamic_ctx.dither_pitch_mm = dither_pitch_mm;
|
||||
outer_wall_gradient_dynamic_ctx.halftone_dot_size_mm = halftone_dot_size_mm;
|
||||
outer_wall_gradient_dynamic_ctx.active_halftone_angle_deg =
|
||||
halftone_screen_angle_deg_for_gcode(texture_filament_color_mode, active_component_idx);
|
||||
outer_wall_gradient_dynamic_ctx.flow_reference_width_mm = flow_reference_width_mm;
|
||||
outer_wall_gradient_dynamic_ctx.base_centerline_shift_mm = base_centerline_shift_mm;
|
||||
outer_wall_gradient_dynamic_ctx.layer_height_mm = layer_height_mm;
|
||||
@@ -11340,6 +11486,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
clamped_shift_coord_for_gcode(shift_y, shift_scaled, max_shift_coord, mod.shift_dy);
|
||||
};
|
||||
|
||||
size_t texture_path_segment_idx = 0;
|
||||
for (const Line &line : path.polyline.lines()) {
|
||||
if (!outer_wall_gradient_dynamic_ctx.enabled)
|
||||
break;
|
||||
@@ -11353,6 +11500,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
const double dy = by - ay;
|
||||
const double len = std::hypot(dx, dy);
|
||||
mod.length_mm = unscale<double>(len);
|
||||
const OuterWallGradientSurfaceCoord surface_coord =
|
||||
halftone_surface_coord_for_source_distance(
|
||||
halftone_source_distance_for_segment_fraction(texture_path_segment_idx, 0.5));
|
||||
++texture_path_segment_idx;
|
||||
if (len <= EPSILON) {
|
||||
outer_wall_gradient_segment_mods.emplace_back(mod);
|
||||
continue;
|
||||
@@ -11386,13 +11537,23 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
!vertex_color_weight_field->empty()) {
|
||||
const float mid_x_mm = 0.5f * (unscale<float>(line.a.x()) + unscale<float>(line.b.x()));
|
||||
const float mid_y_mm = 0.5f * (unscale<float>(line.a.y()) + unscale<float>(line.b.y()));
|
||||
const float desired_strength = sample_vertex_color_weight_field_for_gcode(
|
||||
float desired_strength = sample_vertex_color_weight_field_for_gcode(
|
||||
*vertex_color_weight_field,
|
||||
mid_x_mm,
|
||||
mid_y_mm,
|
||||
active_component_idx,
|
||||
high_resolution_texture_sampling,
|
||||
compact_offset_mode);
|
||||
compact_offset_mode && !halftone_dithering_enabled);
|
||||
if (halftone_dithering_enabled)
|
||||
desired_strength = halftone_screen_on_for_gcode(desired_strength,
|
||||
surface_coord.u_mm,
|
||||
surface_coord.v_mm,
|
||||
halftone_dot_size_mm,
|
||||
halftone_screen_angle_deg_for_gcode(
|
||||
texture_filament_color_mode,
|
||||
active_component_idx)) ?
|
||||
1.f :
|
||||
0.f;
|
||||
inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f);
|
||||
}
|
||||
} else {
|
||||
@@ -12071,7 +12232,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
|
||||
const Layer *surface_layer = m_layer;
|
||||
|
||||
auto dynamic_modulation_for_line = [&outer_wall_gradient_dynamic_ctx, surface_layer](const Line &line) {
|
||||
auto dynamic_modulation_for_line =
|
||||
[&outer_wall_gradient_dynamic_ctx, surface_layer](const Line &line, const OuterWallGradientSurfaceCoord &surface_coord) {
|
||||
OuterWallGradientSegmentMod mod;
|
||||
if (!outer_wall_gradient_dynamic_ctx.enabled)
|
||||
return mod;
|
||||
@@ -12123,13 +12285,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
outer_wall_gradient_dynamic_ctx.vertex_color_weight_field->component_count) {
|
||||
const float mid_x_mm = 0.5f * (unscale<float>(line.a.x()) + unscale<float>(line.b.x()));
|
||||
const float mid_y_mm = 0.5f * (unscale<float>(line.a.y()) + unscale<float>(line.b.y()));
|
||||
const float desired_strength = sample_vertex_color_weight_field_for_gcode(
|
||||
float desired_strength = sample_vertex_color_weight_field_for_gcode(
|
||||
*outer_wall_gradient_dynamic_ctx.vertex_color_weight_field,
|
||||
mid_x_mm,
|
||||
mid_y_mm,
|
||||
outer_wall_gradient_dynamic_ctx.active_component_idx,
|
||||
outer_wall_gradient_dynamic_ctx.high_resolution_texture_sampling,
|
||||
outer_wall_gradient_dynamic_ctx.compact_offset_mode);
|
||||
outer_wall_gradient_dynamic_ctx.compact_offset_mode &&
|
||||
!outer_wall_gradient_dynamic_ctx.halftone_dithering_enabled);
|
||||
if (outer_wall_gradient_dynamic_ctx.halftone_dithering_enabled)
|
||||
desired_strength = halftone_screen_on_for_gcode(desired_strength,
|
||||
surface_coord.u_mm,
|
||||
surface_coord.v_mm,
|
||||
outer_wall_gradient_dynamic_ctx.halftone_dot_size_mm,
|
||||
outer_wall_gradient_dynamic_ctx.active_halftone_angle_deg) ?
|
||||
1.f :
|
||||
0.f;
|
||||
inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f);
|
||||
}
|
||||
} else {
|
||||
@@ -12366,7 +12537,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
continue;
|
||||
}
|
||||
|
||||
const OuterWallGradientSegmentMod sub_mod = dynamic_modulation_for_line(sub_line);
|
||||
const OuterWallGradientSurfaceCoord surface_coord =
|
||||
halftone_surface_coord_for_source_distance(
|
||||
halftone_source_distance_for_segment_fraction(
|
||||
segment_idx,
|
||||
(double(sub_idx) - 0.5) / double(subsegment_count)));
|
||||
const OuterWallGradientSegmentMod sub_mod = dynamic_modulation_for_line(sub_line, surface_coord);
|
||||
const Point target_point = make_shifted_point(sub_line.b, sub_mod.shift_dx, sub_mod.shift_dy);
|
||||
const Vec2d target_xy = this->point_to_gcode(target_point);
|
||||
if (!is_reasonable_quantized_gcode_point_for_gcode(target_xy)) {
|
||||
@@ -12546,11 +12722,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
gcode += m_writer.set_speed(last_set_speed, "", comment);
|
||||
Point prev_point_model = new_points[0].p;
|
||||
if (outer_wall_gradient_modulated_path && new_points.size() > 1) {
|
||||
const Line first_line(new_points[0].p, new_points[1].p);
|
||||
const OuterWallGradientSurfaceCoord first_surface_coord =
|
||||
halftone_surface_coord_for_source_distance(first_line.length() * SCALING_FACTOR * 0.5);
|
||||
const OuterWallGradientSegmentMod first_mod =
|
||||
(outer_wall_gradient_dynamic_ctx.enabled &&
|
||||
(outer_wall_gradient_dynamic_ctx.object_center_mode ||
|
||||
outer_wall_gradient_dynamic_ctx.vertex_color_match_mode)) ?
|
||||
dynamic_modulation_for_line(Line(new_points[0].p, new_points[1].p)) :
|
||||
dynamic_modulation_for_line(first_line, first_surface_coord) :
|
||||
segment_modulation_at(0);
|
||||
prev_point_model = make_shifted_point(prev_point_model, first_mod.shift_dx, first_mod.shift_dy);
|
||||
}
|
||||
@@ -12566,17 +12745,27 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
pre_fan_enabled = true;
|
||||
|
||||
double path_length = 0.;
|
||||
double source_path_length_mm = 0.;
|
||||
for (size_t i = 1; i < new_points.size(); i++) {
|
||||
std::string tempDescription = description;
|
||||
const ProcessedPoint &processed_point = new_points[i];
|
||||
const ProcessedPoint &pre_processed_point = new_points[i-1];
|
||||
const Line parent_source_line(pre_processed_point.p, processed_point.p);
|
||||
const double parent_source_length_mm = parent_source_line.length() * SCALING_FACTOR;
|
||||
const double safe_parent_source_length_mm =
|
||||
std::isfinite(parent_source_length_mm) ? std::max(0.0, parent_source_length_mm) : 0.0;
|
||||
const double segment_source_start_mm = source_path_length_mm;
|
||||
if (std::isfinite(parent_source_length_mm))
|
||||
source_path_length_mm += parent_source_length_mm;
|
||||
const bool dynamic_line_modulation =
|
||||
outer_wall_gradient_dynamic_ctx.enabled &&
|
||||
(outer_wall_gradient_dynamic_ctx.object_center_mode ||
|
||||
outer_wall_gradient_dynamic_ctx.vertex_color_match_mode);
|
||||
const OuterWallGradientSurfaceCoord segment_surface_coord =
|
||||
halftone_surface_coord_for_source_distance(segment_source_start_mm + safe_parent_source_length_mm * 0.5);
|
||||
const OuterWallGradientSegmentMod segment_mod =
|
||||
dynamic_line_modulation ?
|
||||
dynamic_modulation_for_line(Line(pre_processed_point.p, processed_point.p)) :
|
||||
dynamic_modulation_for_line(parent_source_line, segment_surface_coord) :
|
||||
segment_modulation_at(i - 1);
|
||||
const Point processed_target_point = make_shifted_point(processed_point.p, segment_mod.shift_dx, segment_mod.shift_dy);
|
||||
Vec2d p = this->point_to_gcode_quantized(processed_target_point);
|
||||
@@ -12687,7 +12876,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
Point(coord_t(std::llround(double(parent_line.a.x()) + (double(parent_line.b.x()) - double(parent_line.a.x())) * t)),
|
||||
coord_t(std::llround(double(parent_line.a.y()) + (double(parent_line.b.y()) - double(parent_line.a.y())) * t)));
|
||||
const Line sub_line(sub_a, sub_b);
|
||||
const OuterWallGradientSegmentMod sub_mod = dynamic_modulation_for_line(sub_line);
|
||||
const OuterWallGradientSurfaceCoord sub_surface_coord =
|
||||
halftone_surface_coord_for_source_distance(
|
||||
segment_source_start_mm + safe_parent_source_length_mm *
|
||||
((double(sub_idx) - 0.5) / double(subsegment_count)));
|
||||
const OuterWallGradientSegmentMod sub_mod = dynamic_modulation_for_line(sub_line, sub_surface_coord);
|
||||
const Point sub_target_point = make_shifted_point(sub_line.b, sub_mod.shift_dx, sub_mod.shift_dy);
|
||||
Vec2d sub_p = this->point_to_gcode_quantized(sub_target_point);
|
||||
if (!is_reasonable_quantized_gcode_point_for_gcode(sub_p)) {
|
||||
|
||||
@@ -1083,9 +1083,13 @@ std::string TextureMappingManager::serialize_entries()
|
||||
texture["dithering_enabled"] = zone.dithering_enabled;
|
||||
texture["dithering_method"] = dithering_method_name(zone.dithering_method);
|
||||
texture["dithering_resolution_mm"] =
|
||||
std::clamp(finite_or(zone.dithering_resolution_mm, TextureMappingZone::DefaultDitheringResolutionMm), 0.04f, 0.25f);
|
||||
std::clamp(finite_or(zone.dithering_resolution_mm, TextureMappingZone::DefaultDitheringResolutionMm),
|
||||
TextureMappingZone::MinDitheringResolutionMm,
|
||||
TextureMappingZone::MaxDitheringResolutionMm);
|
||||
texture["halftone_dot_size_mm"] =
|
||||
std::clamp(finite_or(zone.halftone_dot_size_mm, TextureMappingZone::DefaultHalftoneDotSizeMm), 0.08f, 2.f);
|
||||
std::clamp(finite_or(zone.halftone_dot_size_mm, TextureMappingZone::DefaultHalftoneDotSizeMm),
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm);
|
||||
texture["contrast_pct"] = std::clamp(finite_or(zone.contrast_pct, 100.f), 25.f, 300.f);
|
||||
texture["high_resolution_sampling"] = zone.high_resolution_sampling;
|
||||
texture["tone_gamma"] = normalize_tone_gamma(zone.tone_gamma);
|
||||
@@ -1235,9 +1239,13 @@ void TextureMappingManager::load_entries(const std::string &serialized,
|
||||
zone.dithering_method = dithering_method_from_name(
|
||||
texture.value("dithering_method", dithering_method_name(TextureMappingZone::DefaultDitheringMethod)));
|
||||
zone.dithering_resolution_mm =
|
||||
std::clamp(texture.value("dithering_resolution_mm", TextureMappingZone::DefaultDitheringResolutionMm), 0.04f, 0.25f);
|
||||
std::clamp(texture.value("dithering_resolution_mm", TextureMappingZone::DefaultDitheringResolutionMm),
|
||||
TextureMappingZone::MinDitheringResolutionMm,
|
||||
TextureMappingZone::MaxDitheringResolutionMm);
|
||||
zone.halftone_dot_size_mm =
|
||||
std::clamp(texture.value("halftone_dot_size_mm", TextureMappingZone::DefaultHalftoneDotSizeMm), 0.08f, 2.f);
|
||||
std::clamp(texture.value("halftone_dot_size_mm", TextureMappingZone::DefaultHalftoneDotSizeMm),
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm);
|
||||
if (zone.dithering_enabled)
|
||||
zone.compact_offset_mode = true;
|
||||
zone.contrast_pct = std::clamp(texture.value("contrast_pct", 100.f), 25.f, 300.f);
|
||||
|
||||
@@ -110,9 +110,13 @@ struct TextureMappingZone
|
||||
static constexpr int DefaultGenericSolverMode = int(GenericSolverV2);
|
||||
static constexpr int DefaultGenericSolverMixModel = int(GenericSolverPigmentPainter);
|
||||
static constexpr bool DefaultDitheringEnabled = false;
|
||||
static constexpr int DefaultDitheringMethod = int(DitheringFloydSteinberg);
|
||||
static constexpr int DefaultDitheringMethod = int(DitheringHalftone);
|
||||
static constexpr float MinDitheringResolutionMm = 0.04f;
|
||||
static constexpr float MaxDitheringResolutionMm = 10.f;
|
||||
static constexpr float DefaultDitheringResolutionMm = 0.08f;
|
||||
static constexpr float DefaultHalftoneDotSizeMm = 0.32f;
|
||||
static constexpr float MinHalftoneDotSizeMm = 0.08f;
|
||||
static constexpr float MaxHalftoneDotSizeMm = 50.f;
|
||||
static constexpr float DefaultHalftoneDotSizeMm = 0.8f;
|
||||
static constexpr float DefaultContrastPct = 100.f;
|
||||
static constexpr bool DefaultHighResolutionSampling = true;
|
||||
static constexpr float DefaultToneGamma = 1.f;
|
||||
|
||||
@@ -1623,7 +1623,12 @@ float ordered_bayer_threshold_for_texture_preview(int x, int y)
|
||||
float halftone_threshold_for_texture_preview(int x, int y, float dot_size_mm)
|
||||
{
|
||||
const float period_px =
|
||||
std::clamp(std::clamp(dot_size_mm, 0.08f, 2.f) / TextureMappingZone::DefaultDitheringResolutionMm, 2.f, 64.f);
|
||||
std::clamp(std::clamp(dot_size_mm,
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm) /
|
||||
TextureMappingZone::DefaultDitheringResolutionMm,
|
||||
2.f,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm / TextureMappingZone::DefaultDitheringResolutionMm);
|
||||
const float u = float(x) / period_px - std::floor(float(x) / period_px);
|
||||
const float v = float(y) / period_px - std::floor(float(y) / period_px);
|
||||
const float dx = u - 0.5f;
|
||||
@@ -1785,7 +1790,9 @@ std::vector<float> component_weights_for_texture_preview(const TexturePreviewSim
|
||||
if (component_count == 0)
|
||||
return {};
|
||||
|
||||
if (settings.dithering_enabled && settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues)) {
|
||||
if (settings.dithering_enabled &&
|
||||
settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues) &&
|
||||
settings.dithering_method != int(TextureMappingZone::DitheringHalftone)) {
|
||||
const std::vector<TexturePreviewBinaryDitherCandidate> candidates = binary_dither_candidates_for_texture_preview(settings);
|
||||
const std::array<float, 3> target_oklab = texture_preview_target_oklab(settings, sample_rgba);
|
||||
const size_t candidate_idx = nearest_binary_dither_candidate_for_texture_preview(candidates, target_oklab);
|
||||
@@ -1961,9 +1968,16 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
|
||||
settings.dithering_method = std::clamp(zone->dithering_method,
|
||||
int(TextureMappingZone::DitheringClosest),
|
||||
int(TextureMappingZone::DitheringHalftone));
|
||||
settings.dithering_resolution_mm = std::clamp(zone->dithering_resolution_mm, 0.04f, 0.25f);
|
||||
settings.halftone_dot_size_mm = std::clamp(zone->halftone_dot_size_mm, 0.08f, 2.f);
|
||||
settings.compact_offset_mode = zone->compact_offset_mode || settings.dithering_enabled;
|
||||
settings.dithering_resolution_mm = std::clamp(zone->dithering_resolution_mm,
|
||||
TextureMappingZone::MinDitheringResolutionMm,
|
||||
TextureMappingZone::MaxDitheringResolutionMm);
|
||||
settings.halftone_dot_size_mm = std::clamp(zone->halftone_dot_size_mm,
|
||||
TextureMappingZone::MinHalftoneDotSizeMm,
|
||||
TextureMappingZone::MaxHalftoneDotSizeMm);
|
||||
const bool halftone_dithering_enabled =
|
||||
settings.dithering_enabled && settings.dithering_method == int(TextureMappingZone::DitheringHalftone);
|
||||
settings.compact_offset_mode =
|
||||
halftone_dithering_enabled ? false : zone->compact_offset_mode || settings.dithering_enabled;
|
||||
settings.use_legacy_fixed_color_mode = zone->use_legacy_fixed_color_mode;
|
||||
settings.contrast_pct = std::clamp(zone->contrast_pct, 25.f, 300.f);
|
||||
settings.tone_gamma = (!std::isfinite(zone->tone_gamma) || zone->tone_gamma <= 0.f) ?
|
||||
@@ -2098,6 +2112,7 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
|
||||
const bool use_binary_dithering =
|
||||
settings.dithering_enabled &&
|
||||
settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues) &&
|
||||
settings.dithering_method != int(TextureMappingZone::DitheringHalftone) &&
|
||||
!use_raw_offsets;
|
||||
const std::vector<TexturePreviewBinaryDitherCandidate> binary_dither_candidates =
|
||||
use_binary_dithering ? binary_dither_candidates_for_texture_preview(settings) :
|
||||
|
||||
@@ -1346,10 +1346,12 @@ public:
|
||||
wxEmptyString,
|
||||
wxDefaultPosition,
|
||||
wxSize(FromDIP(84), -1),
|
||||
wxSP_ARROW_KEYS | wxALIGN_RIGHT,
|
||||
0.04,
|
||||
0.25,
|
||||
std::clamp(double(dithering_resolution_mm), 0.04, 0.25),
|
||||
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER,
|
||||
double(TextureMappingZone::MinDitheringResolutionMm),
|
||||
double(TextureMappingZone::MaxDitheringResolutionMm),
|
||||
std::clamp(double(dithering_resolution_mm),
|
||||
double(TextureMappingZone::MinDitheringResolutionMm),
|
||||
double(TextureMappingZone::MaxDitheringResolutionMm)),
|
||||
0.01);
|
||||
m_dithering_resolution_spin->SetDigits(2);
|
||||
dithering_resolution_row->Add(m_dithering_resolution_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
|
||||
@@ -1367,12 +1369,14 @@ public:
|
||||
wxID_ANY,
|
||||
wxEmptyString,
|
||||
wxDefaultPosition,
|
||||
wxSize(FromDIP(84), -1),
|
||||
wxSP_ARROW_KEYS | wxALIGN_RIGHT,
|
||||
0.08,
|
||||
2.0,
|
||||
std::clamp(double(halftone_dot_size_mm), 0.08, 2.0),
|
||||
0.01);
|
||||
wxSize(FromDIP(96), -1),
|
||||
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER,
|
||||
double(TextureMappingZone::MinHalftoneDotSizeMm),
|
||||
double(TextureMappingZone::MaxHalftoneDotSizeMm),
|
||||
std::clamp(double(halftone_dot_size_mm),
|
||||
double(TextureMappingZone::MinHalftoneDotSizeMm),
|
||||
double(TextureMappingZone::MaxHalftoneDotSizeMm)),
|
||||
0.1);
|
||||
m_halftone_dot_size_spin->SetDigits(2);
|
||||
halftone_dot_size_row->Add(m_halftone_dot_size_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
|
||||
halftone_dot_size_row->Add(new wxStaticText(m_halftone_dot_size_panel, wxID_ANY, _L("mm")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -1638,16 +1642,16 @@ public:
|
||||
return float(std::clamp(m_dithering_resolution_spin != nullptr ?
|
||||
m_dithering_resolution_spin->GetValue() :
|
||||
double(TextureMappingZone::DefaultDitheringResolutionMm),
|
||||
0.04,
|
||||
0.25));
|
||||
double(TextureMappingZone::MinDitheringResolutionMm),
|
||||
double(TextureMappingZone::MaxDitheringResolutionMm)));
|
||||
}
|
||||
float halftone_dot_size_mm() const
|
||||
{
|
||||
return float(std::clamp(m_halftone_dot_size_spin != nullptr ?
|
||||
m_halftone_dot_size_spin->GetValue() :
|
||||
double(TextureMappingZone::DefaultHalftoneDotSizeMm),
|
||||
0.08,
|
||||
2.0));
|
||||
double(TextureMappingZone::MinHalftoneDotSizeMm),
|
||||
double(TextureMappingZone::MaxHalftoneDotSizeMm)));
|
||||
}
|
||||
bool minimum_visibility_offset_enabled() const
|
||||
{
|
||||
@@ -1904,6 +1908,19 @@ private:
|
||||
m_options_book->SetMinSize(wxSize(FromDIP(420), max_height));
|
||||
}
|
||||
|
||||
void layout_current_options_page()
|
||||
{
|
||||
if (m_options_book == nullptr)
|
||||
return;
|
||||
const int selection = m_options_book->GetSelection();
|
||||
if (selection >= 0 && selection < int(m_options_book->GetPageCount())) {
|
||||
wxWindow *page = m_options_book->GetPage(size_t(selection));
|
||||
if (page != nullptr)
|
||||
page->Layout();
|
||||
}
|
||||
m_options_book->Layout();
|
||||
}
|
||||
|
||||
void update_strength_offsets_visibility(bool fit_dialog)
|
||||
{
|
||||
if (m_strength_offsets_toggle_button != nullptr)
|
||||
@@ -1957,6 +1974,7 @@ private:
|
||||
m_compact_offset_mode_checkbox->SetValue(true);
|
||||
m_compact_offset_mode_checkbox->Enable(!enabled);
|
||||
}
|
||||
layout_current_options_page();
|
||||
if (!fit_dialog)
|
||||
return;
|
||||
update_options_book_min_size();
|
||||
|
||||
Reference in New Issue
Block a user