Implement perimeter path modulation method for texture mapping
This commit is contained in:
@@ -474,6 +474,8 @@ set(lisbslic3r_sources
|
||||
TriangleSelector.hpp
|
||||
TriangleSetSampling.cpp
|
||||
TriangleSetSampling.hpp
|
||||
TextureMappingOffset.cpp
|
||||
TextureMappingOffset.hpp
|
||||
TextureMapping.cpp
|
||||
TextureMapping.hpp
|
||||
TriangulateWall.cpp
|
||||
|
||||
@@ -6350,17 +6350,17 @@ static float local_surface_stair_step_distance_for_gcode(const Layer *layer,
|
||||
|
||||
static bool is_horizontal_overhang_gradient_row_for_gcode(const TextureMappingZone &zone)
|
||||
{
|
||||
return zone.enabled && !zone.deleted && (zone.is_2d_gradient() || zone.is_image_texture());
|
||||
return zone.enabled && !zone.deleted && !zone.perimeter_path_modulation && (zone.is_2d_gradient() || zone.is_image_texture());
|
||||
}
|
||||
|
||||
static bool is_vertex_color_match_overhang_row_for_gcode(const TextureMappingZone &zone)
|
||||
{
|
||||
return zone.enabled && !zone.deleted && zone.is_image_texture();
|
||||
return zone.enabled && !zone.deleted && !zone.perimeter_path_modulation && zone.is_image_texture();
|
||||
}
|
||||
|
||||
static bool is_2d_offset_gradient_row_for_gcode(const TextureMappingZone &zone)
|
||||
{
|
||||
return zone.enabled && !zone.deleted && zone.is_2d_gradient();
|
||||
return zone.enabled && !zone.deleted && !zone.perimeter_path_modulation && zone.is_2d_gradient();
|
||||
}
|
||||
|
||||
static std::array<float, 4> unpack_rgba_u32(uint32_t packed_rgba)
|
||||
|
||||
@@ -8,16 +8,341 @@
|
||||
#include "BoundingBox.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "TextureMapping.hpp"
|
||||
#include "TextureMappingOffset.hpp"
|
||||
#include "Algorithm/RegionExpansion.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/algorithm/clamp.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct PerimeterPathBoundarySample {
|
||||
Point point;
|
||||
double inward_x { 0.0 };
|
||||
double inward_y { 0.0 };
|
||||
float inset_mm { 0.f };
|
||||
};
|
||||
|
||||
static bool perimeter_texture_expolygons_contain_point(const ExPolygons &expolygons, const Point &point)
|
||||
{
|
||||
return std::any_of(expolygons.begin(), expolygons.end(), [&point](const ExPolygon &expolygon) {
|
||||
return expolygon.contains(point, true);
|
||||
});
|
||||
}
|
||||
|
||||
static std::vector<ExPolygons> perimeter_texture_build_erode_ladder(const ExPolygon &source,
|
||||
float max_inset_mm,
|
||||
float &step_mm)
|
||||
{
|
||||
step_mm = std::clamp(max_inset_mm / 10.f, 0.025f, 0.08f);
|
||||
const int level_count = std::clamp(int(std::ceil(max_inset_mm / std::max(step_mm, 1e-4f))) + 1, 1, 96);
|
||||
std::vector<ExPolygons> ladder(static_cast<size_t>(level_count));
|
||||
ladder.front().push_back(source);
|
||||
for (int level = 1; level < level_count; ++level) {
|
||||
const float distance_scaled = float(scale_(double(step_mm) * double(level)));
|
||||
ladder[size_t(level)] = offset_ex(source, -distance_scaled);
|
||||
if (ladder[size_t(level)].empty()) {
|
||||
for (int fill = level + 1; fill < level_count; ++fill)
|
||||
ladder[size_t(fill)].clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ladder;
|
||||
}
|
||||
|
||||
static float perimeter_texture_safe_inset_for_sample(const ExPolygon &source,
|
||||
const std::vector<ExPolygons> &erode_ladder,
|
||||
float erode_step_mm,
|
||||
const PerimeterPathBoundarySample &sample,
|
||||
float desired_mm,
|
||||
float max_inset_mm)
|
||||
{
|
||||
const float clamped_desired = std::clamp(desired_mm, 0.f, max_inset_mm);
|
||||
if (clamped_desired <= EPSILON)
|
||||
return 0.f;
|
||||
|
||||
auto point_at_inset = [&sample](float inset_mm) {
|
||||
const double inset_scaled = scale_(double(inset_mm));
|
||||
return Point(coord_t(std::llround(double(sample.point.x()) + sample.inward_x * inset_scaled)),
|
||||
coord_t(std::llround(double(sample.point.y()) + sample.inward_y * inset_scaled)));
|
||||
};
|
||||
|
||||
auto inset_allowed = [&](float inset_mm) {
|
||||
const Point candidate = point_at_inset(inset_mm);
|
||||
if (!source.contains(candidate, true))
|
||||
return false;
|
||||
const size_t level = std::min(erode_ladder.empty() ? size_t(0) :
|
||||
size_t(std::floor(std::max(0.f, inset_mm - 0.01f) / std::max(erode_step_mm, 1e-4f))),
|
||||
erode_ladder.empty() ? size_t(0) : erode_ladder.size() - 1);
|
||||
if (level > 0) {
|
||||
if (level >= erode_ladder.size() || erode_ladder[level].empty())
|
||||
return false;
|
||||
if (!perimeter_texture_expolygons_contain_point(erode_ladder[level], candidate))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (inset_allowed(clamped_desired))
|
||||
return clamped_desired;
|
||||
|
||||
float low = 0.f;
|
||||
float high = clamped_desired;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
const float mid = 0.5f * (low + high);
|
||||
if (inset_allowed(mid))
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
|
||||
static std::vector<PerimeterPathBoundarySample> perimeter_texture_sample_polygon_boundary(
|
||||
const Polygon &polygon,
|
||||
const TextureMappingOffsetContext &context,
|
||||
const ExPolygon &source)
|
||||
{
|
||||
std::vector<PerimeterPathBoundarySample> samples;
|
||||
const Points &points = polygon.points;
|
||||
if (points.size() < 3)
|
||||
return samples;
|
||||
|
||||
float erode_step_mm = 0.f;
|
||||
const std::vector<ExPolygons> erode_ladder =
|
||||
perimeter_texture_build_erode_ladder(source, context.max_width_delta_mm, erode_step_mm);
|
||||
const double pitch_scaled = scale_(context.high_resolution_texture_sampling ? 0.08 : 0.16);
|
||||
|
||||
for (size_t idx = 0; idx < points.size(); ++idx) {
|
||||
const Point &a = points[idx];
|
||||
const Point &b = points[(idx + 1) % points.size()];
|
||||
const double dx = double(b.x()) - double(a.x());
|
||||
const double dy = double(b.y()) - double(a.y());
|
||||
const double len = std::hypot(dx, dy);
|
||||
if (!std::isfinite(len) || len <= EPSILON)
|
||||
continue;
|
||||
|
||||
const double inward_x = -dy / len;
|
||||
const double inward_y = dx / len;
|
||||
const int sample_count = std::clamp(int(std::ceil(len / std::max(pitch_scaled, 1.0))), 1, 4096);
|
||||
for (int sample_idx = 0; sample_idx < sample_count; ++sample_idx) {
|
||||
const double t = double(sample_idx) / double(sample_count);
|
||||
PerimeterPathBoundarySample sample;
|
||||
sample.point = Point(coord_t(std::llround(double(a.x()) + dx * t)),
|
||||
coord_t(std::llround(double(a.y()) + dy * t)));
|
||||
sample.inward_x = inward_x;
|
||||
sample.inward_y = inward_y;
|
||||
const float desired_mm = texture_mapping_offset_surface_inset_mm(context, sample.point, inward_x, inward_y);
|
||||
sample.inset_mm = perimeter_texture_safe_inset_for_sample(source,
|
||||
erode_ladder,
|
||||
erode_step_mm,
|
||||
sample,
|
||||
desired_mm,
|
||||
context.max_width_delta_mm);
|
||||
samples.emplace_back(sample);
|
||||
}
|
||||
}
|
||||
|
||||
if (samples.size() < 3)
|
||||
return {};
|
||||
|
||||
auto sample_distance_mm = [](const PerimeterPathBoundarySample &lhs, const PerimeterPathBoundarySample &rhs) {
|
||||
const double dx = double(lhs.point.x()) - double(rhs.point.x());
|
||||
const double dy = double(lhs.point.y()) - double(rhs.point.y());
|
||||
return unscale<float>(std::hypot(dx, dy));
|
||||
};
|
||||
|
||||
auto smooth_insets = [&samples, &sample_distance_mm](float radius_mm) {
|
||||
if (samples.size() < 3)
|
||||
return;
|
||||
radius_mm = std::clamp(radius_mm, 0.05f, 1.5f);
|
||||
std::vector<float> smoothed(samples.size(), 0.f);
|
||||
for (size_t idx = 0; idx < samples.size(); ++idx) {
|
||||
float weighted_sum = samples[idx].inset_mm;
|
||||
float weight_sum = 1.f;
|
||||
float distance_mm = 0.f;
|
||||
for (size_t step = 1; step < samples.size(); ++step) {
|
||||
const size_t prev = (idx + samples.size() - step) % samples.size();
|
||||
const size_t next_prev = (prev + 1) % samples.size();
|
||||
distance_mm += sample_distance_mm(samples[prev], samples[next_prev]);
|
||||
if (distance_mm > radius_mm)
|
||||
break;
|
||||
const float weight = 1.f - distance_mm / radius_mm;
|
||||
weighted_sum += samples[prev].inset_mm * weight;
|
||||
weight_sum += weight;
|
||||
}
|
||||
distance_mm = 0.f;
|
||||
for (size_t step = 1; step < samples.size(); ++step) {
|
||||
const size_t next = (idx + step) % samples.size();
|
||||
const size_t prev_next = next == 0 ? samples.size() - 1 : next - 1;
|
||||
distance_mm += sample_distance_mm(samples[prev_next], samples[next]);
|
||||
if (distance_mm > radius_mm)
|
||||
break;
|
||||
const float weight = 1.f - distance_mm / radius_mm;
|
||||
weighted_sum += samples[next].inset_mm * weight;
|
||||
weight_sum += weight;
|
||||
}
|
||||
smoothed[idx] = weight_sum > EPSILON ? std::min(samples[idx].inset_mm, weighted_sum / weight_sum) : samples[idx].inset_mm;
|
||||
}
|
||||
for (size_t idx = 0; idx < samples.size(); ++idx)
|
||||
samples[idx].inset_mm = smoothed[idx];
|
||||
};
|
||||
|
||||
smooth_insets(std::max(0.30f, 1.15f * context.base_outer_width_mm));
|
||||
|
||||
for (int pass = 0; pass < 4; ++pass) {
|
||||
for (size_t idx = 0; idx < samples.size(); ++idx) {
|
||||
const size_t prev = idx == 0 ? samples.size() - 1 : idx - 1;
|
||||
const float limit = sample_distance_mm(samples[idx], samples[prev]) * 0.35f + 0.015f;
|
||||
if (samples[idx].inset_mm > samples[prev].inset_mm + limit)
|
||||
samples[idx].inset_mm = samples[prev].inset_mm + limit;
|
||||
}
|
||||
for (size_t idx = samples.size(); idx-- > 0;) {
|
||||
const size_t next = (idx + 1) % samples.size();
|
||||
const float limit = sample_distance_mm(samples[idx], samples[next]) * 0.35f + 0.015f;
|
||||
if (samples[idx].inset_mm > samples[next].inset_mm + limit)
|
||||
samples[idx].inset_mm = samples[next].inset_mm + limit;
|
||||
}
|
||||
}
|
||||
|
||||
smooth_insets(std::max(0.20f, 0.45f * context.base_outer_width_mm));
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
static Polygon perimeter_texture_moved_polygon_from_samples(const std::vector<PerimeterPathBoundarySample> &samples)
|
||||
{
|
||||
Polygon polygon;
|
||||
polygon.points.reserve(samples.size());
|
||||
Point last_point;
|
||||
bool has_last = false;
|
||||
for (const PerimeterPathBoundarySample &sample : samples) {
|
||||
const double inset_scaled = scale_(double(sample.inset_mm));
|
||||
Point moved(coord_t(std::llround(double(sample.point.x()) + sample.inward_x * inset_scaled)),
|
||||
coord_t(std::llround(double(sample.point.y()) + sample.inward_y * inset_scaled)));
|
||||
if (!has_last || moved != last_point) {
|
||||
polygon.points.emplace_back(moved);
|
||||
last_point = moved;
|
||||
has_last = true;
|
||||
}
|
||||
}
|
||||
if (polygon.points.size() > 1 && polygon.points.front() == polygon.points.back())
|
||||
polygon.points.pop_back();
|
||||
remove_same_neighbor(polygon);
|
||||
return polygon;
|
||||
}
|
||||
|
||||
static ExPolygons perimeter_texture_modulated_expolygon(const ExPolygon &source,
|
||||
const TextureMappingOffsetContext &context,
|
||||
bool &ok)
|
||||
{
|
||||
ok = false;
|
||||
if (source.empty() || source.contour.points.size() < 3)
|
||||
return {};
|
||||
|
||||
const double source_area = std::abs(source.area());
|
||||
if (!std::isfinite(source_area) || source_area <= 0.0)
|
||||
return {};
|
||||
|
||||
const std::vector<PerimeterPathBoundarySample> contour_samples =
|
||||
perimeter_texture_sample_polygon_boundary(source.contour, context, source);
|
||||
if (contour_samples.size() < 3)
|
||||
return {};
|
||||
|
||||
bool has_meaningful_inset = false;
|
||||
for (const PerimeterPathBoundarySample &sample : contour_samples) {
|
||||
if (sample.inset_mm > 0.002f) {
|
||||
has_meaningful_inset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::vector<std::vector<PerimeterPathBoundarySample>> hole_samples;
|
||||
hole_samples.reserve(source.holes.size());
|
||||
for (const Polygon &hole : source.holes) {
|
||||
hole_samples.emplace_back(perimeter_texture_sample_polygon_boundary(hole, context, source));
|
||||
if (hole_samples.back().size() < 3)
|
||||
return {};
|
||||
for (const PerimeterPathBoundarySample &sample : hole_samples.back()) {
|
||||
if (sample.inset_mm > 0.002f) {
|
||||
has_meaningful_inset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!has_meaningful_inset) {
|
||||
ok = true;
|
||||
return ExPolygons{ source };
|
||||
}
|
||||
|
||||
ExPolygon moved;
|
||||
moved.contour = perimeter_texture_moved_polygon_from_samples(contour_samples);
|
||||
if (moved.contour.points.size() < 3)
|
||||
return {};
|
||||
moved.contour.make_counter_clockwise();
|
||||
moved.holes.reserve(hole_samples.size());
|
||||
for (const std::vector<PerimeterPathBoundarySample> &samples : hole_samples) {
|
||||
Polygon hole = perimeter_texture_moved_polygon_from_samples(samples);
|
||||
if (hole.points.size() < 3)
|
||||
return {};
|
||||
hole.make_clockwise();
|
||||
moved.holes.emplace_back(std::move(hole));
|
||||
}
|
||||
|
||||
ExPolygons simplified = moved.simplify(scale_(0.006));
|
||||
if (simplified.empty())
|
||||
return {};
|
||||
|
||||
ExPolygons source_only;
|
||||
source_only.emplace_back(source);
|
||||
ExPolygons clipped = intersection_ex(simplified, source_only);
|
||||
if (clipped.empty())
|
||||
return {};
|
||||
remove_same_neighbor(clipped);
|
||||
|
||||
const double clipped_area = area(clipped);
|
||||
if (!std::isfinite(clipped_area) || clipped_area <= source_area * 0.10)
|
||||
return {};
|
||||
if (clipped_area > source_area * 1.002)
|
||||
return {};
|
||||
|
||||
ok = true;
|
||||
return clipped;
|
||||
}
|
||||
|
||||
static SurfaceCollection perimeter_path_modulated_surfaces(const LayerRegion &layer_region,
|
||||
const SurfaceCollection &slices,
|
||||
const TextureMappingZone &zone,
|
||||
unsigned int texture_zone_id)
|
||||
{
|
||||
const Layer *layer = layer_region.layer();
|
||||
if (layer == nullptr || layer->object() == nullptr)
|
||||
return slices;
|
||||
|
||||
std::optional<TextureMappingOffsetContext> context =
|
||||
build_texture_mapping_offset_context_for_layer(*layer->object(), *layer, zone, texture_zone_id);
|
||||
if (!context)
|
||||
return slices;
|
||||
|
||||
SurfaceCollection out;
|
||||
out.surfaces.reserve(slices.surfaces.size());
|
||||
for (const Surface &surface : slices.surfaces) {
|
||||
bool ok = false;
|
||||
ExPolygons modulated = perimeter_texture_modulated_expolygon(surface.expolygon, *context, ok);
|
||||
if (ok && !modulated.empty())
|
||||
out.append(std::move(modulated), surface);
|
||||
else
|
||||
out.surfaces.emplace_back(surface);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool region_uses_overhang_texture_mapping(const Print &print, const PrintRegionConfig ®ion_config)
|
||||
{
|
||||
const int filament_id = region_config.wall_filament.value;
|
||||
@@ -28,6 +353,26 @@ static bool region_uses_overhang_texture_mapping(const Print &print, const Print
|
||||
return zone != nullptr && zone->enabled && !zone->deleted && (zone->is_2d_gradient() || zone->is_image_texture());
|
||||
}
|
||||
|
||||
static const TextureMappingZone *perimeter_path_modulation_zone_for_region(const Print &print,
|
||||
const PrintRegionConfig ®ion_config,
|
||||
unsigned int &texture_zone_id)
|
||||
{
|
||||
const int filament_id = region_config.wall_filament.value;
|
||||
if (filament_id <= 0)
|
||||
return nullptr;
|
||||
|
||||
texture_zone_id = unsigned(filament_id);
|
||||
const TextureMappingZone *zone = print.texture_mapping_manager().zone_from_id(texture_zone_id);
|
||||
if (zone == nullptr ||
|
||||
!zone->enabled ||
|
||||
zone->deleted ||
|
||||
!zone->perimeter_path_modulation ||
|
||||
(!zone->is_2d_gradient() && !zone->is_image_texture()))
|
||||
return nullptr;
|
||||
|
||||
return zone;
|
||||
}
|
||||
|
||||
Flow LayerRegion::flow(FlowRole role) const
|
||||
{
|
||||
return this->flow(role, m_layer->height);
|
||||
@@ -93,9 +438,20 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
(this->layer()->id() >= size_t(region_config.bottom_shell_layers.value) &&
|
||||
this->layer()->print_z >= region_config.bottom_shell_thickness - EPSILON);
|
||||
|
||||
SurfaceCollection modulated_slices;
|
||||
const SurfaceCollection *perimeter_slices = &slices;
|
||||
unsigned int perimeter_texture_zone_id = 0;
|
||||
bool use_perimeter_path_modulation = false;
|
||||
if (const TextureMappingZone *zone =
|
||||
perimeter_path_modulation_zone_for_region(*this->layer()->object()->print(), region_config, perimeter_texture_zone_id)) {
|
||||
modulated_slices = perimeter_path_modulated_surfaces(*this, slices, *zone, perimeter_texture_zone_id);
|
||||
perimeter_slices = &modulated_slices;
|
||||
use_perimeter_path_modulation = true;
|
||||
}
|
||||
|
||||
PerimeterGenerator g(
|
||||
// input:
|
||||
&slices,
|
||||
perimeter_slices,
|
||||
&compatible_regions,
|
||||
this->layer()->height,
|
||||
this->layer()->slice_z,
|
||||
@@ -125,6 +481,15 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
|
||||
g.layer_id = (int)this->layer()->id();
|
||||
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
if (use_perimeter_path_modulation) {
|
||||
const Flow normal_ext_perimeter_flow = g.ext_perimeter_flow;
|
||||
const float texture_external_width_mm =
|
||||
std::max(0.05f, float(print_config.texture_mapping_outer_wall_gradient_max_line_width.value));
|
||||
const float min_width_for_positive_spacing_mm =
|
||||
std::max(0.01f, float(this->layer()->height) * float(1. - 0.25 * PI) + 1e-4f);
|
||||
g.ext_perimeter_flow = normal_ext_perimeter_flow.with_width(std::max(texture_external_width_mm, min_width_for_positive_spacing_mm));
|
||||
g.ext_perimeter_flow.set_spacing(std::min(g.ext_perimeter_flow.spacing(), normal_ext_perimeter_flow.spacing()));
|
||||
}
|
||||
g.overhang_flow = this->bridging_flow(frPerimeter, object_config.thick_bridges);
|
||||
g.solid_infill_flow = this->flow(frSolidInfill);
|
||||
|
||||
|
||||
@@ -512,6 +512,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "texture_mapping_outer_wall_gradient_min_line_width"
|
||||
|| opt_key == "texture_mapping_definitions"
|
||||
|| opt_key == "texture_mapping_global_settings") {
|
||||
osteps.emplace_back(posPerimeters);
|
||||
steps.emplace_back(psWipeTower);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (
|
||||
|
||||
@@ -1169,6 +1169,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "texture_mapping_outer_wall_gradient_max_line_width"
|
||||
|| opt_key == "texture_mapping_outer_wall_gradient_min_line_width"
|
||||
|| opt_key == "texture_mapping_definitions"
|
||||
|| opt_key == "texture_mapping_background_color"
|
||||
|| opt_key == "texture_mapping_global_settings") {
|
||||
steps.emplace_back(posSlice);
|
||||
} else if (
|
||||
@@ -1423,7 +1424,6 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "support_interface_flow_ratio"
|
||||
|| opt_key == "brim_flow_ratio"
|
||||
|| opt_key == "filament_flow_ratio"
|
||||
|| opt_key == "texture_mapping_background_color"
|
||||
|| opt_key == "scarf_joint_flow_ratio"
|
||||
|| opt_key == "spiral_starting_flow_ratio"
|
||||
|| opt_key == "spiral_finishing_flow_ratio") {
|
||||
|
||||
@@ -794,6 +794,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
|
||||
reduce_outer_surface_texture == rhs.reduce_outer_surface_texture &&
|
||||
seam_hiding == rhs.seam_hiding &&
|
||||
nonlinear_offset_adjustment == rhs.nonlinear_offset_adjustment &&
|
||||
perimeter_path_modulation == rhs.perimeter_path_modulation &&
|
||||
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 &&
|
||||
@@ -1043,6 +1044,7 @@ std::string TextureMappingManager::serialize_entries()
|
||||
texture["reduce_outer_surface_texture"] = zone.reduce_outer_surface_texture;
|
||||
texture["hide_seams"] = zone.seam_hiding;
|
||||
texture["nonlinear_offset_adjustment"] = zone.nonlinear_offset_adjustment;
|
||||
texture["perimeter_path_modulation"] = zone.perimeter_path_modulation;
|
||||
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"] = zone.high_speed_image_texture_sampling;
|
||||
@@ -1174,6 +1176,8 @@ void TextureMappingManager::load_entries(const std::string &serialized,
|
||||
zone.reduce_outer_surface_texture = texture.value("reduce_outer_surface_texture", false);
|
||||
zone.seam_hiding = texture.value("hide_seams", false);
|
||||
zone.nonlinear_offset_adjustment = texture.value("nonlinear_offset_adjustment", false);
|
||||
zone.perimeter_path_modulation =
|
||||
texture.value("perimeter_path_modulation", TextureMappingZone::DefaultPerimeterPathModulation);
|
||||
zone.compact_offset_mode = texture.value("compact_offset_mode", TextureMappingZone::DefaultCompactOffsetMode);
|
||||
zone.use_legacy_fixed_color_mode =
|
||||
texture.value("use_legacy_fixed_color_mode", TextureMappingZone::DefaultUseLegacyFixedColorMode);
|
||||
|
||||
@@ -94,6 +94,7 @@ struct TextureMappingZone
|
||||
static constexpr bool DefaultReduceOuterSurfaceTexture = false;
|
||||
static constexpr bool DefaultSeamHiding = false;
|
||||
static constexpr bool DefaultNonlinearOffsetAdjustment = false;
|
||||
static constexpr bool DefaultPerimeterPathModulation = false;
|
||||
static constexpr bool DefaultCompactOffsetMode = true;
|
||||
static constexpr bool DefaultUseLegacyFixedColorMode = false;
|
||||
static constexpr bool DefaultHighSpeedImageTextureSampling = true;
|
||||
@@ -138,6 +139,7 @@ struct TextureMappingZone
|
||||
bool reduce_outer_surface_texture = DefaultReduceOuterSurfaceTexture;
|
||||
bool seam_hiding = DefaultSeamHiding;
|
||||
bool nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment;
|
||||
bool perimeter_path_modulation = DefaultPerimeterPathModulation;
|
||||
bool compact_offset_mode = DefaultCompactOffsetMode;
|
||||
bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
|
||||
bool high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;
|
||||
@@ -183,6 +185,7 @@ struct TextureMappingZone
|
||||
reduce_outer_surface_texture = DefaultReduceOuterSurfaceTexture;
|
||||
seam_hiding = DefaultSeamHiding;
|
||||
nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment;
|
||||
perimeter_path_modulation = DefaultPerimeterPathModulation;
|
||||
compact_offset_mode = DefaultCompactOffsetMode;
|
||||
use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
|
||||
high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;
|
||||
|
||||
2181
src/libslic3r/TextureMappingOffset.cpp
Normal file
2181
src/libslic3r/TextureMappingOffset.cpp
Normal file
File diff suppressed because it is too large
Load Diff
91
src/libslic3r/TextureMappingOffset.hpp
Normal file
91
src/libslic3r/TextureMappingOffset.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// original author: sentientstardust
|
||||
|
||||
#ifndef slic3r_TextureMappingOffset_hpp_
|
||||
#define slic3r_TextureMappingOffset_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
#include "TextureMapping.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Layer;
|
||||
class PrintObject;
|
||||
|
||||
struct TextureMappingOffsetWeightField {
|
||||
float min_x_mm { 0.f };
|
||||
float min_y_mm { 0.f };
|
||||
float bucket_width_mm { 1.f };
|
||||
float bucket_height_mm { 1.f };
|
||||
int bucket_width { 0 };
|
||||
int bucket_height { 0 };
|
||||
size_t component_count { 0 };
|
||||
std::vector<float> sample_x_mm;
|
||||
std::vector<float> sample_y_mm;
|
||||
std::vector<float> sample_weight;
|
||||
std::vector<float> sample_component_weights;
|
||||
std::vector<std::vector<uint32_t>> buckets;
|
||||
std::vector<float> fallback_weights;
|
||||
bool raw_component_weights_from_texture { false };
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return bucket_width <= 0 ||
|
||||
bucket_height <= 0 ||
|
||||
component_count == 0 ||
|
||||
sample_x_mm.empty() ||
|
||||
sample_y_mm.size() != sample_x_mm.size() ||
|
||||
sample_weight.size() != sample_x_mm.size() ||
|
||||
sample_component_weights.size() != sample_x_mm.size() * component_count;
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureMappingOffsetContext {
|
||||
bool vertex_color_match_mode { false };
|
||||
bool object_center_mode { false };
|
||||
bool high_resolution_texture_sampling { false };
|
||||
bool compact_offset_mode { false };
|
||||
bool nonlinear_offset_adjustment { false };
|
||||
Point object_center;
|
||||
unsigned int active_component_id { 0 };
|
||||
size_t active_component_idx { size_t(-1) };
|
||||
std::vector<unsigned int> component_ids;
|
||||
std::vector<float> component_distances_mm;
|
||||
std::vector<float> rotated_angles;
|
||||
TextureMappingOffsetWeightField weight_field;
|
||||
float inset_strength_reference_mm { 0.f };
|
||||
float fade_factor { 1.f };
|
||||
float max_width_delta_mm { 0.f };
|
||||
float active_component_strength_factor { 1.f };
|
||||
float active_component_minimum_offset_factor { 0.f };
|
||||
float active_component_td_width_factor { 1.f };
|
||||
float base_outer_width_mm { 0.f };
|
||||
float layer_height_mm { 0.f };
|
||||
const Layer *layer { nullptr };
|
||||
};
|
||||
|
||||
std::vector<unsigned int> decode_texture_mapping_offset_component_ids(const TextureMappingZone &zone, size_t num_physical);
|
||||
float normalize_texture_mapping_offset_angle_deg(float angle);
|
||||
float texture_mapping_offset_fade_factor(int fade_mode, float progress01);
|
||||
float texture_mapping_offset_filament_strength_factor(const TextureMappingZone &zone, unsigned int physical_filament_id);
|
||||
float texture_mapping_offset_filament_minimum_offset_factor(const TextureMappingZone &zone, unsigned int physical_filament_id);
|
||||
|
||||
std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_for_layer(
|
||||
const PrintObject &print_object,
|
||||
const Layer &layer,
|
||||
const TextureMappingZone &zone,
|
||||
unsigned int texture_zone_id);
|
||||
|
||||
float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext &context,
|
||||
const Point &point,
|
||||
double inward_x,
|
||||
double inward_y);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
@@ -1076,6 +1076,7 @@ public:
|
||||
bool reduce_outer_surface_texture,
|
||||
bool seam_hiding,
|
||||
bool nonlinear_offset_adjustment,
|
||||
bool perimeter_path_modulation,
|
||||
bool compact_offset_mode,
|
||||
bool use_legacy_fixed_color_mode,
|
||||
bool high_speed_image_texture_sampling,
|
||||
@@ -1303,6 +1304,9 @@ public:
|
||||
m_nonlinear_offset_adjustment_checkbox->SetToolTip(
|
||||
_L("Adjusts line-width offsets using a surface-visibility model derived from Kuipers et al. 2018."));
|
||||
experimental_box->Add(m_nonlinear_offset_adjustment_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
|
||||
m_perimeter_path_modulation_checkbox = new wxCheckBox(experimental_page, wxID_ANY, _L("Use perimeter path modulation"));
|
||||
m_perimeter_path_modulation_checkbox->SetValue(perimeter_path_modulation);
|
||||
experimental_box->Add(m_perimeter_path_modulation_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
|
||||
m_compact_offset_mode_checkbox = new wxCheckBox(experimental_page, wxID_ANY, _L("Compact Offset Mode"));
|
||||
m_compact_offset_mode_checkbox->SetValue(compact_offset_mode);
|
||||
m_compact_offset_mode_checkbox->SetToolTip(
|
||||
@@ -1551,6 +1555,7 @@ public:
|
||||
bool reduce_outer_surface_texture() const { return m_reduce_outer_surface_texture_checkbox && m_reduce_outer_surface_texture_checkbox->GetValue(); }
|
||||
bool seam_hiding() const { return m_seam_hiding_checkbox && m_seam_hiding_checkbox->GetValue(); }
|
||||
bool nonlinear_offset_adjustment() const { return m_nonlinear_offset_adjustment_checkbox && m_nonlinear_offset_adjustment_checkbox->GetValue(); }
|
||||
bool perimeter_path_modulation() const { return m_perimeter_path_modulation_checkbox && m_perimeter_path_modulation_checkbox->GetValue(); }
|
||||
bool compact_offset_mode() const { return m_compact_offset_mode_checkbox && m_compact_offset_mode_checkbox->GetValue(); }
|
||||
bool use_legacy_fixed_color_mode() const { return m_use_legacy_fixed_color_mode_checkbox && m_use_legacy_fixed_color_mode_checkbox->GetValue(); }
|
||||
bool high_speed_image_texture_sampling() const { return m_high_speed_image_texture_sampling_checkbox == nullptr || m_high_speed_image_texture_sampling_checkbox->GetValue(); }
|
||||
@@ -1857,6 +1862,7 @@ private:
|
||||
wxCheckBox *m_reduce_outer_surface_texture_checkbox {nullptr};
|
||||
wxCheckBox *m_seam_hiding_checkbox {nullptr};
|
||||
wxCheckBox *m_nonlinear_offset_adjustment_checkbox {nullptr};
|
||||
wxCheckBox *m_perimeter_path_modulation_checkbox {nullptr};
|
||||
wxCheckBox *m_compact_offset_mode_checkbox {nullptr};
|
||||
wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr};
|
||||
wxCheckBox *m_high_speed_image_texture_sampling_checkbox {nullptr};
|
||||
@@ -5964,6 +5970,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.reduce_outer_surface_texture,
|
||||
updated.seam_hiding,
|
||||
updated.nonlinear_offset_adjustment,
|
||||
updated.perimeter_path_modulation,
|
||||
updated.compact_offset_mode,
|
||||
updated.use_legacy_fixed_color_mode,
|
||||
updated.high_speed_image_texture_sampling,
|
||||
@@ -5998,6 +6005,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.reduce_outer_surface_texture = dlg.reduce_outer_surface_texture();
|
||||
updated.seam_hiding = dlg.seam_hiding();
|
||||
updated.nonlinear_offset_adjustment = dlg.nonlinear_offset_adjustment();
|
||||
updated.perimeter_path_modulation = dlg.perimeter_path_modulation();
|
||||
updated.compact_offset_mode = dlg.compact_offset_mode();
|
||||
updated.use_legacy_fixed_color_mode = dlg.use_legacy_fixed_color_mode();
|
||||
updated.high_speed_image_texture_sampling = dlg.high_speed_image_texture_sampling();
|
||||
|
||||
Reference in New Issue
Block a user