Add 4x resolution option for polygon tracing. Ensure opaque filaments are sorted deeper than translucent ones even if colors are re-ordered

This commit is contained in:
sentientstardust
2026-05-28 21:12:29 +01:00
parent 29abec47b1
commit afbcab395f
6 changed files with 325 additions and 184 deletions

View File

@@ -502,7 +502,7 @@ struct TopSurfaceImageRegionPlan {
bool contoning_blue_noise_error_diffusion_enabled = false;
bool contoning_supersampled_cells_enabled = false;
bool contoning_polygonize_color_regions_enabled = false;
bool contoning_polygonize_high_resolution_enabled = TextureMappingZone::DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled;
int contoning_polygonize_resolution = TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution;
bool contoning_surface_anchored_stacks_enabled = false;
int contoning_flat_surface_infill_mode = TextureMappingZone::SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode;
};
@@ -1165,7 +1165,7 @@ struct TopSurfaceImageContoningStackPlanKey {
bool supersampled { false };
bool blue_noise { false };
bool polygonize { false };
bool polygonize_high_resolution { false };
int polygonize_resolution { 1 };
bool operator<(const TopSurfaceImageContoningStackPlanKey &rhs) const
{
@@ -1192,7 +1192,7 @@ struct TopSurfaceImageContoningStackPlanKey {
supersampled,
blue_noise,
polygonize,
polygonize_high_resolution) <
polygonize_resolution) <
std::tie(rhs.source_layer,
rhs.source_layer_id,
rhs.target_layer,
@@ -1216,7 +1216,7 @@ struct TopSurfaceImageContoningStackPlanKey {
rhs.supersampled,
rhs.blue_noise,
rhs.polygonize,
rhs.polygonize_high_resolution);
rhs.polygonize_resolution);
}
};
@@ -1343,144 +1343,21 @@ static std::optional<std::array<float, 3>> top_surface_image_contoning_stack_rgb
return mix_color_solver_components(colors, weights, ColorSolverMixModel::PigmentPainter);
}
static std::optional<std::array<float, 3>> top_surface_image_contoning_component_rgb(
unsigned int component_id,
const PrintConfig &print_config)
{
if (component_id == 0 || component_id > print_config.filament_colour.values.size())
return std::nullopt;
ColorRGB color;
if (!decode_color(print_config.filament_colour.get_at(size_t(component_id - 1)), color))
return std::nullopt;
return std::array<float, 3>{ color.r(), color.g(), color.b() };
}
static void top_surface_image_contoning_sort_stack_for_top_color(std::vector<unsigned int> &bottom_to_top,
const std::array<float, 3> &mix_rgb,
const PrintConfig &print_config)
{
if (bottom_to_top.size() < 2)
return;
const std::array<float, 3> mix_oklab = color_solver_oklab_from_srgb(mix_rgb);
std::stable_sort(bottom_to_top.begin(), bottom_to_top.end(), [&mix_oklab, &print_config](unsigned int lhs, unsigned int rhs) {
const std::optional<std::array<float, 3>> lhs_rgb =
top_surface_image_contoning_component_rgb(lhs, print_config);
const std::optional<std::array<float, 3>> rhs_rgb =
top_surface_image_contoning_component_rgb(rhs, print_config);
const float lhs_error = lhs_rgb ?
top_surface_image_contoning_oklab_error(color_solver_oklab_from_srgb(*lhs_rgb), mix_oklab) :
std::numeric_limits<float>::max();
const float rhs_error = rhs_rgb ?
top_surface_image_contoning_oklab_error(color_solver_oklab_from_srgb(*rhs_rgb), mix_oklab) :
std::numeric_limits<float>::max();
return lhs_error > rhs_error;
});
}
static bool top_surface_image_contoning_can_finish_without_repeat(const std::vector<unsigned int> &ids,
const std::vector<int> &counts,
unsigned int previous_id,
int remaining)
{
for (size_t idx = 0; idx < ids.size() && idx < counts.size(); ++idx) {
const int limit = ids[idx] == previous_id ? remaining / 2 : (remaining + 1) / 2;
if (counts[idx] > limit)
return false;
}
return true;
}
static void top_surface_image_contoning_spread_stack_repeats(std::vector<unsigned int> &bottom_to_top)
{
if (bottom_to_top.size() < 3)
return;
std::vector<unsigned int> ids;
std::vector<int> counts;
std::vector<int> ranks;
for (size_t pos = 0; pos < bottom_to_top.size(); ++pos) {
const unsigned int id = bottom_to_top[pos];
auto it = std::find(ids.begin(), ids.end(), id);
if (it == ids.end()) {
ids.emplace_back(id);
counts.emplace_back(1);
ranks.emplace_back(int(pos));
} else {
const size_t idx = size_t(it - ids.begin());
++counts[idx];
ranks[idx] = int(pos);
}
}
if (ids.size() < 2)
return;
std::vector<unsigned int> top_to_bottom;
top_to_bottom.reserve(bottom_to_top.size());
unsigned int previous_id = 0;
int remaining = int(bottom_to_top.size());
auto better_candidate = [&counts, &ranks, &ids](int lhs, int rhs) {
if (rhs < 0)
return true;
if (ranks[size_t(lhs)] != ranks[size_t(rhs)])
return ranks[size_t(lhs)] > ranks[size_t(rhs)];
if (counts[size_t(lhs)] != counts[size_t(rhs)])
return counts[size_t(lhs)] > counts[size_t(rhs)];
return ids[size_t(lhs)] < ids[size_t(rhs)];
};
auto select_candidate = [&](bool require_feasible, bool allow_repeat) {
int best = -1;
for (size_t idx = 0; idx < ids.size(); ++idx) {
if (counts[idx] <= 0 || (!allow_repeat && ids[idx] == previous_id))
continue;
--counts[idx];
const bool feasible =
top_surface_image_contoning_can_finish_without_repeat(ids, counts, ids[idx], remaining - 1);
++counts[idx];
if (require_feasible && !feasible)
continue;
if (better_candidate(int(idx), best))
best = int(idx);
}
return best;
};
while (remaining > 0) {
int selected = select_candidate(true, false);
if (selected < 0)
selected = select_candidate(false, false);
if (selected < 0)
selected = select_candidate(false, true);
if (selected < 0)
break;
top_to_bottom.emplace_back(ids[size_t(selected)]);
--counts[size_t(selected)];
previous_id = ids[size_t(selected)];
--remaining;
}
if (top_to_bottom.size() != bottom_to_top.size())
return;
std::reverse(top_to_bottom.begin(), top_to_bottom.end());
bottom_to_top = std::move(top_to_bottom);
}
static float top_surface_image_contoning_sample_pitch_mm(const TopSurfaceImageRegionPlan &plan,
const BoundingBox &bbox)
{
float pitch = std::clamp(plan.contoning_external_width_mm,
0.25f,
std::max(0.25f, plan.contoning_min_feature_mm * 0.5f));
const bool high_resolution =
plan.contoning_polygonize_color_regions_enabled &&
plan.contoning_polygonize_high_resolution_enabled;
const float min_pitch = high_resolution ? 0.125f : 0.25f;
if (high_resolution)
pitch = std::max(min_pitch, pitch * 0.5f);
const int polygonize_resolution = plan.contoning_polygonize_color_regions_enabled ?
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(plan.contoning_polygonize_resolution) :
1;
const float min_pitch = 0.25f / float(polygonize_resolution);
if (polygonize_resolution > 1)
pitch = std::max(min_pitch, pitch / float(polygonize_resolution));
const double width_mm = unscale<double>(bbox.max.x() - bbox.min.x());
const double height_mm = unscale<double>(bbox.max.y() - bbox.min.y());
const double max_samples = high_resolution ? 2600000.0 : 650000.0;
const double max_samples = 650000.0 * double(polygonize_resolution) * double(polygonize_resolution);
if (width_mm > 0.0 && height_mm > 0.0) {
const double estimated = std::ceil(width_mm / double(pitch)) * std::ceil(height_mm / double(pitch));
if (estimated > max_samples)
@@ -2103,8 +1980,6 @@ static std::optional<TopSurfaceImageContoningSolvedLabel> top_surface_image_cont
top_surface_image_contoning_stack_rgb(stack.bottom_to_top, print_config);
if (!stack_rgb)
return std::nullopt;
top_surface_image_contoning_sort_stack_for_top_color(stack.bottom_to_top, *stack_rgb, print_config);
top_surface_image_contoning_spread_stack_repeats(stack.bottom_to_top);
auto label_it = label_by_stack.find(stack.bottom_to_top);
int label = -1;
if (label_it == label_by_stack.end()) {
@@ -2592,9 +2467,9 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl
key.supersampled = plan.contoning_supersampled_cells_enabled;
key.blue_noise = use_blue_noise_error_diffusion;
key.polygonize = plan.contoning_polygonize_color_regions_enabled;
key.polygonize_high_resolution =
plan.contoning_polygonize_color_regions_enabled &&
plan.contoning_polygonize_high_resolution_enabled;
key.polygonize_resolution = plan.contoning_polygonize_color_regions_enabled ?
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(plan.contoning_polygonize_resolution) :
1;
return key;
}
@@ -3243,8 +3118,8 @@ static std::vector<TopSurfaceImageRegionPlan> top_surface_image_region_plans(
plan.contoning_blue_noise_error_diffusion_enabled = zone->top_surface_contoning_blue_noise_error_diffusion_enabled;
plan.contoning_supersampled_cells_enabled = zone->top_surface_contoning_supersampled_cells_enabled;
plan.contoning_polygonize_color_regions_enabled = zone->top_surface_contoning_polygonize_color_regions_enabled;
plan.contoning_polygonize_high_resolution_enabled =
zone->top_surface_contoning_polygonize_high_resolution_enabled;
plan.contoning_polygonize_resolution =
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(zone->top_surface_contoning_polygonize_resolution);
plan.contoning_surface_anchored_stacks_enabled =
zone->effective_top_surface_contoning_surface_anchored_stacks_enabled();
const TextureMappingContoningSolver contoning_solver(*zone, print_config, components);

View File

@@ -1145,7 +1145,8 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
top_surface_contoning_blue_noise_error_diffusion_enabled == rhs.top_surface_contoning_blue_noise_error_diffusion_enabled &&
top_surface_contoning_supersampled_cells_enabled == rhs.top_surface_contoning_supersampled_cells_enabled &&
top_surface_contoning_polygonize_color_regions_enabled == rhs.top_surface_contoning_polygonize_color_regions_enabled &&
top_surface_contoning_polygonize_high_resolution_enabled == rhs.top_surface_contoning_polygonize_high_resolution_enabled &&
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(top_surface_contoning_polygonize_resolution) ==
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(rhs.top_surface_contoning_polygonize_resolution) &&
effective_top_surface_contoning_surface_anchored_stacks_enabled() == rhs.effective_top_surface_contoning_surface_anchored_stacks_enabled() &&
compact_offset_mode == rhs.compact_offset_mode &&
use_legacy_fixed_color_mode == rhs.use_legacy_fixed_color_mode &&
@@ -1543,8 +1544,8 @@ std::string TextureMappingManager::serialize_entries()
zone.top_surface_contoning_supersampled_cells_enabled;
texture["top_surface_contoning_polygonize_color_regions_enabled"] =
zone.top_surface_contoning_polygonize_color_regions_enabled;
texture["top_surface_contoning_polygonize_high_resolution_enabled"] =
zone.top_surface_contoning_polygonize_high_resolution_enabled;
texture["top_surface_contoning_polygonize_resolution"] =
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(zone.top_surface_contoning_polygonize_resolution);
texture["top_surface_contoning_surface_anchored_stacks_enabled"] =
zone.effective_top_surface_contoning_surface_anchored_stacks_enabled();
texture["compact_offset_mode"] = zone.compact_offset_mode;
@@ -1834,9 +1835,12 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.top_surface_contoning_polygonize_color_regions_enabled =
texture.value("top_surface_contoning_polygonize_color_regions_enabled",
TextureMappingZone::DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled);
zone.top_surface_contoning_polygonize_high_resolution_enabled =
texture.value("top_surface_contoning_polygonize_high_resolution_enabled",
TextureMappingZone::DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled);
auto polygonize_resolution_it = texture.find("top_surface_contoning_polygonize_resolution");
zone.top_surface_contoning_polygonize_resolution =
TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(
polygonize_resolution_it != texture.end() && polygonize_resolution_it->is_number_integer() ?
polygonize_resolution_it->get<int>() :
TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution);
zone.top_surface_contoning_surface_anchored_stacks_enabled =
texture.value("top_surface_contoning_surface_anchored_stacks_enabled",
TextureMappingZone::DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled);

View File

@@ -202,7 +202,7 @@ struct TextureMappingZone
static constexpr bool DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled = false;
static constexpr bool DefaultTopSurfaceContoningSupersampledCellsEnabled = false;
static constexpr bool DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled = false;
static constexpr bool DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled = true;
static constexpr int DefaultTopSurfaceContoningPolygonizeResolution = 2;
static constexpr bool DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled = false;
static constexpr bool DefaultCompactOffsetMode = true;
static constexpr bool DefaultUseLegacyFixedColorMode = false;
@@ -278,6 +278,11 @@ struct TextureMappingZone
return ShowExperimentalTopSurfaceContoningOptions && value;
}
static constexpr int normalize_top_surface_contoning_polygonize_resolution(int value)
{
return value <= 1 ? 1 : (value >= 4 ? 4 : 2);
}
struct LinearGradientAnchor {
bool valid = false;
size_t object_id = 0;
@@ -355,7 +360,7 @@ struct TextureMappingZone
bool top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled;
bool top_surface_contoning_supersampled_cells_enabled = DefaultTopSurfaceContoningSupersampledCellsEnabled;
bool top_surface_contoning_polygonize_color_regions_enabled = DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled;
bool top_surface_contoning_polygonize_high_resolution_enabled = DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled;
int top_surface_contoning_polygonize_resolution = DefaultTopSurfaceContoningPolygonizeResolution;
bool top_surface_contoning_surface_anchored_stacks_enabled = DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled;
bool compact_offset_mode = DefaultCompactOffsetMode;
bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
@@ -546,7 +551,7 @@ struct TextureMappingZone
top_surface_contoning_blue_noise_error_diffusion_enabled = DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled;
top_surface_contoning_supersampled_cells_enabled = DefaultTopSurfaceContoningSupersampledCellsEnabled;
top_surface_contoning_polygonize_color_regions_enabled = DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled;
top_surface_contoning_polygonize_high_resolution_enabled = DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled;
top_surface_contoning_polygonize_resolution = DefaultTopSurfaceContoningPolygonizeResolution;
top_surface_contoning_surface_anchored_stacks_enabled = DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled;
compact_offset_mode = DefaultCompactOffsetMode;
use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;

View File

@@ -15,6 +15,9 @@
namespace Slic3r {
namespace {
constexpr float OPAQUE_CONTONING_TD_THRESHOLD_MM = 0.5f;
constexpr float INFERRED_BLACK_TD_MM = 0.1f;
float clamp01(float value)
{
if (!std::isfinite(value))
@@ -31,6 +34,81 @@ float filament_luminance(const PrintConfig &config, unsigned int component_id)
return 0.2126f * color.r() + 0.7152f * color.g() + 0.0722f * color.b();
}
bool explicit_transmission_distance_mm(const TextureMappingZone &zone, unsigned int component_id, float &td_mm)
{
if (component_id == 0)
return false;
const size_t idx = size_t(component_id - 1);
if (idx >= zone.filament_transmission_distances_mm.size())
return false;
const float td = zone.filament_transmission_distances_mm[idx];
if (!std::isfinite(td) || td <= 0.f)
return false;
td_mm = td;
return true;
}
bool black_role_component(int filament_color_mode, size_t component_idx, size_t component_count)
{
switch (std::clamp(filament_color_mode,
int(TextureMappingZone::FilamentColorAny),
int(TextureMappingZone::FilamentColorRGBKW))) {
case int(TextureMappingZone::FilamentColorCMYK):
case int(TextureMappingZone::FilamentColorRGBK):
return component_count == 4 && component_idx == 3;
case int(TextureMappingZone::FilamentColorBW):
return component_count == 2 && component_idx == 0;
case int(TextureMappingZone::FilamentColorCMYKW):
case int(TextureMappingZone::FilamentColorRGBKW):
return component_count == 5 && component_idx == 3;
default:
return false;
}
}
bool is_black_color_filament(const PrintConfig &config, unsigned int component_id)
{
ColorRGB color;
if (component_id == 0 || component_id > config.filament_colour.values.size() ||
!decode_color(config.filament_colour.get_at(size_t(component_id - 1)), color))
return false;
const float r = clamp01(color.r());
const float g = clamp01(color.g());
const float b = clamp01(color.b());
const float max_channel = std::max({ r, g, b });
const float min_channel = std::min({ r, g, b });
const float luminance = 0.2126f * r + 0.7152f * g + 0.0722f * b;
return max_channel <= 0.20f && luminance <= 0.12f && max_channel - min_channel <= 0.08f;
}
std::vector<float> effective_transmission_distances_mm(const TextureMappingZone &zone,
const PrintConfig &config,
const std::vector<unsigned int> &component_ids)
{
std::vector<float> out(component_ids.size(), 0.f);
for (size_t idx = 0; idx < component_ids.size(); ++idx) {
float td_mm = 0.f;
if (explicit_transmission_distance_mm(zone, component_ids[idx], td_mm)) {
out[idx] = td_mm;
} else if (black_role_component(zone.filament_color_mode, idx, component_ids.size()) ||
is_black_color_filament(config, component_ids[idx])) {
out[idx] = INFERRED_BLACK_TD_MM;
}
}
return out;
}
float effective_transmission_distance_for_component(const std::vector<unsigned int> &component_ids,
const std::vector<float> &effective_tds,
unsigned int component_id)
{
const auto it = std::find(component_ids.begin(), component_ids.end(), component_id);
if (it == component_ids.end())
return 0.f;
const size_t idx = size_t(it - component_ids.begin());
return idx < effective_tds.size() ? effective_tds[idx] : 0.f;
}
float perceptual_error(const std::array<float, 3> &lhs, const std::array<float, 3> &rhs)
{
const float dl = lhs[0] - rhs[0];
@@ -55,6 +133,101 @@ void enumerate_counts(size_t component_idx,
}
}
bool can_finish_without_repeat(const std::vector<unsigned int> &ids,
const std::vector<int> &counts,
unsigned int previous_id,
int remaining)
{
for (size_t idx = 0; idx < ids.size() && idx < counts.size(); ++idx) {
const int limit = ids[idx] == previous_id ? remaining / 2 : (remaining + 1) / 2;
if (counts[idx] > limit)
return false;
}
return true;
}
void spread_surface_repeats(std::vector<unsigned int> &surface_to_deep)
{
if (surface_to_deep.size() < 3)
return;
std::vector<unsigned int> ids;
std::vector<int> counts;
std::vector<int> ranks;
ids.reserve(surface_to_deep.size());
counts.reserve(surface_to_deep.size());
ranks.reserve(surface_to_deep.size());
for (size_t pos = 0; pos < surface_to_deep.size(); ++pos) {
const unsigned int id = surface_to_deep[pos];
auto it = std::find(ids.begin(), ids.end(), id);
if (it == ids.end()) {
ids.emplace_back(id);
counts.emplace_back(1);
ranks.emplace_back(int(pos));
} else {
++counts[size_t(it - ids.begin())];
}
}
if (ids.size() < 2)
return;
std::vector<unsigned int> reordered;
reordered.reserve(surface_to_deep.size());
unsigned int previous_id = surface_to_deep.front();
int remaining = int(surface_to_deep.size());
const auto first_it = std::find(ids.begin(), ids.end(), previous_id);
if (first_it != ids.end()) {
const size_t first_idx = size_t(first_it - ids.begin());
reordered.emplace_back(previous_id);
--counts[first_idx];
--remaining;
}
auto better_candidate = [&counts, &ranks, &ids](int lhs, int rhs) {
if (rhs < 0)
return true;
if (ranks[size_t(lhs)] != ranks[size_t(rhs)])
return ranks[size_t(lhs)] < ranks[size_t(rhs)];
if (counts[size_t(lhs)] != counts[size_t(rhs)])
return counts[size_t(lhs)] > counts[size_t(rhs)];
return ids[size_t(lhs)] < ids[size_t(rhs)];
};
auto select_candidate = [&](bool require_feasible, bool allow_repeat) {
int best = -1;
for (size_t idx = 0; idx < ids.size(); ++idx) {
if (counts[idx] <= 0 || (!allow_repeat && ids[idx] == previous_id))
continue;
--counts[idx];
const bool feasible =
can_finish_without_repeat(ids, counts, ids[idx], remaining - 1);
++counts[idx];
if (require_feasible && !feasible)
continue;
if (better_candidate(int(idx), best))
best = int(idx);
}
return best;
};
while (remaining > 0) {
int selected = select_candidate(true, false);
if (selected < 0)
selected = select_candidate(false, false);
if (selected < 0)
selected = select_candidate(false, true);
if (selected < 0)
break;
reordered.emplace_back(ids[size_t(selected)]);
--counts[size_t(selected)];
previous_id = ids[size_t(selected)];
--remaining;
}
if (reordered.size() == surface_to_deep.size())
surface_to_deep = std::move(reordered);
}
}
std::optional<std::array<float, 3>> texture_mapping_contoning_component_colors(
@@ -89,26 +262,27 @@ std::vector<unsigned int> texture_mapping_contoning_components_bottom_to_top(
if (component_ids.empty())
return component_ids;
bool has_complete_td = true;
for (unsigned int component_id : component_ids) {
const size_t idx = size_t(component_id - 1);
const float td = idx < zone.filament_transmission_distances_mm.size() ?
zone.filament_transmission_distances_mm[idx] :
0.f;
if (!std::isfinite(td) || td <= 0.f) {
has_complete_td = false;
break;
}
}
const std::vector<unsigned int> td_component_ids = component_ids;
const std::vector<float> effective_tds = effective_transmission_distances_mm(zone, config, td_component_ids);
const bool has_complete_td =
std::all_of(effective_tds.begin(), effective_tds.end(), [](float td) { return std::isfinite(td) && td > 0.f; });
if (has_complete_td) {
std::stable_sort(component_ids.begin(), component_ids.end(), [&zone](unsigned int lhs, unsigned int rhs) {
return zone.filament_transmission_distances_mm[size_t(lhs - 1)] <
zone.filament_transmission_distances_mm[size_t(rhs - 1)];
std::stable_sort(component_ids.begin(), component_ids.end(), [&effective_tds, &td_component_ids](unsigned int lhs, unsigned int rhs) {
return effective_transmission_distance_for_component(td_component_ids, effective_tds, lhs) <
effective_transmission_distance_for_component(td_component_ids, effective_tds, rhs);
});
return component_ids;
}
std::stable_sort(component_ids.begin(), component_ids.end(), [&config](unsigned int lhs, unsigned int rhs) {
std::stable_sort(component_ids.begin(), component_ids.end(), [&config, &effective_tds, &td_component_ids](unsigned int lhs, unsigned int rhs) {
const float lhs_td = effective_transmission_distance_for_component(td_component_ids, effective_tds, lhs);
const float rhs_td = effective_transmission_distance_for_component(td_component_ids, effective_tds, rhs);
const bool lhs_opaque = lhs_td > 0.f && lhs_td < OPAQUE_CONTONING_TD_THRESHOLD_MM;
const bool rhs_opaque = rhs_td > 0.f && rhs_td < OPAQUE_CONTONING_TD_THRESHOLD_MM;
if (lhs_opaque != rhs_opaque)
return lhs_opaque;
if (lhs_opaque && std::abs(lhs_td - rhs_td) > 1e-6f)
return lhs_td < rhs_td;
return filament_luminance(config, lhs) < filament_luminance(config, rhs);
});
return component_ids;
@@ -168,6 +342,7 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin
m_component_luminance.reserve(m_component_ids.size());
for (const unsigned int id : m_component_ids)
m_component_luminance.emplace_back(filament_luminance(config, id));
m_effective_transmission_distances_mm = effective_transmission_distances_mm(zone, config, m_component_ids);
m_components_bottom_to_top = texture_mapping_contoning_components_bottom_to_top(zone, config, m_component_ids);
}
@@ -206,6 +381,71 @@ TextureMappingContoningSolver::candidates_for_depth(int stack_layers) const
return m_candidates_by_depth.emplace(depth, std::move(candidates)).first->second;
}
void TextureMappingContoningSolver::arrange_stack_for_light_path(std::vector<unsigned int> &bottom_to_top,
const std::array<float, 3> &target_rgb) const
{
if (bottom_to_top.size() < 2)
return;
const std::array<float, 3> target_oklab = color_solver_oklab_from_srgb(target_rgb);
auto component_error = [this, &target_oklab](unsigned int component_id) {
const auto it = std::find(m_component_ids.begin(), m_component_ids.end(), component_id);
if (it == m_component_ids.end())
return std::numeric_limits<float>::max();
const size_t idx = size_t(it - m_component_ids.begin());
if (idx >= m_component_colors.size())
return std::numeric_limits<float>::max();
return perceptual_error(color_solver_oklab_from_srgb(m_component_colors[idx]), target_oklab);
};
std::stable_sort(bottom_to_top.begin(), bottom_to_top.end(), [&component_error](unsigned int lhs, unsigned int rhs) {
return component_error(lhs) > component_error(rhs);
});
std::vector<unsigned int> surface_to_deep;
surface_to_deep.reserve(bottom_to_top.size());
for (auto it = bottom_to_top.rbegin(); it != bottom_to_top.rend(); ++it)
surface_to_deep.emplace_back(*it);
struct OpaqueItem {
unsigned int component_id = 0;
float td_mm = 0.f;
size_t order = 0;
};
std::vector<unsigned int> translucent;
std::vector<OpaqueItem> opaque;
translucent.reserve(surface_to_deep.size());
opaque.reserve(surface_to_deep.size());
for (size_t idx = 0; idx < surface_to_deep.size(); ++idx) {
const unsigned int component_id = surface_to_deep[idx];
const float td_mm =
effective_transmission_distance_for_component(m_component_ids, m_effective_transmission_distances_mm, component_id);
if (td_mm > 0.f && td_mm < OPAQUE_CONTONING_TD_THRESHOLD_MM)
opaque.push_back({ component_id, td_mm, idx });
else
translucent.emplace_back(component_id);
}
spread_surface_repeats(translucent);
std::stable_sort(opaque.begin(), opaque.end(), [](const OpaqueItem &lhs, const OpaqueItem &rhs) {
if (std::abs(lhs.td_mm - rhs.td_mm) > 1e-6f)
return lhs.td_mm > rhs.td_mm;
return lhs.order < rhs.order;
});
surface_to_deep.clear();
surface_to_deep.reserve(translucent.size() + opaque.size());
surface_to_deep.insert(surface_to_deep.end(), translucent.begin(), translucent.end());
for (const OpaqueItem &item : opaque)
surface_to_deep.emplace_back(item.component_id);
bottom_to_top.clear();
bottom_to_top.reserve(surface_to_deep.size());
for (auto it = surface_to_deep.rbegin(); it != surface_to_deep.rend(); ++it)
bottom_to_top.emplace_back(*it);
}
TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::array<float, 3> &target_rgb, int stack_layers) const
{
TextureMappingContoningStack out;
@@ -251,6 +491,7 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr
out.bottom_to_top.emplace_back(m_components_bottom_to_top.empty() ? m_component_ids.front() : m_components_bottom_to_top.back());
if (int(out.bottom_to_top.size()) > depth)
out.bottom_to_top.resize(size_t(depth));
arrange_stack_for_light_path(out.bottom_to_top, target_rgb);
return out;
}

View File

@@ -42,11 +42,14 @@ private:
};
const std::vector<Candidate>& candidates_for_depth(int stack_layers) const;
void arrange_stack_for_light_path(std::vector<unsigned int> &bottom_to_top,
const std::array<float, 3> &target_rgb) const;
std::vector<unsigned int> m_component_ids;
std::vector<unsigned int> m_components_bottom_to_top;
std::vector<std::array<float, 3>> m_component_colors;
std::vector<float> m_component_luminance;
std::vector<float> m_effective_transmission_distances_mm;
mutable std::map<int, std::vector<Candidate>> m_candidates_by_depth;
};

View File

@@ -1982,7 +1982,7 @@ public:
bool top_surface_contoning_blue_noise_error_diffusion_enabled,
bool top_surface_contoning_supersampled_cells_enabled,
bool top_surface_contoning_polygonize_color_regions_enabled,
bool top_surface_contoning_polygonize_high_resolution_enabled,
int top_surface_contoning_polygonize_resolution,
bool top_surface_contoning_surface_anchored_stacks_enabled,
const TextureMappingManager &texture_mapping_zones,
const TextureMappingGlobalSettings &global_settings,
@@ -2769,12 +2769,23 @@ public:
0,
wxEXPAND | wxTOP | wxBOTTOM,
gap / 2);
m_top_surface_contoning_polygonize_high_resolution_checkbox =
new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("High-resolution polygon tracing"));
m_top_surface_contoning_polygonize_high_resolution_checkbox->SetValue(top_surface_contoning_polygonize_high_resolution_enabled);
m_top_surface_contoning_polygonize_high_resolution_checkbox->SetMinSize(
wxSize(-1, std::max(m_top_surface_contoning_polygonize_high_resolution_checkbox->GetBestSize().GetHeight(), FromDIP(24))));
contoning_checkboxes_root->Add(m_top_surface_contoning_polygonize_high_resolution_checkbox,
m_top_surface_contoning_polygonize_resolution_panel = new wxPanel(m_top_surface_contoning_checkboxes_panel, wxID_ANY);
auto *contoning_polygonize_resolution_row = new wxBoxSizer(wxHORIZONTAL);
m_top_surface_contoning_polygonize_resolution_panel->SetSizer(contoning_polygonize_resolution_row);
contoning_polygonize_resolution_row->Add(new wxStaticText(m_top_surface_contoning_polygonize_resolution_panel, wxID_ANY, _L("Polygon tracing resolution")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
wxArrayString contoning_polygonize_resolution_choices;
contoning_polygonize_resolution_choices.Add(_L("1x"));
contoning_polygonize_resolution_choices.Add(_L("2x"));
contoning_polygonize_resolution_choices.Add(_L("4x (slow)"));
m_top_surface_contoning_polygonize_resolution_choice =
new wxChoice(m_top_surface_contoning_polygonize_resolution_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, contoning_polygonize_resolution_choices);
const int polygonize_resolution = TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(top_surface_contoning_polygonize_resolution);
m_top_surface_contoning_polygonize_resolution_choice->SetSelection(polygonize_resolution == 1 ? 0 : (polygonize_resolution == 4 ? 2 : 1));
contoning_polygonize_resolution_row->Add(m_top_surface_contoning_polygonize_resolution_choice, 1, wxEXPAND);
contoning_checkboxes_root->Add(m_top_surface_contoning_polygonize_resolution_panel,
0,
wxEXPAND | wxTOP | wxBOTTOM,
gap / 2);
@@ -3239,11 +3250,12 @@ public:
return m_top_surface_contoning_polygonize_color_regions_checkbox != nullptr &&
m_top_surface_contoning_polygonize_color_regions_checkbox->GetValue();
}
bool top_surface_contoning_polygonize_high_resolution_enabled() const
int top_surface_contoning_polygonize_resolution() const
{
return m_top_surface_contoning_polygonize_high_resolution_checkbox == nullptr ?
TextureMappingZone::DefaultTopSurfaceContoningPolygonizeHighResolutionEnabled :
m_top_surface_contoning_polygonize_high_resolution_checkbox->GetValue();
if (m_top_surface_contoning_polygonize_resolution_choice == nullptr)
return TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution;
const int selection = m_top_surface_contoning_polygonize_resolution_choice->GetSelection();
return selection == 0 ? 1 : (selection == 2 ? 4 : 2);
}
bool top_surface_contoning_surface_anchored_stacks_enabled() const
{
@@ -3757,10 +3769,10 @@ private:
contoning &&
m_top_surface_contoning_polygonize_color_regions_checkbox != nullptr &&
m_top_surface_contoning_polygonize_color_regions_checkbox->GetValue();
if (m_top_surface_contoning_polygonize_high_resolution_checkbox != nullptr) {
m_top_surface_contoning_polygonize_high_resolution_checkbox->Show(polygonize_color_regions);
m_top_surface_contoning_polygonize_high_resolution_checkbox->Enable(polygonize_color_regions);
}
if (m_top_surface_contoning_polygonize_resolution_panel != nullptr)
m_top_surface_contoning_polygonize_resolution_panel->Show(polygonize_color_regions);
if (m_top_surface_contoning_polygonize_resolution_choice != nullptr)
m_top_surface_contoning_polygonize_resolution_choice->Enable(polygonize_color_regions);
if (m_top_surface_contoning_surface_anchored_stacks_checkbox != nullptr) {
m_top_surface_contoning_surface_anchored_stacks_checkbox->Show(contoning);
m_top_surface_contoning_surface_anchored_stacks_checkbox->Enable(contoning);
@@ -3839,7 +3851,8 @@ private:
wxCheckBox *m_top_surface_contoning_blue_noise_error_diffusion_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_supersampled_cells_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_polygonize_color_regions_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_polygonize_high_resolution_checkbox {nullptr};
wxPanel *m_top_surface_contoning_polygonize_resolution_panel {nullptr};
wxChoice *m_top_surface_contoning_polygonize_resolution_choice {nullptr};
wxCheckBox *m_top_surface_contoning_surface_anchored_stacks_checkbox {nullptr};
wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr};
wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr};
@@ -8863,7 +8876,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.top_surface_contoning_blue_noise_error_diffusion_enabled,
updated.top_surface_contoning_supersampled_cells_enabled,
updated.top_surface_contoning_polygonize_color_regions_enabled,
updated.top_surface_contoning_polygonize_high_resolution_enabled,
updated.top_surface_contoning_polygonize_resolution,
updated.top_surface_contoning_surface_anchored_stacks_enabled,
bundle->texture_mapping_zones,
bundle->texture_mapping_global_settings,
@@ -8936,8 +8949,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
dlg.top_surface_contoning_supersampled_cells_enabled();
updated.top_surface_contoning_polygonize_color_regions_enabled =
dlg.top_surface_contoning_polygonize_color_regions_enabled();
updated.top_surface_contoning_polygonize_high_resolution_enabled =
dlg.top_surface_contoning_polygonize_high_resolution_enabled();
updated.top_surface_contoning_polygonize_resolution =
dlg.top_surface_contoning_polygonize_resolution();
updated.top_surface_contoning_surface_anchored_stacks_enabled =
dlg.top_surface_contoning_surface_anchored_stacks_enabled();
if (updated.top_surface_image_printing_enabled &&