Create top-surface images with variable line widths (experimental)

This commit is contained in:
sentientstardust
2026-05-24 14:17:12 +01:00
parent 8e9a0cbfdc
commit 3cfd8c656b
10 changed files with 1111 additions and 41 deletions

View File

@@ -20,6 +20,12 @@ void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities,
ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths)
: no_sort(false)
, texture_mapping_extruder_override(-1)
, texture_mapping_top_surface_image(false)
, texture_mapping_top_surface_zone_id(0)
, texture_mapping_top_surface_desired_component_id(0)
, texture_mapping_top_surface_stack_depth(-1)
, texture_mapping_top_surface_fixed_coloring(true)
{
this->append(paths);
}
@@ -32,6 +38,11 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE
this->entities[i] = this->entities[i]->clone();
this->no_sort = other.no_sort;
this->texture_mapping_extruder_override = other.texture_mapping_extruder_override;
this->texture_mapping_top_surface_image = other.texture_mapping_top_surface_image;
this->texture_mapping_top_surface_zone_id = other.texture_mapping_top_surface_zone_id;
this->texture_mapping_top_surface_desired_component_id = other.texture_mapping_top_surface_desired_component_id;
this->texture_mapping_top_surface_stack_depth = other.texture_mapping_top_surface_stack_depth;
this->texture_mapping_top_surface_fixed_coloring = other.texture_mapping_top_surface_fixed_coloring;
return *this;
}
@@ -40,6 +51,11 @@ void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c)
std::swap(this->entities, c.entities);
std::swap(this->no_sort, c.no_sort);
std::swap(this->texture_mapping_extruder_override, c.texture_mapping_extruder_override);
std::swap(this->texture_mapping_top_surface_image, c.texture_mapping_top_surface_image);
std::swap(this->texture_mapping_top_surface_zone_id, c.texture_mapping_top_surface_zone_id);
std::swap(this->texture_mapping_top_surface_desired_component_id, c.texture_mapping_top_surface_desired_component_id);
std::swap(this->texture_mapping_top_surface_stack_depth, c.texture_mapping_top_surface_stack_depth);
std::swap(this->texture_mapping_top_surface_fixed_coloring, c.texture_mapping_top_surface_fixed_coloring);
}
void ExtrusionEntityCollection::clear()

View File

@@ -32,9 +32,41 @@ public:
ExtrusionEntitiesPtr entities; // we own these entities
bool no_sort;
int texture_mapping_extruder_override;
ExtrusionEntityCollection(): no_sort(false), texture_mapping_extruder_override(-1) {}
ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort), texture_mapping_extruder_override(other.texture_mapping_extruder_override), is_reverse(other.is_reverse) { this->append(other.entities); }
ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort), texture_mapping_extruder_override(other.texture_mapping_extruder_override), is_reverse(other.is_reverse) {}
bool texture_mapping_top_surface_image;
unsigned int texture_mapping_top_surface_zone_id;
unsigned int texture_mapping_top_surface_desired_component_id;
int texture_mapping_top_surface_stack_depth;
bool texture_mapping_top_surface_fixed_coloring;
ExtrusionEntityCollection()
: no_sort(false)
, texture_mapping_extruder_override(-1)
, texture_mapping_top_surface_image(false)
, texture_mapping_top_surface_zone_id(0)
, texture_mapping_top_surface_desired_component_id(0)
, texture_mapping_top_surface_stack_depth(-1)
, texture_mapping_top_surface_fixed_coloring(true)
{}
ExtrusionEntityCollection(const ExtrusionEntityCollection &other)
: no_sort(other.no_sort)
, texture_mapping_extruder_override(other.texture_mapping_extruder_override)
, texture_mapping_top_surface_image(other.texture_mapping_top_surface_image)
, texture_mapping_top_surface_zone_id(other.texture_mapping_top_surface_zone_id)
, texture_mapping_top_surface_desired_component_id(other.texture_mapping_top_surface_desired_component_id)
, texture_mapping_top_surface_stack_depth(other.texture_mapping_top_surface_stack_depth)
, texture_mapping_top_surface_fixed_coloring(other.texture_mapping_top_surface_fixed_coloring)
, is_reverse(other.is_reverse)
{ this->append(other.entities); }
ExtrusionEntityCollection(ExtrusionEntityCollection &&other)
: entities(std::move(other.entities))
, no_sort(other.no_sort)
, texture_mapping_extruder_override(other.texture_mapping_extruder_override)
, texture_mapping_top_surface_image(other.texture_mapping_top_surface_image)
, texture_mapping_top_surface_zone_id(other.texture_mapping_top_surface_zone_id)
, texture_mapping_top_surface_desired_component_id(other.texture_mapping_top_surface_desired_component_id)
, texture_mapping_top_surface_stack_depth(other.texture_mapping_top_surface_stack_depth)
, texture_mapping_top_surface_fixed_coloring(other.texture_mapping_top_surface_fixed_coloring)
, is_reverse(other.is_reverse)
{}
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
@@ -42,6 +74,11 @@ public:
this->entities = std::move(other.entities);
this->no_sort = other.no_sort;
this->texture_mapping_extruder_override = other.texture_mapping_extruder_override;
this->texture_mapping_top_surface_image = other.texture_mapping_top_surface_image;
this->texture_mapping_top_surface_zone_id = other.texture_mapping_top_surface_zone_id;
this->texture_mapping_top_surface_desired_component_id = other.texture_mapping_top_surface_desired_component_id;
this->texture_mapping_top_surface_stack_depth = other.texture_mapping_top_surface_stack_depth;
this->texture_mapping_top_surface_fixed_coloring = other.texture_mapping_top_surface_fixed_coloring;
is_reverse = other.is_reverse;
return *this;
}

View File

@@ -3,11 +3,15 @@
#include <memory>
#include "../ClipperUtils.hpp"
#include "../Color.hpp"
#include "../ColorSolver.hpp"
#include "../Geometry.hpp"
#include "../Layer.hpp"
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Surface.hpp"
#include "../TextureMapping.hpp"
#include "../TextureMappingOffset.hpp"
#include "AABBTreeLines.hpp"
#include "ExtrusionEntity.hpp"
@@ -20,6 +24,10 @@
#include "FillConcentric.hpp"
#include "libslic3r.h"
#include <cmath>
#include <limits>
#include <optional>
namespace Slic3r {
// Calculate infill rotation angle (in radians) for a given layer from a rotation template.
@@ -271,6 +279,17 @@ struct SurfaceFillParams
// Params for Lateral honeycomb
float infill_overhang_angle = 60.f;
bool texture_mapping_top_surface_image = false;
unsigned int texture_mapping_top_surface_zone_id = 0;
unsigned int texture_mapping_top_surface_component_id = 0;
int texture_mapping_top_surface_stack_depth = -1;
bool texture_mapping_top_surface_fixed_coloring = true;
float texture_mapping_top_surface_min_width_mm = TextureMappingZone::DefaultTopSurfaceImageMinLineWidthMm;
float texture_mapping_top_surface_max_width_mm = TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm;
bool texture_mapping_top_surface_same_layer_partition = false;
int texture_mapping_top_surface_component_index = 0;
int texture_mapping_top_surface_component_count = 0;
bool operator<(const SurfaceFillParams &rhs) const {
#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
@@ -303,6 +322,16 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_image);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_zone_id);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_component_id);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_stack_depth);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_fixed_coloring);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_min_width_mm);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_max_width_mm);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_same_layer_partition);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_component_index);
RETURN_COMPARE_NON_EQUAL(texture_mapping_top_surface_component_count);
return false;
}
@@ -330,7 +359,17 @@ struct SurfaceFillParams
this->lateral_lattice_angle_2 == rhs.lateral_lattice_angle_2 &&
this->infill_lock_depth == rhs.infill_lock_depth &&
this->skin_infill_depth == rhs.skin_infill_depth &&
this->infill_overhang_angle == rhs.infill_overhang_angle;
this->infill_overhang_angle == rhs.infill_overhang_angle &&
this->texture_mapping_top_surface_image == rhs.texture_mapping_top_surface_image &&
this->texture_mapping_top_surface_zone_id == rhs.texture_mapping_top_surface_zone_id &&
this->texture_mapping_top_surface_component_id == rhs.texture_mapping_top_surface_component_id &&
this->texture_mapping_top_surface_stack_depth == rhs.texture_mapping_top_surface_stack_depth &&
this->texture_mapping_top_surface_fixed_coloring == rhs.texture_mapping_top_surface_fixed_coloring &&
this->texture_mapping_top_surface_min_width_mm == rhs.texture_mapping_top_surface_min_width_mm &&
this->texture_mapping_top_surface_max_width_mm == rhs.texture_mapping_top_surface_max_width_mm &&
this->texture_mapping_top_surface_same_layer_partition == rhs.texture_mapping_top_surface_same_layer_partition &&
this->texture_mapping_top_surface_component_index == rhs.texture_mapping_top_surface_component_index &&
this->texture_mapping_top_surface_component_count == rhs.texture_mapping_top_surface_component_count;
}
};
@@ -346,6 +385,543 @@ struct SurfaceFill {
ExPolygons no_overlap_expolygons;
};
struct TopSurfaceImageStackSlice {
unsigned int component_id = 0;
int depth = 0;
size_t component_index = 0;
size_t component_count = 0;
bool same_layer_partition = false;
float angle_rad = float(PI / 4.0);
ExPolygons area;
};
struct TopSurfaceImageRegionPlan {
const TextureMappingZone *zone = nullptr;
unsigned int zone_id = 0;
std::vector<unsigned int> components_bottom_to_top;
std::vector<TopSurfaceImageStackSlice> slices;
float min_width_mm = TextureMappingZone::DefaultTopSurfaceImageMinLineWidthMm;
float max_width_mm = TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm;
bool fixed_coloring = true;
bool same_layer_partition = false;
int colored_top_layers = TextureMappingZone::DefaultTopSurfaceImageColoredTopLayers;
};
static float top_surface_image_filament_luminance(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 std::numeric_limits<float>::max();
return 0.2126f * color.r() + 0.7152f * color.g() + 0.0722f * color.b();
}
static std::vector<unsigned int> top_surface_image_components_bottom_to_top(const TextureMappingZone &zone,
const PrintConfig &config,
std::vector<unsigned int> components)
{
components.erase(std::remove_if(components.begin(), components.end(), [](unsigned int id) { return id == 0; }), components.end());
components.erase(std::unique(components.begin(), components.end()), components.end());
if (components.empty())
return components;
bool has_complete_td = true;
for (unsigned int component_id : components) {
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;
}
}
if (has_complete_td) {
std::stable_sort(components.begin(), components.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)];
});
return components;
}
auto black_it = std::min_element(components.begin(), components.end(), [&config](unsigned int lhs, unsigned int rhs) {
return top_surface_image_filament_luminance(config, lhs) < top_surface_image_filament_luminance(config, rhs);
});
if (black_it != components.end() && black_it != components.begin())
std::rotate(components.begin(), black_it, std::next(black_it));
return components;
}
static std::optional<std::array<float, 4>> top_surface_image_equal_blend_background(const PrintConfig &config,
const std::vector<unsigned int> &component_ids)
{
std::vector<std::array<float, 3>> colors;
colors.reserve(component_ids.size());
for (unsigned int component_id : component_ids) {
if (component_id == 0 || component_id > config.filament_colour.values.size())
return std::nullopt;
ColorRGB color;
if (!decode_color(config.filament_colour.get_at(size_t(component_id - 1)), color))
return std::nullopt;
colors.push_back({ color.r(), color.g(), color.b() });
}
if (colors.empty())
return std::nullopt;
std::vector<float> weights(colors.size(), 1.f / float(colors.size()));
const std::array<float, 3> mixed = mix_color_solver_components(colors, weights, ColorSolverMixModel::PigmentPainter);
return std::array<float, 4> { mixed[0], mixed[1], mixed[2], 1.f };
}
static ExPolygons top_surface_image_visible_top_mask(const Layer &layer, unsigned int zone_id)
{
ExPolygons mask;
for (const LayerRegion *layerm : layer.regions()) {
if (layerm == nullptr || unsigned(std::max(0, layerm->region().config().solid_infill_filament.value)) != zone_id)
continue;
for (const Surface &surface : layerm->fill_surfaces.surfaces)
if (surface.is_top())
mask.emplace_back(surface.expolygon);
}
return mask.size() > 1 ? union_ex(mask) : mask;
}
static std::vector<TopSurfaceImageRegionPlan> top_surface_image_region_plans(const Layer &layer)
{
std::vector<TopSurfaceImageRegionPlan> plans(layer.regions().size());
const PrintObject *object = layer.object();
if (object == nullptr || object->print() == nullptr)
return plans;
const Print *print = object->print();
const PrintConfig &print_config = print->config();
const TextureMappingManager &texture_mgr = print->texture_mapping_manager();
const size_t num_physical = print_config.filament_colour.values.size();
if (num_physical == 0)
return plans;
Polygons current_layer_perimeters;
for (const LayerRegion *layerm : layer.regions())
if (layerm != nullptr)
layerm->perimeters.polygons_covered_by_width(current_layer_perimeters, 0.f);
for (size_t region_id = 0; region_id < layer.regions().size(); ++region_id) {
const LayerRegion *layerm = layer.regions()[region_id];
if (layerm == nullptr)
continue;
const int raw_zone_id = layerm->region().config().solid_infill_filament.value;
if (raw_zone_id <= 0)
continue;
const unsigned int zone_id = unsigned(raw_zone_id);
const TextureMappingZone *zone = texture_mgr.zone_from_id(zone_id);
if (zone == nullptr || !zone->enabled || zone->deleted || !zone->top_surface_image_printing_active())
continue;
std::vector<std::string> filament_colours = print_config.filament_colour.values;
filament_colours.resize(num_physical, "#FFFFFF");
std::vector<unsigned int> components =
TextureMappingManager::effective_texture_component_ids(*zone, num_physical, filament_colours);
components.erase(std::remove_if(components.begin(), components.end(), [num_physical](unsigned int id) {
return id == 0 || id > num_physical;
}), components.end());
components.erase(std::unique(components.begin(), components.end()), components.end());
if (components.empty())
continue;
TopSurfaceImageRegionPlan plan;
plan.zone = zone;
plan.zone_id = zone_id;
plan.same_layer_partition =
zone->top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageSameLayer45Partition);
plan.components_bottom_to_top = plan.same_layer_partition ?
components :
top_surface_image_components_bottom_to_top(*zone, print_config, components);
if (plan.components_bottom_to_top.empty())
continue;
plan.max_width_mm = std::clamp(zone->top_surface_image_max_line_width_mm,
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
TextureMappingZone::MaxTopSurfaceImageLineWidthMm);
plan.min_width_mm = std::clamp(zone->top_surface_image_min_line_width_mm,
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
plan.max_width_mm);
plan.fixed_coloring = zone->top_surface_image_fixed_coloring_filaments;
plan.colored_top_layers = std::clamp(zone->top_surface_image_colored_top_layers,
TextureMappingZone::MinTopSurfaceImageColoredTopLayers,
TextureMappingZone::MaxTopSurfaceImageColoredTopLayers);
const int stack_depth = plan.same_layer_partition ?
plan.colored_top_layers :
int(plan.components_bottom_to_top.size());
for (int depth = 0; depth < stack_depth; ++depth) {
const Layer *top_layer = &layer;
for (int i = 0; i < depth && top_layer != nullptr; ++i)
top_layer = top_layer->upper_layer;
if (top_layer == nullptr)
break;
ExPolygons area = top_surface_image_visible_top_mask(*top_layer, zone_id);
if (area.empty())
continue;
if (!current_layer_perimeters.empty())
area = diff_ex(area, current_layer_perimeters, ApplySafetyOffset::Yes);
if (area.empty())
continue;
if (plan.same_layer_partition) {
const ExPolygons depth_area = area;
for (size_t component_idx = 0; component_idx < plan.components_bottom_to_top.size(); ++component_idx) {
TopSurfaceImageStackSlice slice;
slice.component_id = plan.components_bottom_to_top[component_idx];
slice.depth = depth;
slice.component_index = component_idx;
slice.component_count = plan.components_bottom_to_top.size();
slice.same_layer_partition = true;
slice.angle_rad = (depth & 1) ? float(-PI / 4.0) : float(PI / 4.0);
slice.area = depth_area;
plan.slices.emplace_back(std::move(slice));
}
} else {
TopSurfaceImageStackSlice slice;
slice.component_id = plan.components_bottom_to_top[size_t(stack_depth - 1 - depth)];
slice.depth = depth;
slice.component_index = size_t(stack_depth - 1 - depth);
slice.component_count = plan.components_bottom_to_top.size();
slice.area = std::move(area);
plan.slices.emplace_back(std::move(slice));
}
}
if (!plan.slices.empty())
plans[region_id] = std::move(plan);
}
return plans;
}
static SurfaceFill& surface_fill_for_params(std::vector<SurfaceFill> &surface_fills, const SurfaceFillParams &params)
{
for (SurfaceFill &fill : surface_fills)
if (fill.params == params)
return fill;
surface_fills.emplace_back(params);
return surface_fills.back();
}
static void append_surface_fill_expolygons(SurfaceFill &fill,
size_t region_id,
const Surface &surface,
ExPolygons &&expolygons,
const LayerRegion &layerm)
{
if (expolygons.empty())
return;
if (fill.region_id == size_t(-1)) {
fill.region_id = region_id;
fill.surface = surface;
fill.surface.expolygon = ExPolygon();
fill.expolygons = std::move(expolygons);
fill.region_id_group.push_back(region_id);
fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons;
} else {
append(fill.expolygons, std::move(expolygons));
auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id);
if (t == fill.region_id_group.end()) {
fill.region_id_group.push_back(region_id);
fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons);
}
}
}
static SurfaceFillParams top_surface_image_params_for_slice(const Layer &layer,
const Surface &surface,
const SurfaceFillParams &base_params,
const TopSurfaceImageRegionPlan &plan,
const TopSurfaceImageStackSlice &slice)
{
SurfaceFillParams params = base_params;
params.extruder = slice.component_id;
params.pattern = ipRectilinear;
params.density = 100.f;
params.angle = slice.angle_rad;
params.fixed_angle = true;
params.bridge = false;
params.bridge_angle = 0.f;
params.multiline = 1;
params.anchor_length = 1000.f;
params.anchor_length_max = 1000.f;
params.extrusion_role = surface.is_top() ? erTopSolidInfill : erSolidInfill;
const PrintConfig &print_config = layer.object()->print()->config();
const float nozzle = slice.component_id > 0 && size_t(slice.component_id - 1) < print_config.nozzle_diameter.values.size() ?
float(print_config.nozzle_diameter.get_at(size_t(slice.component_id - 1))) :
float(print_config.nozzle_diameter.values.empty() ? 0.4 : print_config.nozzle_diameter.values.front());
const float height = float((surface.thickness == -1) ? layer.height : surface.thickness);
params.flow = Flow(plan.max_width_mm, height, nozzle);
params.spacing = slice.same_layer_partition && slice.component_count > 0 ?
(plan.min_width_mm * float(slice.component_count) + (plan.max_width_mm - plan.min_width_mm)) :
params.flow.spacing();
params.texture_mapping_top_surface_image = true;
params.texture_mapping_top_surface_zone_id = plan.zone_id;
params.texture_mapping_top_surface_component_id = slice.component_id;
params.texture_mapping_top_surface_stack_depth = slice.depth;
params.texture_mapping_top_surface_fixed_coloring = plan.fixed_coloring;
params.texture_mapping_top_surface_min_width_mm = plan.min_width_mm;
params.texture_mapping_top_surface_max_width_mm = plan.max_width_mm;
params.texture_mapping_top_surface_same_layer_partition = slice.same_layer_partition;
params.texture_mapping_top_surface_component_index = int(slice.component_index);
params.texture_mapping_top_surface_component_count = int(slice.component_count);
return params;
}
static ExtrusionPaths top_surface_image_split_path(const ExtrusionPath &path,
const TextureMappingOffsetContext &context,
float min_width_mm,
float max_width_mm,
float nozzle_diameter)
{
ExtrusionPaths out;
if (path.polyline.points.size() < 2)
return out;
const float height = path.height > 0.f ? path.height : context.layer_height_mm;
const float sample_step_mm = std::clamp(max_width_mm * 0.5f, 0.16f, 0.40f);
for (size_t point_idx = 1; point_idx < path.polyline.points.size(); ++point_idx) {
const Point p0 = path.polyline.points[point_idx - 1];
const Point p1 = path.polyline.points[point_idx];
const double len_mm = unscale<double>(p0.distance_to(p1));
if (!std::isfinite(len_mm) || len_mm <= EPSILON)
continue;
const int steps = std::max(1, int(std::ceil(len_mm / sample_step_mm)));
for (int step = 0; step < steps; ++step) {
const double t0 = double(step) / double(steps);
const double t1 = double(step + 1) / double(steps);
const Point q0 = lerp(p0, p1, t0);
const Point q1 = lerp(p0, p1, t1);
if (q0 == q1)
continue;
const Point qm = lerp(p0, p1, 0.5 * (t0 + t1));
const std::vector<float> weights =
sample_weight_field_components(context.weight_field,
unscale<float>(qm.x()),
unscale<float>(qm.y()),
context.high_resolution_texture_sampling);
const float coverage = context.active_component_idx < weights.size() ?
std::clamp(weights[context.active_component_idx], 0.f, 1.f) :
0.f;
const float width = std::clamp(min_width_mm + coverage * (max_width_mm - min_width_mm),
min_width_mm,
max_width_mm);
Polyline polyline;
polyline.points.emplace_back(q0);
polyline.points.emplace_back(q1);
ExtrusionPath segment(std::move(polyline), path);
segment.width = width;
segment.height = height;
segment.mm3_per_mm = Flow(width, height, nozzle_diameter).mm3_per_mm();
out.emplace_back(std::move(segment));
}
}
return out;
}
static ExtrusionEntity* top_surface_image_modulated_entity(const ExtrusionEntity &entity,
const TextureMappingOffsetContext &context,
float min_width_mm,
float max_width_mm,
float nozzle_diameter)
{
if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath *>(&entity)) {
ExtrusionPaths paths = top_surface_image_split_path(*path, context, min_width_mm, max_width_mm, nozzle_diameter);
if (paths.empty())
return entity.clone();
return new ExtrusionMultiPath(std::move(paths));
}
if (const ExtrusionMultiPath *multi_path = dynamic_cast<const ExtrusionMultiPath *>(&entity)) {
ExtrusionPaths paths;
for (const ExtrusionPath &path : multi_path->paths) {
ExtrusionPaths split = top_surface_image_split_path(path, context, min_width_mm, max_width_mm, nozzle_diameter);
append(paths, std::move(split));
}
if (paths.empty())
return entity.clone();
ExtrusionMultiPath *out = new ExtrusionMultiPath(std::move(paths));
if (!multi_path->can_reverse())
out->set_reverse();
return out;
}
if (const ExtrusionLoop *loop = dynamic_cast<const ExtrusionLoop *>(&entity)) {
ExtrusionPaths paths;
for (const ExtrusionPath &path : loop->paths) {
ExtrusionPaths split = top_surface_image_split_path(path, context, min_width_mm, max_width_mm, nozzle_diameter);
append(paths, std::move(split));
}
if (paths.empty())
return entity.clone();
return new ExtrusionLoop(std::move(paths));
}
if (const ExtrusionEntityCollection *collection = dynamic_cast<const ExtrusionEntityCollection *>(&entity)) {
ExtrusionEntityCollection *out = new ExtrusionEntityCollection(*collection);
for (ExtrusionEntity *&child : out->entities) {
ExtrusionEntity *replacement =
top_surface_image_modulated_entity(*child, context, min_width_mm, max_width_mm, nozzle_diameter);
delete child;
child = replacement;
}
return out;
}
return entity.clone();
}
static std::vector<float> top_surface_image_same_layer_fractions(const std::optional<TextureMappingOffsetContext> &context,
int component_count,
float x_mm,
float y_mm)
{
component_count = std::max(1, component_count);
std::vector<float> fractions(size_t(component_count), 1.f / float(component_count));
if (!context)
return fractions;
std::vector<float> weights =
sample_weight_field_components(context->weight_field,
x_mm,
y_mm,
context->high_resolution_texture_sampling);
if (weights.size() != size_t(component_count))
return fractions;
fractions.assign(size_t(component_count), 0.f);
float sum = 0.f;
for (size_t i = 0; i < weights.size(); ++i) {
fractions[i] = std::max(0.f, weights[i]);
sum += fractions[i];
}
if (sum <= EPSILON || !std::isfinite(sum)) {
std::fill(fractions.begin(), fractions.end(), 1.f / float(component_count));
return fractions;
}
for (float &fraction : fractions)
fraction /= sum;
return fractions;
}
static void top_surface_image_same_layer_partition_fill(ExtrusionEntityCollection &collection,
const Surface &surface,
const SurfaceFillParams &params,
const std::optional<TextureMappingOffsetContext> &context)
{
int component_count = std::max(1, params.texture_mapping_top_surface_component_count);
int component_index = std::clamp(params.texture_mapping_top_surface_component_index, 0, component_count - 1);
if (context && int(context->component_ids.size()) == component_count) {
auto component_it = std::find(context->component_ids.begin(),
context->component_ids.end(),
params.texture_mapping_top_surface_component_id);
if (component_it != context->component_ids.end())
component_index = int(std::distance(context->component_ids.begin(), component_it));
component_index = std::clamp(component_index, 0, component_count - 1);
}
const float min_width = std::clamp(params.texture_mapping_top_surface_min_width_mm,
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
TextureMappingZone::MaxTopSurfaceImageLineWidthMm);
const float max_width = std::clamp(params.texture_mapping_top_surface_max_width_mm,
min_width,
TextureMappingZone::MaxTopSurfaceImageLineWidthMm);
const float pitch = min_width * float(component_count) + (max_width - min_width);
if (pitch <= EPSILON)
return;
const BoundingBox bbox = get_extents(surface.expolygon);
if (!bbox.defined)
return;
const double theta = params.angle;
const double cos_t = std::cos(theta);
const double sin_t = std::sin(theta);
double min_u = std::numeric_limits<double>::max();
double min_v = std::numeric_limits<double>::max();
double max_u = -std::numeric_limits<double>::max();
double max_v = -std::numeric_limits<double>::max();
for (size_t i = 0; i < 4; ++i) {
const Point corner = bbox[i];
const double x = unscale<double>(corner.x());
const double y = unscale<double>(corner.y());
const double u = x * cos_t + y * sin_t;
const double v = -x * sin_t + y * cos_t;
min_u = std::min(min_u, u);
max_u = std::max(max_u, u);
min_v = std::min(min_v, v);
max_v = std::max(max_v, v);
}
const double sample_step = std::clamp(double(max_width), 0.32, 0.80);
const double u_start = std::floor((min_u - pitch) / sample_step) * sample_step;
const double u_end = std::ceil((max_u + pitch) / sample_step) * sample_step;
const int band_start = int(std::floor((min_v - pitch) / double(pitch)));
const int band_end = int(std::ceil((max_v + pitch) / double(pitch)));
const ExPolygons clip { surface.expolygon };
for (int band = band_start; band <= band_end; ++band) {
const double band_v = double(band) * double(pitch);
const double sample_v = band_v + double(pitch) * 0.5;
for (double u0 = u_start; u0 < u_end - EPSILON; u0 += sample_step) {
const double u1 = std::min(u0 + sample_step, u_end);
const double um = 0.5 * (u0 + u1);
const double sample_x = um * cos_t - sample_v * sin_t;
const double sample_y = um * sin_t + sample_v * cos_t;
const std::vector<float> fractions =
top_surface_image_same_layer_fractions(context,
component_count,
float(sample_x),
float(sample_y));
const float available_width = std::max(0.f, pitch - min_width * float(component_count));
double lane_offset = 0.0;
float lane_width = min_width;
for (int i = 0; i < component_count; ++i) {
const float width = min_width + available_width * fractions[size_t(i)];
if (i == component_index) {
lane_width = width;
break;
}
lane_offset += width;
}
lane_width = std::clamp(lane_width, min_width, max_width);
const double v_center = band_v + lane_offset + 0.5 * double(lane_width);
const double x0 = u0 * cos_t - v_center * sin_t;
const double y0 = u0 * sin_t + v_center * cos_t;
const double x1 = u1 * cos_t - v_center * sin_t;
const double y1 = u1 * sin_t + v_center * cos_t;
Polyline polyline;
polyline.points.emplace_back(Point::new_scale(x0, y0));
polyline.points.emplace_back(Point::new_scale(x1, y1));
if (polyline.points.front() == polyline.points.back())
continue;
ExtrusionPath path(params.extrusion_role,
Flow(lane_width, params.flow.height(), params.flow.nozzle_diameter()).mm3_per_mm(),
lane_width,
params.flow.height());
path.polyline = std::move(polyline);
path.intersect_expolygons(clip, &collection);
}
}
}
static void apply_top_surface_image_collection_metadata(ExtrusionEntityCollection &collection,
const SurfaceFillParams &params,
const std::optional<TextureMappingOffsetContext> &context)
{
collection.texture_mapping_top_surface_image = true;
collection.texture_mapping_top_surface_zone_id = params.texture_mapping_top_surface_zone_id;
collection.texture_mapping_top_surface_desired_component_id = params.texture_mapping_top_surface_component_id;
collection.texture_mapping_top_surface_stack_depth = params.texture_mapping_top_surface_stack_depth;
collection.texture_mapping_top_surface_fixed_coloring = params.texture_mapping_top_surface_fixed_coloring;
if (params.texture_mapping_top_surface_fixed_coloring &&
params.texture_mapping_top_surface_component_id > 0)
collection.texture_mapping_extruder_override = int(params.texture_mapping_top_surface_component_id - 1);
if (!context)
return;
ExtrusionEntitiesPtr replacement;
replacement.reserve(collection.entities.size());
for (const ExtrusionEntity *entity : collection.entities)
replacement.emplace_back(top_surface_image_modulated_entity(*entity,
*context,
params.texture_mapping_top_surface_min_width_mm,
params.texture_mapping_top_surface_max_width_mm,
params.flow.nozzle_diameter()));
collection.clear();
collection.entities = std::move(replacement);
}
// Detect narrow infill regions
// Based on the anti-vibration algorithm from PrusaSlicer:
@@ -836,6 +1412,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
SurfaceFillParams params;
bool has_internal_voids = false;
const PrintObjectConfig& object_config = layer.object()->config();
const std::vector<TopSurfaceImageRegionPlan> top_surface_plans = top_surface_image_region_plans(layer);
auto append_flow_param = [](std::map<Flow, ExPolygons> &flow_params, Flow flow, const ExPolygon &exp) {
auto it = flow_params.find(flow);
@@ -1012,23 +1589,50 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
if (surface.surface_type != stInternalVoid) {
const SurfaceFillParams *params = region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()];
if (params != nullptr) {
SurfaceFill &fill = surface_fills[params->idx];
if (fill.region_id == size_t(-1)) {
fill.region_id = region_id;
fill.surface = surface;
fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
//BBS
fill.region_id_group.push_back(region_id);
fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons;
} else {
fill.expolygons.emplace_back(surface.expolygon);
//BBS
auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id);
if (t == fill.region_id_group.end()) {
fill.region_id_group.push_back(region_id);
fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons);
}
}
ExPolygons remaining = { surface.expolygon };
if (region_id < top_surface_plans.size() &&
top_surface_plans[region_id].zone != nullptr &&
(surface.is_top() || surface.surface_type == stInternalSolid) &&
!surface.is_bridge()) {
const TopSurfaceImageRegionPlan &plan = top_surface_plans[region_id];
for (size_t slice_idx = 0; slice_idx < plan.slices.size();) {
const TopSurfaceImageStackSlice &slice = plan.slices[slice_idx];
if (remaining.empty())
break;
ExPolygons image_expolygons = intersection_ex(remaining, slice.area, ApplySafetyOffset::Yes);
if (image_expolygons.empty()) {
++slice_idx;
continue;
}
ExPolygons image_clip = image_expolygons;
if (slice.same_layer_partition) {
size_t same_depth_end = slice_idx;
while (same_depth_end < plan.slices.size() &&
plan.slices[same_depth_end].same_layer_partition &&
plan.slices[same_depth_end].depth == slice.depth)
++same_depth_end;
for (size_t same_idx = slice_idx; same_idx < same_depth_end; ++same_idx) {
SurfaceFillParams image_params =
top_surface_image_params_for_slice(layer, surface, *params, plan, plan.slices[same_idx]);
ExPolygons component_expolygons = image_expolygons;
SurfaceFill &image_fill = surface_fill_for_params(surface_fills, image_params);
append_surface_fill_expolygons(image_fill, region_id, surface, std::move(component_expolygons), layerm);
}
slice_idx = same_depth_end;
} else {
SurfaceFillParams image_params =
top_surface_image_params_for_slice(layer, surface, *params, plan, slice);
SurfaceFill &image_fill = surface_fill_for_params(surface_fills, image_params);
append_surface_fill_expolygons(image_fill, region_id, surface, std::move(image_expolygons), layerm);
++slice_idx;
}
remaining = diff_ex(remaining, image_clip, ApplySafetyOffset::Yes);
}
}
if (!remaining.empty()) {
SurfaceFill &fill = surface_fills[params->idx];
append_surface_fill_expolygons(fill, region_id, surface, std::move(remaining), layerm);
}
}
}
}
@@ -1037,6 +1641,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
Polygons all_polygons;
for (SurfaceFill &fill : surface_fills)
if (! fill.expolygons.empty()) {
if (fill.params.texture_mapping_top_surface_same_layer_partition)
continue;
if (fill.expolygons.size() > 1 || ! all_polygons.empty()) {
Polygons polys = to_polygons(std::move(fill.expolygons));
// Make a union of polygons, use a safety offset, subtract the preceding polygons.
@@ -1098,7 +1704,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
region_id = region_some_infill;
const LayerRegion& layerm = *layer.regions()[region_id];
for (SurfaceFill &surface_fill : surface_fills)
if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height()) < EPSILON) {
if (surface_fill.surface.surface_type == stInternalSolid &&
!surface_fill.params.texture_mapping_top_surface_image &&
std::abs(layer.height - surface_fill.params.flow.height()) < EPSILON) {
internal_solid_fill = &surface_fill;
break;
}
@@ -1135,7 +1743,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
if (layer.object()->config().detect_narrow_internal_solid_infill) {
size_t surface_fills_size = surface_fills.size();
for (size_t i = 0; i < surface_fills_size; i++) {
if (surface_fills[i].surface.surface_type != stInternalSolid)
if (surface_fills[i].surface.surface_type != stInternalSolid ||
surface_fills[i].params.texture_mapping_top_surface_image)
continue;
ExPolygons normal_infill;
@@ -1300,6 +1909,37 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
}
if (surface_fill.params.pattern == ipGrid)
params.can_reverse = false;
std::optional<TextureMappingOffsetContext> top_surface_image_context;
if (surface_fill.params.texture_mapping_top_surface_image) {
const Print *print = this->object()->print();
const TextureMappingZone *zone = print != nullptr ?
print->texture_mapping_manager().zone_from_id(surface_fill.params.texture_mapping_top_surface_zone_id) :
nullptr;
if (print != nullptr && zone != nullptr) {
const PrintConfig &print_config = print->config();
std::vector<std::string> filament_colours = print_config.filament_colour.values;
filament_colours.resize(print_config.filament_colour.values.size(), "#FFFFFF");
std::vector<unsigned int> components =
TextureMappingManager::effective_texture_component_ids(*zone,
print_config.filament_colour.values.size(),
filament_colours);
const std::optional<std::array<float, 4>> background =
top_surface_image_equal_blend_background(print_config, components);
top_surface_image_context =
build_texture_mapping_offset_context_for_layer(*this->object(),
*this,
*zone,
surface_fill.params.texture_mapping_top_surface_zone_id,
surface_fill.params.texture_mapping_top_surface_component_id,
surface_fill.params.texture_mapping_top_surface_max_width_mm,
float(this->height),
std::nullopt,
surface_fill.params.texture_mapping_top_surface_min_width_mm,
background);
}
}
for (ExPolygon& expoly : surface_fill.expolygons) {
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
@@ -1328,9 +1968,30 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.density = elefant_density * (elefant_layers - (f->layer_id - 1)) / elefant_layers;
}
// make fill
f->fill_surface_extrusion(&surface_fill.surface,
params,
m_regions[surface_fill.region_id]->fills.entities);
ExtrusionEntitiesPtr &fill_entities = m_regions[surface_fill.region_id]->fills.entities;
if (surface_fill.params.texture_mapping_top_surface_same_layer_partition) {
ExtrusionEntityCollection *collection = new ExtrusionEntityCollection();
top_surface_image_same_layer_partition_fill(*collection,
surface_fill.surface,
surface_fill.params,
top_surface_image_context);
if (!collection->empty()) {
apply_top_surface_image_collection_metadata(*collection, surface_fill.params, std::nullopt);
fill_entities.push_back(collection);
} else {
delete collection;
}
} else {
const size_t fill_entities_before = fill_entities.size();
f->fill_surface_extrusion(&surface_fill.surface,
params,
fill_entities);
if (surface_fill.params.texture_mapping_top_surface_image) {
for (size_t i = fill_entities_before; i < fill_entities.size(); ++i)
if (ExtrusionEntityCollection *collection = dynamic_cast<ExtrusionEntityCollection *>(fill_entities[i]))
apply_top_surface_image_collection_metadata(*collection, surface_fill.params, top_surface_image_context);
}
}
}
}
@@ -1341,8 +2002,14 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
for (LayerRegion *layerm : m_regions)
for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) {
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
if (const auto *thin_fill_collection = dynamic_cast<const ExtrusionEntityCollection *>(thin_fill))
if (const auto *thin_fill_collection = dynamic_cast<const ExtrusionEntityCollection *>(thin_fill)) {
collection.texture_mapping_extruder_override = thin_fill_collection->texture_mapping_extruder_override;
collection.texture_mapping_top_surface_image = thin_fill_collection->texture_mapping_top_surface_image;
collection.texture_mapping_top_surface_zone_id = thin_fill_collection->texture_mapping_top_surface_zone_id;
collection.texture_mapping_top_surface_desired_component_id = thin_fill_collection->texture_mapping_top_surface_desired_component_id;
collection.texture_mapping_top_surface_stack_depth = thin_fill_collection->texture_mapping_top_surface_stack_depth;
collection.texture_mapping_top_surface_fixed_coloring = thin_fill_collection->texture_mapping_top_surface_fixed_coloring;
}
layerm->fills.entities.push_back(&collection);
collection.entities.push_back(thin_fill->clone());
}

View File

@@ -113,6 +113,14 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c
(this->num_physical_filaments == 0 || unsigned(extrusions.texture_mapping_extruder_override) < this->num_physical_filaments))
return unsigned(extrusions.texture_mapping_extruder_override);
if (extrusions.texture_mapping_top_surface_image &&
!extrusions.texture_mapping_top_surface_fixed_coloring &&
extrusions.texture_mapping_top_surface_desired_component_id > 0) {
const unsigned int desired = extrusions.texture_mapping_top_surface_desired_component_id - 1;
if (this->has_extruder(desired))
return desired;
}
assert(region.config().wall_filament.value > 0);
assert(region.config().sparse_infill_filament.value > 0);
assert(region.config().solid_infill_filament.value > 0);
@@ -214,6 +222,19 @@ static FilamentChangeStats calc_filament_change_info_by_toolorder(const PrintCon
static void apply_first_layer_order(const DynamicPrintConfig* config, std::vector<unsigned int>& tool_order);
static void apply_top_surface_no_fixed_carry(LayerTools &lt, unsigned int last_extruder_id)
{
if (last_extruder_id == 0 || lt.extruders.empty())
return;
if (std::find(lt.extruders.begin(), lt.extruders.end(), last_extruder_id) != lt.extruders.end())
return;
for (unsigned int desired : lt.top_surface_image_no_fixed_desired_extruders)
if (desired == last_extruder_id) {
lt.extruders.insert(lt.extruders.begin(), desired);
return;
}
}
void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& tool_order_layer0)
{
if(m_layer_tools.empty() || tool_order_layer0.empty())
@@ -267,6 +288,7 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& too
break;
}
}
apply_top_surface_no_fixed_carry(lt, last_extruder_id);
last_extruder_id = lt.extruders.back();
}
@@ -311,7 +333,8 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id)
++ last_extruder_id;
}
for (LayerTools &lt : m_layer_tools) {
for (size_t layer_idx = 0; layer_idx < m_layer_tools.size(); ++layer_idx) {
LayerTools &lt = m_layer_tools[layer_idx];
if (lt.extruders.empty())
continue;
if (lt.extruders.size() == 1 && lt.extruders.front() == 0)
@@ -345,6 +368,8 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id)
}
}
if (layer_idx > 0)
apply_top_surface_no_fixed_carry(lt, last_extruder_id);
last_extruder_id = lt.extruders.back();
}
@@ -847,6 +872,13 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
has_texture_override_fill = true;
continue;
}
if (fill->texture_mapping_top_surface_image &&
!fill->texture_mapping_top_surface_fixed_coloring &&
fill->texture_mapping_top_surface_desired_component_id >= 1 &&
(layer_tools.num_physical_filaments == 0 ||
fill->texture_mapping_top_surface_desired_component_id <= layer_tools.num_physical_filaments))
layer_tools.top_surface_image_no_fixed_desired_extruders.emplace_back(
fill->texture_mapping_top_surface_desired_component_id);
ExtrusionRole role = fill->entities.empty() ? erNone : fill->entities.front()->role();
if (is_solid_infill(role))
has_solid_infill = true;
@@ -933,6 +965,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
sort_remove_duplicates(layer.extruders);
sort_remove_duplicates(layer.texture_mapping_extruders);
sort_remove_duplicates(layer.texture_mapping_component_extruders);
sort_remove_duplicates(layer.top_surface_image_no_fixed_desired_extruders);
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
if (layer.extruders.empty() && layer.has_object)

View File

@@ -158,6 +158,7 @@ public:
std::vector<unsigned int> extruders;
std::vector<unsigned int> texture_mapping_extruders;
std::vector<unsigned int> texture_mapping_component_extruders;
std::vector<unsigned int> top_surface_image_no_fixed_desired_extruders;
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
// If not overriden, it is set to 0.
unsigned int extruder_override = 0;

View File

@@ -666,6 +666,20 @@ static int modulation_mode_from_name(const std::string &name)
return int(TextureMappingZone::ModulationLineWidth);
}
static std::string top_surface_image_printing_method_name(int method)
{
if (method == int(TextureMappingZone::TopSurfaceImageSameLayer45Partition))
return std::string("same_layer_45_partition");
return std::string("same_angle_45_width");
}
static int top_surface_image_printing_method_from_name(const std::string &name)
{
if (name == "same_layer_45_partition")
return int(TextureMappingZone::TopSurfaceImageSameLayer45Partition);
return int(TextureMappingZone::TopSurfaceImageSameAngle45Width);
}
static std::string top_visible_recolor_aggressiveness_name(int mode)
{
switch (clamp_int(mode,
@@ -1024,6 +1038,12 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
top_visible_perimeter_recolor_aggressiveness == rhs.top_visible_perimeter_recolor_aggressiveness &&
top_visible_perimeter_recolor_above_layers == rhs.top_visible_perimeter_recolor_above_layers &&
top_visible_perimeter_recolor_point_sampling == rhs.top_visible_perimeter_recolor_point_sampling &&
top_surface_image_printing_enabled == rhs.top_surface_image_printing_enabled &&
top_surface_image_printing_method == rhs.top_surface_image_printing_method &&
std::abs(top_surface_image_min_line_width_mm - rhs.top_surface_image_min_line_width_mm) <= eps &&
std::abs(top_surface_image_max_line_width_mm - rhs.top_surface_image_max_line_width_mm) <= eps &&
top_surface_image_colored_top_layers == rhs.top_surface_image_colored_top_layers &&
top_surface_image_fixed_coloring_filaments == rhs.top_surface_image_fixed_coloring_filaments &&
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 &&
@@ -1362,6 +1382,25 @@ std::string TextureMappingManager::serialize_entries()
TextureMappingZone::MinTopVisiblePerimeterRecolorAboveLayers,
TextureMappingZone::MaxTopVisiblePerimeterRecolorAboveLayers);
texture["top_visible_perimeter_recolor_point_sampling"] = zone.top_visible_perimeter_recolor_point_sampling;
texture["top_surface_image_printing_enabled"] = zone.top_surface_image_printing_enabled;
texture["top_surface_image_printing_method"] =
top_surface_image_printing_method_name(zone.top_surface_image_printing_method);
const float top_surface_max_width =
std::clamp(finite_or(zone.top_surface_image_max_line_width_mm,
TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm),
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
TextureMappingZone::MaxTopSurfaceImageLineWidthMm);
texture["top_surface_image_min_line_width_mm"] =
std::clamp(finite_or(zone.top_surface_image_min_line_width_mm,
TextureMappingZone::DefaultTopSurfaceImageMinLineWidthMm),
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
top_surface_max_width);
texture["top_surface_image_max_line_width_mm"] = top_surface_max_width;
texture["top_surface_image_colored_top_layers"] =
clamp_int(zone.top_surface_image_colored_top_layers,
TextureMappingZone::MinTopSurfaceImageColoredTopLayers,
TextureMappingZone::MaxTopSurfaceImageColoredTopLayers);
texture["top_surface_image_fixed_coloring_filaments"] = zone.top_surface_image_fixed_coloring_filaments;
texture["compact_offset_mode"] = zone.compact_offset_mode;
texture["use_legacy_fixed_color_mode"] = zone.use_legacy_fixed_color_mode;
texture["high_speed_image_texture_sampling"] = true;
@@ -1560,6 +1599,33 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.top_visible_perimeter_recolor_point_sampling =
texture.value("top_visible_perimeter_recolor_point_sampling",
TextureMappingZone::DefaultTopVisiblePerimeterRecolorPointSampling);
zone.top_surface_image_printing_enabled =
texture.value("top_surface_image_printing_enabled",
TextureMappingZone::DefaultTopSurfaceImagePrintingEnabled);
zone.top_surface_image_printing_method =
top_surface_image_printing_method_from_name(
texture.value("top_surface_image_printing_method",
top_surface_image_printing_method_name(TextureMappingZone::DefaultTopSurfaceImagePrintingMethod)));
zone.top_surface_image_max_line_width_mm =
std::clamp(finite_or(texture.value("top_surface_image_max_line_width_mm",
TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm),
TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm),
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
TextureMappingZone::MaxTopSurfaceImageLineWidthMm);
zone.top_surface_image_min_line_width_mm =
std::clamp(finite_or(texture.value("top_surface_image_min_line_width_mm",
TextureMappingZone::DefaultTopSurfaceImageMinLineWidthMm),
TextureMappingZone::DefaultTopSurfaceImageMinLineWidthMm),
TextureMappingZone::MinTopSurfaceImageLineWidthMm,
zone.top_surface_image_max_line_width_mm);
zone.top_surface_image_colored_top_layers =
clamp_int(texture.value("top_surface_image_colored_top_layers",
TextureMappingZone::DefaultTopSurfaceImageColoredTopLayers),
TextureMappingZone::MinTopSurfaceImageColoredTopLayers,
TextureMappingZone::MaxTopSurfaceImageColoredTopLayers);
zone.top_surface_image_fixed_coloring_filaments =
texture.value("top_surface_image_fixed_coloring_filaments",
TextureMappingZone::DefaultTopSurfaceImageFixedColoringFilaments);
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);

View File

@@ -66,6 +66,11 @@ struct TextureMappingZone
TopVisibleRecolorAggressive = 2
};
enum TopSurfaceImagePrintingMethod : uint8_t {
TopSurfaceImageSameAngle45Width = 0,
TopSurfaceImageSameLayer45Partition = 1
};
enum FilamentColorMode : uint8_t {
FilamentColorAny = 0,
FilamentColorRGB = 1,
@@ -140,6 +145,16 @@ struct TextureMappingZone
static constexpr int MaxTopVisiblePerimeterRecolorAboveLayers = 5;
static constexpr int DefaultTopVisiblePerimeterRecolorAboveLayers = 2;
static constexpr bool DefaultTopVisiblePerimeterRecolorPointSampling = true;
static constexpr bool DefaultTopSurfaceImagePrintingEnabled = false;
static constexpr int DefaultTopSurfaceImagePrintingMethod = int(TopSurfaceImageSameAngle45Width);
static constexpr float MinTopSurfaceImageLineWidthMm = 0.32f;
static constexpr float MaxTopSurfaceImageLineWidthMm = 0.80f;
static constexpr float DefaultTopSurfaceImageMinLineWidthMm = 0.32f;
static constexpr float DefaultTopSurfaceImageMaxLineWidthMm = 0.80f;
static constexpr int MinTopSurfaceImageColoredTopLayers = 1;
static constexpr int MaxTopSurfaceImageColoredTopLayers = 20;
static constexpr int DefaultTopSurfaceImageColoredTopLayers = 3;
static constexpr bool DefaultTopSurfaceImageFixedColoringFilaments = true;
static constexpr bool DefaultCompactOffsetMode = true;
static constexpr bool DefaultUseLegacyFixedColorMode = false;
static constexpr bool DefaultHighSpeedImageTextureSampling = true;
@@ -228,6 +243,12 @@ struct TextureMappingZone
int top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness;
int top_visible_perimeter_recolor_above_layers = DefaultTopVisiblePerimeterRecolorAboveLayers;
bool top_visible_perimeter_recolor_point_sampling = DefaultTopVisiblePerimeterRecolorPointSampling;
bool top_surface_image_printing_enabled = DefaultTopSurfaceImagePrintingEnabled;
int top_surface_image_printing_method = DefaultTopSurfaceImagePrintingMethod;
float top_surface_image_min_line_width_mm = DefaultTopSurfaceImageMinLineWidthMm;
float top_surface_image_max_line_width_mm = DefaultTopSurfaceImageMaxLineWidthMm;
int top_surface_image_colored_top_layers = DefaultTopSurfaceImageColoredTopLayers;
bool top_surface_image_fixed_coloring_filaments = DefaultTopSurfaceImageFixedColoringFilaments;
bool compact_offset_mode = DefaultCompactOffsetMode;
bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
bool high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;
@@ -272,6 +293,13 @@ struct TextureMappingZone
bool is_linear_gradient() const { return surface_pattern == int(LinearGradient); }
bool is_radial_linear_gradient() const { return is_linear_gradient() && linear_gradient_mode == int(LinearGradientRadial); }
bool is_surface_gradient() const { return is_2d_gradient() || is_linear_gradient(); }
bool top_surface_image_printing_active() const
{
return top_surface_image_printing_enabled &&
is_image_texture() &&
(top_surface_image_printing_method == int(TopSurfaceImageSameAngle45Width) ||
top_surface_image_printing_method == int(TopSurfaceImageSameLayer45Partition));
}
void apply_default_modulation_mode()
{
@@ -309,6 +337,12 @@ struct TextureMappingZone
top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness;
top_visible_perimeter_recolor_above_layers = DefaultTopVisiblePerimeterRecolorAboveLayers;
top_visible_perimeter_recolor_point_sampling = DefaultTopVisiblePerimeterRecolorPointSampling;
top_surface_image_printing_enabled = DefaultTopSurfaceImagePrintingEnabled;
top_surface_image_printing_method = DefaultTopSurfaceImagePrintingMethod;
top_surface_image_min_line_width_mm = DefaultTopSurfaceImageMinLineWidthMm;
top_surface_image_max_line_width_mm = DefaultTopSurfaceImageMaxLineWidthMm;
top_surface_image_colored_top_layers = DefaultTopSurfaceImageColoredTopLayers;
top_surface_image_fixed_coloring_filaments = DefaultTopSurfaceImageFixedColoringFilaments;
compact_offset_mode = DefaultCompactOffsetMode;
use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;

View File

@@ -1510,7 +1510,8 @@ TextureMappingOffsetWeightField build_texture_mapping_offset_weight_field(
float layer_z_mm,
float layer_z_falloff_mm,
bool high_resolution_texture_sampling,
bool high_speed_image_texture_sampling)
bool high_speed_image_texture_sampling,
std::optional<std::array<float, 4>> image_background_rgba_override)
{
TextureMappingOffsetWeightField weight_field;
if (component_colors.empty())
@@ -1616,7 +1617,8 @@ TextureMappingOffsetWeightField build_texture_mapping_offset_weight_field(
const indexed_triangle_set &its = mesh_ptr->its;
const Transform3d volume_trafo = object_trafo * volume->get_matrix();
const std::array<float, 4> background_color = texture_mapping_background_color(*volume);
const std::array<float, 4> background_color =
image_background_rgba_override ? *image_background_rgba_override : texture_mapping_background_color(*volume);
if (!volume->texture_mapping_color_facets.empty()) {
std::vector<ColorFacetTriangle> color_facets;
@@ -2813,7 +2815,9 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
unsigned int active_component_id_override,
std::optional<float> base_outer_width_mm_override,
std::optional<float> layer_height_mm_override,
std::optional<Vec2d> plate_origin_mm_override)
std::optional<Vec2d> plate_origin_mm_override,
std::optional<float> min_outer_width_mm_override,
std::optional<std::array<float, 4>> image_background_rgba_override)
{
const Print *print = print_object.print();
if (print == nullptr)
@@ -2946,10 +2950,11 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
const float base_outer_width_mm = base_outer_width_mm_override ?
std::max(0.05f, *base_outer_width_mm_override) :
std::max(0.05f, float(print_config.texture_mapping_outer_wall_gradient_max_line_width.value));
const float config_min_gradient_width_mm = std::clamp(
float(print_config.texture_mapping_outer_wall_gradient_min_line_width.value),
0.05f,
base_outer_width_mm);
const float config_min_gradient_width_mm = min_outer_width_mm_override ?
std::clamp(*min_outer_width_mm_override, 0.05f, base_outer_width_mm) :
std::clamp(float(print_config.texture_mapping_outer_wall_gradient_min_line_width.value),
0.05f,
base_outer_width_mm);
const float layer_height_mm = layer_height_mm_override ?
std::max(0.01f, *layer_height_mm_override) :
std::max(0.01f, float(layer.height));
@@ -3019,7 +3024,8 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
float(layer.print_z),
layer_sample_falloff_mm,
high_resolution_texture_sampling,
zone.high_speed_image_texture_sampling);
zone.high_speed_image_texture_sampling,
image_background_rgba_override);
if (weight_field.empty())
return std::nullopt;
}

View File

@@ -103,7 +103,9 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
unsigned int active_component_id_override = 0,
std::optional<float> base_outer_width_mm_override = std::nullopt,
std::optional<float> layer_height_mm_override = std::nullopt,
std::optional<Vec2d> plate_origin_mm_override = std::nullopt);
std::optional<Vec2d> plate_origin_mm_override = std::nullopt,
std::optional<float> min_outer_width_mm_override = std::nullopt,
std::optional<std::array<float, 4>> image_background_rgba_override = std::nullopt);
std::vector<float> sample_weight_field_components(const TextureMappingOffsetWeightField &weight_field,
float x_mm,

View File

@@ -1867,6 +1867,12 @@ public:
int dithering_method,
float dithering_resolution_mm,
float halftone_dot_size_mm,
bool top_surface_image_printing_enabled,
int top_surface_image_printing_method,
float top_surface_image_min_line_width_mm,
float top_surface_image_max_line_width_mm,
int top_surface_image_colored_top_layers,
bool top_surface_image_fixed_coloring_filaments,
const TextureMappingManager &texture_mapping_zones,
const TextureMappingGlobalSettings &global_settings,
const TextureMappingPrimeTowerImage &prime_tower_image,
@@ -1892,9 +1898,10 @@ public:
tab_choices.Add(_L("Modulation Mode"));
tab_choices.Add(_L("Preview Options"));
tab_choices.Add(_L("Experimental Options"));
tab_choices.Add(_L("Top-surface coloring (experimental)"));
tab_choices.Add(_L("Prime Tower Texture Mapping"));
m_options_tab_choice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, tab_choices);
m_options_tab_choice->SetSelection(std::clamp(initial_options_tab, 0, 5));
m_options_tab_choice->SetSelection(std::clamp(initial_options_tab, 0, 6));
tab_row->Add(m_options_tab_choice, 1, wxALIGN_CENTER_VERTICAL);
root->Add(tab_row, 0, wxEXPAND | wxALL, gap);
@@ -1914,6 +1921,9 @@ public:
auto *experimental_page = new wxPanel(m_options_book, wxID_ANY);
auto *experimental_root = new wxBoxSizer(wxVERTICAL);
experimental_page->SetSizer(experimental_root);
auto *top_surface_page = new wxPanel(m_options_book, wxID_ANY);
auto *top_surface_root = new wxBoxSizer(wxVERTICAL);
top_surface_page->SetSizer(top_surface_root);
auto *global_page = new wxPanel(m_options_book, wxID_ANY);
auto *global_root = new wxBoxSizer(wxVERTICAL);
global_page->SetSizer(global_root);
@@ -2315,6 +2325,104 @@ public:
update_modulation_mode_options_visibility(false);
experimental_root->Add(experimental_box, 0, wxEXPAND | wxALL, gap);
auto *top_surface_box = new wxStaticBoxSizer(wxVERTICAL, top_surface_page, _L("Top-surface coloring"));
m_top_surface_image_printing_enabled_checkbox =
new wxCheckBox(top_surface_page, wxID_ANY, _L("Enable top-surface image coloring"));
m_top_surface_image_printing_enabled_checkbox->SetValue(top_surface_image_printing_enabled);
top_surface_box->Add(m_top_surface_image_printing_enabled_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *top_surface_method_row = new wxBoxSizer(wxHORIZONTAL);
top_surface_method_row->Add(new wxStaticText(top_surface_page, wxID_ANY, _L("Method")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
wxArrayString top_surface_method_choices;
top_surface_method_choices.Add(_L("45 degree width modulation"));
top_surface_method_choices.Add(_L("Same-layer 45 partition"));
m_top_surface_image_method_choice =
new wxChoice(top_surface_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, top_surface_method_choices);
m_top_surface_image_method_choice->SetSelection(std::clamp(top_surface_image_printing_method,
int(TextureMappingZone::TopSurfaceImageSameAngle45Width),
int(TextureMappingZone::TopSurfaceImageSameLayer45Partition)));
top_surface_method_row->Add(m_top_surface_image_method_choice, 1, wxALIGN_CENTER_VERTICAL);
top_surface_box->Add(top_surface_method_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *top_surface_width_row = new wxBoxSizer(wxHORIZONTAL);
top_surface_width_row->Add(new wxStaticText(top_surface_page, wxID_ANY, _L("Line width")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
m_top_surface_image_min_line_width_spin =
new wxSpinCtrlDouble(top_surface_page,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(FromDIP(84), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER,
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
double(TextureMappingZone::MaxTopSurfaceImageLineWidthMm),
std::clamp(double(top_surface_image_min_line_width_mm),
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
double(TextureMappingZone::MaxTopSurfaceImageLineWidthMm)),
0.01);
m_top_surface_image_min_line_width_spin->SetDigits(2);
top_surface_width_row->Add(m_top_surface_image_min_line_width_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
top_surface_width_row->Add(new wxStaticText(top_surface_page, wxID_ANY, _L("to")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap / 2);
m_top_surface_image_max_line_width_spin =
new wxSpinCtrlDouble(top_surface_page,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(FromDIP(84), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER,
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
double(TextureMappingZone::MaxTopSurfaceImageLineWidthMm),
std::clamp(double(top_surface_image_max_line_width_mm),
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
double(TextureMappingZone::MaxTopSurfaceImageLineWidthMm)),
0.01);
m_top_surface_image_max_line_width_spin->SetDigits(2);
top_surface_width_row->Add(m_top_surface_image_max_line_width_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
top_surface_width_row->Add(new wxStaticText(top_surface_page, wxID_ANY, _L("mm")), 0, wxALIGN_CENTER_VERTICAL);
top_surface_box->Add(top_surface_width_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
m_top_surface_image_colored_top_layers_panel = new wxPanel(top_surface_page, wxID_ANY);
auto *top_surface_colored_layers_row = new wxBoxSizer(wxHORIZONTAL);
m_top_surface_image_colored_top_layers_panel->SetSizer(top_surface_colored_layers_row);
top_surface_colored_layers_row->Add(new wxStaticText(m_top_surface_image_colored_top_layers_panel, wxID_ANY, _L("Colored top layers")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
m_top_surface_image_colored_top_layers_spin =
new wxSpinCtrl(m_top_surface_image_colored_top_layers_panel,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(FromDIP(70), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT,
TextureMappingZone::MinTopSurfaceImageColoredTopLayers,
TextureMappingZone::MaxTopSurfaceImageColoredTopLayers,
std::clamp(top_surface_image_colored_top_layers,
TextureMappingZone::MinTopSurfaceImageColoredTopLayers,
TextureMappingZone::MaxTopSurfaceImageColoredTopLayers));
top_surface_colored_layers_row->Add(m_top_surface_image_colored_top_layers_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
top_surface_colored_layers_row->Add(new wxStaticText(m_top_surface_image_colored_top_layers_panel, wxID_ANY, _L("layers")),
0,
wxALIGN_CENTER_VERTICAL);
top_surface_box->Add(m_top_surface_image_colored_top_layers_panel, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
m_top_surface_image_fixed_coloring_filaments_checkbox =
new wxCheckBox(top_surface_page, wxID_ANY, _L("Fixed top-surface coloring filaments"));
m_top_surface_image_fixed_coloring_filaments_checkbox->SetValue(top_surface_image_fixed_coloring_filaments);
top_surface_box->Add(m_top_surface_image_fixed_coloring_filaments_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, gap);
m_top_surface_image_printing_enabled_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) {
update_top_surface_image_options_visibility(false);
});
m_top_surface_image_method_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) {
update_top_surface_image_options_visibility(true);
});
top_surface_root->Add(top_surface_box, 0, wxEXPAND | wxALL, gap);
update_top_surface_image_options_visibility(false);
m_prime_tower_mapping_enabled_checkbox = new wxCheckBox(global_page, wxID_ANY, _L("Enable prime tower image mapping"));
m_prime_tower_mapping_enabled_checkbox->SetValue(m_global_settings.enabled);
global_root->Add(m_prime_tower_mapping_enabled_checkbox, 0, wxEXPAND | wxALL, gap);
@@ -2451,14 +2559,15 @@ public:
m_options_book->AddPage(print_settings_page, _L("Modulation Mode"));
m_options_book->AddPage(preview_page, _L("Preview Options"));
m_options_book->AddPage(experimental_page, _L("Experimental Options"));
m_options_book->AddPage(top_surface_page, _L("Top-surface coloring (experimental)"));
m_options_book->AddPage(global_page, _L("Prime Tower Texture Mapping"));
update_options_book_min_size();
m_options_tab_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) {
if (m_options_book)
m_options_book->SetSelection(std::clamp(evt.GetSelection(), 0, 5));
m_options_book->SetSelection(std::clamp(evt.GetSelection(), 0, 6));
});
if (m_options_book)
m_options_book->SetSelection(std::clamp(initial_options_tab, 0, 5));
m_options_book->SetSelection(std::clamp(initial_options_tab, 0, 6));
root->Add(m_options_book, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
if (wxSizer *buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL))
root->Add(buttons, 0, wxEXPAND | wxALL, gap);
@@ -2563,6 +2672,55 @@ public:
double(TextureMappingZone::MinHalftoneDotSizeMm),
double(TextureMappingZone::MaxHalftoneDotSizeMm)));
}
bool top_surface_image_printing_enabled() const
{
return m_top_surface_image_printing_enabled_checkbox != nullptr &&
m_top_surface_image_printing_enabled_checkbox->GetValue();
}
int top_surface_image_printing_method() const
{
return m_top_surface_image_method_choice ?
std::clamp(m_top_surface_image_method_choice->GetSelection(),
int(TextureMappingZone::TopSurfaceImageSameAngle45Width),
int(TextureMappingZone::TopSurfaceImageSameLayer45Partition)) :
TextureMappingZone::DefaultTopSurfaceImagePrintingMethod;
}
float top_surface_image_min_line_width_mm() const
{
const double max_width = m_top_surface_image_max_line_width_spin != nullptr ?
m_top_surface_image_max_line_width_spin->GetValue() :
double(TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm);
const double value = m_top_surface_image_min_line_width_spin != nullptr ?
m_top_surface_image_min_line_width_spin->GetValue() :
double(TextureMappingZone::DefaultTopSurfaceImageMinLineWidthMm);
return float(std::clamp(value,
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
std::clamp(max_width,
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
double(TextureMappingZone::MaxTopSurfaceImageLineWidthMm))));
}
float top_surface_image_max_line_width_mm() const
{
const double value = m_top_surface_image_max_line_width_spin != nullptr ?
m_top_surface_image_max_line_width_spin->GetValue() :
double(TextureMappingZone::DefaultTopSurfaceImageMaxLineWidthMm);
return float(std::clamp(value,
double(TextureMappingZone::MinTopSurfaceImageLineWidthMm),
double(TextureMappingZone::MaxTopSurfaceImageLineWidthMm)));
}
int top_surface_image_colored_top_layers() const
{
return m_top_surface_image_colored_top_layers_spin ?
std::clamp(m_top_surface_image_colored_top_layers_spin->GetValue(),
TextureMappingZone::MinTopSurfaceImageColoredTopLayers,
TextureMappingZone::MaxTopSurfaceImageColoredTopLayers) :
TextureMappingZone::DefaultTopSurfaceImageColoredTopLayers;
}
bool top_surface_image_fixed_coloring_filaments() const
{
return m_top_surface_image_fixed_coloring_filaments_checkbox == nullptr ||
m_top_surface_image_fixed_coloring_filaments_checkbox->GetValue();
}
bool minimum_visibility_offset_enabled() const
{
return m_minimum_visibility_offset_checkbox && m_minimum_visibility_offset_checkbox->GetValue();
@@ -2638,7 +2796,7 @@ public:
const TextureMappingPrimeTowerImage& prime_tower_image() const { return m_prime_tower_image; }
const TextureMappingPrimeTowerImage& prime_tower_image_back() const { return m_prime_tower_image_back; }
int selected_options_tab() const { return std::clamp(m_options_tab_choice ? m_options_tab_choice->GetSelection() : 0, 0, 5); }
int selected_options_tab() const { return std::clamp(m_options_tab_choice ? m_options_tab_choice->GetSelection() : 0, 0, 6); }
bool strength_offsets_expanded() const { return m_strength_offsets_expanded; }
std::vector<float> component_strengths_pct() const
@@ -2937,6 +3095,37 @@ private:
}
}
void update_top_surface_image_options_visibility(bool fit_dialog)
{
const bool enabled =
m_top_surface_image_printing_enabled_checkbox != nullptr &&
m_top_surface_image_printing_enabled_checkbox->GetValue();
if (m_top_surface_image_method_choice != nullptr)
m_top_surface_image_method_choice->Enable(enabled);
if (m_top_surface_image_min_line_width_spin != nullptr)
m_top_surface_image_min_line_width_spin->Enable(enabled);
if (m_top_surface_image_max_line_width_spin != nullptr)
m_top_surface_image_max_line_width_spin->Enable(enabled);
const bool same_layer =
enabled &&
m_top_surface_image_method_choice != nullptr &&
m_top_surface_image_method_choice->GetSelection() == int(TextureMappingZone::TopSurfaceImageSameLayer45Partition);
if (m_top_surface_image_colored_top_layers_panel != nullptr)
m_top_surface_image_colored_top_layers_panel->Show(same_layer);
if (m_top_surface_image_colored_top_layers_spin != nullptr)
m_top_surface_image_colored_top_layers_spin->Enable(same_layer);
if (m_top_surface_image_fixed_coloring_filaments_checkbox != nullptr)
m_top_surface_image_fixed_coloring_filaments_checkbox->Enable(enabled);
layout_current_options_page();
if (!fit_dialog)
return;
update_options_book_min_size();
if (GetSizer() != nullptr) {
Layout();
Fit();
}
}
wxChoice *m_options_tab_choice {nullptr};
wxSimplebook *m_options_book {nullptr};
wxChoice *m_texture_mapping_mode_choice {nullptr};
@@ -2969,6 +3158,13 @@ private:
wxSpinCtrlDouble *m_dithering_resolution_spin {nullptr};
wxPanel *m_halftone_dot_size_panel {nullptr};
wxSpinCtrlDouble *m_halftone_dot_size_spin {nullptr};
wxCheckBox *m_top_surface_image_printing_enabled_checkbox {nullptr};
wxChoice *m_top_surface_image_method_choice {nullptr};
wxSpinCtrlDouble *m_top_surface_image_min_line_width_spin {nullptr};
wxSpinCtrlDouble *m_top_surface_image_max_line_width_spin {nullptr};
wxPanel *m_top_surface_image_colored_top_layers_panel {nullptr};
wxSpinCtrl *m_top_surface_image_colored_top_layers_spin {nullptr};
wxCheckBox *m_top_surface_image_fixed_coloring_filaments_checkbox {nullptr};
wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr};
wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr};
wxSpinCtrl *m_minimum_visibility_offset_spin {nullptr};
@@ -7912,6 +8108,12 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.dithering_method,
updated.dithering_resolution_mm,
updated.halftone_dot_size_mm,
updated.top_surface_image_printing_enabled,
updated.top_surface_image_printing_method,
updated.top_surface_image_min_line_width_mm,
updated.top_surface_image_max_line_width_mm,
updated.top_surface_image_colored_top_layers,
updated.top_surface_image_fixed_coloring_filaments,
bundle->texture_mapping_zones,
bundle->texture_mapping_global_settings,
wxGetApp().model().texture_mapping_prime_tower_image,
@@ -7955,6 +8157,12 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.dithering_method = dlg.dithering_method();
updated.dithering_resolution_mm = dlg.dithering_resolution_mm();
updated.halftone_dot_size_mm = dlg.halftone_dot_size_mm();
updated.top_surface_image_printing_enabled = dlg.top_surface_image_printing_enabled();
updated.top_surface_image_printing_method = dlg.top_surface_image_printing_method();
updated.top_surface_image_min_line_width_mm = dlg.top_surface_image_min_line_width_mm();
updated.top_surface_image_max_line_width_mm = dlg.top_surface_image_max_line_width_mm();
updated.top_surface_image_colored_top_layers = dlg.top_surface_image_colored_top_layers();
updated.top_surface_image_fixed_coloring_filaments = dlg.top_surface_image_fixed_coloring_filaments();
if (updated.dithering_enabled)
updated.compact_offset_mode = true;
updated.minimum_visibility_offset_enabled = dlg.minimum_visibility_offset_enabled();