Allow loading filament calibration model (top-surface coloring)
This commit is contained in:
@@ -80,6 +80,106 @@ std::array<float, 3> linear_to_srgb_color(const std::array<float, 3> &rgb)
|
||||
};
|
||||
}
|
||||
|
||||
std::array<float, 3> eval_linear_rgb_model(const std::vector<float> &features,
|
||||
const std::vector<float> &coefficients)
|
||||
{
|
||||
if (features.empty() || coefficients.size() != features.size() * 3)
|
||||
return { 0.f, 0.f, 0.f };
|
||||
|
||||
std::array<float, 3> linear { 0.f, 0.f, 0.f };
|
||||
for (size_t feature_idx = 0; feature_idx < features.size(); ++feature_idx) {
|
||||
const float feature = std::isfinite(features[feature_idx]) ? features[feature_idx] : 0.f;
|
||||
for (size_t channel = 0; channel < 3; ++channel) {
|
||||
const float coeff = coefficients[feature_idx * 3 + channel];
|
||||
if (std::isfinite(coeff))
|
||||
linear[channel] += feature * coeff;
|
||||
}
|
||||
}
|
||||
for (float &channel : linear)
|
||||
channel = clamp01(channel);
|
||||
return linear_to_srgb_color(linear);
|
||||
}
|
||||
|
||||
std::array<float, 3> eval_current_linear_affine_model(const std::array<float, 3> &base_srgb,
|
||||
const std::vector<float> &coefficients)
|
||||
{
|
||||
if (coefficients.size() != 12)
|
||||
return base_srgb;
|
||||
const std::array<float, 3> base_linear = srgb_to_linear_color(base_srgb);
|
||||
return eval_linear_rgb_model({ base_linear[0], base_linear[1], base_linear[2], 1.f }, coefficients);
|
||||
}
|
||||
|
||||
std::array<float, 3> eval_alpha_effective_model(const std::vector<uint16_t> &surface_to_deep,
|
||||
const ColorSolverCalibratedStackModel &model)
|
||||
{
|
||||
const size_t component_count = model.component_count;
|
||||
if (component_count == 0 || model.alphas.size() < component_count ||
|
||||
model.coefficients_linear_rgb.size() != (component_count + 1) * 3)
|
||||
return { 0.f, 0.f, 0.f };
|
||||
|
||||
std::vector<float> features(component_count + 1, 0.f);
|
||||
float remaining = 1.f;
|
||||
for (uint16_t component_idx : surface_to_deep) {
|
||||
const size_t component = size_t(component_idx);
|
||||
if (component >= component_count)
|
||||
continue;
|
||||
const float alpha = std::clamp(std::isfinite(model.alphas[component]) ? model.alphas[component] : 0.f, 0.f, 0.9999f);
|
||||
features[component] += remaining * alpha;
|
||||
remaining *= 1.f - alpha;
|
||||
if (remaining <= 1e-5f)
|
||||
break;
|
||||
}
|
||||
features[component_count] = std::max(0.f, remaining);
|
||||
return eval_linear_rgb_model(features, model.coefficients_linear_rgb);
|
||||
}
|
||||
|
||||
std::array<float, 3> eval_depth_kernel_linear_model(const std::vector<uint16_t> &surface_to_deep,
|
||||
const ColorSolverCalibratedStackModel &model)
|
||||
{
|
||||
const size_t component_count = model.component_count;
|
||||
const size_t kernel_count = model.taus.size();
|
||||
const size_t feature_count = 1 + component_count + component_count * kernel_count;
|
||||
if (component_count == 0 || kernel_count == 0 ||
|
||||
model.coefficients_linear_rgb.size() != feature_count * 3)
|
||||
return { 0.f, 0.f, 0.f };
|
||||
|
||||
std::vector<float> features(feature_count, 0.f);
|
||||
features[0] = 1.f;
|
||||
if (!surface_to_deep.empty() && size_t(surface_to_deep.front()) < component_count)
|
||||
features[1 + size_t(surface_to_deep.front())] = 1.f;
|
||||
|
||||
for (size_t kernel_idx = 0; kernel_idx < kernel_count; ++kernel_idx) {
|
||||
const float tau = model.taus[kernel_idx];
|
||||
const size_t base = 1 + component_count + kernel_idx * component_count;
|
||||
float norm = 0.f;
|
||||
for (size_t depth_idx = 0; depth_idx < surface_to_deep.size(); ++depth_idx) {
|
||||
const size_t component = size_t(surface_to_deep[depth_idx]);
|
||||
if (component >= component_count)
|
||||
continue;
|
||||
const float weight =
|
||||
std::isfinite(tau) && tau > 0.f ?
|
||||
std::exp(-float(depth_idx) / tau) :
|
||||
1.f;
|
||||
norm += weight;
|
||||
features[base + component] += weight;
|
||||
}
|
||||
if (norm > 1e-12f) {
|
||||
const float inv_norm = 1.f / norm;
|
||||
for (size_t component = 0; component < component_count; ++component)
|
||||
features[base + component] *= inv_norm;
|
||||
}
|
||||
}
|
||||
return eval_linear_rgb_model(features, model.coefficients_linear_rgb);
|
||||
}
|
||||
|
||||
bool calibrated_stack_model_valid_for_mix(const ColorSolverCalibratedStackModel *model,
|
||||
size_t component_count)
|
||||
{
|
||||
return model != nullptr && model->valid() &&
|
||||
(model->kind == ColorSolverCalibratedStackModelKind::CurrentLinearAffine ||
|
||||
model->component_count == component_count);
|
||||
}
|
||||
|
||||
float linear_luminance(const std::array<float, 3> &rgb)
|
||||
{
|
||||
return 0.2126f * rgb[0] + 0.7152f * rgb[1] + 0.0722f * rgb[2];
|
||||
@@ -688,17 +788,44 @@ std::array<float, 3> mix_ordered_stack_with_buffers(const std::vector<std::array
|
||||
bool beer_lambert_rgb_correction,
|
||||
bool td_effective_alpha_correction,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles,
|
||||
const std::vector<float> &layer_opacities_by_depth)
|
||||
const std::vector<float> &layer_opacities_by_depth,
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model)
|
||||
{
|
||||
if (colors_with_background.empty() || surface_to_deep.empty())
|
||||
return colors_with_background.empty() ? std::array<float, 3>{ { 0.f, 0.f, 0.f } } : colors_with_background.back();
|
||||
|
||||
const size_t component_count = colors_with_background.size() - 1;
|
||||
if (calibrated_stack_model_valid_for_mix(calibrated_stack_model, component_count)) {
|
||||
switch (calibrated_stack_model->kind) {
|
||||
case ColorSolverCalibratedStackModelKind::CurrentLinearAffine: {
|
||||
const std::array<float, 3> base =
|
||||
mix_ordered_stack_with_buffers(colors_with_background,
|
||||
weights,
|
||||
surface_to_deep,
|
||||
layer_opacities,
|
||||
mix_model,
|
||||
surface_scatter,
|
||||
beer_lambert_rgb_correction,
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
layer_opacities_by_depth,
|
||||
nullptr);
|
||||
return eval_current_linear_affine_model(base, calibrated_stack_model->coefficients_linear_rgb);
|
||||
}
|
||||
case ColorSolverCalibratedStackModelKind::AlphaEffective:
|
||||
return eval_alpha_effective_model(surface_to_deep, *calibrated_stack_model);
|
||||
case ColorSolverCalibratedStackModelKind::DepthKernelLinear:
|
||||
return eval_depth_kernel_linear_model(surface_to_deep, *calibrated_stack_model);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (td_effective_alpha_correction)
|
||||
return mix_ordered_stack_td_effective_alpha(colors_with_background, surface_to_deep, layer_opacities, 0.f, component_roles, layer_opacities_by_depth);
|
||||
if (beer_lambert_rgb_correction)
|
||||
return mix_ordered_stack_beer_lambert_rgb(colors_with_background, surface_to_deep, layer_opacities, surface_scatter, layer_opacities_by_depth);
|
||||
|
||||
const size_t component_count = colors_with_background.size() - 1;
|
||||
weights.assign(colors_with_background.size(), 0.f);
|
||||
float transmission = 1.f;
|
||||
bool surface_layer = true;
|
||||
@@ -1041,7 +1168,8 @@ void append_ordered_stack_candidate(ColorSolverOrderedStackCandidateSet &c
|
||||
bool beer_lambert_rgb_correction,
|
||||
bool td_effective_alpha_correction,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles,
|
||||
const std::vector<float> &layer_opacities_by_depth)
|
||||
const std::vector<float> &layer_opacities_by_depth,
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model)
|
||||
{
|
||||
std::vector<uint16_t> simulated_surface_to_deep;
|
||||
const std::vector<uint16_t> *mix_stack = &surface_to_deep;
|
||||
@@ -1059,7 +1187,8 @@ void append_ordered_stack_candidate(ColorSolverOrderedStackCandidateSet &c
|
||||
beer_lambert_rgb_correction,
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
layer_opacities_by_depth);
|
||||
layer_opacities_by_depth,
|
||||
calibrated_stack_model);
|
||||
const std::array<float, 3> perceptual = oklab_from_srgb(mixed);
|
||||
candidates.rgbs.emplace_back(mixed[0]);
|
||||
candidates.rgbs.emplace_back(mixed[1]);
|
||||
@@ -1087,6 +1216,43 @@ ColorSolverMode color_solver_mode_from_index(int mode)
|
||||
return ColorSolverMode(std::clamp(mode, int(ColorSolverMode::RGB), int(ColorSolverMode::OklabSoftCap4Dark4)));
|
||||
}
|
||||
|
||||
bool ColorSolverCalibratedStackModel::valid() const
|
||||
{
|
||||
switch (kind) {
|
||||
case ColorSolverCalibratedStackModelKind::CurrentLinearAffine:
|
||||
return coefficients_linear_rgb.size() == 12;
|
||||
case ColorSolverCalibratedStackModelKind::AlphaEffective:
|
||||
return component_count > 0 &&
|
||||
alphas.size() >= component_count &&
|
||||
coefficients_linear_rgb.size() == (component_count + 1) * 3;
|
||||
case ColorSolverCalibratedStackModelKind::DepthKernelLinear:
|
||||
return component_count > 0 &&
|
||||
!taus.empty() &&
|
||||
coefficients_linear_rgb.size() == (1 + component_count + component_count * taus.size()) * 3;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ColorSolverCalibratedStackModel::cache_key() const
|
||||
{
|
||||
if (!valid())
|
||||
return std::string();
|
||||
std::ostringstream key;
|
||||
key << "cal" << int(kind) << "n" << component_count;
|
||||
auto append_values = [&key](const char *label, const std::vector<float> &values) {
|
||||
key << '|' << label;
|
||||
for (float value : values) {
|
||||
const float safe = std::isfinite(value) ? std::clamp(value, -1000.f, 1000.f) : 0.f;
|
||||
key << ',' << int(std::lround(safe * 1000000.f));
|
||||
}
|
||||
};
|
||||
append_values("a", alphas);
|
||||
append_values("t", taus);
|
||||
append_values("w", coefficients_linear_rgb);
|
||||
return key.str();
|
||||
}
|
||||
|
||||
int color_solver_total_units_for_component_count(size_t component_count)
|
||||
{
|
||||
return component_count <= 4 ? 40 : (component_count == 5 ? 24 : (component_count == 6 ? 20 : 12));
|
||||
@@ -1138,7 +1304,8 @@ std::array<float, 3> mix_color_solver_ordered_stack(const std::vector<std::array
|
||||
bool beer_lambert_rgb_correction,
|
||||
bool td_effective_alpha_correction,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles,
|
||||
const std::vector<float> &layer_opacities_by_depth)
|
||||
const std::vector<float> &layer_opacities_by_depth,
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model)
|
||||
{
|
||||
if (component_colors.empty() || surface_to_deep.empty())
|
||||
return background_rgb;
|
||||
@@ -1155,7 +1322,8 @@ std::array<float, 3> mix_color_solver_ordered_stack(const std::vector<std::array
|
||||
beer_lambert_rgb_correction,
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
layer_opacities_by_depth);
|
||||
layer_opacities_by_depth,
|
||||
calibrated_stack_model);
|
||||
}
|
||||
|
||||
std::array<float, 3> color_solver_oklab_from_srgb(const std::array<float, 3> &rgb)
|
||||
@@ -1294,7 +1462,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector<std
|
||||
bool td_effective_alpha_correction,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles,
|
||||
bool beam_search_stack_expansion,
|
||||
const std::vector<float> &layer_opacities_by_depth)
|
||||
const std::vector<float> &layer_opacities_by_depth,
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model)
|
||||
{
|
||||
std::ostringstream key;
|
||||
key << component_colors.size();
|
||||
@@ -1346,6 +1515,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector<std
|
||||
key << ',' << int(std::lround(opacity * 1000000.f));
|
||||
}
|
||||
}
|
||||
if (calibrated_stack_model != nullptr && calibrated_stack_model->valid())
|
||||
key << '|' << calibrated_stack_model->cache_key();
|
||||
return key.str();
|
||||
}
|
||||
|
||||
@@ -1363,7 +1534,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
|
||||
bool td_effective_alpha_correction,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles,
|
||||
bool beam_search_stack_expansion,
|
||||
const std::vector<float> &layer_opacities_by_depth)
|
||||
const std::vector<float> &layer_opacities_by_depth,
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model)
|
||||
{
|
||||
ColorSolverOrderedStackCandidateSet candidates;
|
||||
int simulated_depth = simulated_stack_depth > 0 ? simulated_stack_depth : stack_depth;
|
||||
@@ -1405,7 +1577,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
|
||||
beer_lambert_rgb_correction,
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
layer_opacities_by_depth);
|
||||
layer_opacities_by_depth,
|
||||
calibrated_stack_model);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1459,7 +1632,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
|
||||
beer_lambert_rgb_correction,
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
layer_opacities_by_depth);
|
||||
layer_opacities_by_depth,
|
||||
calibrated_stack_model);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1491,7 +1665,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates
|
||||
bool td_effective_alpha_correction,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles,
|
||||
bool beam_search_stack_expansion,
|
||||
const std::vector<float> &layer_opacities_by_depth)
|
||||
const std::vector<float> &layer_opacities_by_depth,
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model)
|
||||
{
|
||||
const std::string key =
|
||||
color_solver_ordered_stack_candidate_cache_key(component_colors,
|
||||
@@ -1507,7 +1682,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
beam_search_stack_expansion,
|
||||
layer_opacities_by_depth);
|
||||
layer_opacities_by_depth,
|
||||
calibrated_stack_model);
|
||||
auto it = cache.find(key);
|
||||
if (it != cache.end())
|
||||
return it->second;
|
||||
@@ -1526,7 +1702,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates
|
||||
td_effective_alpha_correction,
|
||||
component_roles,
|
||||
beam_search_stack_expansion,
|
||||
layer_opacities_by_depth)).first->second;
|
||||
layer_opacities_by_depth,
|
||||
calibrated_stack_model)).first->second;
|
||||
}
|
||||
|
||||
ColorSolverOrderedStackResult solve_color_solver_ordered_stack_result_for_target(
|
||||
|
||||
@@ -55,6 +55,25 @@ enum class ColorSolverStackComponentRole : uint8_t
|
||||
White = 5
|
||||
};
|
||||
|
||||
enum class ColorSolverCalibratedStackModelKind : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
CurrentLinearAffine = 1,
|
||||
AlphaEffective = 2,
|
||||
DepthKernelLinear = 3
|
||||
};
|
||||
|
||||
struct ColorSolverCalibratedStackModel {
|
||||
ColorSolverCalibratedStackModelKind kind { ColorSolverCalibratedStackModelKind::None };
|
||||
size_t component_count { 0 };
|
||||
std::vector<float> alphas;
|
||||
std::vector<float> taus;
|
||||
std::vector<float> coefficients_linear_rgb;
|
||||
|
||||
bool valid() const;
|
||||
std::string cache_key() const;
|
||||
};
|
||||
|
||||
struct ColorSolverCandidateSet {
|
||||
struct KdNode {
|
||||
uint32_t candidate_idx { 0 };
|
||||
@@ -132,7 +151,8 @@ std::array<float, 3> mix_color_solver_ordered_stack(const std::vector<std::array
|
||||
bool beer_lambert_rgb_correction = false,
|
||||
bool td_effective_alpha_correction = false,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles = {},
|
||||
const std::vector<float> &layer_opacities_by_depth = {});
|
||||
const std::vector<float> &layer_opacities_by_depth = {},
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model = nullptr);
|
||||
std::array<float, 3> color_solver_oklab_from_srgb(const std::array<float, 3> &rgb);
|
||||
std::array<float, 3> color_solver_srgb_from_oklab(const std::array<float, 3> &oklab);
|
||||
|
||||
@@ -163,7 +183,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector<std
|
||||
bool td_effective_alpha_correction = false,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles = {},
|
||||
bool beam_search_stack_expansion = false,
|
||||
const std::vector<float> &layer_opacities_by_depth = {});
|
||||
const std::vector<float> &layer_opacities_by_depth = {},
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model = nullptr);
|
||||
ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
const std::vector<float> &layer_opacities,
|
||||
@@ -178,7 +199,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
|
||||
bool td_effective_alpha_correction = false,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles = {},
|
||||
bool beam_search_stack_expansion = false,
|
||||
const std::vector<float> &layer_opacities_by_depth = {});
|
||||
const std::vector<float> &layer_opacities_by_depth = {},
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model = nullptr);
|
||||
const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates(
|
||||
ColorSolverOrderedStackCandidateCache &cache,
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
@@ -194,7 +216,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates
|
||||
bool td_effective_alpha_correction = false,
|
||||
const std::vector<ColorSolverStackComponentRole> &component_roles = {},
|
||||
bool beam_search_stack_expansion = false,
|
||||
const std::vector<float> &layer_opacities_by_depth = {});
|
||||
const std::vector<float> &layer_opacities_by_depth = {},
|
||||
const ColorSolverCalibratedStackModel *calibrated_stack_model = nullptr);
|
||||
ColorSolverOrderedStackResult solve_color_solver_ordered_stack_result_for_target(
|
||||
const ColorSolverOrderedStackCandidateSet &candidates,
|
||||
const std::array<float, 3> &target_rgb,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <cstdio>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
@@ -484,6 +485,80 @@ static nlohmann::json floats_to_json(const std::vector<float> &values)
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::array<float, 3> color_array_from_hex(const std::string &hex)
|
||||
{
|
||||
const RGB rgb = parse_hex_color(hex);
|
||||
return { { float(rgb.r) / 255.f, float(rgb.g) / 255.f, float(rgb.b) / 255.f } };
|
||||
}
|
||||
|
||||
static std::string json_string_or_empty(const nlohmann::json &object, const std::initializer_list<const char*> &keys)
|
||||
{
|
||||
if (!object.is_object())
|
||||
return std::string();
|
||||
for (const char *key : keys) {
|
||||
auto it = object.find(key);
|
||||
if (it != object.end() && it->is_string())
|
||||
return it->get<std::string>();
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
static float json_float_or(const nlohmann::json &object, const std::initializer_list<const char*> &keys, float fallback)
|
||||
{
|
||||
if (!object.is_object())
|
||||
return fallback;
|
||||
for (const char *key : keys) {
|
||||
auto it = object.find(key);
|
||||
if (it != object.end() && it->is_number())
|
||||
return it->get<float>();
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
static std::vector<float> json_float_vector_or_empty(const nlohmann::json &object, const std::initializer_list<const char*> &keys)
|
||||
{
|
||||
if (!object.is_object())
|
||||
return {};
|
||||
for (const char *key : keys) {
|
||||
auto it = object.find(key);
|
||||
if (it != object.end())
|
||||
return floats_from_json(*it);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::vector<float> calibration_taus_from_json(const nlohmann::json &object)
|
||||
{
|
||||
std::vector<float> taus;
|
||||
if (!object.is_object())
|
||||
return taus;
|
||||
auto taus_it = object.find("taus");
|
||||
if (taus_it != object.end() && taus_it->is_array()) {
|
||||
for (const nlohmann::json &item : *taus_it)
|
||||
taus.emplace_back(item.is_number() ? item.get<float>() : -1.f);
|
||||
return taus;
|
||||
}
|
||||
auto kernels_it = object.find("depth_kernels");
|
||||
if (kernels_it != object.end() && kernels_it->is_array()) {
|
||||
for (const nlohmann::json &item : *kernels_it) {
|
||||
if (item.is_object()) {
|
||||
auto tau_it = item.find("tau_layers");
|
||||
taus.emplace_back(tau_it != item.end() && tau_it->is_number() ? tau_it->get<float>() : -1.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return taus;
|
||||
}
|
||||
|
||||
static std::string lower_ascii(std::string value)
|
||||
{
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
|
||||
const char lowered = char(std::tolower(c));
|
||||
return lowered == '-' || lowered == ' ' ? '_' : lowered;
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
static nlohmann::json point3_to_json(const std::array<float, 3> &point)
|
||||
{
|
||||
return nlohmann::json::array({ point[0], point[1], point[2] });
|
||||
@@ -737,6 +812,14 @@ static std::string top_surface_contoning_color_prediction_mode_name(int mode)
|
||||
return "rgb_beer_lambert";
|
||||
if (mode == int(TextureMappingZone::ContoningColorPredictionBasicReflectance))
|
||||
return "basic_reflectance";
|
||||
if (mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine))
|
||||
return "calibrated_current_linear_affine";
|
||||
if (mode == int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective))
|
||||
return "calibrated_td_alpha_effective";
|
||||
if (mode == int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective))
|
||||
return "calibrated_free_alpha_effective";
|
||||
if (mode == int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear))
|
||||
return "calibrated_depth_kernel_linear";
|
||||
return "default";
|
||||
}
|
||||
|
||||
@@ -754,6 +837,23 @@ static int top_surface_contoning_color_prediction_mode_from_name(std::string nam
|
||||
name == "basic_reflectance_no_td_correction" ||
|
||||
name == "no_td_correction")
|
||||
return int(TextureMappingZone::ContoningColorPredictionBasicReflectance);
|
||||
if (name == "calibrated_current_linear_affine" ||
|
||||
name == "calibrated_current_affine" ||
|
||||
name == "current_linear_affine" ||
|
||||
name == "current_linear_affine_global")
|
||||
return int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine);
|
||||
if (name == "calibrated_td_alpha_effective" ||
|
||||
name == "td_alpha_effective" ||
|
||||
name == "td_alpha_effective_global")
|
||||
return int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective);
|
||||
if (name == "calibrated_free_alpha_effective" ||
|
||||
name == "free_alpha_effective" ||
|
||||
name == "free_alpha_effective_global")
|
||||
return int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective);
|
||||
if (name == "calibrated_depth_kernel_linear" ||
|
||||
name == "depth_kernel_linear" ||
|
||||
name == "depth_kernel_linear_global")
|
||||
return int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear);
|
||||
return TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
}
|
||||
|
||||
@@ -1123,6 +1223,476 @@ bool TextureMappingGlobalSettings::is_generic_solver_color_mode(const std::strin
|
||||
return normalize_color_mode_name(mode) == "generic_solver";
|
||||
}
|
||||
|
||||
bool TextureMappingColorCalibration::has_mode(int mode) const
|
||||
{
|
||||
switch (TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(mode)) {
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine):
|
||||
return current_linear_affine.valid();
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective):
|
||||
return td_alpha_effective.valid();
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective):
|
||||
return free_alpha_effective.valid();
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear):
|
||||
return depth_kernel_linear.valid();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static const nlohmann::json *calibration_model_json(const nlohmann::json &models, const char *base_key)
|
||||
{
|
||||
if (!models.is_object())
|
||||
return nullptr;
|
||||
auto it = models.find(base_key);
|
||||
if (it != models.end())
|
||||
return &*it;
|
||||
const std::string global_key = std::string(base_key) + "_global";
|
||||
it = models.find(global_key);
|
||||
return it != models.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
static ColorSolverCalibratedStackModel calibration_current_linear_affine_model(const nlohmann::json &raw)
|
||||
{
|
||||
ColorSolverCalibratedStackModel model;
|
||||
model.kind = ColorSolverCalibratedStackModelKind::CurrentLinearAffine;
|
||||
model.coefficients_linear_rgb = raw.is_array() ?
|
||||
floats_from_json(raw) :
|
||||
json_float_vector_or_empty(raw, { "weights", "coefficients_linear_rgb" });
|
||||
if (!model.valid())
|
||||
model = {};
|
||||
return model;
|
||||
}
|
||||
|
||||
static ColorSolverCalibratedStackModel calibration_alpha_model(const nlohmann::json &raw)
|
||||
{
|
||||
ColorSolverCalibratedStackModel model;
|
||||
model.kind = ColorSolverCalibratedStackModelKind::AlphaEffective;
|
||||
model.alphas = json_float_vector_or_empty(raw, {
|
||||
"alphas",
|
||||
"alphas_at_nominal_layer_height",
|
||||
"learned_alphas"
|
||||
});
|
||||
model.component_count = model.alphas.size();
|
||||
model.coefficients_linear_rgb = json_float_vector_or_empty(raw, { "weights", "coefficients_linear_rgb" });
|
||||
if (!model.valid())
|
||||
model = {};
|
||||
return model;
|
||||
}
|
||||
|
||||
static ColorSolverCalibratedStackModel calibration_depth_kernel_model(const nlohmann::json &raw, size_t fallback_component_count)
|
||||
{
|
||||
ColorSolverCalibratedStackModel model;
|
||||
model.kind = ColorSolverCalibratedStackModelKind::DepthKernelLinear;
|
||||
model.component_count = size_t(std::max(0, raw.value("component_count", int(fallback_component_count))));
|
||||
model.taus = calibration_taus_from_json(raw);
|
||||
model.coefficients_linear_rgb = json_float_vector_or_empty(raw, { "weights", "coefficients_linear_rgb" });
|
||||
if (!model.valid())
|
||||
model = {};
|
||||
return model;
|
||||
}
|
||||
|
||||
static TextureMappingColorCalibrationFilament calibration_filament_from_json(const std::string &fallback_name,
|
||||
const nlohmann::json &raw)
|
||||
{
|
||||
TextureMappingColorCalibrationFilament filament;
|
||||
filament.name = json_string_or_empty(raw, { "name", "role", "id" });
|
||||
if (filament.name.empty())
|
||||
filament.name = fallback_name;
|
||||
const std::string hex = json_string_or_empty(raw, { "color", "display_color_hex", "display_color" });
|
||||
if (!hex.empty())
|
||||
filament.color = color_array_from_hex(hex);
|
||||
else
|
||||
filament.color = point3_from_json(raw.value("display_color_srgb", nlohmann::json::array()));
|
||||
filament.td_mm = json_float_or(raw, { "td_mm", "transmission_distance_mm" }, 0.f);
|
||||
return filament;
|
||||
}
|
||||
|
||||
static std::vector<std::string> calibration_component_order_from_json(const nlohmann::json &root)
|
||||
{
|
||||
const nlohmann::json *order = nullptr;
|
||||
auto order_it = root.find("component_order");
|
||||
if (order_it != root.end() && order_it->is_array())
|
||||
order = &*order_it;
|
||||
auto applicability_it = root.find("applicability");
|
||||
if (order == nullptr && applicability_it != root.end() && applicability_it->is_object()) {
|
||||
auto app_order_it = applicability_it->find("component_order");
|
||||
if (app_order_it != applicability_it->end() && app_order_it->is_array())
|
||||
order = &*app_order_it;
|
||||
}
|
||||
std::vector<std::string> out;
|
||||
if (order == nullptr)
|
||||
return out;
|
||||
for (const nlohmann::json &item : *order)
|
||||
if (item.is_string())
|
||||
out.emplace_back(item.get<std::string>());
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<TextureMappingColorCalibrationFilament> calibration_filaments_from_json(const nlohmann::json &root)
|
||||
{
|
||||
std::vector<TextureMappingColorCalibrationFilament> filaments;
|
||||
auto filaments_it = root.find("filaments");
|
||||
if (filaments_it == root.end())
|
||||
return filaments;
|
||||
|
||||
if (filaments_it->is_array()) {
|
||||
filaments.reserve(filaments_it->size());
|
||||
for (const nlohmann::json &item : *filaments_it)
|
||||
filaments.emplace_back(calibration_filament_from_json(std::string(), item));
|
||||
return filaments;
|
||||
}
|
||||
|
||||
if (!filaments_it->is_object())
|
||||
return filaments;
|
||||
|
||||
std::map<std::string, TextureMappingColorCalibrationFilament> by_name;
|
||||
for (auto it = filaments_it->begin(); it != filaments_it->end(); ++it)
|
||||
by_name.emplace(lower_ascii(it.key()), calibration_filament_from_json(it.key(), it.value()));
|
||||
|
||||
for (const std::string &name : calibration_component_order_from_json(root)) {
|
||||
auto it = by_name.find(lower_ascii(name));
|
||||
if (it == by_name.end())
|
||||
continue;
|
||||
filaments.emplace_back(it->second);
|
||||
by_name.erase(it);
|
||||
}
|
||||
for (const auto &item : by_name)
|
||||
filaments.emplace_back(item.second);
|
||||
return filaments;
|
||||
}
|
||||
|
||||
static const ColorSolverCalibratedStackModel &calibration_model_for_mode(const TextureMappingColorCalibration &calibration,
|
||||
int mode)
|
||||
{
|
||||
switch (TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(mode)) {
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine):
|
||||
return calibration.current_linear_affine;
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective):
|
||||
return calibration.td_alpha_effective;
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective):
|
||||
return calibration.free_alpha_effective;
|
||||
default:
|
||||
return calibration.depth_kernel_linear;
|
||||
}
|
||||
}
|
||||
|
||||
struct TextureMappingCalibrationMatch {
|
||||
std::vector<size_t> calibration_index_by_component;
|
||||
std::string warning;
|
||||
bool complete { false };
|
||||
};
|
||||
|
||||
static float calibration_color_distance(const std::array<float, 3> &lhs, const std::array<float, 3> &rhs)
|
||||
{
|
||||
const std::array<float, 3> lhs_oklab = color_solver_oklab_from_srgb(lhs);
|
||||
const std::array<float, 3> rhs_oklab = color_solver_oklab_from_srgb(rhs);
|
||||
const float dl = lhs_oklab[0] - rhs_oklab[0];
|
||||
const float da = lhs_oklab[1] - rhs_oklab[1];
|
||||
const float db = lhs_oklab[2] - rhs_oklab[2];
|
||||
return std::sqrt(dl * dl + da * da + db * db);
|
||||
}
|
||||
|
||||
static TextureMappingCalibrationMatch match_calibration_filaments(const TextureMappingColorCalibration &calibration,
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
const std::vector<float> &component_tds_mm)
|
||||
{
|
||||
TextureMappingCalibrationMatch match;
|
||||
match.calibration_index_by_component.assign(component_colors.size(), size_t(-1));
|
||||
if (component_colors.empty()) {
|
||||
match.complete = true;
|
||||
return match;
|
||||
}
|
||||
if (calibration.filaments.empty()) {
|
||||
match.warning = "The calibration file does not include filament color/TD metadata.";
|
||||
return match;
|
||||
}
|
||||
|
||||
std::vector<bool> used(calibration.filaments.size(), false);
|
||||
std::vector<std::string> notes;
|
||||
for (size_t component_idx = 0; component_idx < component_colors.size(); ++component_idx) {
|
||||
size_t best_idx = size_t(-1);
|
||||
float best_score = std::numeric_limits<float>::max();
|
||||
float best_color_distance = std::numeric_limits<float>::max();
|
||||
for (size_t calibration_idx = 0; calibration_idx < calibration.filaments.size(); ++calibration_idx) {
|
||||
if (used[calibration_idx])
|
||||
continue;
|
||||
const TextureMappingColorCalibrationFilament &filament = calibration.filaments[calibration_idx];
|
||||
const float color_distance = calibration_color_distance(component_colors[component_idx], filament.color);
|
||||
float td_score = 0.f;
|
||||
const float component_td = component_idx < component_tds_mm.size() ? component_tds_mm[component_idx] : 0.f;
|
||||
if (component_td > 0.f && filament.td_mm > 0.f)
|
||||
td_score = std::min(0.12f, std::abs(component_td - filament.td_mm) / std::max({ component_td, filament.td_mm, 1.f }) * 0.12f);
|
||||
const float score = color_distance + td_score;
|
||||
if (score < best_score) {
|
||||
best_score = score;
|
||||
best_idx = calibration_idx;
|
||||
best_color_distance = color_distance;
|
||||
}
|
||||
}
|
||||
if (best_idx == size_t(-1))
|
||||
continue;
|
||||
used[best_idx] = true;
|
||||
match.calibration_index_by_component[component_idx] = best_idx;
|
||||
|
||||
const TextureMappingColorCalibrationFilament &filament = calibration.filaments[best_idx];
|
||||
const float component_td = component_idx < component_tds_mm.size() ? component_tds_mm[component_idx] : 0.f;
|
||||
if (best_color_distance > 0.16f) {
|
||||
notes.emplace_back("component " + std::to_string(component_idx + 1) +
|
||||
" color does not closely match calibrated " + filament.name);
|
||||
}
|
||||
if (component_td > 0.f && filament.td_mm > 0.f) {
|
||||
const float td_delta = std::abs(component_td - filament.td_mm);
|
||||
if (td_delta > std::max(1.f, std::max(component_td, filament.td_mm) * 0.35f)) {
|
||||
notes.emplace_back("component " + std::to_string(component_idx + 1) +
|
||||
" TD differs from calibrated " + filament.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
match.complete = std::all_of(match.calibration_index_by_component.begin(),
|
||||
match.calibration_index_by_component.end(),
|
||||
[](size_t idx) { return idx != size_t(-1); });
|
||||
if (!match.complete)
|
||||
notes.emplace_back("the calibration file does not contain enough matching filaments");
|
||||
if (!notes.empty()) {
|
||||
match.warning = "Top-surface color calibration mismatch: ";
|
||||
for (size_t idx = 0; idx < notes.size(); ++idx) {
|
||||
if (idx > 0)
|
||||
match.warning += "; ";
|
||||
match.warning += notes[idx];
|
||||
}
|
||||
match.warning += ".";
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
static std::optional<ColorSolverCalibratedStackModel> remap_calibration_model(
|
||||
const ColorSolverCalibratedStackModel &source,
|
||||
int mode,
|
||||
const TextureMappingCalibrationMatch &match,
|
||||
size_t target_component_count)
|
||||
{
|
||||
if (!source.valid())
|
||||
return std::nullopt;
|
||||
if (TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(mode) ==
|
||||
int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine))
|
||||
return source;
|
||||
if (!match.complete || target_component_count == 0)
|
||||
return std::nullopt;
|
||||
|
||||
if (source.kind == ColorSolverCalibratedStackModelKind::AlphaEffective) {
|
||||
ColorSolverCalibratedStackModel out;
|
||||
out.kind = ColorSolverCalibratedStackModelKind::AlphaEffective;
|
||||
out.component_count = target_component_count;
|
||||
out.alphas.assign(target_component_count, 0.f);
|
||||
out.coefficients_linear_rgb.assign((target_component_count + 1) * 3, 0.f);
|
||||
const size_t source_component_count = source.component_count;
|
||||
if (source.alphas.size() < source_component_count ||
|
||||
source.coefficients_linear_rgb.size() != (source_component_count + 1) * 3)
|
||||
return std::nullopt;
|
||||
for (size_t component_idx = 0; component_idx < target_component_count; ++component_idx) {
|
||||
const size_t source_idx = match.calibration_index_by_component[component_idx];
|
||||
if (source_idx >= source_component_count)
|
||||
return std::nullopt;
|
||||
out.alphas[component_idx] = source.alphas[source_idx];
|
||||
for (size_t channel = 0; channel < 3; ++channel)
|
||||
out.coefficients_linear_rgb[component_idx * 3 + channel] = source.coefficients_linear_rgb[source_idx * 3 + channel];
|
||||
}
|
||||
for (size_t channel = 0; channel < 3; ++channel)
|
||||
out.coefficients_linear_rgb[target_component_count * 3 + channel] =
|
||||
source.coefficients_linear_rgb[source_component_count * 3 + channel];
|
||||
return out.valid() ? std::optional<ColorSolverCalibratedStackModel>(out) : std::nullopt;
|
||||
}
|
||||
|
||||
if (source.kind == ColorSolverCalibratedStackModelKind::DepthKernelLinear) {
|
||||
ColorSolverCalibratedStackModel out;
|
||||
out.kind = ColorSolverCalibratedStackModelKind::DepthKernelLinear;
|
||||
out.component_count = target_component_count;
|
||||
out.taus = source.taus;
|
||||
out.coefficients_linear_rgb.assign((1 + target_component_count + target_component_count * out.taus.size()) * 3, 0.f);
|
||||
const size_t source_component_count = source.component_count;
|
||||
if (source.coefficients_linear_rgb.size() != (1 + source_component_count + source_component_count * source.taus.size()) * 3)
|
||||
return std::nullopt;
|
||||
auto copy_row = [&source, &out](size_t dst_row, size_t src_row) {
|
||||
for (size_t channel = 0; channel < 3; ++channel)
|
||||
out.coefficients_linear_rgb[dst_row * 3 + channel] = source.coefficients_linear_rgb[src_row * 3 + channel];
|
||||
};
|
||||
copy_row(0, 0);
|
||||
for (size_t component_idx = 0; component_idx < target_component_count; ++component_idx) {
|
||||
const size_t source_idx = match.calibration_index_by_component[component_idx];
|
||||
if (source_idx >= source_component_count)
|
||||
return std::nullopt;
|
||||
copy_row(1 + component_idx, 1 + source_idx);
|
||||
}
|
||||
for (size_t tau_idx = 0; tau_idx < out.taus.size(); ++tau_idx) {
|
||||
for (size_t component_idx = 0; component_idx < target_component_count; ++component_idx) {
|
||||
const size_t source_idx = match.calibration_index_by_component[component_idx];
|
||||
if (source_idx >= source_component_count)
|
||||
return std::nullopt;
|
||||
const size_t dst_row = 1 + target_component_count + tau_idx * target_component_count + component_idx;
|
||||
const size_t src_row = 1 + source_component_count + tau_idx * source_component_count + source_idx;
|
||||
copy_row(dst_row, src_row);
|
||||
}
|
||||
}
|
||||
return out.valid() ? std::optional<ColorSolverCalibratedStackModel>(out) : std::nullopt;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<TextureMappingColorCalibration> texture_mapping_parse_top_surface_color_calibration(
|
||||
const std::string &json_text,
|
||||
std::string *error)
|
||||
{
|
||||
if (error != nullptr)
|
||||
error->clear();
|
||||
if (json_text.empty())
|
||||
return std::nullopt;
|
||||
|
||||
nlohmann::json root = nlohmann::json::parse(json_text, nullptr, false);
|
||||
if (root.is_discarded() || !root.is_object()) {
|
||||
if (error != nullptr)
|
||||
*error = "The calibration file is not valid JSON.";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TextureMappingColorCalibration calibration;
|
||||
calibration.display_name = json_string_or_empty(root, { "display_name", "name", "profile_id" });
|
||||
calibration.filaments = calibration_filaments_from_json(root);
|
||||
const nlohmann::json models = root.contains("weights") && root["weights"].is_object() ?
|
||||
root["weights"] :
|
||||
root.value("models", nlohmann::json::object());
|
||||
|
||||
if (const nlohmann::json *raw = calibration_model_json(models, "current_linear_affine"))
|
||||
calibration.current_linear_affine = calibration_current_linear_affine_model(*raw);
|
||||
if (const nlohmann::json *raw = calibration_model_json(models, "td_alpha_effective"))
|
||||
calibration.td_alpha_effective = calibration_alpha_model(*raw);
|
||||
if (const nlohmann::json *raw = calibration_model_json(models, "free_alpha_effective"))
|
||||
calibration.free_alpha_effective = calibration_alpha_model(*raw);
|
||||
if (const nlohmann::json *raw = calibration_model_json(models, "depth_kernel_linear"))
|
||||
calibration.depth_kernel_linear = calibration_depth_kernel_model(*raw, calibration.filaments.size());
|
||||
|
||||
if (!calibration.current_linear_affine.valid() &&
|
||||
!calibration.td_alpha_effective.valid() &&
|
||||
!calibration.free_alpha_effective.valid() &&
|
||||
!calibration.depth_kernel_linear.valid()) {
|
||||
if (error != nullptr)
|
||||
*error = "The calibration file does not contain any supported color prediction weights.";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return calibration;
|
||||
}
|
||||
|
||||
std::vector<int> texture_mapping_top_surface_color_calibration_supported_modes(const std::string &json_text)
|
||||
{
|
||||
std::vector<int> out;
|
||||
std::optional<TextureMappingColorCalibration> calibration =
|
||||
texture_mapping_parse_top_surface_color_calibration(json_text);
|
||||
if (!calibration)
|
||||
return out;
|
||||
const std::array<int, 4> modes = {
|
||||
int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine),
|
||||
int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective),
|
||||
int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective),
|
||||
int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear)
|
||||
};
|
||||
for (int mode : modes)
|
||||
if (calibration->has_mode(mode))
|
||||
out.emplace_back(mode);
|
||||
return out;
|
||||
}
|
||||
|
||||
int texture_mapping_top_surface_color_calibration_first_supported_mode(const std::string &json_text)
|
||||
{
|
||||
std::optional<TextureMappingColorCalibration> calibration =
|
||||
texture_mapping_parse_top_surface_color_calibration(json_text);
|
||||
if (!calibration)
|
||||
return TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
|
||||
const std::array<std::pair<int, const char *>, 4> modes = {
|
||||
std::pair<int, const char *>(int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine), "current_linear_affine"),
|
||||
std::pair<int, const char *>(int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective), "td_alpha_effective"),
|
||||
std::pair<int, const char *>(int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective), "free_alpha_effective"),
|
||||
std::pair<int, const char *>(int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear), "depth_kernel_linear")
|
||||
};
|
||||
size_t best_pos = std::numeric_limits<size_t>::max();
|
||||
int best_mode = TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
for (const auto &item : modes) {
|
||||
if (!calibration->has_mode(item.first))
|
||||
continue;
|
||||
const size_t pos = json_text.find(std::string("\"") + item.second);
|
||||
const size_t effective_pos = pos == std::string::npos ? std::numeric_limits<size_t>::max() - size_t(item.first) : pos;
|
||||
if (effective_pos < best_pos) {
|
||||
best_pos = effective_pos;
|
||||
best_mode = item.first;
|
||||
}
|
||||
}
|
||||
return best_mode;
|
||||
}
|
||||
|
||||
std::string texture_mapping_top_surface_color_calibration_display_name(const std::string &json_text)
|
||||
{
|
||||
std::optional<TextureMappingColorCalibration> calibration =
|
||||
texture_mapping_parse_top_surface_color_calibration(json_text);
|
||||
return calibration ? calibration->display_name : std::string();
|
||||
}
|
||||
|
||||
std::string texture_mapping_top_surface_color_calibration_warning(const TextureMappingZone &zone,
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
const std::vector<float> &component_tds_mm)
|
||||
{
|
||||
if (zone.top_surface_color_calibration_json.empty())
|
||||
return std::string();
|
||||
std::string error;
|
||||
std::optional<TextureMappingColorCalibration> calibration =
|
||||
texture_mapping_parse_top_surface_color_calibration(zone.top_surface_color_calibration_json, &error);
|
||||
if (!calibration)
|
||||
return error.empty() ? "Top-surface color calibration could not be loaded." : error;
|
||||
|
||||
const TextureMappingCalibrationMatch match =
|
||||
match_calibration_filaments(*calibration, component_colors, component_tds_mm);
|
||||
if (!match.warning.empty())
|
||||
return match.warning;
|
||||
|
||||
const int mode = TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(zone.top_surface_contoning_color_prediction_mode);
|
||||
if (TextureMappingZone::top_surface_contoning_color_prediction_mode_is_calibrated(mode) &&
|
||||
!calibration->has_mode(mode))
|
||||
return "The selected calibrated color prediction mode is not present in the loaded calibration file.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::optional<ColorSolverCalibratedStackModel> texture_mapping_top_surface_color_calibrated_model(
|
||||
const TextureMappingZone &zone,
|
||||
int mode,
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
const std::vector<float> &component_tds_mm,
|
||||
std::string *warning)
|
||||
{
|
||||
if (warning != nullptr)
|
||||
warning->clear();
|
||||
std::string error;
|
||||
std::optional<TextureMappingColorCalibration> calibration =
|
||||
texture_mapping_parse_top_surface_color_calibration(zone.top_surface_color_calibration_json, &error);
|
||||
if (!calibration) {
|
||||
if (warning != nullptr)
|
||||
*warning = error;
|
||||
return std::nullopt;
|
||||
}
|
||||
const ColorSolverCalibratedStackModel &source = calibration_model_for_mode(*calibration, mode);
|
||||
if (!source.valid())
|
||||
return std::nullopt;
|
||||
|
||||
const TextureMappingCalibrationMatch match =
|
||||
match_calibration_filaments(*calibration, component_colors, component_tds_mm);
|
||||
if (warning != nullptr)
|
||||
*warning = match.warning;
|
||||
return remap_calibration_model(source, mode, match, component_colors.size());
|
||||
}
|
||||
|
||||
bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
|
||||
{
|
||||
constexpr float eps = 1e-6f;
|
||||
@@ -1238,6 +1808,8 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
|
||||
top_surface_contoning_td_effective_alpha_correction_enabled == rhs.top_surface_contoning_td_effective_alpha_correction_enabled &&
|
||||
top_surface_contoning_variable_layer_height_compensation_enabled == rhs.top_surface_contoning_variable_layer_height_compensation_enabled &&
|
||||
effective_top_surface_contoning_beam_search_stack_expansion_enabled() == rhs.effective_top_surface_contoning_beam_search_stack_expansion_enabled() &&
|
||||
top_surface_color_calibration_name == rhs.top_surface_color_calibration_name &&
|
||||
top_surface_color_calibration_json == rhs.top_surface_color_calibration_json &&
|
||||
compact_offset_mode == rhs.compact_offset_mode &&
|
||||
use_legacy_fixed_color_mode == rhs.use_legacy_fixed_color_mode &&
|
||||
high_speed_image_texture_sampling == rhs.high_speed_image_texture_sampling &&
|
||||
@@ -1660,6 +2232,12 @@ std::string TextureMappingManager::serialize_entries()
|
||||
zone.top_surface_contoning_variable_layer_height_compensation_enabled;
|
||||
texture["top_surface_contoning_beam_search_stack_expansion_enabled"] =
|
||||
zone.effective_top_surface_contoning_beam_search_stack_expansion_enabled();
|
||||
if (!zone.top_surface_color_calibration_json.empty()) {
|
||||
texture["top_surface_color_calibration_name"] = zone.top_surface_color_calibration_name;
|
||||
nlohmann::json calibration = nlohmann::json::parse(zone.top_surface_color_calibration_json, nullptr, false);
|
||||
texture["top_surface_color_calibration"] =
|
||||
calibration.is_discarded() ? nlohmann::json(zone.top_surface_color_calibration_json) : std::move(calibration);
|
||||
}
|
||||
texture["compact_offset_mode"] = zone.compact_offset_mode;
|
||||
texture["use_legacy_fixed_color_mode"] = zone.use_legacy_fixed_color_mode;
|
||||
texture["high_speed_image_texture_sampling"] = true;
|
||||
@@ -1985,9 +2563,9 @@ void TextureMappingManager::load_entries(const std::string &serialized,
|
||||
top_surface_contoning_color_prediction_mode_from_name(color_prediction_mode_it->get<std::string>()) :
|
||||
clamp_int(color_prediction_mode_it != texture.end() && color_prediction_mode_it->is_number_integer() ?
|
||||
color_prediction_mode_it->get<int>() :
|
||||
TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode,
|
||||
TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode,
|
||||
int(TextureMappingZone::ContoningColorPredictionDefault),
|
||||
int(TextureMappingZone::ContoningColorPredictionBasicReflectance));
|
||||
int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear));
|
||||
zone.top_surface_contoning_beer_lambert_rgb_correction_enabled =
|
||||
TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled;
|
||||
zone.top_surface_contoning_td_effective_alpha_correction_enabled =
|
||||
@@ -1998,6 +2576,30 @@ void TextureMappingManager::load_entries(const std::string &serialized,
|
||||
zone.top_surface_contoning_beam_search_stack_expansion_enabled =
|
||||
texture.value("top_surface_contoning_beam_search_stack_expansion_enabled",
|
||||
TextureMappingZone::DefaultTopSurfaceContoningBeamSearchStackExpansionEnabled);
|
||||
zone.top_surface_color_calibration_name =
|
||||
texture.value("top_surface_color_calibration_name", std::string());
|
||||
zone.top_surface_color_calibration_json.clear();
|
||||
auto color_calibration_it = texture.find("top_surface_color_calibration");
|
||||
if (color_calibration_it != texture.end()) {
|
||||
zone.top_surface_color_calibration_json =
|
||||
color_calibration_it->is_string() ?
|
||||
color_calibration_it->get<std::string>() :
|
||||
color_calibration_it->dump();
|
||||
if (zone.top_surface_color_calibration_name.empty())
|
||||
zone.top_surface_color_calibration_name =
|
||||
texture_mapping_top_surface_color_calibration_display_name(zone.top_surface_color_calibration_json);
|
||||
}
|
||||
if (TextureMappingZone::top_surface_contoning_color_prediction_mode_is_calibrated(zone.top_surface_contoning_color_prediction_mode) &&
|
||||
!texture_mapping_parse_top_surface_color_calibration(zone.top_surface_color_calibration_json)) {
|
||||
zone.top_surface_contoning_color_prediction_mode =
|
||||
TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
} else if (TextureMappingZone::top_surface_contoning_color_prediction_mode_is_calibrated(zone.top_surface_contoning_color_prediction_mode)) {
|
||||
const std::vector<int> modes =
|
||||
texture_mapping_top_surface_color_calibration_supported_modes(zone.top_surface_color_calibration_json);
|
||||
if (std::find(modes.begin(), modes.end(), zone.top_surface_contoning_color_prediction_mode) == modes.end())
|
||||
zone.top_surface_contoning_color_prediction_mode =
|
||||
texture_mapping_top_surface_color_calibration_first_supported_mode(zone.top_surface_color_calibration_json);
|
||||
}
|
||||
zone.apply_top_surface_contoning_experimental_defaults();
|
||||
zone.compact_offset_mode = texture.value("compact_offset_mode", TextureMappingZone::DefaultCompactOffsetMode);
|
||||
zone.use_legacy_fixed_color_mode =
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ColorSolver.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct TextureMappingZone
|
||||
@@ -93,7 +96,11 @@ struct TextureMappingZone
|
||||
ContoningColorPredictionDefault = 0,
|
||||
ContoningColorPredictionTdEffectiveAlpha = 1,
|
||||
ContoningColorPredictionBeerLambertRgb = 2,
|
||||
ContoningColorPredictionBasicReflectance = 3
|
||||
ContoningColorPredictionBasicReflectance = 3,
|
||||
ContoningColorPredictionCalibratedCurrentLinearAffine = 4,
|
||||
ContoningColorPredictionCalibratedTdAlphaEffective = 5,
|
||||
ContoningColorPredictionCalibratedFreeAlphaEffective = 6,
|
||||
ContoningColorPredictionCalibratedDepthKernelLinear = 7
|
||||
};
|
||||
|
||||
enum TopSurfaceContoningPolygonizationMode : uint8_t {
|
||||
@@ -379,7 +386,14 @@ struct TextureMappingZone
|
||||
return SlicerDefaultTopSurfaceContoningColorPredictionMode;
|
||||
return std::clamp(mode,
|
||||
int(ContoningColorPredictionTdEffectiveAlpha),
|
||||
int(ContoningColorPredictionBasicReflectance));
|
||||
int(ContoningColorPredictionCalibratedDepthKernelLinear));
|
||||
}
|
||||
|
||||
static bool top_surface_contoning_color_prediction_mode_is_calibrated(int mode)
|
||||
{
|
||||
const int effective_mode = effective_top_surface_contoning_color_prediction_mode(mode);
|
||||
return effective_mode >= int(ContoningColorPredictionCalibratedCurrentLinearAffine) &&
|
||||
effective_mode <= int(ContoningColorPredictionCalibratedDepthKernelLinear);
|
||||
}
|
||||
|
||||
static bool effective_top_surface_contoning_layer_phase_enabled(bool value, bool surface_anchored_stacks)
|
||||
@@ -488,6 +502,8 @@ struct TextureMappingZone
|
||||
int top_surface_contoning_color_prediction_mode = DefaultTopSurfaceContoningColorPredictionMode;
|
||||
bool top_surface_contoning_variable_layer_height_compensation_enabled = DefaultTopSurfaceContoningVariableLayerHeightCompensationEnabled;
|
||||
bool top_surface_contoning_beam_search_stack_expansion_enabled = DefaultTopSurfaceContoningBeamSearchStackExpansionEnabled;
|
||||
std::string top_surface_color_calibration_name;
|
||||
std::string top_surface_color_calibration_json;
|
||||
bool compact_offset_mode = DefaultCompactOffsetMode;
|
||||
bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
|
||||
bool high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;
|
||||
@@ -672,7 +688,8 @@ struct TextureMappingZone
|
||||
mode == int(ContoningColorPredictionBeerLambertRgb);
|
||||
top_surface_contoning_beer_lambert_rgb_correction_enabled = beer_lambert_rgb;
|
||||
top_surface_contoning_td_effective_alpha_correction_enabled =
|
||||
mode == int(ContoningColorPredictionTdEffectiveAlpha);
|
||||
mode == int(ContoningColorPredictionTdEffectiveAlpha) ||
|
||||
mode == int(ContoningColorPredictionCalibratedCurrentLinearAffine);
|
||||
}
|
||||
|
||||
void apply_top_surface_contoning_experimental_defaults()
|
||||
@@ -782,6 +799,8 @@ struct TextureMappingZone
|
||||
top_surface_contoning_color_prediction_mode = DefaultTopSurfaceContoningColorPredictionMode;
|
||||
top_surface_contoning_variable_layer_height_compensation_enabled = DefaultTopSurfaceContoningVariableLayerHeightCompensationEnabled;
|
||||
top_surface_contoning_beam_search_stack_expansion_enabled = DefaultTopSurfaceContoningBeamSearchStackExpansionEnabled;
|
||||
top_surface_color_calibration_name.clear();
|
||||
top_surface_color_calibration_json.clear();
|
||||
compact_offset_mode = DefaultCompactOffsetMode;
|
||||
use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
|
||||
high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;
|
||||
@@ -834,6 +853,39 @@ struct TextureMappingZone
|
||||
bool operator!=(const TextureMappingZone &rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
|
||||
struct TextureMappingColorCalibrationFilament {
|
||||
std::string name;
|
||||
std::array<float, 3> color { { 0.f, 0.f, 0.f } };
|
||||
float td_mm { 0.f };
|
||||
};
|
||||
|
||||
struct TextureMappingColorCalibration {
|
||||
std::string display_name;
|
||||
std::vector<TextureMappingColorCalibrationFilament> filaments;
|
||||
ColorSolverCalibratedStackModel current_linear_affine;
|
||||
ColorSolverCalibratedStackModel td_alpha_effective;
|
||||
ColorSolverCalibratedStackModel free_alpha_effective;
|
||||
ColorSolverCalibratedStackModel depth_kernel_linear;
|
||||
|
||||
bool has_mode(int mode) const;
|
||||
};
|
||||
|
||||
std::optional<TextureMappingColorCalibration> texture_mapping_parse_top_surface_color_calibration(
|
||||
const std::string &json_text,
|
||||
std::string *error = nullptr);
|
||||
std::vector<int> texture_mapping_top_surface_color_calibration_supported_modes(const std::string &json_text);
|
||||
int texture_mapping_top_surface_color_calibration_first_supported_mode(const std::string &json_text);
|
||||
std::string texture_mapping_top_surface_color_calibration_display_name(const std::string &json_text);
|
||||
std::string texture_mapping_top_surface_color_calibration_warning(const TextureMappingZone &zone,
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
const std::vector<float> &component_tds_mm);
|
||||
std::optional<ColorSolverCalibratedStackModel> texture_mapping_top_surface_color_calibrated_model(
|
||||
const TextureMappingZone &zone,
|
||||
int mode,
|
||||
const std::vector<std::array<float, 3>> &component_colors,
|
||||
const std::vector<float> &component_tds_mm,
|
||||
std::string *warning = nullptr);
|
||||
|
||||
struct TextureMappingPrimeTowerImage
|
||||
{
|
||||
std::vector<uint8_t> rgba;
|
||||
|
||||
@@ -407,12 +407,17 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin
|
||||
{
|
||||
m_mix_model = color_solver_mix_model_from_index(zone.generic_solver_mix_model);
|
||||
m_td_adjustment_enabled = zone.top_surface_contoning_td_adjustment_enabled;
|
||||
const int effective_color_prediction_mode =
|
||||
TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(
|
||||
zone.top_surface_contoning_color_prediction_mode);
|
||||
m_td_effective_alpha_correction_enabled =
|
||||
m_td_adjustment_enabled && zone.top_surface_contoning_td_effective_alpha_correction_enabled;
|
||||
m_td_adjustment_enabled &&
|
||||
(effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha) ||
|
||||
effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine));
|
||||
m_beer_lambert_rgb_correction_enabled =
|
||||
m_td_adjustment_enabled &&
|
||||
!m_td_effective_alpha_correction_enabled &&
|
||||
zone.top_surface_contoning_beer_lambert_rgb_correction_enabled;
|
||||
effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb);
|
||||
m_variable_layer_height_compensation_enabled =
|
||||
m_td_adjustment_enabled && zone.top_surface_contoning_variable_layer_height_compensation_enabled;
|
||||
m_beam_search_stack_expansion_enabled = zone.effective_top_surface_contoning_beam_search_stack_expansion_enabled();
|
||||
@@ -439,6 +444,17 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin
|
||||
m_background_rgb = mix_color_solver_components(m_component_colors, background_weights, m_mix_model);
|
||||
m_effective_transmission_distances_mm =
|
||||
effective_transmission_distances_mm(zone, config, m_component_ids, m_td_adjustment_enabled);
|
||||
if (TextureMappingZone::top_surface_contoning_color_prediction_mode_is_calibrated(effective_color_prediction_mode)) {
|
||||
std::string warning;
|
||||
std::optional<ColorSolverCalibratedStackModel> calibrated_model =
|
||||
texture_mapping_top_surface_color_calibrated_model(zone,
|
||||
effective_color_prediction_mode,
|
||||
m_component_colors,
|
||||
m_effective_transmission_distances_mm,
|
||||
&warning);
|
||||
if (calibrated_model && calibrated_model->valid())
|
||||
m_calibrated_stack_model = *calibrated_model;
|
||||
}
|
||||
m_component_layer_opacity.reserve(m_effective_transmission_distances_mm.size());
|
||||
for (float td_mm : m_effective_transmission_distances_mm)
|
||||
m_component_layer_opacity.emplace_back(layer_opacity_from_td(td_mm > 0.f ? td_mm : 3.f, m_layer_height_mm));
|
||||
@@ -556,7 +572,8 @@ std::optional<std::array<float, 3>> TextureMappingContoningSolver::stack_rgb(
|
||||
m_beer_lambert_rgb_correction_enabled,
|
||||
m_td_effective_alpha_correction_enabled,
|
||||
m_component_roles,
|
||||
depth_layer_opacities);
|
||||
depth_layer_opacities,
|
||||
m_calibrated_stack_model.valid() ? &m_calibrated_stack_model : nullptr);
|
||||
}
|
||||
|
||||
std::vector<float> TextureMappingContoningSolver::layer_opacities_by_depth(
|
||||
@@ -690,7 +707,8 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr
|
||||
m_td_effective_alpha_correction_enabled,
|
||||
m_component_roles,
|
||||
m_beam_search_stack_expansion_enabled,
|
||||
depth_layer_opacities);
|
||||
depth_layer_opacities,
|
||||
m_calibrated_stack_model.valid() ? &m_calibrated_stack_model : nullptr);
|
||||
}
|
||||
const ColorSolverOrderedStackResult solved =
|
||||
solve_color_solver_ordered_stack_result_for_target(*ordered_candidates, target_rgb, ColorSolverMode::OklabSoftCap4Dark4);
|
||||
|
||||
@@ -77,6 +77,7 @@ private:
|
||||
bool m_td_effective_alpha_correction_enabled { false };
|
||||
bool m_variable_layer_height_compensation_enabled { false };
|
||||
bool m_beam_search_stack_expansion_enabled { false };
|
||||
ColorSolverCalibratedStackModel m_calibrated_stack_model;
|
||||
std::vector<ColorSolverStackComponentRole> m_component_roles;
|
||||
mutable std::map<int, std::vector<Candidate>> m_candidates_by_depth;
|
||||
mutable std::shared_ptr<std::mutex> m_candidate_mutex { std::make_shared<std::mutex>() };
|
||||
|
||||
@@ -106,6 +106,8 @@ struct TexturePreviewSimulationSettings
|
||||
float contoning_flat_surface_surface_scatter = k_contoning_preview_surface_scatter;
|
||||
std::array<float, 3> contoning_flat_surface_background_rgb { { 0.f, 0.f, 0.f } };
|
||||
std::vector<float> contoning_flat_surface_layer_opacities;
|
||||
std::vector<float> contoning_flat_surface_transmission_distances_mm;
|
||||
ColorSolverCalibratedStackModel contoning_flat_surface_calibrated_stack_model;
|
||||
float minimum_visibility_offset_factor = 0.f;
|
||||
std::vector<unsigned int> component_ids;
|
||||
std::vector<std::array<float, 3>> component_colors;
|
||||
@@ -588,7 +590,7 @@ float texture_preview_layer_opacity_from_td(float td_mm, float layer_height_mm)
|
||||
return std::clamp(1.f - std::pow(0.05f, 2.f * safe_layer_height / safe_td), 1e-4f, 0.9999f);
|
||||
}
|
||||
|
||||
std::vector<float> contoning_flat_surface_preview_layer_opacities(
|
||||
std::vector<float> contoning_flat_surface_preview_transmission_distances(
|
||||
const TextureMappingZone &zone,
|
||||
const TexturePreviewSimulationSettings &settings)
|
||||
{
|
||||
@@ -608,11 +610,21 @@ std::vector<float> contoning_flat_surface_preview_layer_opacities(
|
||||
td_mm = k_contoning_preview_inferred_black_td_mm;
|
||||
if (td_mm <= 0.f)
|
||||
td_mm = texture_preview_estimated_transmission_distance_mm(settings.component_colors[idx]);
|
||||
out.emplace_back(texture_preview_layer_opacity_from_td(td_mm, settings.contoning_flat_surface_layer_height_mm));
|
||||
out.emplace_back(td_mm);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<float> contoning_flat_surface_preview_layer_opacities(
|
||||
const TexturePreviewSimulationSettings &settings)
|
||||
{
|
||||
std::vector<float> out;
|
||||
out.reserve(settings.contoning_flat_surface_transmission_distances_mm.size());
|
||||
for (const float td_mm : settings.contoning_flat_surface_transmission_distances_mm)
|
||||
out.emplace_back(texture_preview_layer_opacity_from_td(td_mm, settings.contoning_flat_surface_layer_height_mm));
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<float, 2> raw_offset_visibility_factors_for_texture_preview(const TextureMappingZone &zone)
|
||||
{
|
||||
const float base_outer_width_mm =
|
||||
@@ -2610,7 +2622,11 @@ std::array<float, 3> contoning_flat_surface_rgb_for_texture_preview(
|
||||
settings.contoning_flat_surface_surface_scatter,
|
||||
settings.contoning_flat_surface_beer_lambert_rgb_correction,
|
||||
settings.contoning_flat_surface_td_effective_alpha_correction,
|
||||
settings.component_roles);
|
||||
settings.component_roles,
|
||||
std::vector<float>{},
|
||||
settings.contoning_flat_surface_calibrated_stack_model.valid() ?
|
||||
&settings.contoning_flat_surface_calibrated_stack_model :
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
if (!candidates.empty()) {
|
||||
@@ -2763,13 +2779,16 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
|
||||
settings.contoning_flat_surface_pattern_filaments = std::max(1, std::min(stack_layers, pattern_filaments));
|
||||
settings.contoning_flat_surface_td_adjustment =
|
||||
zone->top_surface_contoning_active() && zone->top_surface_contoning_td_adjustment_enabled;
|
||||
const int effective_color_prediction_mode =
|
||||
TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(
|
||||
zone->top_surface_contoning_color_prediction_mode);
|
||||
settings.contoning_flat_surface_beer_lambert_rgb_correction =
|
||||
settings.contoning_flat_surface_td_adjustment &&
|
||||
!zone->top_surface_contoning_td_effective_alpha_correction_enabled &&
|
||||
zone->top_surface_contoning_beer_lambert_rgb_correction_enabled;
|
||||
effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb);
|
||||
settings.contoning_flat_surface_td_effective_alpha_correction =
|
||||
settings.contoning_flat_surface_td_adjustment &&
|
||||
zone->top_surface_contoning_td_effective_alpha_correction_enabled;
|
||||
(effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha) ||
|
||||
effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine));
|
||||
settings.contoning_flat_surface_beam_search_stack_expansion =
|
||||
zone->effective_top_surface_contoning_beam_search_stack_expansion_enabled();
|
||||
settings.contoning_flat_surface_surface_scatter =
|
||||
@@ -2826,12 +2845,27 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
|
||||
mix_color_solver_components(settings.component_colors,
|
||||
background_weights,
|
||||
color_solver_mix_model_from_index(settings.generic_solver_mix_model));
|
||||
settings.contoning_flat_surface_transmission_distances_mm =
|
||||
contoning_flat_surface_preview_transmission_distances(*zone, settings);
|
||||
settings.contoning_flat_surface_layer_opacities =
|
||||
contoning_flat_surface_preview_layer_opacities(*zone, settings);
|
||||
contoning_flat_surface_preview_layer_opacities(settings);
|
||||
if (settings.contoning_flat_surface_layer_opacities.size() != settings.component_colors.size()) {
|
||||
settings.contoning_flat_surface_td_adjustment = false;
|
||||
settings.contoning_flat_surface_beer_lambert_rgb_correction = false;
|
||||
settings.contoning_flat_surface_td_effective_alpha_correction = false;
|
||||
} else {
|
||||
const int effective_color_prediction_mode =
|
||||
TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(
|
||||
zone->top_surface_contoning_color_prediction_mode);
|
||||
if (TextureMappingZone::top_surface_contoning_color_prediction_mode_is_calibrated(effective_color_prediction_mode)) {
|
||||
std::optional<ColorSolverCalibratedStackModel> calibrated_model =
|
||||
texture_mapping_top_surface_color_calibrated_model(*zone,
|
||||
effective_color_prediction_mode,
|
||||
settings.component_colors,
|
||||
settings.contoning_flat_surface_transmission_distances_mm);
|
||||
if (calibrated_model && calibrated_model->valid())
|
||||
settings.contoning_flat_surface_calibrated_stack_model = *calibrated_model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2888,6 +2922,8 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume,
|
||||
if (settings.contoning_flat_surface_td_effective_alpha_correction)
|
||||
for (const ColorSolverStackComponentRole role : settings.component_roles)
|
||||
mix(std::hash<int>{}(int(role)));
|
||||
if (settings.contoning_flat_surface_calibrated_stack_model.valid())
|
||||
mix(std::hash<std::string>{}(settings.contoning_flat_surface_calibrated_stack_model.cache_key()));
|
||||
}
|
||||
mix(std::hash<int>{}(int(std::lround(settings.contrast_pct * 100.f))));
|
||||
mix(std::hash<int>{}(int(std::lround(settings.tone_gamma * 1000.f))));
|
||||
@@ -2980,7 +3016,11 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
|
||||
settings.contoning_flat_surface_beer_lambert_rgb_correction,
|
||||
settings.contoning_flat_surface_td_effective_alpha_correction,
|
||||
settings.component_roles,
|
||||
settings.contoning_flat_surface_beam_search_stack_expansion) :
|
||||
settings.contoning_flat_surface_beam_search_stack_expansion,
|
||||
std::vector<float>{},
|
||||
settings.contoning_flat_surface_calibrated_stack_model.valid() ?
|
||||
&settings.contoning_flat_surface_calibrated_stack_model :
|
||||
nullptr) :
|
||||
ColorSolverOrderedStackCandidateSet{};
|
||||
const std::vector<TexturePreviewMixCandidate> contoning_flat_surface_candidates =
|
||||
use_contoning_flat_surface_quantization && contoning_flat_surface_ordered_candidates.empty() ?
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
@@ -1038,6 +1040,50 @@ static wxColour parse_texture_mapping_color(const std::string &hex)
|
||||
return wxColour(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
}
|
||||
|
||||
static std::array<float, 3> texture_mapping_color_array(const std::string &hex)
|
||||
{
|
||||
const wxColour color = parse_texture_mapping_color(hex);
|
||||
return { { float(color.Red()) / 255.f, float(color.Green()) / 255.f, float(color.Blue()) / 255.f } };
|
||||
}
|
||||
|
||||
static std::vector<std::array<float, 3>> texture_mapping_component_color_arrays(const std::vector<unsigned int> &ids,
|
||||
const std::vector<std::string> &physical_colors)
|
||||
{
|
||||
std::vector<std::array<float, 3>> out;
|
||||
out.reserve(ids.size());
|
||||
for (unsigned int id : ids) {
|
||||
if (id > 0 && size_t(id - 1) < physical_colors.size())
|
||||
out.emplace_back(texture_mapping_color_array(physical_colors[size_t(id - 1)]));
|
||||
else
|
||||
out.emplace_back(std::array<float, 3>{ { 0.5f, 0.5f, 0.5f } });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<float> texture_mapping_component_tds(const TextureMappingZone &zone,
|
||||
const std::vector<unsigned int> &ids)
|
||||
{
|
||||
std::vector<float> out;
|
||||
out.reserve(ids.size());
|
||||
for (unsigned int id : ids) {
|
||||
const size_t idx = id > 0 ? size_t(id - 1) : size_t(-1);
|
||||
out.emplace_back(idx < zone.filament_transmission_distances_mm.size() ?
|
||||
zone.filament_transmission_distances_mm[idx] :
|
||||
0.f);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static wxString texture_mapping_zone_color_calibration_warning(const TextureMappingZone &zone,
|
||||
const std::vector<unsigned int> &ids,
|
||||
const std::vector<std::string> &physical_colors)
|
||||
{
|
||||
return from_u8(texture_mapping_top_surface_color_calibration_warning(
|
||||
zone,
|
||||
texture_mapping_component_color_arrays(ids, physical_colors),
|
||||
texture_mapping_component_tds(zone, ids)));
|
||||
}
|
||||
|
||||
static std::string encode_texture_mapping_component_ids(const std::vector<unsigned int> &ids)
|
||||
{
|
||||
std::string out;
|
||||
@@ -2101,6 +2147,14 @@ static wxString texture_mapping_contoning_color_prediction_mode_label(int mode)
|
||||
return _L("RGB Beer-Lambert - not recommended");
|
||||
case int(TextureMappingZone::ContoningColorPredictionBasicReflectance):
|
||||
return _L("Basic Reflectance");
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine):
|
||||
return _L("Calibrated: current linear affine");
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective):
|
||||
return _L("Calibrated: TD alpha effective");
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective):
|
||||
return _L("Calibrated: free alpha effective");
|
||||
case int(TextureMappingZone::ContoningColorPredictionCalibratedDepthKernelLinear):
|
||||
return _L("Calibrated: depth kernel linear");
|
||||
default:
|
||||
return _L("TD Effective Alpha");
|
||||
}
|
||||
@@ -2116,34 +2170,6 @@ static wxString texture_mapping_contoning_color_prediction_default_label()
|
||||
return label;
|
||||
}
|
||||
|
||||
static int texture_mapping_contoning_color_prediction_mode_choice_selection(int mode)
|
||||
{
|
||||
if (mode == int(TextureMappingZone::ContoningColorPredictionDefault))
|
||||
return 0;
|
||||
switch (TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(mode)) {
|
||||
case int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb):
|
||||
return 2;
|
||||
case int(TextureMappingZone::ContoningColorPredictionBasicReflectance):
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int texture_mapping_contoning_color_prediction_mode_from_choice_selection(int selection)
|
||||
{
|
||||
switch (selection) {
|
||||
case 1:
|
||||
return int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha);
|
||||
case 2:
|
||||
return int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb);
|
||||
case 3:
|
||||
return int(TextureMappingZone::ContoningColorPredictionBasicReflectance);
|
||||
default:
|
||||
return TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
}
|
||||
}
|
||||
|
||||
static int texture_mapping_contoning_polygonization_mode_choice_selection(int mode)
|
||||
{
|
||||
return TextureMappingZone::effective_top_surface_contoning_polygonization_mode(mode) ==
|
||||
@@ -2164,6 +2190,7 @@ public:
|
||||
int texture_mapping_mode,
|
||||
int filament_color_mode,
|
||||
const std::vector<unsigned int> &component_ids,
|
||||
const std::vector<std::string> &component_color_hexes,
|
||||
const std::vector<float> &component_strengths_pct,
|
||||
const std::vector<float> &component_minimum_offsets_pct,
|
||||
const std::vector<float> &component_transmission_distances_mm,
|
||||
@@ -2227,6 +2254,8 @@ public:
|
||||
bool top_surface_contoning_beer_lambert_rgb_correction_enabled,
|
||||
bool top_surface_contoning_td_effective_alpha_correction_enabled,
|
||||
bool top_surface_contoning_variable_layer_height_compensation_enabled,
|
||||
const std::string &top_surface_color_calibration_name,
|
||||
const std::string &top_surface_color_calibration_json,
|
||||
const TextureMappingZoneShellUsageSummary &top_surface_contoning_shell_usage,
|
||||
const TextureMappingManager &texture_mapping_zones,
|
||||
const TextureMappingGlobalSettings &global_settings,
|
||||
@@ -2238,6 +2267,9 @@ public:
|
||||
, m_global_settings(global_settings)
|
||||
, m_prime_tower_image(prime_tower_image)
|
||||
, m_prime_tower_image_back(prime_tower_image_back)
|
||||
, m_component_color_hexes(component_color_hexes)
|
||||
, m_top_surface_color_calibration_name(top_surface_color_calibration_name)
|
||||
, m_top_surface_color_calibration_json(top_surface_color_calibration_json)
|
||||
{
|
||||
(void) reduce_outer_surface_texture;
|
||||
(void) top_surface_contoning_min_feature_mm;
|
||||
@@ -2946,20 +2978,40 @@ public:
|
||||
0,
|
||||
wxEXPAND | wxTOP | wxBOTTOM,
|
||||
gap / 2);
|
||||
auto *color_calibration_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
color_calibration_row->Add(new wxStaticText(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Top-surface color calibration")),
|
||||
0,
|
||||
wxALIGN_CENTER_VERTICAL | wxRIGHT,
|
||||
gap);
|
||||
m_top_surface_color_calibration_label =
|
||||
new wxStaticText(m_top_surface_contoning_checkboxes_panel, wxID_ANY, wxEmptyString);
|
||||
color_calibration_row->Add(m_top_surface_color_calibration_label, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
|
||||
auto *load_top_surface_color_calibration_button =
|
||||
new wxButton(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Load..."));
|
||||
auto *clear_top_surface_color_calibration_button =
|
||||
new wxButton(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Clear"));
|
||||
color_calibration_row->Add(load_top_surface_color_calibration_button, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, std::max(FromDIP(2), gap / 2));
|
||||
color_calibration_row->Add(clear_top_surface_color_calibration_button, 0, wxALIGN_CENTER_VERTICAL);
|
||||
contoning_checkboxes_root->Add(color_calibration_row, 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2);
|
||||
m_top_surface_color_calibration_warning_text =
|
||||
new wxStaticText(m_top_surface_contoning_checkboxes_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
|
||||
m_top_surface_color_calibration_warning_text->SetForegroundColour(wxColour(180, 86, 0));
|
||||
contoning_checkboxes_root->Add(m_top_surface_color_calibration_warning_text, 0, wxEXPAND | wxBOTTOM, gap / 2);
|
||||
load_top_surface_color_calibration_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
|
||||
load_top_surface_color_calibration();
|
||||
});
|
||||
clear_top_surface_color_calibration_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
|
||||
clear_top_surface_color_calibration();
|
||||
});
|
||||
auto *contoning_td_correction_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
contoning_td_correction_row->Add(new wxStaticText(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Color prediction mode")),
|
||||
0,
|
||||
wxALIGN_CENTER_VERTICAL | wxRIGHT,
|
||||
gap);
|
||||
wxArrayString contoning_td_correction_choices;
|
||||
contoning_td_correction_choices.Add(texture_mapping_contoning_color_prediction_default_label());
|
||||
contoning_td_correction_choices.Add(texture_mapping_contoning_color_prediction_mode_label(int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha)));
|
||||
contoning_td_correction_choices.Add(texture_mapping_contoning_color_prediction_mode_label(int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb)));
|
||||
contoning_td_correction_choices.Add(texture_mapping_contoning_color_prediction_mode_label(int(TextureMappingZone::ContoningColorPredictionBasicReflectance)));
|
||||
m_top_surface_contoning_td_correction_choice =
|
||||
new wxChoice(m_top_surface_contoning_checkboxes_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, contoning_td_correction_choices);
|
||||
m_top_surface_contoning_td_correction_choice->SetSelection(
|
||||
texture_mapping_contoning_color_prediction_mode_choice_selection(top_surface_contoning_color_prediction_mode));
|
||||
new wxChoice(m_top_surface_contoning_checkboxes_panel, wxID_ANY);
|
||||
rebuild_top_surface_contoning_color_prediction_choices(top_surface_contoning_color_prediction_mode);
|
||||
update_top_surface_color_calibration_label();
|
||||
contoning_td_correction_row->Add(m_top_surface_contoning_td_correction_choice, 1, wxALIGN_CENTER_VERTICAL);
|
||||
contoning_checkboxes_root->Add(contoning_td_correction_row, 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2);
|
||||
m_top_surface_contoning_variable_layer_height_compensation_checkbox =
|
||||
@@ -3202,6 +3254,7 @@ public:
|
||||
}
|
||||
if (m_top_surface_contoning_td_correction_choice != nullptr) {
|
||||
m_top_surface_contoning_td_correction_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) {
|
||||
update_top_surface_color_calibration_label();
|
||||
update_top_surface_image_options_visibility(false);
|
||||
queue_top_surface_contoning_message_update(true);
|
||||
});
|
||||
@@ -3240,9 +3293,11 @@ public:
|
||||
if (spin == nullptr)
|
||||
continue;
|
||||
spin->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent &) {
|
||||
update_top_surface_color_calibration_label();
|
||||
queue_top_surface_contoning_message_update(true);
|
||||
});
|
||||
spin->Bind(wxEVT_TEXT, [this](wxCommandEvent &) {
|
||||
update_top_surface_color_calibration_label();
|
||||
queue_top_surface_contoning_message_update(true);
|
||||
});
|
||||
}
|
||||
@@ -3743,16 +3798,27 @@ public:
|
||||
}
|
||||
int top_surface_contoning_color_prediction_mode() const
|
||||
{
|
||||
return m_top_surface_contoning_td_correction_choice == nullptr ?
|
||||
TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode :
|
||||
texture_mapping_contoning_color_prediction_mode_from_choice_selection(
|
||||
m_top_surface_contoning_td_correction_choice->GetSelection());
|
||||
if (m_top_surface_contoning_td_correction_choice == nullptr)
|
||||
return TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
const int selection = m_top_surface_contoning_td_correction_choice->GetSelection();
|
||||
return selection >= 0 && size_t(selection) < m_top_surface_contoning_color_prediction_modes.size() ?
|
||||
m_top_surface_contoning_color_prediction_modes[size_t(selection)] :
|
||||
TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode;
|
||||
}
|
||||
std::string top_surface_color_calibration_name() const
|
||||
{
|
||||
return m_top_surface_color_calibration_name;
|
||||
}
|
||||
std::string top_surface_color_calibration_json() const
|
||||
{
|
||||
return m_top_surface_color_calibration_json;
|
||||
}
|
||||
bool top_surface_contoning_td_effective_alpha_correction_selected() const
|
||||
{
|
||||
return TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(
|
||||
top_surface_contoning_color_prediction_mode()) ==
|
||||
int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha);
|
||||
const int effective_mode = TextureMappingZone::effective_top_surface_contoning_color_prediction_mode(
|
||||
top_surface_contoning_color_prediction_mode());
|
||||
return effective_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha) ||
|
||||
effective_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine);
|
||||
}
|
||||
bool top_surface_contoning_surface_scatter_enabled() const
|
||||
{
|
||||
@@ -4014,6 +4080,135 @@ private:
|
||||
update_prime_tower_image_label();
|
||||
}
|
||||
|
||||
std::vector<std::array<float, 3>> top_surface_color_calibration_component_colors() const
|
||||
{
|
||||
std::vector<std::array<float, 3>> colors;
|
||||
colors.reserve(m_component_color_hexes.size());
|
||||
for (const std::string &hex : m_component_color_hexes)
|
||||
colors.emplace_back(texture_mapping_color_array(hex));
|
||||
return colors;
|
||||
}
|
||||
|
||||
void rebuild_top_surface_contoning_color_prediction_choices(int selected_mode)
|
||||
{
|
||||
if (m_top_surface_contoning_td_correction_choice == nullptr)
|
||||
return;
|
||||
|
||||
m_top_surface_contoning_color_prediction_modes.clear();
|
||||
auto add_mode = [this](int mode) {
|
||||
m_top_surface_contoning_color_prediction_modes.emplace_back(mode);
|
||||
m_top_surface_contoning_td_correction_choice->Append(
|
||||
mode == int(TextureMappingZone::ContoningColorPredictionDefault) ?
|
||||
texture_mapping_contoning_color_prediction_default_label() :
|
||||
texture_mapping_contoning_color_prediction_mode_label(mode));
|
||||
};
|
||||
|
||||
m_top_surface_contoning_td_correction_choice->Clear();
|
||||
add_mode(int(TextureMappingZone::ContoningColorPredictionDefault));
|
||||
add_mode(int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha));
|
||||
add_mode(int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb));
|
||||
add_mode(int(TextureMappingZone::ContoningColorPredictionBasicReflectance));
|
||||
for (int mode : texture_mapping_top_surface_color_calibration_supported_modes(m_top_surface_color_calibration_json))
|
||||
add_mode(mode);
|
||||
|
||||
auto it = std::find(m_top_surface_contoning_color_prediction_modes.begin(),
|
||||
m_top_surface_contoning_color_prediction_modes.end(),
|
||||
selected_mode);
|
||||
if (it == m_top_surface_contoning_color_prediction_modes.end())
|
||||
it = std::find(m_top_surface_contoning_color_prediction_modes.begin(),
|
||||
m_top_surface_contoning_color_prediction_modes.end(),
|
||||
int(TextureMappingZone::ContoningColorPredictionDefault));
|
||||
m_top_surface_contoning_td_correction_choice->SetSelection(
|
||||
it != m_top_surface_contoning_color_prediction_modes.end() ?
|
||||
int(it - m_top_surface_contoning_color_prediction_modes.begin()) :
|
||||
0);
|
||||
}
|
||||
|
||||
void update_top_surface_color_calibration_label()
|
||||
{
|
||||
if (m_top_surface_color_calibration_label != nullptr) {
|
||||
wxString label = _L("No calibration loaded");
|
||||
wxString tooltip;
|
||||
if (!m_top_surface_color_calibration_json.empty()) {
|
||||
std::string name = m_top_surface_color_calibration_name;
|
||||
if (name.empty())
|
||||
name = texture_mapping_top_surface_color_calibration_display_name(m_top_surface_color_calibration_json);
|
||||
if (name.empty())
|
||||
name = "Loaded calibration";
|
||||
tooltip = from_u8(name);
|
||||
wxString display = from_u8(name);
|
||||
if (display.length() > 42)
|
||||
display = display.Left(19) + "..." + display.Right(20);
|
||||
label = display;
|
||||
}
|
||||
m_top_surface_color_calibration_label->SetLabel(label);
|
||||
m_top_surface_color_calibration_label->SetToolTip(tooltip);
|
||||
}
|
||||
|
||||
if (m_top_surface_color_calibration_warning_text != nullptr) {
|
||||
TextureMappingZone tmp;
|
||||
tmp.top_surface_color_calibration_name = m_top_surface_color_calibration_name;
|
||||
tmp.top_surface_color_calibration_json = m_top_surface_color_calibration_json;
|
||||
tmp.top_surface_contoning_color_prediction_mode = top_surface_contoning_color_prediction_mode();
|
||||
const wxString warning = from_u8(texture_mapping_top_surface_color_calibration_warning(
|
||||
tmp,
|
||||
top_surface_color_calibration_component_colors(),
|
||||
component_transmission_distances_mm()));
|
||||
m_top_surface_color_calibration_warning_text->SetLabel(warning);
|
||||
m_top_surface_color_calibration_warning_text->Show(!warning.empty());
|
||||
m_top_surface_color_calibration_warning_text->Wrap(FromDIP(390));
|
||||
}
|
||||
Layout();
|
||||
}
|
||||
|
||||
void load_top_surface_color_calibration()
|
||||
{
|
||||
wxFileDialog dlg(this,
|
||||
_L("Load top-surface color calibration"),
|
||||
wxEmptyString,
|
||||
wxEmptyString,
|
||||
_L("JSON files (*.json)|*.json"),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
boost::nowide::ifstream file(into_u8(dlg.GetPath()), std::ios::binary);
|
||||
if (!file) {
|
||||
MessageDialog msg(this, _L("Could not read the selected calibration file."), _L("Invalid calibration"), wxOK | wxICON_WARNING);
|
||||
msg.ShowModal();
|
||||
return;
|
||||
}
|
||||
std::ostringstream ss;
|
||||
ss << file.rdbuf();
|
||||
const std::string json_text = ss.str();
|
||||
std::string error;
|
||||
if (!texture_mapping_parse_top_surface_color_calibration(json_text, &error)) {
|
||||
MessageDialog msg(this,
|
||||
error.empty() ? _L("The selected file is not a supported top-surface color calibration.") : from_u8(error),
|
||||
_L("Invalid calibration"),
|
||||
wxOK | wxICON_WARNING);
|
||||
msg.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
m_top_surface_color_calibration_json = json_text;
|
||||
m_top_surface_color_calibration_name =
|
||||
boost::filesystem::path(into_u8(dlg.GetPath())).filename().string();
|
||||
const int mode = texture_mapping_top_surface_color_calibration_first_supported_mode(m_top_surface_color_calibration_json);
|
||||
rebuild_top_surface_contoning_color_prediction_choices(mode);
|
||||
update_top_surface_color_calibration_label();
|
||||
update_top_surface_image_options_visibility(true);
|
||||
}
|
||||
|
||||
void clear_top_surface_color_calibration()
|
||||
{
|
||||
m_top_surface_color_calibration_name.clear();
|
||||
m_top_surface_color_calibration_json.clear();
|
||||
rebuild_top_surface_contoning_color_prediction_choices(TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode);
|
||||
update_top_surface_color_calibration_label();
|
||||
update_top_surface_image_options_visibility(true);
|
||||
}
|
||||
|
||||
void reset_strengths_and_offsets()
|
||||
{
|
||||
for (wxSlider *slider : m_minimum_offset_sliders)
|
||||
@@ -4770,6 +4965,9 @@ private:
|
||||
wxCheckBox *m_top_surface_contoning_beam_search_stack_expansion_checkbox {nullptr};
|
||||
wxCheckBox *m_top_surface_contoning_surface_scatter_checkbox {nullptr};
|
||||
wxChoice *m_top_surface_contoning_td_correction_choice {nullptr};
|
||||
std::vector<int> m_top_surface_contoning_color_prediction_modes;
|
||||
wxStaticText *m_top_surface_color_calibration_label {nullptr};
|
||||
wxStaticText *m_top_surface_color_calibration_warning_text {nullptr};
|
||||
wxCheckBox *m_top_surface_contoning_variable_layer_height_compensation_checkbox {nullptr};
|
||||
wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr};
|
||||
wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr};
|
||||
@@ -4789,6 +4987,9 @@ private:
|
||||
TextureMappingGlobalSettings m_global_settings;
|
||||
TextureMappingPrimeTowerImage m_prime_tower_image;
|
||||
TextureMappingPrimeTowerImage m_prime_tower_image_back;
|
||||
std::vector<std::string> m_component_color_hexes;
|
||||
std::string m_top_surface_color_calibration_name;
|
||||
std::string m_top_surface_color_calibration_json;
|
||||
std::vector<wxSlider*> m_minimum_offset_sliders;
|
||||
std::vector<wxSpinCtrl*> m_minimum_offset_spins;
|
||||
std::vector<wxSlider*> m_strength_sliders;
|
||||
@@ -9128,6 +9329,21 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
_L("Preview Result Colors"),
|
||||
bundle->texture_mapping_global_settings.preview_simulate_colors);
|
||||
editor_sizer->Add(preview_colors_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
|
||||
auto *color_calibration_warning_text = new wxStaticText(editor, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
|
||||
color_calibration_warning_text->SetForegroundColour(wxColour(180, 86, 0));
|
||||
editor_sizer->Add(color_calibration_warning_text, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
|
||||
auto update_color_calibration_warning_text = [this, color_calibration_warning_text, num_physical, physical_colors](const TextureMappingZone &zone) {
|
||||
if (color_calibration_warning_text == nullptr)
|
||||
return;
|
||||
const std::vector<unsigned int> warning_ids = zone.is_image_texture() ?
|
||||
TextureMappingManager::effective_texture_component_ids(zone, num_physical, physical_colors) :
|
||||
texture_mapping_selected_ids(zone, num_physical);
|
||||
const wxString warning = texture_mapping_zone_color_calibration_warning(zone, warning_ids, physical_colors);
|
||||
color_calibration_warning_text->SetLabel(warning);
|
||||
color_calibration_warning_text->Show(!warning.empty());
|
||||
color_calibration_warning_text->Wrap(FromDIP(300));
|
||||
};
|
||||
update_color_calibration_warning_text(entry);
|
||||
|
||||
auto *button_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *offset_btn = new wxButton(editor, wxID_ANY, _L("2D Gradient Settings"));
|
||||
@@ -9141,7 +9357,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
linear_gradient_stops_bar, linear_gradient_mode_choice, linear_gradient_radius_spin,
|
||||
linear_gradient_radius_percent_chk,
|
||||
surface_pattern_from_selection, linear_gradient_mode_from_selection, mode_choice,
|
||||
show_direction_arrow_chk, contrast_spin, apply_zone]() {
|
||||
show_direction_arrow_chk, contrast_spin, apply_zone, update_color_calibration_warning_text]() {
|
||||
if (mgr_ptr == nullptr)
|
||||
return;
|
||||
auto &rows = mgr_ptr->zones();
|
||||
@@ -9193,6 +9409,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.show_linear_gradient_direction_arrow = show_direction_arrow_chk != nullptr && show_direction_arrow_chk->GetValue();
|
||||
updated.contrast_pct = contrast_spin != nullptr ? std::clamp(float(contrast_spin->GetValue()), 25.f, 300.f) : updated.contrast_pct;
|
||||
updated.high_resolution_sampling = true;
|
||||
update_color_calibration_warning_text(updated);
|
||||
apply_zone(std::move(updated));
|
||||
};
|
||||
|
||||
@@ -9216,7 +9433,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
}
|
||||
evt.Skip();
|
||||
});
|
||||
auto update_pattern_visibility = [this, editor_sizer, filaments_row, linear_gradient_mode_row, linear_gradient_mode_choice, linear_gradient_stops_bar, linear_gradient_row, linear_gradient_point_row, linear_gradient_buttons_row, linear_gradient_radius_row, show_direction_arrow_row, mode_row, contrast_row, preview_colors_row, offset_btn, advanced_btn, surface_choice, row, editor, update_texture_mapping_area_height, set_linear_gradient_picker_button_labels, sync_linear_gradient_stop_controls]() {
|
||||
auto update_pattern_visibility = [this, editor_sizer, filaments_row, linear_gradient_mode_row, linear_gradient_mode_choice, linear_gradient_stops_bar, linear_gradient_row, linear_gradient_point_row, linear_gradient_buttons_row, linear_gradient_radius_row, show_direction_arrow_row, mode_row, contrast_row, preview_colors_row, color_calibration_warning_text, offset_btn, advanced_btn, surface_choice, row, editor, update_texture_mapping_area_height, set_linear_gradient_picker_button_labels, sync_linear_gradient_stop_controls]() {
|
||||
const int selection = surface_choice == nullptr ? 0 : surface_choice->GetSelection();
|
||||
const bool image_texture = selection == 0;
|
||||
const bool linear_gradient = selection == 1;
|
||||
@@ -9233,6 +9450,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
editor_sizer->Show(mode_row, image_texture, true);
|
||||
editor_sizer->Show(contrast_row, image_texture, true);
|
||||
editor_sizer->Show(preview_colors_row, image_texture, true);
|
||||
if (color_calibration_warning_text != nullptr)
|
||||
editor_sizer->Show(color_calibration_warning_text, !color_calibration_warning_text->GetLabel().empty(), true);
|
||||
if (offset_btn != nullptr)
|
||||
offset_btn->Show(selection == 2);
|
||||
if (advanced_btn != nullptr)
|
||||
@@ -9306,6 +9525,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
surface_choice,
|
||||
contrast_spin,
|
||||
set_sidebar_checkbox_value,
|
||||
update_color_calibration_warning_text,
|
||||
surface_pattern_from_selection,
|
||||
apply_zone,
|
||||
last_mode_selection](wxCommandEvent &) mutable {
|
||||
@@ -9388,6 +9608,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
if (filament_checks[idx] != nullptr)
|
||||
set_sidebar_checkbox_value(filament_checks[idx],
|
||||
std::find(ids.begin(), ids.end(), unsigned(idx + 1)) != ids.end());
|
||||
update_color_calibration_warning_text(updated);
|
||||
apply_zone(std::move(updated));
|
||||
};
|
||||
mode_choice->Bind(wxEVT_COMBOBOX, on_mode_choice);
|
||||
@@ -9733,8 +9954,11 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
strengths.reserve(ids.size());
|
||||
offsets.reserve(ids.size());
|
||||
transmission_distances.reserve(ids.size());
|
||||
std::vector<std::string> component_color_hexes;
|
||||
component_color_hexes.reserve(ids.size());
|
||||
for (const unsigned int id : ids) {
|
||||
const size_t idx = id > 0 ? size_t(id - 1) : size_t(0);
|
||||
component_color_hexes.emplace_back(idx < physical_colors.size() ? physical_colors[idx] : std::string("#808080"));
|
||||
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() ?
|
||||
@@ -9747,6 +9971,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.texture_mapping_mode,
|
||||
updated.filament_color_mode,
|
||||
ids,
|
||||
component_color_hexes,
|
||||
strengths,
|
||||
offsets,
|
||||
transmission_distances,
|
||||
@@ -9810,6 +10035,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.top_surface_contoning_beer_lambert_rgb_correction_enabled,
|
||||
updated.top_surface_contoning_td_effective_alpha_correction_enabled,
|
||||
updated.top_surface_contoning_variable_layer_height_compensation_enabled,
|
||||
updated.top_surface_color_calibration_name,
|
||||
updated.top_surface_color_calibration_json,
|
||||
shell_usage,
|
||||
bundle->texture_mapping_zones,
|
||||
bundle->texture_mapping_global_settings,
|
||||
@@ -9906,6 +10133,10 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
dlg.top_surface_contoning_td_effective_alpha_correction_enabled();
|
||||
updated.top_surface_contoning_variable_layer_height_compensation_enabled =
|
||||
dlg.top_surface_contoning_variable_layer_height_compensation_enabled();
|
||||
updated.top_surface_color_calibration_name =
|
||||
dlg.top_surface_color_calibration_name();
|
||||
updated.top_surface_color_calibration_json =
|
||||
dlg.top_surface_color_calibration_json();
|
||||
if (updated.top_surface_image_printing_enabled &&
|
||||
updated.top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageContoning)) {
|
||||
updated.modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2);
|
||||
|
||||
Reference in New Issue
Block a user