Add v2 perimeter path modulation mode. Add option for support generation to use modulated overhang geometry
This commit is contained in:
@@ -75,14 +75,47 @@ static inline bool layer_needs_raw_backup(const Layer *layer)
|
||||
void Layer::backup_untyped_slices()
|
||||
{
|
||||
if (layer_needs_raw_backup(this)) {
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
layerm->raw_slices = to_expolygons(layerm->slices.surfaces);
|
||||
layerm->unmodulated_raw_slices = layerm->raw_slices;
|
||||
layerm->perimeter_path_modulation_v2_applied = false;
|
||||
layerm->perimeter_path_modulation_v2_fallback_slices.clear();
|
||||
layerm->perimeter_path_modulation_v2_has_fallback_slices = false;
|
||||
layerm->perimeter_path_modulation_v2_fallback_is_modulated = false;
|
||||
}
|
||||
} else {
|
||||
assert(m_regions.size() == 1);
|
||||
m_regions.front()->raw_slices.clear();
|
||||
m_regions.front()->unmodulated_raw_slices.clear();
|
||||
m_regions.front()->perimeter_path_modulation_v2_applied = false;
|
||||
m_regions.front()->perimeter_path_modulation_v2_fallback_slices.clear();
|
||||
m_regions.front()->perimeter_path_modulation_v2_has_fallback_slices = false;
|
||||
m_regions.front()->perimeter_path_modulation_v2_fallback_is_modulated = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::commit_perimeter_path_modulation_v2_fallbacks()
|
||||
{
|
||||
bool changed = false;
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
if (layerm == nullptr || !layerm->perimeter_path_modulation_v2_has_fallback_slices)
|
||||
continue;
|
||||
layerm->slices = std::move(layerm->perimeter_path_modulation_v2_fallback_slices);
|
||||
layerm->raw_slices = to_expolygons(layerm->slices.surfaces);
|
||||
layerm->perimeter_path_modulation_v2_applied = layerm->perimeter_path_modulation_v2_fallback_is_modulated;
|
||||
layerm->perimeter_path_modulation_v2_has_fallback_slices = false;
|
||||
layerm->perimeter_path_modulation_v2_fallback_is_modulated = false;
|
||||
changed = true;
|
||||
}
|
||||
if (!changed)
|
||||
return;
|
||||
this->make_slices();
|
||||
this->lslices_bboxes.clear();
|
||||
this->lslices_bboxes.reserve(this->lslices.size());
|
||||
for (const ExPolygon &expoly : this->lslices)
|
||||
this->lslices_bboxes.emplace_back(get_extents(expoly));
|
||||
}
|
||||
|
||||
void Layer::restore_untyped_slices()
|
||||
{
|
||||
if (layer_needs_raw_backup(this)) {
|
||||
@@ -182,8 +215,14 @@ void Layer::make_perimeters()
|
||||
// keep track of regions whose perimeters we have already generated
|
||||
std::vector<unsigned char> done(m_regions.size(), false);
|
||||
|
||||
auto has_perimeter_input = [](const LayerRegion *layerm) {
|
||||
return layerm != nullptr &&
|
||||
(!layerm->slices.empty() ||
|
||||
(layerm->perimeter_path_modulation_v2_applied && !layerm->unmodulated_raw_slices.empty()));
|
||||
};
|
||||
|
||||
for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm)
|
||||
if ((*layerm)->slices.empty()) {
|
||||
if (!has_perimeter_input(*layerm)) {
|
||||
(*layerm)->perimeters.clear();
|
||||
(*layerm)->fills.clear();
|
||||
(*layerm)->thin_fills.clear();
|
||||
@@ -199,7 +238,7 @@ void Layer::make_perimeters()
|
||||
LayerRegionPtrs layerms;
|
||||
layerms.push_back(*layerm);
|
||||
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
|
||||
if (! (*it)->slices.empty()) {
|
||||
if (has_perimeter_input(*it)) {
|
||||
LayerRegion* other_layerm = *it;
|
||||
const PrintRegion &other_region = other_layerm->region();
|
||||
if (is_perimeter_compatible(this_region, other_region))
|
||||
|
||||
@@ -42,6 +42,11 @@ public:
|
||||
// Only backed up for multi-region layers or layers with elephant foot compensation.
|
||||
//FIXME Review whether not to simplify the code by keeping the raw_slices all the time.
|
||||
ExPolygons raw_slices;
|
||||
ExPolygons unmodulated_raw_slices;
|
||||
bool perimeter_path_modulation_v2_applied = false;
|
||||
SurfaceCollection perimeter_path_modulation_v2_fallback_slices;
|
||||
bool perimeter_path_modulation_v2_has_fallback_slices = false;
|
||||
bool perimeter_path_modulation_v2_fallback_is_modulated = false;
|
||||
|
||||
// collection of extrusion paths/loops filling gaps
|
||||
// These fills are generated by the perimeter generator.
|
||||
@@ -170,6 +175,8 @@ public:
|
||||
void restore_untyped_slices();
|
||||
// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
|
||||
void restore_untyped_slices_no_extra_perimeters();
|
||||
void apply_perimeter_path_modulation_v2();
|
||||
void commit_perimeter_path_modulation_v2_fallbacks();
|
||||
// Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices.
|
||||
ExPolygons merged(float offset) const;
|
||||
template <class T> bool any_internal_region_slice_contains(const T &item) const {
|
||||
|
||||
@@ -411,21 +411,33 @@ static std::optional<PerimeterTextureRecolorSampler> perimeter_texture_make_reco
|
||||
float base_outer_width_mm)
|
||||
{
|
||||
const Layer *layer = layer_region.layer();
|
||||
if (layer == nullptr || layer->object() == nullptr || layer->object()->print() == nullptr)
|
||||
if (layer == nullptr)
|
||||
return std::nullopt;
|
||||
|
||||
const PrintObject *print_object = layer->object();
|
||||
if (print_object == nullptr)
|
||||
return std::nullopt;
|
||||
|
||||
const Print *print = print_object->print();
|
||||
if (print == nullptr || print->canceled())
|
||||
return std::nullopt;
|
||||
|
||||
const PrintConfig &print_config = print->config();
|
||||
const size_t num_physical = print_config.filament_colour.values.size();
|
||||
if (num_physical == 0)
|
||||
return std::nullopt;
|
||||
|
||||
std::optional<TextureMappingOffsetContext> context =
|
||||
build_texture_mapping_offset_context_for_layer(*layer->object(), *layer, zone, texture_zone_id, 0, base_outer_width_mm);
|
||||
if (!context)
|
||||
build_texture_mapping_offset_context_for_layer(*print_object, *layer, zone, texture_zone_id, 0, base_outer_width_mm);
|
||||
if (!context || print->canceled() || context->component_ids.empty())
|
||||
return std::nullopt;
|
||||
|
||||
PerimeterTextureRecolorSampler sampler;
|
||||
sampler.image_texture = zone.is_image_texture();
|
||||
sampler.num_physical = layer->object()->print()->config().filament_colour.values.size();
|
||||
sampler.num_physical = num_physical;
|
||||
if (sampler.image_texture) {
|
||||
sampler.image_context = std::move(*context);
|
||||
sampler.image_component_colors.reserve(sampler.image_context->component_ids.size());
|
||||
const PrintConfig &print_config = layer->object()->print()->config();
|
||||
for (const unsigned int id : sampler.image_context->component_ids) {
|
||||
ColorRGB decoded;
|
||||
if (id >= 1 && id <= print_config.filament_colour.values.size() &&
|
||||
@@ -441,15 +453,19 @@ static std::optional<PerimeterTextureRecolorSampler> perimeter_texture_make_reco
|
||||
sampler.component_ids.reserve(context->component_ids.size());
|
||||
sampler.gradient_contexts.reserve(context->component_ids.size());
|
||||
for (const unsigned int component_id : context->component_ids) {
|
||||
if (print->canceled())
|
||||
return std::nullopt;
|
||||
if (component_id < 1 || component_id > sampler.num_physical)
|
||||
continue;
|
||||
std::optional<TextureMappingOffsetContext> component_context =
|
||||
build_texture_mapping_offset_context_for_layer(*layer->object(), *layer, zone, texture_zone_id, component_id, base_outer_width_mm);
|
||||
build_texture_mapping_offset_context_for_layer(*print_object, *layer, zone, texture_zone_id, component_id, base_outer_width_mm);
|
||||
if (!component_context)
|
||||
continue;
|
||||
sampler.component_ids.emplace_back(component_id);
|
||||
sampler.gradient_contexts.emplace_back(std::move(*component_context));
|
||||
}
|
||||
if (sampler.component_ids.empty() || sampler.component_ids.size() != sampler.gradient_contexts.size())
|
||||
return std::nullopt;
|
||||
return sampler;
|
||||
}
|
||||
|
||||
@@ -971,6 +987,52 @@ static ExPolygons perimeter_texture_recolor_masks_union(const std::vector<ExPoly
|
||||
return out.empty() ? ExPolygons() : union_ex(out);
|
||||
}
|
||||
|
||||
static void perimeter_texture_top_visible_recolor_data(const LayerRegion &layer_region,
|
||||
const SurfaceCollection &slices,
|
||||
const TextureMappingZone &zone,
|
||||
unsigned int texture_zone_id,
|
||||
float texture_external_width_mm,
|
||||
std::vector<ExPolygons> &top_visible_recolor_masks,
|
||||
ExPolygons &top_visible_recolor_protection_mask,
|
||||
PerimeterTextureTopVisibleRecolorThresholds &top_visible_recolor_thresholds)
|
||||
{
|
||||
top_visible_recolor_masks.clear();
|
||||
top_visible_recolor_protection_mask.clear();
|
||||
top_visible_recolor_thresholds =
|
||||
perimeter_texture_top_visible_recolor_thresholds(zone.top_visible_perimeter_recolor_aggressiveness);
|
||||
const PrintRegionConfig ®ion_config = layer_region.region().config();
|
||||
const int wall_loops = std::max(1, region_config.wall_loops.value);
|
||||
const Flow perimeter_flow = layer_region.flow(frPerimeter);
|
||||
const float wall_depth_mm =
|
||||
0.5f * texture_external_width_mm +
|
||||
float(std::max(0, wall_loops - 1)) * float(perimeter_flow.spacing()) +
|
||||
0.5f * float(perimeter_flow.width()) + 0.05f;
|
||||
ExPolygons top_visible_recolor_wall_band;
|
||||
ExPolygons top_visible_recolor_mask =
|
||||
perimeter_texture_top_visible_wall_band_mask(layer_region, slices, wall_depth_mm, &top_visible_recolor_wall_band);
|
||||
if (top_visible_recolor_mask.empty())
|
||||
return;
|
||||
|
||||
top_visible_recolor_masks =
|
||||
perimeter_texture_top_visible_region_recolor_masks(layer_region,
|
||||
slices,
|
||||
top_visible_recolor_wall_band,
|
||||
top_visible_recolor_mask,
|
||||
zone,
|
||||
texture_zone_id,
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_thresholds);
|
||||
if (!perimeter_texture_recolor_masks_have_color(top_visible_recolor_masks)) {
|
||||
top_visible_recolor_masks.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
ExPolygons recolor_union = perimeter_texture_recolor_masks_union(top_visible_recolor_masks);
|
||||
top_visible_recolor_protection_mask = intersection_ex(recolor_union, top_visible_recolor_mask);
|
||||
if (top_visible_recolor_protection_mask.empty())
|
||||
top_visible_recolor_masks.clear();
|
||||
}
|
||||
|
||||
static bool perimeter_texture_split_surfaces_by_recolor_masks(const SurfaceCollection &input,
|
||||
const SurfaceCollection &colored_input,
|
||||
const std::vector<ExPolygons> &masks,
|
||||
@@ -1365,6 +1427,106 @@ static const TextureMappingZone *perimeter_path_modulation_zone_for_region(const
|
||||
return zone;
|
||||
}
|
||||
|
||||
static const TextureMappingZone *perimeter_path_modulation_v2_zone_for_region(const Print &print,
|
||||
const PrintRegionConfig ®ion_config,
|
||||
unsigned int &texture_zone_id)
|
||||
{
|
||||
const TextureMappingZone *zone = perimeter_path_modulation_zone_for_region(print, region_config, texture_zone_id);
|
||||
return zone != nullptr && zone->uses_perimeter_path_modulation_v2() ? zone : nullptr;
|
||||
}
|
||||
|
||||
void Layer::apply_perimeter_path_modulation_v2()
|
||||
{
|
||||
PrintObject *print_object = this->object();
|
||||
if (print_object == nullptr || print_object->print() == nullptr)
|
||||
return;
|
||||
|
||||
bool needs_geometry_update = false;
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
if (layerm == nullptr)
|
||||
continue;
|
||||
unsigned int texture_zone_id = 0;
|
||||
if (layerm->perimeter_path_modulation_v2_applied ||
|
||||
perimeter_path_modulation_v2_zone_for_region(*print_object->print(),
|
||||
layerm->region().config(),
|
||||
texture_zone_id) != nullptr) {
|
||||
needs_geometry_update = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!needs_geometry_update)
|
||||
return;
|
||||
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
if (layerm == nullptr)
|
||||
continue;
|
||||
if (layerm->unmodulated_raw_slices.empty() && !layerm->raw_slices.empty())
|
||||
layerm->unmodulated_raw_slices = layerm->raw_slices;
|
||||
if (layerm->unmodulated_raw_slices.empty() && !layerm->slices.empty())
|
||||
layerm->unmodulated_raw_slices = to_expolygons(layerm->slices.surfaces);
|
||||
if (!layerm->unmodulated_raw_slices.empty() || !layerm->slices.empty()) {
|
||||
layerm->slices.set(layerm->unmodulated_raw_slices, stInternal);
|
||||
layerm->raw_slices = layerm->unmodulated_raw_slices;
|
||||
} else {
|
||||
layerm->raw_slices.clear();
|
||||
}
|
||||
layerm->perimeter_path_modulation_v2_applied = false;
|
||||
layerm->perimeter_path_modulation_v2_fallback_slices.clear();
|
||||
layerm->perimeter_path_modulation_v2_has_fallback_slices = false;
|
||||
layerm->perimeter_path_modulation_v2_fallback_is_modulated = false;
|
||||
}
|
||||
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
if (layerm == nullptr || layerm->slices.empty())
|
||||
continue;
|
||||
unsigned int texture_zone_id = 0;
|
||||
const TextureMappingZone *zone =
|
||||
perimeter_path_modulation_v2_zone_for_region(*print_object->print(),
|
||||
layerm->region().config(),
|
||||
texture_zone_id);
|
||||
if (zone == nullptr)
|
||||
continue;
|
||||
ExPolygons top_visible_recolor_protection_mask;
|
||||
std::vector<ExPolygons> top_visible_recolor_masks;
|
||||
PerimeterTextureTopVisibleRecolorThresholds top_visible_recolor_thresholds;
|
||||
const ExPolygons *top_visible_recolor_mask_ptr = nullptr;
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds_ptr = nullptr;
|
||||
if (zone->recolor_top_visible_perimeter_sections) {
|
||||
const float texture_external_width_mm =
|
||||
std::max(0.05f, float(print_object->print()->config().texture_mapping_outer_wall_gradient_max_line_width.value));
|
||||
perimeter_texture_top_visible_recolor_data(*layerm,
|
||||
layerm->slices,
|
||||
*zone,
|
||||
texture_zone_id,
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_masks,
|
||||
top_visible_recolor_protection_mask,
|
||||
top_visible_recolor_thresholds);
|
||||
if (!top_visible_recolor_protection_mask.empty()) {
|
||||
top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask;
|
||||
top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds;
|
||||
}
|
||||
}
|
||||
SurfaceCollection modulated_slices =
|
||||
perimeter_path_modulated_surfaces(*layerm,
|
||||
layerm->slices,
|
||||
*zone,
|
||||
texture_zone_id,
|
||||
std::nullopt,
|
||||
top_visible_recolor_mask_ptr,
|
||||
top_visible_recolor_thresholds_ptr);
|
||||
layerm->slices = std::move(modulated_slices);
|
||||
layerm->raw_slices = to_expolygons(layerm->slices.surfaces);
|
||||
layerm->perimeter_path_modulation_v2_applied = true;
|
||||
}
|
||||
|
||||
this->make_slices();
|
||||
this->lslices_bboxes.clear();
|
||||
this->lslices_bboxes.reserve(this->lslices.size());
|
||||
for (const ExPolygon &expoly : this->lslices)
|
||||
this->lslices_bboxes.emplace_back(get_extents(expoly));
|
||||
}
|
||||
|
||||
Flow LayerRegion::flow(FlowRole role) const
|
||||
{
|
||||
return this->flow(role, m_layer->height);
|
||||
@@ -1435,8 +1597,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
|
||||
SurfaceCollection modulated_slices;
|
||||
const SurfaceCollection *perimeter_slices = &slices;
|
||||
ExPolygons top_visible_recolor_mask;
|
||||
ExPolygons top_visible_recolor_wall_band;
|
||||
ExPolygons top_visible_recolor_protection_mask;
|
||||
std::vector<ExPolygons> top_visible_recolor_masks;
|
||||
const ExPolygons *top_visible_recolor_mask_ptr = nullptr;
|
||||
@@ -1444,55 +1604,57 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds_ptr = nullptr;
|
||||
unsigned int perimeter_texture_zone_id = 0;
|
||||
bool use_perimeter_path_modulation = false;
|
||||
bool use_legacy_perimeter_path_modulation = false;
|
||||
bool use_perimeter_path_modulation_v2 = false;
|
||||
const TextureMappingZone *perimeter_path_zone =
|
||||
perimeter_path_modulation_zone_for_region(*this->layer()->object()->print(), region_config, perimeter_texture_zone_id);
|
||||
if (perimeter_path_zone != nullptr) {
|
||||
if (perimeter_path_zone->recolor_top_visible_perimeter_sections) {
|
||||
top_visible_recolor_thresholds =
|
||||
perimeter_texture_top_visible_recolor_thresholds(perimeter_path_zone->top_visible_perimeter_recolor_aggressiveness);
|
||||
const int wall_loops = std::max(1, region_config.wall_loops.value);
|
||||
const Flow perimeter_flow = this->flow(frPerimeter);
|
||||
const float wall_depth_mm =
|
||||
0.5f * texture_external_width_mm +
|
||||
float(std::max(0, wall_loops - 1)) * float(perimeter_flow.spacing()) +
|
||||
0.5f * float(perimeter_flow.width()) + 0.05f;
|
||||
top_visible_recolor_mask =
|
||||
perimeter_texture_top_visible_wall_band_mask(*this, slices, wall_depth_mm, &top_visible_recolor_wall_band);
|
||||
if (!top_visible_recolor_mask.empty()) {
|
||||
top_visible_recolor_masks =
|
||||
perimeter_texture_top_visible_region_recolor_masks(*this,
|
||||
slices,
|
||||
top_visible_recolor_wall_band,
|
||||
top_visible_recolor_mask,
|
||||
*perimeter_path_zone,
|
||||
perimeter_texture_zone_id,
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_thresholds);
|
||||
if (perimeter_texture_recolor_masks_have_color(top_visible_recolor_masks)) {
|
||||
ExPolygons recolor_union = perimeter_texture_recolor_masks_union(top_visible_recolor_masks);
|
||||
top_visible_recolor_protection_mask = intersection_ex(recolor_union, top_visible_recolor_mask);
|
||||
if (!top_visible_recolor_protection_mask.empty()) {
|
||||
top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask;
|
||||
top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds;
|
||||
} else {
|
||||
top_visible_recolor_masks.clear();
|
||||
}
|
||||
} else {
|
||||
top_visible_recolor_masks.clear();
|
||||
}
|
||||
perimeter_texture_top_visible_recolor_data(*this,
|
||||
slices,
|
||||
*perimeter_path_zone,
|
||||
perimeter_texture_zone_id,
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_masks,
|
||||
top_visible_recolor_protection_mask,
|
||||
top_visible_recolor_thresholds);
|
||||
if (!top_visible_recolor_protection_mask.empty()) {
|
||||
top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask;
|
||||
top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds;
|
||||
}
|
||||
}
|
||||
modulated_slices = perimeter_path_modulated_surfaces(*this,
|
||||
slices,
|
||||
*perimeter_path_zone,
|
||||
perimeter_texture_zone_id,
|
||||
std::nullopt,
|
||||
top_visible_recolor_mask_ptr,
|
||||
top_visible_recolor_thresholds_ptr);
|
||||
perimeter_slices = &modulated_slices;
|
||||
if (perimeter_path_zone->uses_legacy_perimeter_path_modulation()) {
|
||||
modulated_slices = perimeter_path_modulated_surfaces(*this,
|
||||
slices,
|
||||
*perimeter_path_zone,
|
||||
perimeter_texture_zone_id,
|
||||
std::nullopt,
|
||||
top_visible_recolor_mask_ptr,
|
||||
top_visible_recolor_thresholds_ptr);
|
||||
perimeter_slices = &modulated_slices;
|
||||
use_legacy_perimeter_path_modulation = true;
|
||||
} else if (perimeter_path_zone->uses_perimeter_path_modulation_v2()) {
|
||||
use_perimeter_path_modulation_v2 = true;
|
||||
}
|
||||
use_perimeter_path_modulation = true;
|
||||
}
|
||||
|
||||
SurfaceCollection v2_original_slices;
|
||||
const SurfaceCollection *fallback_original_slices = &slices;
|
||||
if (use_perimeter_path_modulation_v2 && !this->unmodulated_raw_slices.empty()) {
|
||||
v2_original_slices.set(this->unmodulated_raw_slices, stInternal);
|
||||
fallback_original_slices = &v2_original_slices;
|
||||
}
|
||||
|
||||
auto set_perimeter_path_modulation_v2_fallback_slices =
|
||||
[this, use_perimeter_path_modulation_v2](const SurfaceCollection &fallback_slices, bool is_modulated) {
|
||||
if (!use_perimeter_path_modulation_v2)
|
||||
return;
|
||||
this->perimeter_path_modulation_v2_fallback_slices = fallback_slices;
|
||||
this->perimeter_path_modulation_v2_has_fallback_slices = true;
|
||||
this->perimeter_path_modulation_v2_fallback_is_modulated = is_modulated;
|
||||
};
|
||||
|
||||
const bool force_classic_wall_generator = region_uses_overhang_texture_mapping(*this->layer()->object()->print(), region_config);
|
||||
auto texture_external_flow = [&](float external_width_mm) {
|
||||
Flow out = this->flow(frExternalPerimeter);
|
||||
@@ -1592,7 +1754,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
|
||||
SurfaceCollection fill_surfaces_before;
|
||||
ExPolygons fill_no_overlap_before;
|
||||
if (use_perimeter_path_modulation) {
|
||||
if (use_legacy_perimeter_path_modulation || use_perimeter_path_modulation_v2) {
|
||||
fill_surfaces_before = *fill_surfaces;
|
||||
fill_no_overlap_before = *fill_no_overlap;
|
||||
}
|
||||
@@ -1601,14 +1763,16 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
use_perimeter_path_modulation ? std::optional<float>(texture_external_width_mm) : std::optional<float>();
|
||||
process_slices_with_top_visible_recolor(perimeter_slices, &slices, initial_texture_external_width_mm);
|
||||
|
||||
if (use_perimeter_path_modulation && this->perimeters.entities.empty() && this->thin_fills.entities.empty()) {
|
||||
if ((use_legacy_perimeter_path_modulation || use_perimeter_path_modulation_v2) &&
|
||||
this->perimeters.entities.empty() &&
|
||||
this->thin_fills.entities.empty()) {
|
||||
this->perimeters.clear();
|
||||
this->thin_fills.clear();
|
||||
*fill_surfaces = fill_surfaces_before;
|
||||
*fill_no_overlap = fill_no_overlap_before;
|
||||
fill_surfaces_before = *fill_surfaces;
|
||||
fill_no_overlap_before = *fill_no_overlap;
|
||||
process_slices(&slices, std::optional<float>(texture_external_width_mm));
|
||||
process_slices(fallback_original_slices, std::optional<float>(texture_external_width_mm));
|
||||
const bool original_texture_has_extrusions =
|
||||
!this->perimeters.entities.empty() || !this->thin_fills.entities.empty();
|
||||
const std::optional<float> reduced_external_width_mm = perimeter_texture_min_external_width(this->perimeters);
|
||||
@@ -1619,8 +1783,10 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
if (perimeter_texture_apply_recolor_small_perimeter_loops(*this,
|
||||
*perimeter_path_zone,
|
||||
perimeter_texture_zone_id,
|
||||
texture_external_width_mm))
|
||||
texture_external_width_mm)) {
|
||||
set_perimeter_path_modulation_v2_fallback_slices(*fallback_original_slices, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (has_reduced_external_width) {
|
||||
this->perimeters.clear();
|
||||
@@ -1629,14 +1795,18 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
*fill_no_overlap = fill_no_overlap_before;
|
||||
SurfaceCollection reduced_modulated_slices =
|
||||
perimeter_path_modulated_surfaces(*this,
|
||||
slices,
|
||||
*fallback_original_slices,
|
||||
*perimeter_path_zone,
|
||||
perimeter_texture_zone_id,
|
||||
reduced_external_width_mm,
|
||||
top_visible_recolor_mask_ptr,
|
||||
top_visible_recolor_thresholds_ptr);
|
||||
process_slices_with_top_visible_recolor(&reduced_modulated_slices, &slices, reduced_external_width_mm);
|
||||
process_slices_with_top_visible_recolor(&reduced_modulated_slices, fallback_original_slices, reduced_external_width_mm);
|
||||
if (!this->perimeters.entities.empty() || !this->thin_fills.entities.empty())
|
||||
set_perimeter_path_modulation_v2_fallback_slices(reduced_modulated_slices, true);
|
||||
}
|
||||
if (!has_reduced_external_width && (!this->perimeters.entities.empty() || !this->thin_fills.entities.empty()))
|
||||
set_perimeter_path_modulation_v2_fallback_slices(*fallback_original_slices, false);
|
||||
if (this->perimeters.entities.empty() && this->thin_fills.entities.empty()) {
|
||||
this->perimeters.clear();
|
||||
this->thin_fills.clear();
|
||||
@@ -1644,7 +1814,8 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
*fill_no_overlap = fill_no_overlap_before;
|
||||
const std::optional<float> fallback_texture_external_width_mm =
|
||||
original_texture_has_extrusions ? std::optional<float>(texture_external_width_mm) : std::optional<float>();
|
||||
process_slices(&slices, fallback_texture_external_width_mm);
|
||||
process_slices(fallback_original_slices, fallback_texture_external_width_mm);
|
||||
set_perimeter_path_modulation_v2_fallback_slices(*fallback_original_slices, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,6 +514,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "texture_mapping_definitions"
|
||||
|| opt_key == "texture_mapping_global_settings") {
|
||||
osteps.emplace_back(posPerimeters);
|
||||
osteps.emplace_back(posSupportMaterial);
|
||||
steps.emplace_back(psWipeTower);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (
|
||||
@@ -4116,6 +4117,8 @@ const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; tota
|
||||
#define JSON_LAYER_REGION_CONFIG_HASH "config_hash"
|
||||
#define JSON_LAYER_REGION_SLICES "slices"
|
||||
#define JSON_LAYER_REGION_RAW_SLICES "raw_slices"
|
||||
#define JSON_LAYER_REGION_UNMODULATED_RAW_SLICES "unmodulated_raw_slices"
|
||||
#define JSON_LAYER_REGION_PERIMETER_PATH_MODULATION_V2_APPLIED "perimeter_path_modulation_v2_applied"
|
||||
//#define JSON_LAYER_REGION_ENTITIES "entities"
|
||||
#define JSON_LAYER_REGION_THIN_FILLS "thin_fills"
|
||||
#define JSON_LAYER_REGION_FILL_EXPOLYGONS "fill_expolygons"
|
||||
@@ -4326,7 +4329,7 @@ static bool convert_extrusion_to_json(json& entity_json, json& entity_paths_json
|
||||
}
|
||||
|
||||
static void to_json(json& j, const LayerRegion& layer_region) {
|
||||
json unsupported_bridge_edges_json = json::array(), slices_surfaces_json = json::array(), raw_slices_json = json::array(), thin_fills_json, thin_fill_entities_json = json::array();
|
||||
json unsupported_bridge_edges_json = json::array(), slices_surfaces_json = json::array(), raw_slices_json = json::array(), unmodulated_raw_slices_json = json::array(), thin_fills_json, thin_fill_entities_json = json::array();
|
||||
json fill_expolygons_json = json::array(), fill_no_overlap_expolygons_json = json::array(), fill_surfaces_json = json::array(), perimeters_json, perimeter_entities_json = json::array(), fills_json, fill_entities_json = json::array();
|
||||
|
||||
j[JSON_LAYER_REGION_CONFIG_HASH] = layer_region.region().config_hash();
|
||||
@@ -4344,6 +4347,13 @@ static void to_json(json& j, const LayerRegion& layer_region) {
|
||||
raw_slices_json.push_back(std::move(raw_polygon_json));
|
||||
}
|
||||
j.push_back({JSON_LAYER_REGION_RAW_SLICES, std::move(raw_slices_json)});
|
||||
for (const ExPolygon& raw_slice_explogyon : layer_region.unmodulated_raw_slices) {
|
||||
json raw_polygon_json = raw_slice_explogyon;
|
||||
|
||||
unmodulated_raw_slices_json.push_back(std::move(raw_polygon_json));
|
||||
}
|
||||
j.push_back({JSON_LAYER_REGION_UNMODULATED_RAW_SLICES, std::move(unmodulated_raw_slices_json)});
|
||||
j.push_back({JSON_LAYER_REGION_PERIMETER_PATH_MODULATION_V2_APPLIED, layer_region.perimeter_path_modulation_v2_applied});
|
||||
|
||||
//thin fills
|
||||
thin_fills_json[JSON_EXTRUSION_NO_SORT] = layer_region.thin_fills.no_sort;
|
||||
@@ -4631,6 +4641,20 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg
|
||||
polygon = j[JSON_LAYER_REGION_RAW_SLICES][raw_slices_index];
|
||||
layer_region.raw_slices.push_back(std::move(polygon));
|
||||
}
|
||||
if (j.contains(JSON_LAYER_REGION_UNMODULATED_RAW_SLICES)) {
|
||||
int unmodulated_raw_slices_count = j[JSON_LAYER_REGION_UNMODULATED_RAW_SLICES].size();
|
||||
for (int raw_slices_index = 0; raw_slices_index < unmodulated_raw_slices_count; raw_slices_index++)
|
||||
{
|
||||
ExPolygon polygon;
|
||||
|
||||
polygon = j[JSON_LAYER_REGION_UNMODULATED_RAW_SLICES][raw_slices_index];
|
||||
layer_region.unmodulated_raw_slices.push_back(std::move(polygon));
|
||||
}
|
||||
} else {
|
||||
layer_region.unmodulated_raw_slices = layer_region.raw_slices;
|
||||
}
|
||||
layer_region.perimeter_path_modulation_v2_applied =
|
||||
j.value(JSON_LAYER_REGION_PERIMETER_PATH_MODULATION_V2_APPLIED, false);
|
||||
|
||||
//thin fills
|
||||
layer_region.thin_fills.no_sort = j[JSON_LAYER_REGION_THIN_FILLS][JSON_EXTRUSION_NO_SORT];
|
||||
|
||||
@@ -509,6 +509,11 @@ void PrintObject::make_perimeters()
|
||||
m_typed_slices = false;
|
||||
}
|
||||
|
||||
for (auto layer_it = m_layers.rbegin(); layer_it != m_layers.rend(); ++layer_it) {
|
||||
m_print->throw_if_canceled();
|
||||
(*layer_it)->apply_perimeter_path_modulation_v2();
|
||||
}
|
||||
|
||||
// compare each layer to the one below, and mark those slices needing
|
||||
// one additional inner perimeter, like the top of domed objects-
|
||||
|
||||
@@ -593,6 +598,11 @@ void PrintObject::make_perimeters()
|
||||
m_print->throw_if_canceled();
|
||||
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
|
||||
|
||||
for (Layer *layer : m_layers) {
|
||||
m_print->throw_if_canceled();
|
||||
layer->commit_perimeter_path_modulation_v2_fallbacks();
|
||||
}
|
||||
|
||||
this->set_done(posPerimeters);
|
||||
}
|
||||
|
||||
@@ -832,45 +842,169 @@ void PrintObject::generate_support_material()
|
||||
if (this->set_started(posSupportMaterial)) {
|
||||
this->clear_support_layers();
|
||||
|
||||
if(!has_support() && !m_print->get_no_check_flag()) {
|
||||
// BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled
|
||||
// Note: we also need to pop warning if support is disabled and only raft is enabled
|
||||
m_print->set_status(50, L("Checking support necessity"));
|
||||
typedef std::chrono::high_resolution_clock clock_;
|
||||
typedef std::chrono::duration<double, std::ratio<1> > second_;
|
||||
std::chrono::time_point<clock_> t0{ clock_::now() };
|
||||
struct LayerSupportGeometryBackup {
|
||||
Layer *layer { nullptr };
|
||||
ExPolygons lslices;
|
||||
std::vector<BoundingBox> lslices_bboxes;
|
||||
};
|
||||
struct LayerRegionSupportGeometryBackup {
|
||||
LayerRegion *layerm { nullptr };
|
||||
SurfaceCollection slices;
|
||||
ExPolygons raw_slices;
|
||||
SurfaceCollection fill_surfaces;
|
||||
ExPolygons fill_expolygons;
|
||||
ExPolygons fill_no_overlap_expolygons;
|
||||
bool perimeter_path_modulation_v2_applied { false };
|
||||
};
|
||||
|
||||
SupportNecessaryType sntype = this->is_support_necessary();
|
||||
std::vector<LayerSupportGeometryBackup> layer_backups;
|
||||
std::vector<LayerRegionSupportGeometryBackup> region_backups;
|
||||
const bool typed_slices_backup = m_typed_slices;
|
||||
bool support_geometry_swapped = false;
|
||||
|
||||
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
|
||||
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs.";
|
||||
auto rebuild_layer_geometry = [](Layer *layer) {
|
||||
layer->make_slices();
|
||||
layer->lslices_bboxes.clear();
|
||||
layer->lslices_bboxes.reserve(layer->lslices.size());
|
||||
for (const ExPolygon &expoly : layer->lslices)
|
||||
layer->lslices_bboxes.emplace_back(get_extents(expoly));
|
||||
};
|
||||
|
||||
if (sntype != NoNeedSupp) {
|
||||
std::map<SupportNecessaryType, std::string> reasons = {
|
||||
{SharpTail,L("floating regions")},
|
||||
{Cantilever,L("floating cantilever")},
|
||||
{LargeOverhang,L("large overhangs")} };
|
||||
std::string warning_message = Slic3r::format(L("It seems object %s has %s. Please re-orient the object or enable support generation."),
|
||||
this->model_object()->name, reasons[sntype]);
|
||||
this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
|
||||
auto region_uses_unmodulated_support_geometry = [this](const LayerRegion *layerm) {
|
||||
if (layerm == nullptr)
|
||||
return false;
|
||||
const int filament_id = layerm->region().config().wall_filament.value;
|
||||
if (filament_id <= 0)
|
||||
return false;
|
||||
const TextureMappingZone *zone = this->print()->texture_mapping_manager().zone_from_id(unsigned(filament_id));
|
||||
return zone != nullptr &&
|
||||
zone->enabled &&
|
||||
!zone->deleted &&
|
||||
zone->uses_perimeter_path_modulation_v2() &&
|
||||
!zone->use_modulated_overhang_geometry_for_support &&
|
||||
(zone->is_2d_gradient() || zone->is_image_texture());
|
||||
};
|
||||
|
||||
auto restore_support_geometry = [&]() {
|
||||
if (!support_geometry_swapped)
|
||||
return;
|
||||
for (LayerRegionSupportGeometryBackup &backup : region_backups) {
|
||||
backup.layerm->slices = std::move(backup.slices);
|
||||
backup.layerm->raw_slices = std::move(backup.raw_slices);
|
||||
backup.layerm->fill_surfaces = std::move(backup.fill_surfaces);
|
||||
backup.layerm->fill_expolygons = std::move(backup.fill_expolygons);
|
||||
backup.layerm->fill_no_overlap_expolygons = std::move(backup.fill_no_overlap_expolygons);
|
||||
backup.layerm->perimeter_path_modulation_v2_applied = backup.perimeter_path_modulation_v2_applied;
|
||||
}
|
||||
for (LayerSupportGeometryBackup &backup : layer_backups) {
|
||||
backup.layer->lslices = std::move(backup.lslices);
|
||||
backup.layer->lslices_bboxes = std::move(backup.lslices_bboxes);
|
||||
}
|
||||
m_typed_slices = typed_slices_backup;
|
||||
support_geometry_swapped = false;
|
||||
};
|
||||
|
||||
auto use_unmodulated_support_geometry = [&]() {
|
||||
bool needed = false;
|
||||
for (Layer *layer : m_layers) {
|
||||
for (LayerRegion *layerm : layer->m_regions) {
|
||||
if (region_uses_unmodulated_support_geometry(layerm)) {
|
||||
needed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needed)
|
||||
break;
|
||||
}
|
||||
if (!needed)
|
||||
return;
|
||||
|
||||
layer_backups.reserve(m_layers.size());
|
||||
for (Layer *layer : m_layers) {
|
||||
layer_backups.push_back({ layer, layer->lslices, layer->lslices_bboxes });
|
||||
for (LayerRegion *layerm : layer->m_regions) {
|
||||
region_backups.push_back({
|
||||
layerm,
|
||||
layerm->slices,
|
||||
layerm->raw_slices,
|
||||
layerm->fill_surfaces,
|
||||
layerm->fill_expolygons,
|
||||
layerm->fill_no_overlap_expolygons,
|
||||
layerm->perimeter_path_modulation_v2_applied
|
||||
});
|
||||
}
|
||||
}
|
||||
support_geometry_swapped = true;
|
||||
|
||||
for (Layer *layer : m_layers) {
|
||||
bool layer_changed = false;
|
||||
for (LayerRegion *layerm : layer->m_regions) {
|
||||
if (!region_uses_unmodulated_support_geometry(layerm))
|
||||
continue;
|
||||
ExPolygons support_slices =
|
||||
!layerm->unmodulated_raw_slices.empty() ?
|
||||
layerm->unmodulated_raw_slices :
|
||||
(!layerm->raw_slices.empty() ?
|
||||
layerm->raw_slices :
|
||||
to_expolygons(layerm->slices.surfaces));
|
||||
layerm->slices.set(support_slices, stInternal);
|
||||
layerm->raw_slices = std::move(support_slices);
|
||||
layerm->perimeter_path_modulation_v2_applied = false;
|
||||
layer_changed = true;
|
||||
}
|
||||
if (layer_changed)
|
||||
rebuild_layer_geometry(layer);
|
||||
}
|
||||
this->detect_surfaces_type();
|
||||
};
|
||||
|
||||
try {
|
||||
use_unmodulated_support_geometry();
|
||||
|
||||
if(!has_support() && !m_print->get_no_check_flag()) {
|
||||
// BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled
|
||||
// Note: we also need to pop warning if support is disabled and only raft is enabled
|
||||
m_print->set_status(50, L("Checking support necessity"));
|
||||
typedef std::chrono::high_resolution_clock clock_;
|
||||
typedef std::chrono::duration<double, std::ratio<1> > second_;
|
||||
std::chrono::time_point<clock_> t0{ clock_::now() };
|
||||
|
||||
SupportNecessaryType sntype = this->is_support_necessary();
|
||||
|
||||
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
|
||||
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs.";
|
||||
|
||||
if (sntype != NoNeedSupp) {
|
||||
std::map<SupportNecessaryType, std::string> reasons = {
|
||||
{SharpTail,L("floating regions")},
|
||||
{Cantilever,L("floating cantilever")},
|
||||
{LargeOverhang,L("large overhangs")} };
|
||||
std::string warning_message = Slic3r::format(L("It seems object %s has %s. Please re-orient the object or enable support generation."),
|
||||
this->model_object()->name, reasons[sntype]);
|
||||
this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Printing without supports. Empty layer means some objects or object parts are levitating,
|
||||
// therefore they cannot be printed without supports.
|
||||
for (const Layer *layer : m_layers)
|
||||
if (layer->empty())
|
||||
throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
|
||||
// Printing without supports. Empty layer means some objects or object parts are levitating,
|
||||
// therefore they cannot be printed without supports.
|
||||
for (const Layer *layer : m_layers)
|
||||
if (layer->empty())
|
||||
throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) {
|
||||
m_print->set_status(50, L("Generating support"));
|
||||
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) {
|
||||
m_print->set_status(50, L("Generating support"));
|
||||
|
||||
this->_generate_support_material();
|
||||
m_print->throw_if_canceled();
|
||||
this->_generate_support_material();
|
||||
m_print->throw_if_canceled();
|
||||
}
|
||||
restore_support_geometry();
|
||||
this->set_done(posSupportMaterial);
|
||||
} catch (...) {
|
||||
restore_support_geometry();
|
||||
throw;
|
||||
}
|
||||
this->set_done(posSupportMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -526,19 +526,25 @@ static int mapping_mode_from_name(const std::string &name)
|
||||
|
||||
static std::string modulation_mode_name(int mode)
|
||||
{
|
||||
return clamp_int(mode,
|
||||
int(TextureMappingZone::ModulationLineWidth),
|
||||
int(TextureMappingZone::ModulationPerimeterPath)) ==
|
||||
int(TextureMappingZone::ModulationPerimeterPath) ?
|
||||
std::string("perimeter_path") :
|
||||
std::string("line_width");
|
||||
switch (clamp_int(mode,
|
||||
int(TextureMappingZone::ModulationLineWidth),
|
||||
int(TextureMappingZone::ModulationPerimeterPathV2))) {
|
||||
case int(TextureMappingZone::ModulationPerimeterPath):
|
||||
return std::string("perimeter_path");
|
||||
case int(TextureMappingZone::ModulationPerimeterPathV2):
|
||||
return std::string("perimeter_path_v2");
|
||||
default:
|
||||
return std::string("line_width");
|
||||
}
|
||||
}
|
||||
|
||||
static int modulation_mode_from_name(const std::string &name)
|
||||
{
|
||||
return name == "perimeter_path" ?
|
||||
int(TextureMappingZone::ModulationPerimeterPath) :
|
||||
int(TextureMappingZone::ModulationLineWidth);
|
||||
if (name == "perimeter_path")
|
||||
return int(TextureMappingZone::ModulationPerimeterPath);
|
||||
if (name == "perimeter_path_v2")
|
||||
return int(TextureMappingZone::ModulationPerimeterPathV2);
|
||||
return int(TextureMappingZone::ModulationLineWidth);
|
||||
}
|
||||
|
||||
static std::string top_visible_recolor_aggressiveness_name(int mode)
|
||||
@@ -859,6 +865,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
|
||||
seam_hiding == rhs.seam_hiding &&
|
||||
nonlinear_offset_adjustment == rhs.nonlinear_offset_adjustment &&
|
||||
modulation_mode == rhs.modulation_mode &&
|
||||
use_modulated_overhang_geometry_for_support == rhs.use_modulated_overhang_geometry_for_support &&
|
||||
recolor_small_perimeter_loops == rhs.recolor_small_perimeter_loops &&
|
||||
recolor_top_visible_perimeter_sections == rhs.recolor_top_visible_perimeter_sections &&
|
||||
top_visible_perimeter_recolor_aggressiveness == rhs.top_visible_perimeter_recolor_aggressiveness &&
|
||||
@@ -1116,6 +1123,7 @@ std::string TextureMappingManager::serialize_entries()
|
||||
texture["hide_seams"] = zone.seam_hiding;
|
||||
texture["nonlinear_offset_adjustment"] = zone.nonlinear_offset_adjustment;
|
||||
texture["modulation_mode"] = modulation_mode_name(zone.modulation_mode);
|
||||
texture["use_modulated_overhang_geometry_for_support"] = zone.use_modulated_overhang_geometry_for_support;
|
||||
texture["recolor_small_perimeter_loops"] = zone.recolor_small_perimeter_loops || zone.recolor_top_visible_perimeter_sections;
|
||||
texture["recolor_top_visible_perimeter_sections"] = zone.recolor_top_visible_perimeter_sections;
|
||||
texture["top_visible_perimeter_recolor_aggressiveness"] =
|
||||
@@ -1264,6 +1272,9 @@ void TextureMappingManager::load_entries(const std::string &serialized,
|
||||
zone.modulation_mode =
|
||||
modulation_mode_from_name(texture.value("modulation_mode",
|
||||
modulation_mode_name(TextureMappingZone::DefaultModulationMode)));
|
||||
zone.use_modulated_overhang_geometry_for_support =
|
||||
texture.value("use_modulated_overhang_geometry_for_support",
|
||||
TextureMappingZone::DefaultUseModulatedOverhangGeometryForSupport);
|
||||
zone.recolor_small_perimeter_loops =
|
||||
texture.value("recolor_small_perimeter_loops", TextureMappingZone::DefaultRecolorSmallPerimeterLoops);
|
||||
zone.recolor_top_visible_perimeter_sections =
|
||||
|
||||
@@ -48,7 +48,8 @@ struct TextureMappingZone
|
||||
|
||||
enum ModulationMode : uint8_t {
|
||||
ModulationLineWidth = 0,
|
||||
ModulationPerimeterPath = 1
|
||||
ModulationPerimeterPath = 1,
|
||||
ModulationPerimeterPathV2 = 2
|
||||
};
|
||||
|
||||
enum TopVisiblePerimeterRecolorAggressiveness : uint8_t {
|
||||
@@ -114,6 +115,7 @@ struct TextureMappingZone
|
||||
static constexpr bool DefaultSeamHiding = false;
|
||||
static constexpr bool DefaultNonlinearOffsetAdjustment = false;
|
||||
static constexpr int DefaultModulationMode = int(ModulationLineWidth);
|
||||
static constexpr bool DefaultUseModulatedOverhangGeometryForSupport = false;
|
||||
static constexpr bool DefaultRecolorSmallPerimeterLoops = false;
|
||||
static constexpr bool DefaultRecolorTopVisiblePerimeterSections = false;
|
||||
static constexpr int DefaultTopVisiblePerimeterRecolorAggressiveness = int(TopVisibleRecolorAggressive);
|
||||
@@ -170,6 +172,7 @@ struct TextureMappingZone
|
||||
bool seam_hiding = DefaultSeamHiding;
|
||||
bool nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment;
|
||||
int modulation_mode = DefaultModulationMode;
|
||||
bool use_modulated_overhang_geometry_for_support = DefaultUseModulatedOverhangGeometryForSupport;
|
||||
bool recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops;
|
||||
bool recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections;
|
||||
int top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness;
|
||||
@@ -199,7 +202,13 @@ struct TextureMappingZone
|
||||
|
||||
bool is_image_texture() const { return surface_pattern == int(ImageTexture); }
|
||||
bool is_2d_gradient() const { return surface_pattern == int(Gradient2D); }
|
||||
bool uses_perimeter_path_modulation() const { return modulation_mode == int(ModulationPerimeterPath); }
|
||||
bool uses_perimeter_path_modulation() const
|
||||
{
|
||||
return modulation_mode == int(ModulationPerimeterPath) ||
|
||||
modulation_mode == int(ModulationPerimeterPathV2);
|
||||
}
|
||||
bool uses_legacy_perimeter_path_modulation() const { return modulation_mode == int(ModulationPerimeterPath); }
|
||||
bool uses_perimeter_path_modulation_v2() const { return modulation_mode == int(ModulationPerimeterPathV2); }
|
||||
|
||||
void reset_offset_settings()
|
||||
{
|
||||
@@ -224,6 +233,7 @@ struct TextureMappingZone
|
||||
seam_hiding = DefaultSeamHiding;
|
||||
nonlinear_offset_adjustment = DefaultNonlinearOffsetAdjustment;
|
||||
modulation_mode = DefaultModulationMode;
|
||||
use_modulated_overhang_geometry_for_support = DefaultUseModulatedOverhangGeometryForSupport;
|
||||
recolor_small_perimeter_loops = DefaultRecolorSmallPerimeterLoops;
|
||||
recolor_top_visible_perimeter_sections = DefaultRecolorTopVisiblePerimeterSections;
|
||||
top_visible_perimeter_recolor_aggressiveness = DefaultTopVisiblePerimeterRecolorAggressiveness;
|
||||
|
||||
@@ -1426,6 +1426,7 @@ public:
|
||||
bool seam_hiding,
|
||||
bool nonlinear_offset_adjustment,
|
||||
int modulation_mode,
|
||||
bool use_modulated_overhang_geometry_for_support,
|
||||
bool recolor_small_perimeter_loops,
|
||||
bool recolor_top_visible_perimeter_sections,
|
||||
int top_visible_perimeter_recolor_aggressiveness,
|
||||
@@ -1699,11 +1700,12 @@ public:
|
||||
modulation_mode_row->Add(new wxStaticText(print_settings_page, wxID_ANY, _L("Modulation mode:")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
|
||||
wxArrayString modulation_mode_choices;
|
||||
modulation_mode_choices.Add(_L("Line width modulation"));
|
||||
modulation_mode_choices.Add(_L("Perimeter path modulation"));
|
||||
modulation_mode_choices.Add(_L("Perimeter path modulation (legacy v1)"));
|
||||
modulation_mode_choices.Add(_L("Perimeter path modulation (v2)"));
|
||||
m_modulation_mode_choice = new wxChoice(print_settings_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, modulation_mode_choices);
|
||||
m_modulation_mode_choice->SetSelection(std::clamp(modulation_mode,
|
||||
int(TextureMappingZone::ModulationLineWidth),
|
||||
int(TextureMappingZone::ModulationPerimeterPath)));
|
||||
int(TextureMappingZone::ModulationPerimeterPathV2)));
|
||||
modulation_mode_row->Add(m_modulation_mode_choice, 1, wxALIGN_CENTER_VERTICAL);
|
||||
print_settings_root->Add(modulation_mode_row, 0, wxEXPAND | wxALL, gap);
|
||||
m_modulation_mode_choice->Bind(wxEVT_CHOICE, [this](wxCommandEvent &) { update_modulation_mode_options_visibility(false); });
|
||||
@@ -1757,6 +1759,10 @@ public:
|
||||
m_compact_offset_mode_checkbox->SetToolTip(
|
||||
_L("Normalizes sampled filament offsets so the strongest active color uses the full maximum line width."));
|
||||
experimental_box->Add(m_compact_offset_mode_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
|
||||
m_use_modulated_overhang_geometry_for_support_checkbox =
|
||||
new wxCheckBox(experimental_page, wxID_ANY, _L("Use modulated overhang geometry in support generation"));
|
||||
m_use_modulated_overhang_geometry_for_support_checkbox->SetValue(use_modulated_overhang_geometry_for_support);
|
||||
experimental_box->Add(m_use_modulated_overhang_geometry_for_support_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
|
||||
m_dithering_resolution_panel = new wxPanel(experimental_page, wxID_ANY);
|
||||
auto *dithering_resolution_row = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_dithering_resolution_panel->SetSizer(dithering_resolution_row);
|
||||
@@ -2028,12 +2034,17 @@ public:
|
||||
bool reduce_outer_surface_texture() const { return false; }
|
||||
bool seam_hiding() const { return m_seam_hiding_checkbox && m_seam_hiding_checkbox->GetValue(); }
|
||||
bool nonlinear_offset_adjustment() const { return m_nonlinear_offset_adjustment_checkbox && m_nonlinear_offset_adjustment_checkbox->GetValue(); }
|
||||
bool use_modulated_overhang_geometry_for_support() const
|
||||
{
|
||||
return m_use_modulated_overhang_geometry_for_support_checkbox != nullptr &&
|
||||
m_use_modulated_overhang_geometry_for_support_checkbox->GetValue();
|
||||
}
|
||||
int modulation_mode() const
|
||||
{
|
||||
return m_modulation_mode_choice ?
|
||||
std::clamp(m_modulation_mode_choice->GetSelection(),
|
||||
int(TextureMappingZone::ModulationLineWidth),
|
||||
int(TextureMappingZone::ModulationPerimeterPath)) :
|
||||
int(TextureMappingZone::ModulationPerimeterPathV2)) :
|
||||
TextureMappingZone::DefaultModulationMode;
|
||||
}
|
||||
bool compact_offset_mode() const { return dithering_enabled() || (m_compact_offset_mode_checkbox && m_compact_offset_mode_checkbox->GetValue()); }
|
||||
@@ -2383,7 +2394,9 @@ private:
|
||||
|
||||
void update_modulation_mode_options_visibility(bool fit_dialog)
|
||||
{
|
||||
const bool perimeter_path_mode = modulation_mode() == int(TextureMappingZone::ModulationPerimeterPath);
|
||||
const bool perimeter_path_mode = modulation_mode() == int(TextureMappingZone::ModulationPerimeterPath) ||
|
||||
modulation_mode() == int(TextureMappingZone::ModulationPerimeterPathV2);
|
||||
const bool perimeter_path_v2_mode = modulation_mode() == int(TextureMappingZone::ModulationPerimeterPathV2);
|
||||
const bool top_visible_checked =
|
||||
m_recolor_top_visible_perimeter_sections_checkbox != nullptr &&
|
||||
m_recolor_top_visible_perimeter_sections_checkbox->GetValue();
|
||||
@@ -2393,6 +2406,8 @@ private:
|
||||
m_recolor_small_perimeter_loops_checkbox->Enable(perimeter_path_mode && !top_visible_checked);
|
||||
if (m_recolor_top_visible_perimeter_sections_checkbox != nullptr)
|
||||
m_recolor_top_visible_perimeter_sections_checkbox->Enable(perimeter_path_mode);
|
||||
if (m_use_modulated_overhang_geometry_for_support_checkbox != nullptr)
|
||||
m_use_modulated_overhang_geometry_for_support_checkbox->Enable(perimeter_path_v2_mode);
|
||||
const bool top_visible_enabled =
|
||||
perimeter_path_mode &&
|
||||
top_visible_checked;
|
||||
@@ -2457,6 +2472,7 @@ private:
|
||||
wxCheckBox *m_reduce_outer_surface_texture_checkbox {nullptr};
|
||||
wxCheckBox *m_seam_hiding_checkbox {nullptr};
|
||||
wxCheckBox *m_nonlinear_offset_adjustment_checkbox {nullptr};
|
||||
wxCheckBox *m_use_modulated_overhang_geometry_for_support_checkbox {nullptr};
|
||||
wxChoice *m_modulation_mode_choice {nullptr};
|
||||
wxCheckBox *m_recolor_small_perimeter_loops_checkbox {nullptr};
|
||||
wxCheckBox *m_recolor_top_visible_perimeter_sections_checkbox {nullptr};
|
||||
@@ -6642,6 +6658,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.seam_hiding,
|
||||
updated.nonlinear_offset_adjustment,
|
||||
updated.modulation_mode,
|
||||
updated.use_modulated_overhang_geometry_for_support,
|
||||
updated.recolor_small_perimeter_loops,
|
||||
updated.recolor_top_visible_perimeter_sections,
|
||||
updated.top_visible_perimeter_recolor_aggressiveness,
|
||||
@@ -6683,6 +6700,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
|
||||
updated.seam_hiding = dlg.seam_hiding();
|
||||
updated.nonlinear_offset_adjustment = dlg.nonlinear_offset_adjustment();
|
||||
updated.modulation_mode = dlg.modulation_mode();
|
||||
updated.use_modulated_overhang_geometry_for_support = dlg.use_modulated_overhang_geometry_for_support();
|
||||
updated.recolor_small_perimeter_loops = dlg.recolor_small_perimeter_loops();
|
||||
updated.recolor_top_visible_perimeter_sections = dlg.recolor_top_visible_perimeter_sections();
|
||||
updated.top_visible_perimeter_recolor_aggressiveness = dlg.top_visible_perimeter_recolor_aggressiveness();
|
||||
|
||||
Reference in New Issue
Block a user