mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-16 17:05:59 +08:00
SPE-2486: Implement segmentation of layers based on fuzzy skin painting.
This commit is contained in:
parent
2c06c81159
commit
800b742b95
@ -152,9 +152,10 @@ protected:
|
||||
|
||||
private:
|
||||
// Modifying m_slices
|
||||
friend std::string fix_slicing_errors(LayerPtrs&, const std::function<void()>&);
|
||||
template<typename ThrowOnCancel>
|
||||
friend void apply_mm_segmentation(PrintObject& print_object, ThrowOnCancel throw_on_cancel);
|
||||
friend void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel);
|
||||
template<typename ThrowOnCancel>
|
||||
friend void apply_fuzzy_skin_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel);
|
||||
|
||||
Layer *m_layer;
|
||||
const PrintRegion *m_region;
|
||||
|
@ -1762,5 +1762,21 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co
|
||||
return final_path;
|
||||
}
|
||||
|
||||
PrintRegion *PrintObjectRegions::FuzzySkinPaintedRegion::parent_print_object_region(const LayerRangeRegions &layer_range) const
|
||||
{
|
||||
using FuzzySkinParentType = PrintObjectRegions::FuzzySkinPaintedRegion::ParentType;
|
||||
|
||||
if (this->parent_type == FuzzySkinParentType::PaintedRegion) {
|
||||
return layer_range.painted_regions[this->parent].region;
|
||||
}
|
||||
|
||||
assert(this->parent_type == FuzzySkinParentType::VolumeRegion);
|
||||
return layer_range.volume_regions[this->parent].region;
|
||||
}
|
||||
|
||||
int PrintObjectRegions::FuzzySkinPaintedRegion::parent_print_object_region_id(const LayerRangeRegions &layer_range) const
|
||||
{
|
||||
return this->parent_print_object_region(layer_range)->print_object_region_id();
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -150,8 +150,6 @@ using SpanOfConstPtrs = tcb::span<const T* const>;
|
||||
using LayerPtrs = std::vector<Layer*>;
|
||||
using SupportLayerPtrs = std::vector<SupportLayer*>;
|
||||
|
||||
class BoundingBoxf3; // TODO: for temporary constructor parameter
|
||||
|
||||
// Single instance of a PrintObject.
|
||||
// As multiple PrintObjects may be generated for a single ModelObject (their instances differ in rotation around Z),
|
||||
// ModelObject's instancess will be distributed among these multiple PrintObjects.
|
||||
@ -204,6 +202,22 @@ public:
|
||||
PrintRegion *region { nullptr };
|
||||
};
|
||||
|
||||
struct LayerRangeRegions;
|
||||
|
||||
struct FuzzySkinPaintedRegion
|
||||
{
|
||||
enum class ParentType { VolumeRegion, PaintedRegion };
|
||||
|
||||
ParentType parent_type { ParentType::VolumeRegion };
|
||||
// Index of a parent VolumeRegion or PaintedRegion.
|
||||
int parent { -1 };
|
||||
// Pointer to PrintObjectRegions::all_regions.
|
||||
PrintRegion *region { nullptr };
|
||||
|
||||
PrintRegion *parent_print_object_region(const LayerRangeRegions &layer_range) const;
|
||||
int parent_print_object_region_id(const LayerRangeRegions &layer_range) const;
|
||||
};
|
||||
|
||||
// One slice over the PrintObject (possibly the whole PrintObject) and a list of ModelVolumes and their bounding boxes
|
||||
// possibly clipped by the layer_height_range.
|
||||
struct LayerRangeRegions
|
||||
@ -216,8 +230,9 @@ public:
|
||||
std::vector<VolumeExtents> volumes;
|
||||
|
||||
// Sorted in the order of their source ModelVolumes, thus reflecting the order of region clipping, modifier overrides etc.
|
||||
std::vector<VolumeRegion> volume_regions;
|
||||
std::vector<PaintedRegion> painted_regions;
|
||||
std::vector<VolumeRegion> volume_regions;
|
||||
std::vector<PaintedRegion> painted_regions;
|
||||
std::vector<FuzzySkinPaintedRegion> fuzzy_skin_painted_regions;
|
||||
|
||||
bool has_volume(const ObjectID id) const {
|
||||
auto it = lower_bound_by_predicate(this->volumes.begin(), this->volumes.end(), [id](const VolumeExtents &l) { return l.volume_id < id; });
|
||||
@ -340,6 +355,8 @@ public:
|
||||
bool has_support_material() const { return this->has_support() || this->has_raft(); }
|
||||
// Checks if the model object is painted using the multi-material painting gizmo.
|
||||
bool is_mm_painted() const { return this->model_object()->is_mm_painted(); }
|
||||
// Checks if the model object is painted using the fuzzy skin painting gizmo.
|
||||
bool is_fuzzy_skin_painted() const { return this->model_object()->is_fuzzy_skin_painted(); }
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
std::vector<unsigned int> object_extruders() const;
|
||||
|
@ -683,7 +683,6 @@ bool verify_update_print_object_regions(
|
||||
ModelVolumePtrs model_volumes,
|
||||
const PrintRegionConfig &default_region_config,
|
||||
size_t num_extruders,
|
||||
const std::vector<unsigned int> &painting_extruders,
|
||||
PrintObjectRegions &print_object_regions,
|
||||
const std::function<void(const PrintRegionConfig&, const PrintRegionConfig&, const t_config_option_keys&)> &callback_invalidate)
|
||||
{
|
||||
@ -757,7 +756,7 @@ bool verify_update_print_object_regions(
|
||||
}
|
||||
}
|
||||
|
||||
// Verify and / or update PrintRegions produced by color painting.
|
||||
// Verify and / or update PrintRegions produced by color painting.
|
||||
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
|
||||
for (const PrintObjectRegions::PaintedRegion ®ion : layer_range.painted_regions) {
|
||||
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[region.parent];
|
||||
@ -781,6 +780,29 @@ bool verify_update_print_object_regions(
|
||||
print_region_ref_inc(*region.region);
|
||||
}
|
||||
|
||||
// Verify and / or update PrintRegions produced by fuzzy skin painting.
|
||||
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) {
|
||||
for (const PrintObjectRegions::FuzzySkinPaintedRegion ®ion : layer_range.fuzzy_skin_painted_regions) {
|
||||
const PrintRegion &parent_print_region = *region.parent_print_object_region(layer_range);
|
||||
PrintRegionConfig cfg = parent_print_region.config();
|
||||
cfg.fuzzy_skin.value = FuzzySkinType::All;
|
||||
if (cfg != region.region->config()) {
|
||||
// Region configuration changed.
|
||||
if (print_region_ref_cnt(*region.region) == 0) {
|
||||
// Region is referenced for the first time. Just change its parameters.
|
||||
// Stop the background process before assigning new configuration to the regions.
|
||||
t_config_option_keys diff = region.region->config().diff(cfg);
|
||||
callback_invalidate(region.region->config(), cfg, diff);
|
||||
region.region->config_apply_only(cfg, diff, false);
|
||||
} else {
|
||||
// Region is referenced multiple times, thus the region is being split. We need to reslice.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
print_region_ref_inc(*region.region);
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly verify, whether some regions were not merged.
|
||||
{
|
||||
std::vector<const PrintRegion*> regions;
|
||||
@ -884,7 +906,8 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
const Transform3d &trafo,
|
||||
size_t num_extruders,
|
||||
const float xy_size_compensation,
|
||||
const std::vector<unsigned int> &painting_extruders)
|
||||
const std::vector<unsigned int> &painting_extruders,
|
||||
const bool has_painted_fuzzy_skin)
|
||||
{
|
||||
// Reuse the old object or generate a new one.
|
||||
auto out = print_object_regions_old ? std::unique_ptr<PrintObjectRegions>(print_object_regions_old) : std::make_unique<PrintObjectRegions>();
|
||||
@ -906,6 +929,7 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
r.config = range.config;
|
||||
r.volume_regions.clear();
|
||||
r.painted_regions.clear();
|
||||
r.fuzzy_skin_painted_regions.clear();
|
||||
}
|
||||
} else {
|
||||
out->trafo_bboxes = trafo;
|
||||
@ -987,13 +1011,42 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
cfg.infill_extruder.value = painted_extruder_id;
|
||||
layer_range.painted_regions.push_back({ painted_extruder_id, parent_region_id, get_create_region(std::move(cfg))});
|
||||
}
|
||||
// Sort the regions by parent region::print_object_region_id() and extruder_id to help the slicing algorithm when applying MMU segmentation.
|
||||
// Sort the regions by parent region::print_object_region_id() and extruder_id to help the slicing algorithm when applying MM segmentation.
|
||||
std::sort(layer_range.painted_regions.begin(), layer_range.painted_regions.end(), [&layer_range](auto &l, auto &r) {
|
||||
int lid = layer_range.volume_regions[l.parent].region->print_object_region_id();
|
||||
int rid = layer_range.volume_regions[r.parent].region->print_object_region_id();
|
||||
return lid < rid || (lid == rid && l.extruder_id < r.extruder_id); });
|
||||
}
|
||||
|
||||
if (has_painted_fuzzy_skin) {
|
||||
using FuzzySkinParentType = PrintObjectRegions::FuzzySkinPaintedRegion::ParentType;
|
||||
|
||||
for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges_regions) {
|
||||
// FuzzySkinPaintedRegion can override different parts of the Layer than PaintedRegions,
|
||||
// so FuzzySkinPaintedRegion has to point to both VolumeRegion and PaintedRegion.
|
||||
for (int parent_volume_region_id = 0; parent_volume_region_id < int(layer_range.volume_regions.size()); ++parent_volume_region_id) {
|
||||
if (const PrintObjectRegions::VolumeRegion &parent_volume_region = layer_range.volume_regions[parent_volume_region_id]; parent_volume_region.model_volume->is_model_part() || parent_volume_region.model_volume->is_modifier()) {
|
||||
PrintRegionConfig cfg = parent_volume_region.region->config();
|
||||
cfg.fuzzy_skin.value = FuzzySkinType::All;
|
||||
layer_range.fuzzy_skin_painted_regions.push_back({FuzzySkinParentType::VolumeRegion, parent_volume_region_id, get_create_region(std::move(cfg))});
|
||||
}
|
||||
}
|
||||
|
||||
for (int parent_painted_regions_id = 0; parent_painted_regions_id < int(layer_range.painted_regions.size()); ++parent_painted_regions_id) {
|
||||
const PrintObjectRegions::PaintedRegion &parent_painted_region = layer_range.painted_regions[parent_painted_regions_id];
|
||||
|
||||
PrintRegionConfig cfg = parent_painted_region.region->config();
|
||||
cfg.fuzzy_skin.value = FuzzySkinType::All;
|
||||
layer_range.fuzzy_skin_painted_regions.push_back({FuzzySkinParentType::PaintedRegion, parent_painted_regions_id, get_create_region(std::move(cfg))});
|
||||
}
|
||||
|
||||
// Sort the regions by parent region::print_object_region_id() to help the slicing algorithm when applying fuzzy skin segmentation.
|
||||
std::sort(layer_range.fuzzy_skin_painted_regions.begin(), layer_range.fuzzy_skin_painted_regions.end(), [&layer_range](auto &l, auto &r) {
|
||||
return l.parent_print_object_region_id(layer_range) < r.parent_print_object_region_id(layer_range);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return out.release();
|
||||
}
|
||||
|
||||
@ -1450,7 +1503,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
print_object.model_object()->volumes,
|
||||
m_default_region_config,
|
||||
num_extruders,
|
||||
painting_extruders,
|
||||
*print_object_regions,
|
||||
[it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
@ -1477,7 +1529,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
model_object_status.print_instances.front().trafo,
|
||||
num_extruders,
|
||||
print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_size_compensation.value),
|
||||
painting_extruders);
|
||||
painting_extruders,
|
||||
print_object.is_fuzzy_skin_painted());
|
||||
}
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
if ((*it)->m_shared_regions) {
|
||||
@ -1540,7 +1593,7 @@ void Print::cleanup()
|
||||
auto this_objects = SpanOfConstPtrs<PrintObject>(const_cast<const PrintObject* const* const>(&(*it_begin)), it - it_begin);
|
||||
if (! Print::is_shared_print_object_step_valid_unguarded(this_objects, posSupportSpotsSearch))
|
||||
shared_regions->generated_support_points.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Print::is_shared_print_object_step_valid_unguarded(SpanOfConstPtrs<PrintObject> print_objects, PrintObjectStep print_object_step)
|
||||
|
@ -731,6 +731,114 @@ void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_can
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ThrowOnCancel>
|
||||
void apply_fuzzy_skin_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
|
||||
{
|
||||
// Returns fuzzy skin segmentation based on painting in the fuzzy skin painting gizmo.
|
||||
std::vector<std::vector<ExPolygons>> segmentation = fuzzy_skin_segmentation_by_painting(print_object, throw_on_cancel);
|
||||
assert(segmentation.size() == print_object.layer_count());
|
||||
|
||||
struct ByRegion
|
||||
{
|
||||
ExPolygons expolygons;
|
||||
bool needs_merge { false };
|
||||
};
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmentation.size(), std::max(segmentation.size() / 128, size_t(1))), [&print_object, &segmentation, throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||
const auto &layer_ranges = print_object.shared_regions()->layer_ranges;
|
||||
auto it_layer_range = layer_range_first(layer_ranges, print_object.get_layer(int(range.begin()))->slice_z);
|
||||
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel();
|
||||
|
||||
Layer &layer = *print_object.get_layer(int(layer_idx));
|
||||
it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer.slice_z);
|
||||
const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range;
|
||||
|
||||
assert(segmentation[layer_idx].size() == 1);
|
||||
const ExPolygons &fuzzy_skin_segmentation = segmentation[layer_idx][0];
|
||||
const BoundingBox fuzzy_skin_segmentation_bbox = get_extents(fuzzy_skin_segmentation);
|
||||
if (fuzzy_skin_segmentation.empty())
|
||||
continue;
|
||||
|
||||
// Split LayerRegions by painted fuzzy skin regions.
|
||||
// layer_range.fuzzy_skin_painted_regions are sorted by parent PrintObject region ID.
|
||||
std::vector<ByRegion> by_region(layer.region_count());
|
||||
auto it_fuzzy_skin_region_begin = layer_range.fuzzy_skin_painted_regions.cbegin();
|
||||
for (int parent_layer_region_idx = 0; parent_layer_region_idx < layer.region_count(); ++parent_layer_region_idx) {
|
||||
if (it_fuzzy_skin_region_begin == layer_range.fuzzy_skin_painted_regions.cend())
|
||||
continue;
|
||||
|
||||
const LayerRegion &parent_layer_region = *layer.get_region(parent_layer_region_idx);
|
||||
const PrintRegion &parent_print_region = parent_layer_region.region();
|
||||
assert(parent_print_region.print_object_region_id() == parent_layer_region_idx);
|
||||
if (parent_layer_region.slices().empty())
|
||||
continue;
|
||||
|
||||
// Find the first FuzzySkinPaintedRegion, which overrides the parent PrintRegion.
|
||||
auto it_fuzzy_skin_region = std::find_if(it_fuzzy_skin_region_begin, layer_range.fuzzy_skin_painted_regions.cend(), [&layer_range, &parent_print_region](const auto &fuzzy_skin_region) {
|
||||
return fuzzy_skin_region.parent_print_object_region_id(layer_range) == parent_print_region.print_object_region_id();
|
||||
});
|
||||
|
||||
if (it_fuzzy_skin_region == layer_range.fuzzy_skin_painted_regions.cend())
|
||||
continue; // This LayerRegion isn't overrides by any FuzzySkinPaintedRegion.
|
||||
|
||||
assert(it_fuzzy_skin_region->parent_print_object_region(layer_range) == &parent_print_region);
|
||||
|
||||
// Update the beginning FuzzySkinPaintedRegion iterator for the next iteration.
|
||||
it_fuzzy_skin_region_begin = std::next(it_fuzzy_skin_region);
|
||||
|
||||
const BoundingBox parent_layer_region_bbox = get_extents(parent_layer_region.slices().surfaces);
|
||||
Polygons layer_region_remaining_polygons = to_polygons(parent_layer_region.slices().surfaces);
|
||||
// Don't trim by self, it is not reliable.
|
||||
if (parent_layer_region_bbox.overlap(fuzzy_skin_segmentation_bbox) && it_fuzzy_skin_region->region != &parent_print_region) {
|
||||
// Steal from this region.
|
||||
const int target_region_id = it_fuzzy_skin_region->region->print_object_region_id();
|
||||
ExPolygons stolen = intersection_ex(parent_layer_region.slices().surfaces, fuzzy_skin_segmentation);
|
||||
if (!stolen.empty()) {
|
||||
ByRegion &dst = by_region[target_region_id];
|
||||
if (dst.expolygons.empty()) {
|
||||
dst.expolygons = std::move(stolen);
|
||||
} else {
|
||||
append(dst.expolygons, std::move(stolen));
|
||||
dst.needs_merge = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Trim slices of this LayerRegion by the fuzzy skin region.
|
||||
layer_region_remaining_polygons = diff(layer_region_remaining_polygons, fuzzy_skin_segmentation);
|
||||
|
||||
// Filter out unprintable polygons. Detailed explanation is inside apply_mm_segmentation.
|
||||
if (!layer_region_remaining_polygons.empty()) {
|
||||
layer_region_remaining_polygons = opening(union_ex(layer_region_remaining_polygons), scaled<float>(5. * EPSILON), scaled<float>(5. * EPSILON));
|
||||
}
|
||||
}
|
||||
|
||||
if (!layer_region_remaining_polygons.empty()) {
|
||||
ByRegion &dst = by_region[parent_print_region.print_object_region_id()];
|
||||
if (dst.expolygons.empty()) {
|
||||
dst.expolygons = union_ex(layer_region_remaining_polygons);
|
||||
} else {
|
||||
append(dst.expolygons, union_ex(layer_region_remaining_polygons));
|
||||
dst.needs_merge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-create Surfaces of LayerRegions.
|
||||
for (int region_id = 0; region_id < layer.region_count(); ++region_id) {
|
||||
ByRegion &src = by_region[region_id];
|
||||
if (src.needs_merge) {
|
||||
// Multiple regions were merged into one.
|
||||
src.expolygons = closing_ex(src.expolygons, scaled<float>(10. * EPSILON));
|
||||
}
|
||||
|
||||
layer.get_region(region_id)->m_slices.set(std::move(src.expolygons), stInternal);
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
}
|
||||
|
||||
// 1) Decides Z positions of the layers,
|
||||
// 2) Initializes layers and their regions
|
||||
// 3) Slices the object meshes
|
||||
@ -796,6 +904,21 @@ void PrintObject::slice_volumes()
|
||||
apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); });
|
||||
}
|
||||
|
||||
// Is any ModelVolume fuzzy skin painted?
|
||||
if (this->model_object()->is_fuzzy_skin_painted()) {
|
||||
// If XY Size compensation is also enabled, notify the user that XY Size compensation
|
||||
// would not be used because the object has custom fuzzy skin painted.
|
||||
if (m_config.xy_size_compensation.value != 0.f) {
|
||||
this->active_step_add_warning(
|
||||
PrintStateBase::WarningLevel::CRITICAL,
|
||||
_u8L("An object has enabled XY Size compensation which will not be used because it is also fuzzy skin painted.\nXY Size "
|
||||
"compensation cannot be combined with fuzzy skin painting.") +
|
||||
"\n" + (_u8L("Object name")) + ": " + this->model_object()->name);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - Fuzzy skin segmentation";
|
||||
apply_fuzzy_skin_segmentation(*this, [print]() { print->throw_if_canceled(); });
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user