Add filament TD calibration options

This commit is contained in:
sentientstardust
2026-05-07 23:16:35 +01:00
parent 684747a8b9
commit 4d03044159
4 changed files with 411 additions and 57 deletions

View File

@@ -5930,15 +5930,150 @@ static float overhang_filament_minimum_offset_factor_for_gcode(const TextureMapp
return std::clamp(minimum_offset_pct / 100.f, 0.f, 1.f);
}
struct TransmissionDistanceCalibrationContextForGCode {
bool enabled { false };
int mode { int(TextureMappingZone::TDCalibrationNone) };
std::vector<float> own_width_factors;
std::vector<float> neighbor_opacity_ratios;
};
static bool texture_mapping_component_is_black_for_gcode(size_t component_idx,
int filament_color_mode,
const std::vector<std::array<float, 3>> &component_colors)
{
switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))) {
case int(TextureMappingZone::FilamentColorCMYK):
case int(TextureMappingZone::FilamentColorRGBK):
if (component_idx == 3)
return true;
break;
case int(TextureMappingZone::FilamentColorBW):
if (component_idx == 0)
return true;
break;
default:
break;
}
if (component_idx >= component_colors.size())
return false;
const std::array<float, 3> &c = component_colors[component_idx];
const float max_channel = std::max({ c[0], c[1], c[2] });
const float luminance = 0.2126f * c[0] + 0.7152f * c[1] + 0.0722f * c[2];
return max_channel <= 0.18f && luminance <= 0.12f;
}
static bool overhang_filament_explicit_transmission_distance_for_gcode(const TextureMappingZone &zone,
unsigned int physical_filament_id,
float &td_mm)
{
if (physical_filament_id == 0)
return false;
const size_t idx = size_t(physical_filament_id - 1);
if (idx >= zone.filament_transmission_distances_mm.size())
return false;
const float value = zone.filament_transmission_distances_mm[idx];
if (!std::isfinite(value) || value <= 0.f)
return false;
td_mm = std::clamp(value, 0.01f, 50.f);
return true;
}
static float transmission_distance_reference_for_gcode(bool is_black)
{
return is_black ? 0.1f : 3.f;
}
static float transmission_distance_opacity_for_gcode(float td_mm, float path_extension_mm)
{
constexpr float surface_scatter = 0.50f;
constexpr float surface_depth_mm = 0.32f;
const float safe_td = std::clamp(td_mm, 0.01f, 50.f);
const float path_mm = std::max(0.f, surface_depth_mm + std::max(0.f, path_extension_mm));
const float opacity = surface_scatter + (1.f - surface_scatter) * (1.f - std::exp(-path_mm / safe_td));
return std::clamp(opacity, 1e-4f, 1.f);
}
static TransmissionDistanceCalibrationContextForGCode transmission_distance_calibration_context_for_gcode(
const TextureMappingZone &zone,
const std::vector<unsigned int> &component_ids,
const std::vector<std::array<float, 3>> &component_colors,
int filament_color_mode)
{
TransmissionDistanceCalibrationContextForGCode context;
context.mode = std::clamp(zone.transmission_distance_calibration_mode,
int(TextureMappingZone::TDCalibrationNone),
int(TextureMappingZone::TDCalibrationNeighbor));
if (context.mode == int(TextureMappingZone::TDCalibrationNone) || component_ids.empty())
return context;
std::vector<float> explicit_tds(component_ids.size(), 0.f);
bool has_active_explicit_td = false;
for (size_t idx = 0; idx < component_ids.size(); ++idx) {
float td_mm = 0.f;
if (overhang_filament_explicit_transmission_distance_for_gcode(zone, component_ids[idx], td_mm)) {
explicit_tds[idx] = td_mm;
has_active_explicit_td = true;
}
}
if (!has_active_explicit_td)
return context;
const bool neighbor_mode = context.mode == int(TextureMappingZone::TDCalibrationNeighbor);
const float path_extension_mm = neighbor_mode ? 0.16f : 0.12f;
const float own_power = neighbor_mode ? 0.25f : 0.35f;
context.own_width_factors.assign(component_ids.size(), 1.f);
context.neighbor_opacity_ratios.assign(component_ids.size(), 1.f);
context.enabled = true;
for (size_t idx = 0; idx < component_ids.size(); ++idx) {
const bool is_black = texture_mapping_component_is_black_for_gcode(idx, filament_color_mode, component_colors);
const float reference_td_mm = transmission_distance_reference_for_gcode(is_black);
const float actual_td_mm = explicit_tds[idx] > 0.f ? explicit_tds[idx] : reference_td_mm;
const float actual_opacity = transmission_distance_opacity_for_gcode(actual_td_mm, path_extension_mm);
const float reference_opacity = transmission_distance_opacity_for_gcode(reference_td_mm, path_extension_mm);
context.own_width_factors[idx] =
std::clamp(std::pow(reference_opacity / std::max(actual_opacity, 1e-4f), own_power), 0.25f, 2.f);
context.neighbor_opacity_ratios[idx] =
std::clamp(actual_opacity / std::max(reference_opacity, 1e-4f), 0.25f, 4.f);
}
return context;
}
static float transmission_distance_width_factor_for_gcode(const TransmissionDistanceCalibrationContextForGCode &context,
size_t active_component_idx,
size_t previous_component_idx)
{
if (!context.enabled || active_component_idx >= context.own_width_factors.size())
return 1.f;
float factor = context.own_width_factors[active_component_idx];
if (context.mode == int(TextureMappingZone::TDCalibrationNeighbor) &&
previous_component_idx < context.neighbor_opacity_ratios.size())
factor *= std::pow(context.neighbor_opacity_ratios[previous_component_idx], 0.20f);
return std::clamp(factor, 0.25f, 2.f);
}
static float variable_width_delta_for_overhang_range_for_gcode(float inset_strength,
float max_width_delta_limit_mm,
float minimum_offset_factor,
float strength_factor)
float strength_factor,
float transmission_distance_width_factor)
{
if (!std::isfinite(max_width_delta_limit_mm) || max_width_delta_limit_mm <= 0.f)
return 0.f;
const float desired_width_factor = 1.f - std::clamp(inset_strength, 0.f, 1.f);
const float desired_width_factor =
std::clamp((1.f - std::clamp(inset_strength, 0.f, 1.f)) *
std::clamp(transmission_distance_width_factor, 0.f, 2.f),
0.f,
1.f);
const float min_width_factor = std::clamp(minimum_offset_factor, 0.f, 1.f);
const float adjusted_width_factor =
min_width_factor + desired_width_factor * std::clamp(strength_factor, 0.f, 1.f) * (1.f - min_width_factor);
@@ -6014,6 +6149,7 @@ static float variable_width_delta_for_visibility_range_for_gcode(float inset_str
float max_width_delta_limit_mm,
float minimum_offset_factor,
float strength_factor,
float transmission_distance_width_factor,
bool nonlinear_offset_adjustment,
float layer_height_mm,
float stair_step_mm,
@@ -6022,7 +6158,11 @@ static float variable_width_delta_for_visibility_range_for_gcode(float inset_str
if (!std::isfinite(max_width_delta_limit_mm) || max_width_delta_limit_mm <= 0.f)
return 0.f;
float desired_width_factor = 1.f - std::clamp(inset_strength, 0.f, 1.f);
float desired_width_factor =
std::clamp((1.f - std::clamp(inset_strength, 0.f, 1.f)) *
std::clamp(transmission_distance_width_factor, 0.f, 2.f),
0.f,
1.f);
if (nonlinear_offset_adjustment)
desired_width_factor = nonlinear_visibility_width_factor_for_gcode(desired_width_factor,
layer_height_mm,
@@ -8110,6 +8250,12 @@ std::optional<Point> GCode::texture_mapping_seam_hiding_point(const ExtrusionLoo
if (missing_component_color || component_colors.size() != component_ids.size() || component_colors.empty())
return std::nullopt;
const TransmissionDistanceCalibrationContextForGCode td_calibration_context =
transmission_distance_calibration_context_for_gcode(*zone,
component_ids,
component_colors,
texture_filament_color_mode);
std::ostringstream component_key_stream;
for (size_t idx = 0; idx < component_ids.size(); ++idx) {
if (idx > 0)
@@ -8136,6 +8282,7 @@ std::optional<Point> GCode::texture_mapping_seam_hiding_point(const ExtrusionLoo
const VertexColorOverhangWeightField *weight_field { nullptr };
float active_component_strength_factor { 1.f };
float active_component_minimum_offset_factor { 0.f };
float active_component_td_width_factor { 1.f };
float signed_fade_factor { 1.f };
float fade_factor { 1.f };
};
@@ -8156,6 +8303,15 @@ std::optional<Point> GCode::texture_mapping_seam_hiding_point(const ExtrusionLoo
const auto active_component_it = std::find(component_ids.begin(), component_ids.end(), active_component_id);
if (active_component_it == component_ids.end())
return std::nullopt;
const size_t active_component_idx = size_t(active_component_it - component_ids.begin());
size_t previous_component_idx = size_t(-1);
if (layer_index > 0) {
const unsigned int previous_component_id =
texture_mgr.resolve_zone_component(texture_zone_id, num_physical, layer_index - 1);
const auto previous_component_it = std::find(component_ids.begin(), component_ids.end(), previous_component_id);
if (previous_component_it != component_ids.end())
previous_component_idx = size_t(previous_component_it - component_ids.begin());
}
std::ostringstream layer_key_stream;
layer_key_stream << component_key_prefix << "|seamL" << layer->id();
@@ -8199,10 +8355,11 @@ std::optional<Point> GCode::texture_mapping_seam_hiding_point(const ExtrusionLoo
layer,
layer_height_mm,
active_component_id,
size_t(active_component_it - component_ids.begin()),
active_component_idx,
&weight_field,
overhang_filament_strength_factor_for_gcode(*zone, active_component_id),
overhang_filament_minimum_offset_factor_for_gcode(*zone, active_component_id),
transmission_distance_width_factor_for_gcode(td_calibration_context, active_component_idx, previous_component_idx),
signed_fade_factor,
fade_factor
};
@@ -8300,7 +8457,8 @@ std::optional<Point> GCode::texture_mapping_seam_hiding_point(const ExtrusionLoo
variable_width_delta_for_overhang_range_for_gcode(inset_strength,
max_width_delta_limit_mm,
state.active_component_minimum_offset_factor,
state.active_component_strength_factor);
state.active_component_strength_factor,
state.active_component_td_width_factor);
const float width_delta_mm = std::clamp(variable_width_delta_mm, 0.f, max_width_delta_limit_mm);
if (!std::isfinite(width_delta_mm))
return std::numeric_limits<float>::quiet_NaN();
@@ -9027,6 +9185,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
float max_width_delta_mm { 0.f };
float active_component_strength_factor { 1.f };
float active_component_minimum_offset_factor { 0.f };
float active_component_td_width_factor { 1.f };
float base_outer_width_mm { 0.4f };
float flow_reference_width_mm { 0.4f };
float base_centerline_shift_mm { 0.f };
@@ -9220,32 +9379,51 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
const float texture_sagging_ratio =
std::isfinite(zone->sagging_ratio) ? std::clamp(zone->sagging_ratio, 0.f, 6.f) : 0.f;
const bool raw_texture_mapping_mode =
zone->texture_mapping_mode == int(TextureMappingZone::TextureMappingRawValues);
std::vector<std::array<float, 3>> component_colors;
component_colors.reserve(component_ids.size());
bool missing_component_color = false;
for (const unsigned int id : component_ids) {
if (id < 1 || id > m_config.filament_colour.values.size()) {
if (raw_texture_mapping_mode)
component_colors.push_back({ 0.f, 0.f, 0.f });
else
missing_component_color = true;
continue;
}
ColorRGB decoded;
if (!decode_color(m_config.filament_colour.get_at(size_t(id - 1)), decoded)) {
if (raw_texture_mapping_mode)
component_colors.push_back({ 0.f, 0.f, 0.f });
else
missing_component_color = true;
continue;
}
component_colors.push_back({ decoded.r(), decoded.g(), decoded.b() });
}
const TransmissionDistanceCalibrationContextForGCode td_calibration_context =
transmission_distance_calibration_context_for_gcode(*zone,
component_ids,
component_colors,
texture_filament_color_mode);
size_t previous_component_idx = size_t(-1);
if (current_layer_index > 0) {
const unsigned int previous_component_id =
texture_mgr.resolve_zone_component(texture_zone_id, num_physical, current_layer_index - 1);
const auto previous_component_it =
std::find(component_ids.begin(), component_ids.end(), previous_component_id);
if (previous_component_it != component_ids.end())
previous_component_idx = size_t(previous_component_it - component_ids.begin());
}
const float active_component_td_width_factor =
transmission_distance_width_factor_for_gcode(td_calibration_context,
active_component_idx,
previous_component_idx);
const VertexColorOverhangWeightField *vertex_color_weight_field = nullptr;
auto *generic_mix_candidate_cache = &m_generic_solver_mix_candidate_cache;
if (vertex_color_match_mode && layer_object != nullptr) {
const bool raw_texture_mapping_mode =
zone->texture_mapping_mode == int(TextureMappingZone::TextureMappingRawValues);
std::vector<std::array<float, 3>> component_colors;
component_colors.reserve(component_ids.size());
bool missing_component_color = false;
for (const unsigned int id : component_ids) {
if (id < 1 || id > m_config.filament_colour.values.size()) {
if (raw_texture_mapping_mode)
component_colors.push_back({ 0.f, 0.f, 0.f });
else
missing_component_color = true;
continue;
}
ColorRGB decoded;
if (!decode_color(m_config.filament_colour.get_at(size_t(id - 1)), decoded)) {
if (raw_texture_mapping_mode)
component_colors.push_back({ 0.f, 0.f, 0.f });
else
missing_component_color = true;
continue;
}
component_colors.push_back({ decoded.r(), decoded.g(), decoded.b() });
}
if (!missing_component_color && component_colors.size() == component_ids.size() && !component_colors.empty()) {
std::ostringstream component_key_stream;
for (size_t idx = 0; idx < component_ids.size(); ++idx) {
@@ -9321,6 +9499,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
outer_wall_gradient_dynamic_ctx.max_width_delta_mm = effective_max_width_delta_mm;
outer_wall_gradient_dynamic_ctx.active_component_strength_factor = active_component_strength_factor;
outer_wall_gradient_dynamic_ctx.active_component_minimum_offset_factor = active_component_minimum_offset_factor;
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.flow_reference_width_mm = flow_reference_width_mm;
outer_wall_gradient_dynamic_ctx.base_centerline_shift_mm = base_centerline_shift_mm;
@@ -9435,6 +9614,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
max_width_delta_limit_mm,
active_component_minimum_offset_factor,
active_component_strength_factor,
active_component_td_width_factor,
nonlinear_offset_adjustment,
layer_height_mm,
stair_step_mm,
@@ -10151,6 +10331,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
max_width_delta_limit_mm,
outer_wall_gradient_dynamic_ctx.active_component_minimum_offset_factor,
outer_wall_gradient_dynamic_ctx.active_component_strength_factor,
outer_wall_gradient_dynamic_ctx.active_component_td_width_factor,
outer_wall_gradient_dynamic_ctx.nonlinear_offset_adjustment,
outer_wall_gradient_dynamic_ctx.layer_height_mm,
stair_step_mm,

View File

@@ -352,14 +352,27 @@ static std::vector<float> normalize_minimum_offsets(const std::vector<float> &va
return out;
}
static std::vector<float> normalize_transmission_distances(const std::vector<float> &values)
{
std::vector<float> out;
out.reserve(std::min<size_t>(values.size(), 9));
for (size_t i = 0; i < std::min<size_t>(values.size(), 9); ++i) {
const float value = finite_or(values[i], 0.f);
out.emplace_back(value > 0.f ? std::clamp(value, 0.01f, 50.f) : 0.f);
}
while (!out.empty() && std::abs(out.back()) <= 1e-6f)
out.pop_back();
return out;
}
static float normalize_tone_gamma(float value)
{
return (!std::isfinite(value) || value <= 0.f) ? 1.f : std::clamp(value, 0.5f, 3.f);
}
static float normalize_sagging_ratio(float value)
static float normalize_sagging_ratio(float)
{
return std::isfinite(value) ? std::clamp(value, 0.f, 6.f) : 0.f;
return 0.f;
}
static RGB filament_color(unsigned int id, const std::vector<std::string> &filament_colours)
@@ -553,6 +566,52 @@ static int generic_solver_mix_model_from_name(std::string)
return TextureMappingZone::DefaultGenericSolverMixModel;
}
static std::string transmission_distance_calibration_mode_name(int mode)
{
switch (clamp_int(mode,
int(TextureMappingZone::TDCalibrationNone),
int(TextureMappingZone::TDCalibrationNeighbor))) {
case int(TextureMappingZone::TDCalibrationNone): return "none";
case int(TextureMappingZone::TDCalibrationNeighbor): return "neighbor";
default: return "absolute";
}
}
static int transmission_distance_calibration_mode_from_name(std::string name)
{
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return char(std::tolower(c)); });
if (name == "none" || name == "off" || name == "disabled")
return int(TextureMappingZone::TDCalibrationNone);
if (name == "neighbor" || name == "neighbour")
return int(TextureMappingZone::TDCalibrationNeighbor);
return int(TextureMappingZone::TDCalibrationAbsolute);
}
static int transmission_distance_calibration_mode_from_json(const nlohmann::json &texture)
{
const auto mode_it = texture.find("transmission_distance_calibration");
if (mode_it != texture.end()) {
if (mode_it->is_string())
return transmission_distance_calibration_mode_from_name(mode_it->get<std::string>());
if (mode_it->is_number_integer() || mode_it->is_number_unsigned())
return clamp_int(mode_it->get<int>(),
int(TextureMappingZone::TDCalibrationNone),
int(TextureMappingZone::TDCalibrationNeighbor));
if (mode_it->is_boolean())
return mode_it->get<bool>() ?
int(TextureMappingZone::TDCalibrationAbsolute) :
int(TextureMappingZone::TDCalibrationNone);
}
const auto legacy_it = texture.find("transmission_distance_calibration_enabled");
if (legacy_it != texture.end() && legacy_it->is_boolean())
return legacy_it->get<bool>() ?
int(TextureMappingZone::TDCalibrationAbsolute) :
int(TextureMappingZone::TDCalibrationNone);
return TextureMappingZone::DefaultTransmissionDistanceCalibrationMode;
}
static std::string normalized_prime_tower_color_mode_name(std::string name)
{
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return char(std::tolower(c)); });
@@ -708,12 +767,14 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
high_resolution_sampling == rhs.high_resolution_sampling &&
std::abs(tone_gamma - rhs.tone_gamma) <= eps &&
std::abs(sagging_ratio - rhs.sagging_ratio) <= eps &&
transmission_distance_calibration_mode == rhs.transmission_distance_calibration_mode &&
std::abs(preview_opacity_pct - rhs.preview_opacity_pct) <= eps &&
preview_simulate_colors == rhs.preview_simulate_colors &&
preview_limit_resolution == rhs.preview_limit_resolution &&
auto_adjust_filament_selection == rhs.auto_adjust_filament_selection &&
floats_equal(filament_strengths_pct, rhs.filament_strengths_pct) &&
floats_equal(filament_minimum_offsets_pct, rhs.filament_minimum_offsets_pct);
floats_equal(filament_minimum_offsets_pct, rhs.filament_minimum_offsets_pct) &&
floats_equal(filament_transmission_distances_mm, rhs.filament_transmission_distances_mm);
}
uint64_t TextureMappingManager::allocate_stable_id()
@@ -796,6 +857,7 @@ void TextureMappingManager::remove_physical_filament(unsigned int deleted_filame
};
remove_index(zone.filament_strengths_pct);
remove_index(zone.filament_minimum_offsets_pct);
remove_index(zone.filament_transmission_distances_mm);
}
normalize_zone_ids(new_physical_count);
}
@@ -920,6 +982,8 @@ std::string TextureMappingManager::serialize_entries()
const std::string normalized_angles = normalize_offset_angles(zone.offset_angles, component_ids.size());
const std::vector<float> normalized_strengths = normalize_strengths(zone.filament_strengths_pct);
const std::vector<float> normalized_min_offsets = normalize_minimum_offsets(zone.filament_minimum_offsets_pct);
const std::vector<float> normalized_transmission_distances =
normalize_transmission_distances(zone.filament_transmission_distances_mm);
nlohmann::json entry;
entry["schema"] = 2;
@@ -950,12 +1014,16 @@ std::string TextureMappingManager::serialize_entries()
texture["high_resolution_sampling"] = zone.high_resolution_sampling;
texture["tone_gamma"] = normalize_tone_gamma(zone.tone_gamma);
texture["sagging_ratio"] = normalize_sagging_ratio(zone.sagging_ratio);
texture["preview_opacity_pct"] = std::clamp(finite_or(zone.preview_opacity_pct, TextureMappingZone::DefaultPreviewOpacityPct), 0.f, 100.f);
texture["transmission_distance_calibration"] =
transmission_distance_calibration_mode_name(zone.transmission_distance_calibration_mode);
texture["preview_opacity_pct"] =
std::clamp(finite_or(zone.preview_opacity_pct, TextureMappingZone::DefaultPreviewOpacityPct), 0.f, 100.f);
texture["simulate_preview_colors"] = zone.preview_simulate_colors;
texture["limit_preview_resolution"] = zone.preview_limit_resolution;
texture["auto_adjust_filaments"] = zone.auto_adjust_filament_selection;
texture["strength_pct"] = floats_to_json(normalized_strengths);
texture["minimum_offset_pct"] = floats_to_json(normalized_min_offsets);
texture["transmission_distance_mm"] = floats_to_json(normalized_transmission_distances);
entry["texture_options"] = std::move(texture);
nlohmann::json offset;
@@ -1083,12 +1151,17 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.high_resolution_sampling = texture.value("high_resolution_sampling", true);
zone.tone_gamma = normalize_tone_gamma(texture.value("tone_gamma", 1.f));
zone.sagging_ratio = normalize_sagging_ratio(texture.value("sagging_ratio", 0.f));
zone.preview_opacity_pct = std::clamp(texture.value("preview_opacity_pct", TextureMappingZone::DefaultPreviewOpacityPct), 0.f, 100.f);
zone.transmission_distance_calibration_mode = transmission_distance_calibration_mode_from_json(texture);
zone.preview_opacity_pct =
std::clamp(texture.value("preview_opacity_pct", TextureMappingZone::DefaultPreviewOpacityPct), 0.f, 100.f);
zone.preview_simulate_colors = texture.value("simulate_preview_colors", false);
zone.preview_limit_resolution = texture.value("limit_preview_resolution", true);
zone.auto_adjust_filament_selection = texture.value("auto_adjust_filaments", true);
zone.filament_strengths_pct = normalize_strengths(floats_from_json(texture.value("strength_pct", nlohmann::json::array())));
zone.filament_minimum_offsets_pct = normalize_minimum_offsets(floats_from_json(texture.value("minimum_offset_pct", nlohmann::json::array())));
zone.filament_minimum_offsets_pct =
normalize_minimum_offsets(floats_from_json(texture.value("minimum_offset_pct", nlohmann::json::array())));
zone.filament_transmission_distances_mm =
normalize_transmission_distances(floats_from_json(texture.value("transmission_distance_mm", nlohmann::json::array())));
const nlohmann::json offset = entry.value("surface_offset", nlohmann::json::object());
zone.offset_distances =

View File

@@ -69,6 +69,12 @@ struct TextureMappingZone
GenericSolverPigmentPainter = 0
};
enum TransmissionDistanceCalibrationMode : uint8_t {
TDCalibrationNone = 0,
TDCalibrationAbsolute = 1,
TDCalibrationNeighbor = 2
};
static constexpr int DefaultSurfacePattern = int(ImageTexture);
static constexpr int DefaultOffsetMode = int(OffsetBasic);
static constexpr bool DefaultOffsetRotationEnabled = true;
@@ -93,6 +99,7 @@ struct TextureMappingZone
static constexpr bool DefaultHighResolutionSampling = true;
static constexpr float DefaultToneGamma = 1.f;
static constexpr float DefaultSaggingRatio = 0.f;
static constexpr int DefaultTransmissionDistanceCalibrationMode = int(TDCalibrationAbsolute);
static constexpr bool DefaultPreviewSimulateColors = false;
static constexpr bool DefaultPreviewLimitResolution = true;
static constexpr bool DefaultAutoAdjustFilamentSelection = true;
@@ -134,12 +141,14 @@ struct TextureMappingZone
bool high_resolution_sampling = DefaultHighResolutionSampling;
float tone_gamma = DefaultToneGamma;
float sagging_ratio = DefaultSaggingRatio;
int transmission_distance_calibration_mode = DefaultTransmissionDistanceCalibrationMode;
float preview_opacity_pct = DefaultPreviewOpacityPct;
bool preview_simulate_colors = DefaultPreviewSimulateColors;
bool preview_limit_resolution = DefaultPreviewLimitResolution;
bool auto_adjust_filament_selection = DefaultAutoAdjustFilamentSelection;
std::vector<float> filament_strengths_pct;
std::vector<float> filament_minimum_offsets_pct;
std::vector<float> filament_transmission_distances_mm;
bool is_image_texture() const { return surface_pattern == int(ImageTexture); }
bool is_2d_gradient() const { return surface_pattern == int(Gradient2D); }
@@ -175,12 +184,14 @@ struct TextureMappingZone
high_resolution_sampling = DefaultHighResolutionSampling;
tone_gamma = DefaultToneGamma;
sagging_ratio = DefaultSaggingRatio;
transmission_distance_calibration_mode = DefaultTransmissionDistanceCalibrationMode;
preview_opacity_pct = DefaultPreviewOpacityPct;
preview_simulate_colors = DefaultPreviewSimulateColors;
preview_limit_resolution = DefaultPreviewLimitResolution;
auto_adjust_filament_selection = DefaultAutoAdjustFilamentSelection;
filament_strengths_pct.clear();
filament_minimum_offsets_pct.clear();
filament_transmission_distances_mm.clear();
}
bool has_custom_offset_settings() const

View File

@@ -1023,8 +1023,9 @@ public:
const std::vector<unsigned int> &component_ids,
const std::vector<float> &component_strengths_pct,
const std::vector<float> &component_minimum_offsets_pct,
const std::vector<float> &component_transmission_distances_mm,
int transmission_distance_calibration_mode,
float tone_gamma,
float sagging_ratio,
float preview_opacity_pct,
bool force_sequential_filaments,
bool auto_adjust_filament_selection,
@@ -1099,19 +1100,6 @@ public:
tone_gamma_row->Add(new wxStaticText(image_page, wxID_ANY, _L("x")), 0, wxALIGN_CENTER_VERTICAL);
image_root->Add(tone_gamma_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
auto *sagging_row = new wxBoxSizer(wxHORIZONTAL);
sagging_row->Add(new wxStaticText(filament_page, wxID_ANY, _L("Sagging ratio limit")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
m_sagging_ratio_spin = new wxSpinCtrlDouble(filament_page, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(84), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT, 0.0, 6.0, std::clamp(double(sagging_ratio), 0.0, 6.0), 0.1);
m_sagging_ratio_spin->SetDigits(2);
sagging_row->Add(m_sagging_ratio_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
sagging_row->Add(new wxStaticText(filament_page, wxID_ANY, _L("x h")), 0, wxALIGN_CENTER_VERTICAL);
filament_root->Add(sagging_row, 0, wxEXPAND | wxALL, gap);
m_force_sequential_filaments_checkbox = new wxCheckBox(filament_page, wxID_ANY, _L("Force sequential order for filaments"));
m_force_sequential_filaments_checkbox->SetValue(force_sequential_filaments);
filament_root->Add(m_force_sequential_filaments_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
auto *minimum_offsets_box = new wxStaticBoxSizer(wxVERTICAL, filament_page, _L("Per-filament minimum offset"));
auto *strengths_box = new wxStaticBoxSizer(wxVERTICAL, filament_page, _L("Per-filament strength"));
const std::vector<wxString> channel_labels = texture_mapping_channel_labels(filament_color_mode);
@@ -1147,11 +1135,15 @@ public:
box->Add(row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
};
for (size_t i = 0; i < component_ids.size(); ++i) {
const int offset_value = i < component_minimum_offsets_pct.size() ? std::clamp(int(std::lround(component_minimum_offsets_pct[i])), 0, 100) : 0;
const int offset_value = i < component_minimum_offsets_pct.size() ?
std::clamp(int(std::lround(component_minimum_offsets_pct[i])), 0, 100) :
0;
add_percent_row(minimum_offsets_box, i, offset_value, m_minimum_offset_sliders, m_minimum_offset_spins);
}
for (size_t i = 0; i < component_ids.size(); ++i) {
const int strength_value = i < component_strengths_pct.size() ? std::clamp(int(std::lround(component_strengths_pct[i])), 0, 100) : 100;
const int strength_value = i < component_strengths_pct.size() ?
std::clamp(int(std::lround(component_strengths_pct[i])), 0, 100) :
100;
add_percent_row(strengths_box, i, strength_value, m_strength_sliders, m_strength_spins);
}
filament_root->Add(minimum_offsets_box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
@@ -1160,9 +1152,59 @@ public:
reset_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { reset_strengths_and_offsets(); });
filament_root->Add(reset_btn, 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT | wxBOTTOM, gap);
auto *td_box = new wxStaticBoxSizer(wxVERTICAL, filament_page, _L("Transmission distance"));
auto *td_mode_row = new wxBoxSizer(wxHORIZONTAL);
td_mode_row->Add(new wxStaticText(filament_page, wxID_ANY, _L("TD calibration mode")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
wxArrayString td_mode_choices;
td_mode_choices.Add(_L("None"));
td_mode_choices.Add(_L("Absolute"));
td_mode_choices.Add(_L("Neighbor"));
m_transmission_distance_calibration_mode_choice =
new wxChoice(filament_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, td_mode_choices);
m_transmission_distance_calibration_mode_choice->SetSelection(
std::clamp(transmission_distance_calibration_mode,
int(TextureMappingZone::TDCalibrationNone),
int(TextureMappingZone::TDCalibrationNeighbor)));
td_mode_row->Add(m_transmission_distance_calibration_mode_choice, 1, wxALIGN_CENTER_VERTICAL);
td_box->Add(td_mode_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
for (size_t i = 0; i < component_ids.size(); ++i) {
auto *row = new wxBoxSizer(wxHORIZONTAL);
row->Add(new wxStaticText(filament_page, wxID_ANY, component_label(i)), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
const double value = i < component_transmission_distances_mm.size() ?
std::clamp(double(component_transmission_distances_mm[i]), 0.0, 50.0) :
0.0;
auto *spin = new wxSpinCtrlDouble(filament_page,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(FromDIP(84), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT,
0.0,
50.0,
value,
0.1);
spin->SetDigits(2);
row->Add(spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
row->Add(new wxStaticText(filament_page, wxID_ANY, _L("mm")), 0, wxALIGN_CENTER_VERTICAL);
m_transmission_distance_spins.emplace_back(spin);
td_box->Add(row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
}
filament_root->Add(td_box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
m_force_sequential_filaments_checkbox =
new wxCheckBox(filament_page, wxID_ANY, _L("Force sequential order for filaments"));
m_force_sequential_filaments_checkbox->SetValue(force_sequential_filaments);
filament_root->Add(m_force_sequential_filaments_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
auto *preview_box = new wxStaticBoxSizer(wxVERTICAL, preview_page, _L("3D Preview"));
auto *preview_opacity_row = new wxBoxSizer(wxHORIZONTAL);
preview_opacity_row->Add(new wxStaticText(preview_page, wxID_ANY, _L("Texture opacity")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
preview_opacity_row->Add(new wxStaticText(preview_page, wxID_ANY, _L("Texture opacity")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
const int opacity = std::clamp(int(std::lround(preview_opacity_pct)), 0, 100);
m_preview_opacity_slider = new wxSlider(preview_page, wxID_ANY, opacity, 0, 100, wxDefaultPosition, wxSize(FromDIP(180), -1), wxSL_HORIZONTAL | wxSL_AUTOTICKS);
m_preview_opacity_spin = new wxSpinCtrl(preview_page, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(70), -1),
@@ -1391,7 +1433,15 @@ public:
}
float tone_gamma() const { return float(std::clamp(m_tone_gamma_spin ? m_tone_gamma_spin->GetValue() : 1.0, 0.5, 3.0)); }
float sagging_ratio() const { return float(std::clamp(m_sagging_ratio_spin ? m_sagging_ratio_spin->GetValue() : 0.0, 0.0, 6.0)); }
int transmission_distance_calibration_mode() const
{
return m_transmission_distance_calibration_mode_choice ?
std::clamp(m_transmission_distance_calibration_mode_choice->GetSelection(),
int(TextureMappingZone::TDCalibrationNone),
int(TextureMappingZone::TDCalibrationNeighbor)) :
TextureMappingZone::DefaultTransmissionDistanceCalibrationMode;
}
float preview_opacity_pct() const { return float(std::clamp(m_preview_opacity_spin ? m_preview_opacity_spin->GetValue() : 100, 0, 100)); }
bool force_sequential_filaments() const { return m_force_sequential_filaments_checkbox && m_force_sequential_filaments_checkbox->GetValue(); }
bool auto_adjust_filament_selection() const { return m_auto_adjust_filament_selection_checkbox == nullptr || m_auto_adjust_filament_selection_checkbox->GetValue(); }
@@ -1482,6 +1532,17 @@ public:
return out;
}
std::vector<float> component_transmission_distances_mm() const
{
std::vector<float> out;
out.reserve(m_transmission_distance_spins.size());
for (wxSpinCtrlDouble *spin : m_transmission_distance_spins) {
const double value = spin ? spin->GetValue() : 0.0;
out.emplace_back(value > 0.0 ? float(std::clamp(value, 0.01, 50.0)) : 0.f);
}
return out;
}
private:
static std::string prime_tower_color_mode_name(int selection)
{
@@ -1612,7 +1673,7 @@ private:
wxSimplebook *m_options_book {nullptr};
wxChoice *m_texture_mapping_mode_choice {nullptr};
wxSpinCtrlDouble *m_tone_gamma_spin {nullptr};
wxSpinCtrlDouble *m_sagging_ratio_spin {nullptr};
wxChoice *m_transmission_distance_calibration_mode_choice {nullptr};
wxSlider *m_preview_opacity_slider {nullptr};
wxSpinCtrl *m_preview_opacity_spin {nullptr};
wxCheckBox *m_force_sequential_filaments_checkbox {nullptr};
@@ -1640,6 +1701,7 @@ private:
std::vector<wxSpinCtrl*> m_minimum_offset_spins;
std::vector<wxSlider*> m_strength_sliders;
std::vector<wxSpinCtrl*> m_strength_spins;
std::vector<wxSpinCtrlDouble*> m_transmission_distance_spins;
};
} // namespace
@@ -5551,19 +5613,35 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
apply_zone(std::move(updated));
update_texture_mapping_panel(false);
});
advanced_btn->Bind(wxEVT_BUTTON, [this, zone_index, mgr_ptr, palette, apply_zone, bundle, set_config_string](wxCommandEvent &) {
advanced_btn->Bind(wxEVT_BUTTON, [this,
zone_index,
mgr_ptr,
palette,
physical_colors,
apply_zone,
bundle,
set_config_string](wxCommandEvent &) {
if (zone_index >= mgr_ptr->zones().size())
return;
TextureMappingZone updated = mgr_ptr->zones()[zone_index];
const std::vector<unsigned int> ids = texture_mapping_selected_ids(updated, palette.size());
std::vector<unsigned int> ids = updated.is_image_texture() ?
TextureMappingManager::effective_texture_component_ids(updated, palette.size(), physical_colors) :
texture_mapping_selected_ids(updated, palette.size());
if (ids.empty())
ids = texture_mapping_selected_ids(updated, palette.size());
std::vector<float> strengths;
std::vector<float> offsets;
std::vector<float> transmission_distances;
strengths.reserve(ids.size());
offsets.reserve(ids.size());
transmission_distances.reserve(ids.size());
for (const unsigned int id : ids) {
const size_t idx = id > 0 ? size_t(id - 1) : size_t(0);
strengths.emplace_back(idx < updated.filament_strengths_pct.size() ? updated.filament_strengths_pct[idx] : 100.f);
offsets.emplace_back(idx < updated.filament_minimum_offsets_pct.size() ? updated.filament_minimum_offsets_pct[idx] : 0.f);
transmission_distances.emplace_back(idx < updated.filament_transmission_distances_mm.size() ?
updated.filament_transmission_distances_mm[idx] :
0.f);
}
TextureMappingAdvancedOptionsDialog dlg(this,
updated.texture_mapping_mode,
@@ -5571,8 +5649,9 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
ids,
strengths,
offsets,
transmission_distances,
updated.transmission_distance_calibration_mode,
updated.tone_gamma,
updated.sagging_ratio,
updated.preview_opacity_pct,
updated.force_sequential_filaments,
updated.auto_adjust_filament_selection,
@@ -5596,7 +5675,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
return;
updated.texture_mapping_mode = dlg.texture_mapping_mode();
updated.tone_gamma = dlg.tone_gamma();
updated.sagging_ratio = dlg.sagging_ratio();
updated.sagging_ratio = TextureMappingZone::DefaultSaggingRatio;
updated.transmission_distance_calibration_mode = dlg.transmission_distance_calibration_mode();
updated.preview_opacity_pct = dlg.preview_opacity_pct();
updated.force_sequential_filaments = dlg.force_sequential_filaments();
updated.auto_adjust_filament_selection = dlg.auto_adjust_filament_selection();
@@ -5634,6 +5714,15 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.filament_minimum_offsets_pct[size_t(ids[i] - 1)] = dlg_offsets[i];
while (!updated.filament_minimum_offsets_pct.empty() && std::abs(updated.filament_minimum_offsets_pct.back()) <= 1e-6f)
updated.filament_minimum_offsets_pct.pop_back();
if (updated.filament_transmission_distances_mm.size() < palette.size())
updated.filament_transmission_distances_mm.resize(palette.size(), 0.f);
const std::vector<float> dlg_transmission_distances = dlg.component_transmission_distances_mm();
for (size_t i = 0; i < ids.size() && i < dlg_transmission_distances.size(); ++i)
if (ids[i] > 0 && size_t(ids[i] - 1) < updated.filament_transmission_distances_mm.size())
updated.filament_transmission_distances_mm[size_t(ids[i] - 1)] = dlg_transmission_distances[i];
while (!updated.filament_transmission_distances_mm.empty() &&
std::abs(updated.filament_transmission_distances_mm.back()) <= 1e-6f)
updated.filament_transmission_distances_mm.pop_back();
apply_zone(std::move(updated));
if (p->plater != nullptr && !p->plater->is_preview_shown()) {
if (GLCanvas3D *canvas = p->plater->get_view3D_canvas3D())