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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
Reference in New Issue
Block a user