Add top perimeter coloring options

This commit is contained in:
sentientstardust
2026-05-26 07:49:31 +01:00
parent 10c5d14be4
commit 15498a6942
4 changed files with 644 additions and 16 deletions

View File

@@ -411,6 +411,7 @@ struct TopSurfaceImageStackSlice {
bool lower_surface = false;
float angle_rad = float(PI / 4.0);
ExPolygons area;
ExPolygons perimeter_area;
};
struct TopSurfaceImageRegionPlan {
@@ -429,6 +430,9 @@ struct TopSurfaceImageRegionPlan {
int contoning_pattern_filaments = TextureMappingZone::DefaultTopSurfaceContoningPatternFilaments;
float contoning_min_feature_mm = 2.f;
float contoning_external_width_mm = 0.4f;
bool contoning_replace_top_perimeters_with_infill = false;
bool contoning_recolor_surrounding_perimeters = false;
int contoning_perimeter_mode = TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode;
};
enum class TopSurfaceImageSourceSurface {
@@ -1212,6 +1216,7 @@ static std::vector<TopSurfaceImageContoningVectorRegion> top_surface_image_conto
static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan &plan,
const Layer &source_layer,
const ExPolygons &area,
const ExPolygons &perimeter_area,
const PrintObject &object,
const TextureMappingZone &zone,
const PrintConfig &print_config,
@@ -1220,17 +1225,18 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan
TopSurfaceImageSourceSurface source_surface,
const ThrowIfCanceled *throw_if_canceled)
{
if (area.empty() || !solver.valid())
if (perimeter_area.empty() || !solver.valid())
return;
check_canceled(throw_if_canceled);
const int pattern_filaments =
top_surface_image_contoning_pattern_filaments(plan.contoning_stack_layers, plan.contoning_pattern_filaments);
std::vector<ExPolygons> by_component(print_config.filament_colour.values.size() + 1);
std::vector<ExPolygons> perimeter_by_component(print_config.filament_colour.values.size() + 1);
const std::vector<TopSurfaceImageContoningVectorRegion> stack_regions =
top_surface_image_contoning_vector_regions(plan,
source_layer,
area,
perimeter_area,
object,
zone,
print_config,
@@ -1247,20 +1253,33 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan
region.bottom_to_top[size_t(int(region.bottom_to_top.size()) - 1 - pattern_depth)];
if (component_id == 0 || component_id >= by_component.size())
continue;
append(by_component[component_id], region.area);
append(perimeter_by_component[component_id], region.area);
if (!area.empty()) {
ExPolygons component_area = intersection_ex(region.area, area, ApplySafetyOffset::Yes);
if (!component_area.empty())
append(by_component[component_id], std::move(component_area));
}
}
ExPolygons depth_taken;
ExPolygons perimeter_depth_taken;
for (unsigned int component_id = 1; component_id < by_component.size(); ++component_id) {
check_canceled(throw_if_canceled);
if (by_component[component_id].empty())
if (by_component[component_id].empty() && perimeter_by_component[component_id].empty())
continue;
ExPolygons component_area = union_ex(by_component[component_id]);
if (!depth_taken.empty())
ExPolygons component_area = by_component[component_id].empty() ? ExPolygons() : union_ex(by_component[component_id]);
ExPolygons component_perimeter_area =
perimeter_by_component[component_id].empty() ? ExPolygons() : union_ex(perimeter_by_component[component_id]);
if (!depth_taken.empty() && !component_area.empty())
component_area = diff_ex(component_area, depth_taken, ApplySafetyOffset::Yes);
if (component_area.empty())
if (!perimeter_depth_taken.empty() && !component_perimeter_area.empty())
component_perimeter_area = diff_ex(component_perimeter_area, perimeter_depth_taken, ApplySafetyOffset::Yes);
if (component_area.empty() && component_perimeter_area.empty())
continue;
append(depth_taken, component_area);
if (!component_area.empty())
append(depth_taken, component_area);
if (!component_perimeter_area.empty())
append(perimeter_depth_taken, component_perimeter_area);
TopSurfaceImageStackSlice slice;
slice.component_id = component_id;
slice.depth = depth;
@@ -1270,6 +1289,7 @@ static void top_surface_image_append_contoning_slices(TopSurfaceImageRegionPlan
slice.lower_surface = source_surface == TopSurfaceImageSourceSurface::Bottom;
slice.angle_rad = (depth & 1) ? float(-PI / 4.0) : float(PI / 4.0);
slice.area = std::move(component_area);
slice.perimeter_area = std::move(component_perimeter_area);
plan.slices.emplace_back(std::move(slice));
}
}
@@ -1353,6 +1373,15 @@ static std::vector<TopSurfaceImageRegionPlan> top_surface_image_region_plans(con
plan.contoning_external_width_mm = float(layerm->flow(frExternalPerimeter).width());
plan.contoning_min_feature_mm =
texture_mapping_contoning_min_feature_mm(*zone, print_config, components, plan.contoning_external_width_mm);
plan.contoning_replace_top_perimeters_with_infill =
zone->top_surface_contoning_replace_top_perimeters_with_infill;
plan.contoning_recolor_surrounding_perimeters =
zone->top_surface_contoning_recolor_surrounding_perimeters &&
!plan.contoning_replace_top_perimeters_with_infill;
plan.contoning_perimeter_mode =
std::clamp(zone->top_surface_contoning_perimeter_mode,
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill));
const TextureMappingContoningSolver contoning_solver(*zone, print_config, components);
const int stack_depth = plan.contoning ?
@@ -1374,23 +1403,29 @@ static std::vector<TopSurfaceImageRegionPlan> top_surface_image_region_plans(con
if (source_layer == nullptr)
break;
ExPolygons area = top_surface_image_visible_surface_mask(*source_layer, zone_id, source_surface);
if (area.empty())
ExPolygons perimeter_area = top_surface_image_visible_surface_mask(*source_layer, zone_id, source_surface);
if (perimeter_area.empty())
continue;
if (!current_layer_perimeters.empty())
ExPolygons area = perimeter_area;
if (!plan.contoning_replace_top_perimeters_with_infill && !current_layer_perimeters.empty())
area = diff_ex(area, current_layer_perimeters, ApplySafetyOffset::Yes);
if (area.empty())
if (area.empty() && !plan.contoning_recolor_surrounding_perimeters)
continue;
contoning_depth_layers[size_t(depth)] = source_layer;
contoning_depth_areas[size_t(depth)] = std::move(area);
}
for (int depth = 0; depth < stack_depth; ++depth) {
check_canceled(throw_if_canceled);
if (contoning_depth_layers[size_t(depth)] == nullptr || contoning_depth_areas[size_t(depth)].empty())
if (contoning_depth_layers[size_t(depth)] == nullptr)
continue;
ExPolygons perimeter_area =
top_surface_image_visible_surface_mask(*contoning_depth_layers[size_t(depth)], zone_id, source_surface);
if (perimeter_area.empty())
continue;
top_surface_image_append_contoning_slices(plan,
*contoning_depth_layers[size_t(depth)],
contoning_depth_areas[size_t(depth)],
perimeter_area,
*object,
*zone,
print_config,
@@ -1487,6 +1522,440 @@ static void append_surface_fill_expolygons(SurfaceFill &fill,
}
}
struct TopSurfaceImagePerimeterMask {
unsigned int component_id = 0;
unsigned int zone_id = 0;
int depth = 0;
bool lower_surface = false;
float angle_rad = float(PI / 4.0);
ExPolygons area;
};
static std::vector<TopSurfaceImagePerimeterMask> top_surface_image_contoning_perimeter_masks(const TopSurfaceImageRegionPlan &plan)
{
std::vector<TopSurfaceImagePerimeterMask> masks;
for (const TopSurfaceImageStackSlice &slice : plan.slices) {
if (!slice.contoning || slice.component_id == 0 || slice.perimeter_area.empty())
continue;
TopSurfaceImagePerimeterMask mask;
mask.component_id = slice.component_id;
mask.zone_id = plan.zone_id;
mask.depth = slice.depth;
mask.lower_surface = slice.lower_surface;
mask.angle_rad = slice.angle_rad;
mask.area = slice.perimeter_area;
masks.emplace_back(std::move(mask));
}
return masks;
}
static ExPolygons top_surface_image_contoning_printable_area(ExPolygons area, float min_feature_mm)
{
if (area.empty())
return {};
area = union_ex(area);
ExPolygons out;
const double min_area_mm2 = std::max(0.05, double(min_feature_mm) * double(min_feature_mm) * 0.08);
for (ExPolygon &expolygon : area)
if (top_surface_image_scaled_area_mm2(expolygon.area()) >= min_area_mm2)
out.emplace_back(std::move(expolygon));
return out.empty() ? ExPolygons() : union_ex(out);
}
static void top_surface_image_move_collection_entities(ExtrusionEntitiesPtr &out, ExtrusionEntityCollection &collection)
{
for (ExtrusionEntity *entity : collection.entities)
out.emplace_back(entity);
collection.entities.clear();
}
static void top_surface_image_delete_entities(ExtrusionEntitiesPtr &entities)
{
for (ExtrusionEntity *entity : entities)
delete entity;
entities.clear();
}
static void top_surface_image_move_entities(ExtrusionEntitiesPtr &out, ExtrusionEntitiesPtr &entities)
{
for (ExtrusionEntity *entity : entities)
out.emplace_back(entity);
entities.clear();
}
static void top_surface_image_append_entity_minus_mask(ExtrusionEntitiesPtr &out,
const ExtrusionEntity &entity,
const ExPolygons &mask)
{
if (mask.empty()) {
out.emplace_back(entity.clone());
return;
}
if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath *>(&entity)) {
ExtrusionEntityCollection collection;
path->subtract_expolygons(mask, &collection);
top_surface_image_move_collection_entities(out, collection);
return;
}
if (const ExtrusionMultiPath *multipath = dynamic_cast<const ExtrusionMultiPath *>(&entity)) {
for (const ExtrusionPath &path : multipath->paths)
top_surface_image_append_entity_minus_mask(out, path, mask);
return;
}
if (const ExtrusionLoop *loop = dynamic_cast<const ExtrusionLoop *>(&entity)) {
for (const ExtrusionPath &path : loop->paths)
top_surface_image_append_entity_minus_mask(out, path, mask);
return;
}
if (const ExtrusionEntityCollection *collection = dynamic_cast<const ExtrusionEntityCollection *>(&entity)) {
ExtrusionEntityCollection *out_collection = new ExtrusionEntityCollection(*collection);
out_collection->clear();
out_collection->no_sort = collection->no_sort;
out_collection->texture_mapping_extruder_override = collection->texture_mapping_extruder_override;
for (const ExtrusionEntity *child : collection->entities)
if (child != nullptr)
top_surface_image_append_entity_minus_mask(out_collection->entities, *child, mask);
if (out_collection->empty())
delete out_collection;
else
out.emplace_back(out_collection);
return;
}
out.emplace_back(entity.clone());
}
static void top_surface_image_trim_perimeters_by_mask(ExtrusionEntityCollection &perimeters, const ExPolygons &mask)
{
if (perimeters.entities.empty() || mask.empty())
return;
ExtrusionEntitiesPtr replacement;
for (const ExtrusionEntity *entity : perimeters.entities)
if (entity != nullptr)
top_surface_image_append_entity_minus_mask(replacement, *entity, mask);
perimeters.clear();
perimeters.entities = std::move(replacement);
}
static int top_surface_image_perimeter_override_at_point(const Point &point,
const std::vector<TopSurfaceImagePerimeterMask> &masks)
{
for (const TopSurfaceImagePerimeterMask &mask : masks)
if (mask.component_id > 0 && top_surface_image_expolygons_contain_point(mask.area, point))
return int(mask.component_id) - 1;
return -1;
}
static void top_surface_image_append_perimeter_path_piece(ExtrusionEntitiesPtr &out,
const ExtrusionPath &source,
Points &&points,
int extruder_override)
{
Polyline polyline;
polyline.points = std::move(points);
remove_same_neighbor(polyline);
if (polyline.points.size() < 2 || polyline.length() <= SCALED_EPSILON)
return;
ExtrusionPath *path = new ExtrusionPath(std::move(polyline), source);
if (extruder_override < 0) {
out.emplace_back(path);
return;
}
ExtrusionEntityCollection *collection = new ExtrusionEntityCollection();
collection->texture_mapping_extruder_override = extruder_override;
collection->entities.emplace_back(path);
out.emplace_back(collection);
}
static bool top_surface_image_append_recolored_perimeter_path(ExtrusionEntitiesPtr &out,
const ExtrusionPath &path,
const std::vector<TopSurfaceImagePerimeterMask> &masks,
float min_run_length_mm)
{
if (path.polyline.points.size() < 2) {
out.emplace_back(path.clone());
return false;
}
const double min_run_mm = std::max(0.05, double(min_run_length_mm));
bool changed = false;
ExtrusionEntitiesPtr local;
Points current_points;
int current_override = -2;
auto flush = [&]() {
if (current_points.empty())
return;
top_surface_image_append_perimeter_path_piece(local, path, std::move(current_points), current_override);
current_points.clear();
current_override = -2;
};
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::floor(len_mm / min_run_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));
int extruder_override = top_surface_image_perimeter_override_at_point(qm, masks);
if (extruder_override >= 0 && len_mm / double(steps) < min_run_mm - EPSILON)
extruder_override = -1;
changed = changed || extruder_override >= 0;
if (current_points.empty()) {
current_override = extruder_override;
current_points.emplace_back(q0);
current_points.emplace_back(q1);
} else if (current_override == extruder_override && current_points.back() == q0) {
current_points.emplace_back(q1);
} else {
flush();
current_override = extruder_override;
current_points.emplace_back(q0);
current_points.emplace_back(q1);
}
}
}
flush();
if (!changed) {
top_surface_image_delete_entities(local);
out.emplace_back(path.clone());
} else {
top_surface_image_move_entities(out, local);
}
return changed;
}
static bool top_surface_image_append_recolored_perimeter_entity(ExtrusionEntitiesPtr &out,
const ExtrusionEntity &entity,
const std::vector<TopSurfaceImagePerimeterMask> &masks,
float min_run_length_mm)
{
if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath *>(&entity))
return top_surface_image_append_recolored_perimeter_path(out, *path, masks, min_run_length_mm);
if (const ExtrusionMultiPath *multipath = dynamic_cast<const ExtrusionMultiPath *>(&entity)) {
ExtrusionEntitiesPtr local;
bool changed = false;
for (const ExtrusionPath &path : multipath->paths)
changed = top_surface_image_append_recolored_perimeter_path(local, path, masks, min_run_length_mm) || changed;
if (changed) {
top_surface_image_move_entities(out, local);
} else {
top_surface_image_delete_entities(local);
out.emplace_back(entity.clone());
}
return changed;
}
if (const ExtrusionLoop *loop = dynamic_cast<const ExtrusionLoop *>(&entity)) {
ExtrusionEntitiesPtr local;
bool changed = false;
for (const ExtrusionPath &path : loop->paths)
changed = top_surface_image_append_recolored_perimeter_path(local, path, masks, min_run_length_mm) || changed;
if (changed) {
top_surface_image_move_entities(out, local);
} else {
top_surface_image_delete_entities(local);
out.emplace_back(entity.clone());
}
return changed;
}
if (const ExtrusionEntityCollection *collection = dynamic_cast<const ExtrusionEntityCollection *>(&entity)) {
if (collection->texture_mapping_extruder_override >= 0) {
out.emplace_back(entity.clone());
return false;
}
ExtrusionEntityCollection *out_collection = new ExtrusionEntityCollection(*collection);
out_collection->clear();
out_collection->no_sort = collection->no_sort;
bool changed = false;
for (const ExtrusionEntity *child : collection->entities)
if (child != nullptr)
changed = top_surface_image_append_recolored_perimeter_entity(
out_collection->entities, *child, masks, min_run_length_mm) || changed;
if (changed && !out_collection->empty()) {
out.emplace_back(out_collection);
} else {
delete out_collection;
out.emplace_back(entity.clone());
}
return changed;
}
out.emplace_back(entity.clone());
return false;
}
static void top_surface_image_recolor_perimeters_by_masks(ExtrusionEntityCollection &perimeters,
const std::vector<TopSurfaceImagePerimeterMask> &masks,
float min_run_length_mm)
{
if (perimeters.entities.empty() || masks.empty())
return;
ExtrusionEntitiesPtr replacement;
bool changed = false;
for (const ExtrusionEntity *entity : perimeters.entities) {
if (entity == nullptr)
continue;
changed = top_surface_image_append_recolored_perimeter_entity(replacement, *entity, masks, min_run_length_mm) || changed;
}
if (!changed) {
top_surface_image_delete_entities(replacement);
return;
}
perimeters.clear();
perimeters.entities = std::move(replacement);
}
static void top_surface_image_append_colored_block_loops(LayerRegion &layerm,
const ExPolygons &area,
unsigned int component_id)
{
if (area.empty() || component_id == 0)
return;
Polygons loops = to_polygons(area);
if (loops.empty())
return;
Flow flow = layerm.flow(frExternalPerimeter);
ExtrusionEntityCollection *collection = new ExtrusionEntityCollection();
collection->texture_mapping_extruder_override = int(component_id) - 1;
extrusion_entities_append_loops(collection->entities,
std::move(loops),
erPerimeter,
flow.mm3_per_mm(),
float(flow.width()),
float(flow.height()));
if (collection->empty())
delete collection;
else
layerm.perimeters.entities.emplace_back(collection);
}
static void top_surface_image_append_perimeter_infill_surface(std::vector<SurfaceFill> &surface_fills,
LayerRegion &layerm,
size_t region_id,
const TopSurfaceImagePerimeterMask &mask,
ExPolygons &&area)
{
if (area.empty() || mask.component_id == 0)
return;
Surface surface(mask.lower_surface && mask.depth == 0 ? stBottom : (!mask.lower_surface && mask.depth == 0 ? stTop : stInternalSolid));
surface.thickness = layerm.layer()->height;
surface.thickness_layers = 1;
SurfaceFillParams params;
params.extruder = mask.component_id;
params.pattern = ipRectilinear;
params.density = 100.f;
params.angle = mask.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;
const FlowRole flow_role = surface.is_top() ? frTopSolidInfill : frSolidInfill;
params.flow = layerm.flow(flow_role, layerm.layer()->height);
params.spacing = params.flow.spacing();
params.extrusion_role = surface.is_top() ? erTopSolidInfill : (surface.is_bottom() ? erBottomSurface : erSolidInfill);
params.texture_mapping_top_surface_image = true;
params.texture_mapping_top_surface_zone_id = mask.zone_id;
params.texture_mapping_top_surface_component_id = mask.component_id;
params.texture_mapping_top_surface_stack_depth = mask.depth;
params.texture_mapping_top_surface_fixed_coloring = true;
params.texture_mapping_top_surface_contoning = true;
params.texture_mapping_top_surface_component_index = 0;
params.texture_mapping_top_surface_component_count = 1;
SurfaceFill &fill = surface_fill_for_params(surface_fills, params);
append_surface_fill_expolygons(fill, region_id, surface, std::move(area), layerm);
}
static void top_surface_image_apply_contoning_perimeter_options(const Layer &layer,
std::vector<SurfaceFill> &surface_fills,
const std::vector<TopSurfaceImageRegionPlan> &plans,
const ThrowIfCanceled *throw_if_canceled)
{
for (size_t region_id = 0; region_id < plans.size() && region_id < layer.regions().size(); ++region_id) {
check_canceled(throw_if_canceled);
const TopSurfaceImageRegionPlan &plan = plans[region_id];
if (!plan.contoning ||
(!plan.contoning_replace_top_perimeters_with_infill && !plan.contoning_recolor_surrounding_perimeters))
continue;
LayerRegion *layerm = layer.regions()[region_id];
if (layerm == nullptr || layerm->perimeters.entities.empty())
continue;
std::vector<TopSurfaceImagePerimeterMask> masks = top_surface_image_contoning_perimeter_masks(plan);
if (masks.empty())
continue;
Polygons wall_polygons;
layerm->perimeters.polygons_covered_by_width(wall_polygons, 0.f);
ExPolygons wall_area = wall_polygons.empty() ? ExPolygons() : union_ex(wall_polygons);
if (wall_area.empty())
continue;
ExPolygons affected_area;
for (TopSurfaceImagePerimeterMask &mask : masks) {
check_canceled(throw_if_canceled);
mask.area = intersection_ex(mask.area, wall_area, ApplySafetyOffset::Yes);
mask.area = top_surface_image_contoning_printable_area(std::move(mask.area), plan.contoning_min_feature_mm);
if (!mask.area.empty())
append(affected_area, mask.area);
}
if (affected_area.empty())
continue;
affected_area = union_ex(affected_area);
if (plan.contoning_replace_top_perimeters_with_infill) {
top_surface_image_trim_perimeters_by_mask(layerm->perimeters, affected_area);
ExPolygons taken;
for (const TopSurfaceImagePerimeterMask &mask : masks) {
check_canceled(throw_if_canceled);
ExPolygons area = mask.area;
if (!taken.empty())
area = diff_ex(area, taken, ApplySafetyOffset::Yes);
area = top_surface_image_contoning_printable_area(std::move(area), plan.contoning_min_feature_mm);
if (area.empty())
continue;
append(taken, area);
top_surface_image_append_perimeter_infill_surface(surface_fills, *layerm, region_id, mask, std::move(area));
}
continue;
}
if (plan.contoning_perimeter_mode == int(TextureMappingZone::ContoningPerimeterDividedLine)) {
top_surface_image_recolor_perimeters_by_masks(layerm->perimeters, masks, plan.contoning_min_feature_mm);
continue;
}
top_surface_image_trim_perimeters_by_mask(layerm->perimeters, affected_area);
ExPolygons taken;
for (const TopSurfaceImagePerimeterMask &mask : masks) {
check_canceled(throw_if_canceled);
ExPolygons area = mask.area;
if (!taken.empty())
area = diff_ex(area, taken, ApplySafetyOffset::Yes);
area = top_surface_image_contoning_printable_area(std::move(area), plan.contoning_min_feature_mm);
if (area.empty())
continue;
append(taken, area);
if (plan.contoning_perimeter_mode == int(TextureMappingZone::ContoningPerimeterSegmentInfill)) {
top_surface_image_append_perimeter_infill_surface(surface_fills, *layerm, region_id, mask, std::move(area));
} else {
top_surface_image_append_colored_block_loops(*layerm, area, mask.component_id);
top_surface_image_append_perimeter_infill_surface(surface_fills, *layerm, region_id, mask, std::move(area));
}
}
}
}
static bool top_surface_image_slice_matches_surface(const TopSurfaceImageStackSlice &slice, const Surface &surface)
{
return slice.lower_surface ?
@@ -2289,7 +2758,10 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
#endif
}
std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_param, const ThrowIfCanceled *throw_if_canceled = nullptr)
std::vector<SurfaceFill> group_fills(const Layer &layer,
LockRegionParam &lock_param,
const ThrowIfCanceled *throw_if_canceled = nullptr,
bool apply_contoning_perimeter_options = false)
{
check_canceled(throw_if_canceled);
std::vector<SurfaceFill> surface_fills;
@@ -2687,6 +3159,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
}
}
if (apply_contoning_perimeter_options)
top_surface_image_apply_contoning_perimeter_options(layer, surface_fills, top_surface_plans, throw_if_canceled);
return surface_fills;
}
@@ -2727,7 +3202,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree,
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
LockRegionParam lock_param;
std::vector<SurfaceFill> surface_fills = group_fills(*this, lock_param, throw_if_canceled_ptr);
std::vector<SurfaceFill> surface_fills = group_fills(*this, lock_param, throw_if_canceled_ptr, true);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().resolution.value;
check_canceled(throw_if_canceled_ptr);

View File

@@ -1054,6 +1054,9 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
std::abs(top_surface_contoning_min_feature_mm - rhs.top_surface_contoning_min_feature_mm) <= eps &&
top_surface_contoning_color_lower_surfaces == rhs.top_surface_contoning_color_lower_surfaces &&
top_surface_contoning_only_color_surface_infill == rhs.top_surface_contoning_only_color_surface_infill &&
top_surface_contoning_replace_top_perimeters_with_infill == rhs.top_surface_contoning_replace_top_perimeters_with_infill &&
top_surface_contoning_recolor_surrounding_perimeters == rhs.top_surface_contoning_recolor_surrounding_perimeters &&
top_surface_contoning_perimeter_mode == rhs.top_surface_contoning_perimeter_mode &&
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 &&
@@ -1431,6 +1434,15 @@ std::string TextureMappingManager::serialize_entries()
TextureMappingZone::MaxTopSurfaceContoningMinFeatureMm);
texture["top_surface_contoning_color_lower_surfaces"] = zone.top_surface_contoning_color_lower_surfaces;
texture["top_surface_contoning_only_color_surface_infill"] = zone.top_surface_contoning_only_color_surface_infill;
texture["top_surface_contoning_replace_top_perimeters_with_infill"] =
zone.top_surface_contoning_replace_top_perimeters_with_infill;
texture["top_surface_contoning_recolor_surrounding_perimeters"] =
zone.top_surface_contoning_recolor_surrounding_perimeters &&
!zone.top_surface_contoning_replace_top_perimeters_with_infill;
texture["top_surface_contoning_perimeter_mode"] =
clamp_int(zone.top_surface_contoning_perimeter_mode,
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill));
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;
@@ -1684,6 +1696,19 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.top_surface_contoning_only_color_surface_infill =
texture.value("top_surface_contoning_only_color_surface_infill",
TextureMappingZone::DefaultTopSurfaceContoningOnlyColorSurfaceInfill);
zone.top_surface_contoning_replace_top_perimeters_with_infill =
texture.value("top_surface_contoning_replace_top_perimeters_with_infill",
TextureMappingZone::DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill);
zone.top_surface_contoning_recolor_surrounding_perimeters =
texture.value("top_surface_contoning_recolor_surrounding_perimeters",
TextureMappingZone::DefaultTopSurfaceContoningRecolorSurroundingPerimeters);
zone.top_surface_contoning_perimeter_mode =
clamp_int(texture.value("top_surface_contoning_perimeter_mode",
TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode),
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill));
if (zone.top_surface_contoning_replace_top_perimeters_with_infill)
zone.top_surface_contoning_recolor_surrounding_perimeters = false;
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

@@ -72,6 +72,12 @@ struct TextureMappingZone
TopSurfaceImageContoning = 2
};
enum TopSurfaceContoningPerimeterMode : uint8_t {
ContoningPerimeterSegmentBlocks = 0,
ContoningPerimeterDividedLine = 1,
ContoningPerimeterSegmentInfill = 2
};
enum FilamentColorMode : uint8_t {
FilamentColorAny = 0,
FilamentColorRGB = 1,
@@ -170,6 +176,9 @@ struct TextureMappingZone
static constexpr float DefaultTopSurfaceContoningMinFeatureMm = 0.f;
static constexpr bool DefaultTopSurfaceContoningColorLowerSurfaces = true;
static constexpr bool DefaultTopSurfaceContoningOnlyColorSurfaceInfill = true;
static constexpr bool DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill = false;
static constexpr bool DefaultTopSurfaceContoningRecolorSurroundingPerimeters = false;
static constexpr int DefaultTopSurfaceContoningPerimeterMode = int(ContoningPerimeterDividedLine);
static constexpr bool DefaultCompactOffsetMode = true;
static constexpr bool DefaultUseLegacyFixedColorMode = false;
static constexpr bool DefaultHighSpeedImageTextureSampling = true;
@@ -270,6 +279,9 @@ struct TextureMappingZone
float top_surface_contoning_min_feature_mm = DefaultTopSurfaceContoningMinFeatureMm;
bool top_surface_contoning_color_lower_surfaces = DefaultTopSurfaceContoningColorLowerSurfaces;
bool top_surface_contoning_only_color_surface_infill = DefaultTopSurfaceContoningOnlyColorSurfaceInfill;
bool top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill;
bool top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters;
int top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode;
bool compact_offset_mode = DefaultCompactOffsetMode;
bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
bool high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;
@@ -332,7 +344,10 @@ struct TextureMappingZone
}
bool top_surface_contoning_perimeters_active() const
{
return top_surface_contoning_active() && !top_surface_contoning_only_color_surface_infill;
return top_surface_contoning_active() &&
!top_surface_contoning_only_color_surface_infill &&
!top_surface_contoning_replace_top_perimeters_with_infill &&
!top_surface_contoning_recolor_surrounding_perimeters;
}
void apply_default_modulation_mode()
@@ -383,6 +398,9 @@ struct TextureMappingZone
top_surface_contoning_min_feature_mm = DefaultTopSurfaceContoningMinFeatureMm;
top_surface_contoning_color_lower_surfaces = DefaultTopSurfaceContoningColorLowerSurfaces;
top_surface_contoning_only_color_surface_infill = DefaultTopSurfaceContoningOnlyColorSurfaceInfill;
top_surface_contoning_replace_top_perimeters_with_infill = DefaultTopSurfaceContoningReplaceTopPerimetersWithInfill;
top_surface_contoning_recolor_surrounding_perimeters = DefaultTopSurfaceContoningRecolorSurroundingPerimeters;
top_surface_contoning_perimeter_mode = DefaultTopSurfaceContoningPerimeterMode;
compact_offset_mode = DefaultCompactOffsetMode;
use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode;
high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling;

View File

@@ -1905,6 +1905,9 @@ public:
float top_surface_contoning_min_feature_mm,
bool top_surface_contoning_color_lower_surfaces,
bool top_surface_contoning_only_color_surface_infill,
bool top_surface_contoning_replace_top_perimeters_with_infill,
bool top_surface_contoning_recolor_surrounding_perimeters,
int top_surface_contoning_perimeter_mode,
const TextureMappingManager &texture_mapping_zones,
const TextureMappingGlobalSettings &global_settings,
const TextureMappingPrimeTowerImage &prime_tower_image,
@@ -2550,7 +2553,67 @@ public:
0,
wxEXPAND | wxTOP | wxBOTTOM,
gap / 2);
m_top_surface_contoning_replace_top_perimeters_checkbox =
new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Replace top perimeters with infill"));
m_top_surface_contoning_replace_top_perimeters_checkbox->SetValue(top_surface_contoning_replace_top_perimeters_with_infill);
m_top_surface_contoning_replace_top_perimeters_checkbox->SetMinSize(
wxSize(-1, std::max(m_top_surface_contoning_replace_top_perimeters_checkbox->GetBestSize().GetHeight(), FromDIP(24))));
contoning_checkboxes_root->Add(m_top_surface_contoning_replace_top_perimeters_checkbox,
0,
wxEXPAND | wxTOP | wxBOTTOM,
gap / 2);
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox =
new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Recolor surrounding perimeters to match"));
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->SetValue(
top_surface_contoning_recolor_surrounding_perimeters && !top_surface_contoning_replace_top_perimeters_with_infill);
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->SetMinSize(
wxSize(-1, std::max(m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->GetBestSize().GetHeight(), FromDIP(24))));
contoning_checkboxes_root->Add(m_top_surface_contoning_recolor_surrounding_perimeters_checkbox,
0,
wxEXPAND | wxTOP | wxBOTTOM,
gap / 2);
m_top_surface_contoning_perimeter_mode_panel = new wxPanel(m_top_surface_contoning_checkboxes_panel, wxID_ANY);
auto *contoning_perimeter_mode_row = new wxBoxSizer(wxHORIZONTAL);
m_top_surface_contoning_perimeter_mode_panel->SetSizer(contoning_perimeter_mode_row);
contoning_perimeter_mode_row->Add(new wxStaticText(m_top_surface_contoning_perimeter_mode_panel, wxID_ANY, _L("Perimeter mode")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
wxArrayString contoning_perimeter_mode_choices;
contoning_perimeter_mode_choices.Add(_L("Segment perimeter into blocks"));
contoning_perimeter_mode_choices.Add(_L("Divided line"));
contoning_perimeter_mode_choices.Add(_L("Segment perimeter into infill"));
m_top_surface_contoning_perimeter_mode_choice =
new wxChoice(m_top_surface_contoning_perimeter_mode_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, contoning_perimeter_mode_choices);
m_top_surface_contoning_perimeter_mode_choice->SetSelection(
std::clamp(top_surface_contoning_perimeter_mode,
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill)));
contoning_perimeter_mode_row->Add(m_top_surface_contoning_perimeter_mode_choice, 1, wxEXPAND);
contoning_checkboxes_root->Add(m_top_surface_contoning_perimeter_mode_panel,
0,
wxEXPAND | wxTOP | wxBOTTOM,
gap / 2);
top_surface_box->Add(m_top_surface_contoning_checkboxes_panel, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP | wxBOTTOM, gap);
m_top_surface_contoning_only_color_surface_infill_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) {
update_top_surface_image_options_visibility(true);
});
m_top_surface_contoning_replace_top_perimeters_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) {
if (m_top_surface_contoning_replace_top_perimeters_checkbox != nullptr &&
m_top_surface_contoning_replace_top_perimeters_checkbox->GetValue() &&
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox != nullptr) {
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->SetValue(false);
}
update_top_surface_image_options_visibility(true);
});
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) {
if (m_top_surface_contoning_recolor_surrounding_perimeters_checkbox != nullptr &&
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->GetValue() &&
m_top_surface_contoning_replace_top_perimeters_checkbox != nullptr) {
m_top_surface_contoning_replace_top_perimeters_checkbox->SetValue(false);
}
update_top_surface_image_options_visibility(true);
});
m_top_surface_image_printing_enabled_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) {
update_top_surface_image_options_visibility(false);
});
@@ -2904,6 +2967,25 @@ public:
m_top_surface_contoning_only_color_surface_infill_checkbox->GetValue() :
TextureMappingZone::DefaultTopSurfaceContoningOnlyColorSurfaceInfill;
}
bool top_surface_contoning_replace_top_perimeters_with_infill() const
{
return m_top_surface_contoning_replace_top_perimeters_checkbox != nullptr &&
m_top_surface_contoning_replace_top_perimeters_checkbox->GetValue();
}
bool top_surface_contoning_recolor_surrounding_perimeters() const
{
return m_top_surface_contoning_recolor_surrounding_perimeters_checkbox != nullptr &&
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->GetValue() &&
!top_surface_contoning_replace_top_perimeters_with_infill();
}
int top_surface_contoning_perimeter_mode() const
{
return m_top_surface_contoning_perimeter_mode_choice != nullptr ?
std::clamp(m_top_surface_contoning_perimeter_mode_choice->GetSelection(),
int(TextureMappingZone::ContoningPerimeterSegmentBlocks),
int(TextureMappingZone::ContoningPerimeterSegmentInfill)) :
TextureMappingZone::DefaultTopSurfaceContoningPerimeterMode;
}
bool minimum_visibility_offset_enabled() const
{
return m_minimum_visibility_offset_checkbox && m_minimum_visibility_offset_checkbox->GetValue();
@@ -3359,6 +3441,22 @@ private:
m_top_surface_contoning_only_color_surface_infill_checkbox->Show(contoning);
m_top_surface_contoning_only_color_surface_infill_checkbox->Enable(contoning);
}
if (m_top_surface_contoning_replace_top_perimeters_checkbox != nullptr) {
m_top_surface_contoning_replace_top_perimeters_checkbox->Show(contoning);
m_top_surface_contoning_replace_top_perimeters_checkbox->Enable(contoning);
}
if (m_top_surface_contoning_recolor_surrounding_perimeters_checkbox != nullptr) {
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->Show(contoning);
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->Enable(contoning);
}
const bool show_perimeter_mode =
contoning &&
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox != nullptr &&
m_top_surface_contoning_recolor_surrounding_perimeters_checkbox->GetValue();
if (m_top_surface_contoning_perimeter_mode_panel != nullptr)
m_top_surface_contoning_perimeter_mode_panel->Show(show_perimeter_mode);
if (m_top_surface_contoning_perimeter_mode_choice != nullptr)
m_top_surface_contoning_perimeter_mode_choice->Enable(show_perimeter_mode);
if (m_top_surface_image_fixed_coloring_filaments_checkbox != nullptr) {
m_top_surface_image_fixed_coloring_filaments_checkbox->Show(!contoning_selected);
m_top_surface_image_fixed_coloring_filaments_checkbox->Enable(enabled && !contoning_selected);
@@ -3421,6 +3519,10 @@ private:
wxPanel *m_top_surface_contoning_checkboxes_panel {nullptr};
wxCheckBox *m_top_surface_contoning_color_lower_surfaces_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_only_color_surface_infill_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_replace_top_perimeters_checkbox {nullptr};
wxCheckBox *m_top_surface_contoning_recolor_surrounding_perimeters_checkbox {nullptr};
wxPanel *m_top_surface_contoning_perimeter_mode_panel {nullptr};
wxChoice *m_top_surface_contoning_perimeter_mode_choice {nullptr};
wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr};
wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr};
wxSpinCtrl *m_minimum_visibility_offset_spin {nullptr};
@@ -8422,6 +8524,9 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.top_surface_contoning_min_feature_mm,
updated.top_surface_contoning_color_lower_surfaces,
updated.top_surface_contoning_only_color_surface_infill,
updated.top_surface_contoning_replace_top_perimeters_with_infill,
updated.top_surface_contoning_recolor_surrounding_perimeters,
updated.top_surface_contoning_perimeter_mode,
bundle->texture_mapping_zones,
bundle->texture_mapping_global_settings,
wxGetApp().model().texture_mapping_prime_tower_image,
@@ -8477,6 +8582,11 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.top_surface_contoning_min_feature_mm = dlg.top_surface_contoning_min_feature_mm();
updated.top_surface_contoning_color_lower_surfaces = dlg.top_surface_contoning_color_lower_surfaces();
updated.top_surface_contoning_only_color_surface_infill = dlg.top_surface_contoning_only_color_surface_infill();
updated.top_surface_contoning_replace_top_perimeters_with_infill =
dlg.top_surface_contoning_replace_top_perimeters_with_infill();
updated.top_surface_contoning_recolor_surrounding_perimeters =
dlg.top_surface_contoning_recolor_surrounding_perimeters();
updated.top_surface_contoning_perimeter_mode = dlg.top_surface_contoning_perimeter_mode();
if (updated.top_surface_image_printing_enabled &&
updated.top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageContoning)) {
updated.modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2);