mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 05:56:02 +08:00
Ported detect_surfaces_type() to XS/C++
This commit is contained in:
parent
3678977447
commit
d47f6d30af
@ -467,147 +467,6 @@ sub _support_material {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub detect_surfaces_type {
|
|
||||||
my $self = shift;
|
|
||||||
Slic3r::debugf "Detecting solid surfaces...\n";
|
|
||||||
|
|
||||||
for my $region_id (0 .. ($self->print->region_count-1)) {
|
|
||||||
for my $i (0 .. ($self->layer_count - 1)) {
|
|
||||||
my $layerm = $self->get_layer($i)->regions->[$region_id];
|
|
||||||
|
|
||||||
# prepare a reusable subroutine to make surface differences
|
|
||||||
my $difference = sub {
|
|
||||||
my ($subject, $clip, $result_type) = @_;
|
|
||||||
my $diff = diff(
|
|
||||||
[ map @$_, @$subject ],
|
|
||||||
[ map @$_, @$clip ],
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
|
|
||||||
# collapse very narrow parts (using the safety offset in the diff is not enough)
|
|
||||||
my $offset = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width / 10;
|
|
||||||
return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type),
|
|
||||||
@{ offset2_ex($diff, -$offset, +$offset) };
|
|
||||||
};
|
|
||||||
|
|
||||||
# comparison happens against the *full* slices (considering all regions)
|
|
||||||
# unless internal shells are requested
|
|
||||||
my $upper_layer = $i < $self->layer_count - 1 ? $self->get_layer($i+1) : undef;
|
|
||||||
my $lower_layer = $i > 0 ? $self->get_layer($i-1) : undef;
|
|
||||||
|
|
||||||
# find top surfaces (difference between current surfaces
|
|
||||||
# of current layer and upper one)
|
|
||||||
my @top = ();
|
|
||||||
if ($upper_layer) {
|
|
||||||
my $upper_slices = $self->config->interface_shells
|
|
||||||
? [ map $_->expolygon, @{$upper_layer->regions->[$region_id]->slices} ]
|
|
||||||
: $upper_layer->slices;
|
|
||||||
|
|
||||||
@top = $difference->(
|
|
||||||
[ map $_->expolygon, @{$layerm->slices} ],
|
|
||||||
$upper_slices,
|
|
||||||
S_TYPE_TOP,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
# if no upper layer, all surfaces of this one are solid
|
|
||||||
# we clone surfaces because we're going to clear the slices collection
|
|
||||||
@top = map $_->clone, @{$layerm->slices};
|
|
||||||
$_->surface_type(S_TYPE_TOP) for @top;
|
|
||||||
}
|
|
||||||
|
|
||||||
# find bottom surfaces (difference between current surfaces
|
|
||||||
# of current layer and lower one)
|
|
||||||
my @bottom = ();
|
|
||||||
if ($lower_layer) {
|
|
||||||
# any surface lying on the void is a true bottom bridge
|
|
||||||
push @bottom, $difference->(
|
|
||||||
[ map $_->expolygon, @{$layerm->slices} ],
|
|
||||||
$lower_layer->slices,
|
|
||||||
S_TYPE_BOTTOMBRIDGE,
|
|
||||||
);
|
|
||||||
|
|
||||||
# if we have soluble support material, don't bridge
|
|
||||||
if ($self->config->support_material && $self->config->support_material_contact_distance == 0) {
|
|
||||||
$_->surface_type(S_TYPE_BOTTOM) for @bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
# if user requested internal shells, we need to identify surfaces
|
|
||||||
# lying on other slices not belonging to this region
|
|
||||||
if ($self->config->interface_shells) {
|
|
||||||
# non-bridging bottom surfaces: any part of this layer lying
|
|
||||||
# on something else, excluding those lying on our own region
|
|
||||||
my $supported = intersection_ex(
|
|
||||||
[ map @{$_->expolygon}, @{$layerm->slices} ],
|
|
||||||
[ map @$_, @{$lower_layer->slices} ],
|
|
||||||
);
|
|
||||||
push @bottom, $difference->(
|
|
||||||
$supported,
|
|
||||||
[ map $_->expolygon, @{$lower_layer->regions->[$region_id]->slices} ],
|
|
||||||
S_TYPE_BOTTOM,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
# if no lower layer, all surfaces of this one are solid
|
|
||||||
# we clone surfaces because we're going to clear the slices collection
|
|
||||||
@bottom = map $_->clone, @{$layerm->slices};
|
|
||||||
|
|
||||||
# if we have raft layers, consider bottom layer as a bridge
|
|
||||||
# just like any other bottom surface lying on the void
|
|
||||||
if ($self->config->raft_layers > 0 && $self->config->support_material_contact_distance > 0) {
|
|
||||||
$_->surface_type(S_TYPE_BOTTOMBRIDGE) for @bottom;
|
|
||||||
} else {
|
|
||||||
$_->surface_type(S_TYPE_BOTTOM) for @bottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# now, if the object contained a thin membrane, we could have overlapping bottom
|
|
||||||
# and top surfaces; let's do an intersection to discover them and consider them
|
|
||||||
# as bottom surfaces (to allow for bridge detection)
|
|
||||||
if (@top && @bottom) {
|
|
||||||
my $overlapping = intersection_ex([ map $_->p, @top ], [ map $_->p, @bottom ]);
|
|
||||||
Slic3r::debugf " layer %d contains %d membrane(s)\n", $layerm->layer->id, scalar(@$overlapping)
|
|
||||||
if $Slic3r::debug;
|
|
||||||
@top = $difference->([map $_->expolygon, @top], $overlapping, S_TYPE_TOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
# find internal surfaces (difference between top/bottom surfaces and others)
|
|
||||||
my @internal = $difference->(
|
|
||||||
[ map $_->expolygon, @{$layerm->slices} ],
|
|
||||||
[ map $_->expolygon, @top, @bottom ],
|
|
||||||
S_TYPE_INTERNAL,
|
|
||||||
);
|
|
||||||
|
|
||||||
# save surfaces to layer
|
|
||||||
$layerm->slices->clear;
|
|
||||||
$layerm->slices->append($_) for (@bottom, @top, @internal);
|
|
||||||
|
|
||||||
Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
|
|
||||||
$layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
# clip surfaces to the fill boundaries
|
|
||||||
foreach my $layer (@{$self->layers}) {
|
|
||||||
my $layerm = $layer->regions->[$region_id];
|
|
||||||
|
|
||||||
# Note: this method should be idempotent, but fill_surfaces gets modified
|
|
||||||
# in place. However we're now only using its boundaries (which are invariant)
|
|
||||||
# so we're safe. This guarantees idempotence of prepare_infill() also in case
|
|
||||||
# that combine_infill() turns some fill_surface into VOID surfaces.
|
|
||||||
my $fill_boundaries = [ map $_->clone->p, @{$layerm->fill_surfaces} ];
|
|
||||||
$layerm->fill_surfaces->clear;
|
|
||||||
foreach my $surface (@{$layerm->slices}) {
|
|
||||||
my $intersection = intersection_ex(
|
|
||||||
[ $surface->p ],
|
|
||||||
$fill_boundaries,
|
|
||||||
);
|
|
||||||
$layerm->fill_surfaces->append($_)
|
|
||||||
for map Slic3r::Surface->new(expolygon => $_, surface_type => $surface->surface_type),
|
|
||||||
@$intersection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Idempotence of this method is guaranteed by the fact that we don't remove things from
|
# Idempotence of this method is guaranteed by the fact that we don't remove things from
|
||||||
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
|
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
|
||||||
sub clip_fill_surfaces {
|
sub clip_fill_surfaces {
|
||||||
|
@ -61,12 +61,6 @@ Layer::clear_regions()
|
|||||||
this->delete_region(i);
|
this->delete_region(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerRegion*
|
|
||||||
Layer::get_region(int idx)
|
|
||||||
{
|
|
||||||
return this->regions.at(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
LayerRegion*
|
LayerRegion*
|
||||||
Layer::add_region(PrintRegion* print_region)
|
Layer::add_region(PrintRegion* print_region)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,8 @@ class Layer {
|
|||||||
|
|
||||||
|
|
||||||
size_t region_count() const;
|
size_t region_count() const;
|
||||||
LayerRegion* get_region(int idx);
|
LayerRegion* get_region(size_t idx) { return this->regions.at(idx); };
|
||||||
|
const LayerRegion* get_region(size_t idx) const { return this->regions.at(idx); };
|
||||||
LayerRegion* add_region(PrintRegion* print_region);
|
LayerRegion* add_region(PrintRegion* print_region);
|
||||||
|
|
||||||
void make_slices();
|
void make_slices();
|
||||||
|
@ -133,12 +133,6 @@ Print::clear_regions()
|
|||||||
this->delete_region(i);
|
this->delete_region(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintRegion*
|
|
||||||
Print::get_region(size_t idx)
|
|
||||||
{
|
|
||||||
return regions.at(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintRegion*
|
PrintRegion*
|
||||||
Print::add_region()
|
Print::add_region()
|
||||||
{
|
{
|
||||||
|
@ -133,6 +133,7 @@ class PrintObject
|
|||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
|
|
||||||
bool has_support_material() const;
|
bool has_support_material() const;
|
||||||
|
void detect_surfaces_type();
|
||||||
void process_external_surfaces();
|
void process_external_surfaces();
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
void _make_perimeters();
|
void _make_perimeters();
|
||||||
@ -181,7 +182,8 @@ class Print
|
|||||||
bool reload_model_instances();
|
bool reload_model_instances();
|
||||||
|
|
||||||
// methods for handling regions
|
// methods for handling regions
|
||||||
PrintRegion* get_region(size_t idx);
|
PrintRegion* get_region(size_t idx) { return this->regions.at(idx); };
|
||||||
|
const PrintRegion* get_region(size_t idx) const { return this->regions.at(idx); };
|
||||||
PrintRegion* add_region();
|
PrintRegion* add_region();
|
||||||
|
|
||||||
// methods for handling state
|
// methods for handling state
|
||||||
|
@ -340,6 +340,170 @@ PrintObject::has_support_material() const
|
|||||||
|| this->config.support_material_enforce_layers > 0;
|
|| this->config.support_material_enforce_layers > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function analyzes slices of a region (SurfaceCollection slices).
|
||||||
|
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
|
||||||
|
// Initially all slices are of type S_TYPE_INTERNAL.
|
||||||
|
// Slices are compared against the top / bottom slices and regions and classified to the following groups:
|
||||||
|
// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill.
|
||||||
|
// S_TYPE_BOTTOMBRIDGE - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft.
|
||||||
|
// S_TYPE_BOTTOM - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer.
|
||||||
|
// S_TYPE_INTERNAL - Part of a region, which is supported by the same region type.
|
||||||
|
// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
|
||||||
|
void
|
||||||
|
PrintObject::detect_surfaces_type()
|
||||||
|
{
|
||||||
|
//Slic3r::debugf "Detecting solid surfaces...\n";
|
||||||
|
FOREACH_REGION(this->_print, region) {
|
||||||
|
size_t region_id = region - this->_print->regions.begin();
|
||||||
|
|
||||||
|
FOREACH_LAYER(this, layer_it) {
|
||||||
|
size_t layer_idx = layer_it - this->layers.begin();
|
||||||
|
Layer &layer = **layer_it;
|
||||||
|
LayerRegion &layerm = *layer.get_region(region_id);
|
||||||
|
// comparison happens against the *full* slices (considering all regions)
|
||||||
|
// unless internal shells are requested
|
||||||
|
|
||||||
|
const Layer* upper_layer = layer_idx < (this->layer_count()-1) ? this->get_layer(layer_idx+1) : NULL;
|
||||||
|
const Layer* lower_layer = layer_idx > 0 ? this->get_layer(layer_idx-1) : NULL;
|
||||||
|
|
||||||
|
// collapse very narrow parts (using the safety offset in the diff is not enough)
|
||||||
|
const float offset = layerm.flow(frExternalPerimeter).scaled_width() / 10.f;
|
||||||
|
|
||||||
|
const Polygons layerm_slices_surfaces = layerm.slices;
|
||||||
|
|
||||||
|
// find top surfaces (difference between current surfaces
|
||||||
|
// of current layer and upper one)
|
||||||
|
SurfaceCollection top;
|
||||||
|
if (upper_layer != NULL) {
|
||||||
|
const Polygons upper_slices = this->config.interface_shells.value
|
||||||
|
? (Polygons)upper_layer->get_region(region_id)->slices
|
||||||
|
: (Polygons)upper_layer->slices;
|
||||||
|
|
||||||
|
top.append(
|
||||||
|
offset2_ex(
|
||||||
|
diff(layerm_slices_surfaces, upper_slices, true),
|
||||||
|
-offset, offset
|
||||||
|
),
|
||||||
|
stTop
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// if no upper layer, all surfaces of this one are solid
|
||||||
|
// we clone surfaces because we're going to clear the slices collection
|
||||||
|
top = layerm.slices;
|
||||||
|
for (Surfaces::iterator it = top.surfaces.begin(); it != top.surfaces.end(); ++ it)
|
||||||
|
it->surface_type = stTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find bottom surfaces (difference between current surfaces
|
||||||
|
// of current layer and lower one)
|
||||||
|
SurfaceCollection bottom;
|
||||||
|
if (lower_layer != NULL) {
|
||||||
|
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
|
||||||
|
// the support from the print.
|
||||||
|
const SurfaceType surface_type_bottom =
|
||||||
|
(this->config.support_material.value && this->config.support_material_contact_distance.value == 0)
|
||||||
|
? stBottom
|
||||||
|
: stBottomBridge;
|
||||||
|
|
||||||
|
// Any surface lying on the void is a true bottom bridge (an overhang)
|
||||||
|
bottom.append(
|
||||||
|
offset2_ex(
|
||||||
|
diff(layerm_slices_surfaces, lower_layer->slices, true),
|
||||||
|
-offset, offset
|
||||||
|
),
|
||||||
|
surface_type_bottom
|
||||||
|
);
|
||||||
|
|
||||||
|
// if user requested internal shells, we need to identify surfaces
|
||||||
|
// lying on other slices not belonging to this region
|
||||||
|
if (this->config.interface_shells) {
|
||||||
|
// non-bridging bottom surfaces: any part of this layer lying
|
||||||
|
// on something else, excluding those lying on our own region
|
||||||
|
bottom.append(
|
||||||
|
offset2_ex(
|
||||||
|
diff(
|
||||||
|
intersection(layerm_slices_surfaces, lower_layer->slices), // supported
|
||||||
|
lower_layer->get_region(region_id)->slices,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
-offset, offset
|
||||||
|
),
|
||||||
|
stBottom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if no lower layer, all surfaces of this one are solid
|
||||||
|
// we clone surfaces because we're going to clear the slices collection
|
||||||
|
bottom = layerm.slices;
|
||||||
|
|
||||||
|
// if we have raft layers, consider bottom layer as a bridge
|
||||||
|
// just like any other bottom surface lying on the void
|
||||||
|
const SurfaceType surface_type_bottom =
|
||||||
|
(this->config.raft_layers.value > 0 && this->config.support_material_contact_distance.value > 0)
|
||||||
|
? stBottomBridge
|
||||||
|
: stBottom;
|
||||||
|
for (Surfaces::iterator it = bottom.surfaces.begin(); it != bottom.surfaces.end(); ++ it)
|
||||||
|
it->surface_type = surface_type_bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now, if the object contained a thin membrane, we could have overlapping bottom
|
||||||
|
// and top surfaces; let's do an intersection to discover them and consider them
|
||||||
|
// as bottom surfaces (to allow for bridge detection)
|
||||||
|
if (!top.empty() && !bottom.empty()) {
|
||||||
|
const Polygons top_polygons = to_polygons(STDMOVE(top));
|
||||||
|
top.clear();
|
||||||
|
top.append(
|
||||||
|
offset2_ex(diff(top_polygons, bottom, true), -offset, offset),
|
||||||
|
stTop
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save surfaces to layer
|
||||||
|
layerm.slices.clear();
|
||||||
|
layerm.slices.append(STDMOVE(top));
|
||||||
|
layerm.slices.append(STDMOVE(bottom));
|
||||||
|
|
||||||
|
// find internal surfaces (difference between top/bottom surfaces and others)
|
||||||
|
{
|
||||||
|
Polygons topbottom = top; append_to(topbottom, (Polygons)bottom);
|
||||||
|
|
||||||
|
layerm.slices.append(
|
||||||
|
offset2_ex(
|
||||||
|
diff(layerm_slices_surfaces, topbottom, true),
|
||||||
|
-offset, offset
|
||||||
|
),
|
||||||
|
stInternal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
|
||||||
|
$layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // for each layer of a region
|
||||||
|
|
||||||
|
/* Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
||||||
|
Note: this method should be idempotent, but fill_surfaces gets modified
|
||||||
|
in place. However we're now only using its boundaries (which are invariant)
|
||||||
|
so we're safe. This guarantees idempotence of prepare_infill() also in case
|
||||||
|
that combine_infill() turns some fill_surface into VOID surfaces. */
|
||||||
|
FOREACH_LAYER(this, layer_it) {
|
||||||
|
LayerRegion &layerm = *(*layer_it)->get_region(region_id);
|
||||||
|
|
||||||
|
const Polygons fill_boundaries = layerm.fill_surfaces;
|
||||||
|
layerm.fill_surfaces.clear();
|
||||||
|
for (Surfaces::const_iterator surface = layerm.slices.surfaces.begin();
|
||||||
|
surface != layerm.slices.surfaces.end(); ++ surface) {
|
||||||
|
layerm.fill_surfaces.append(
|
||||||
|
intersection_ex(*surface, fill_boundaries),
|
||||||
|
surface->surface_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PrintObject::process_external_surfaces()
|
PrintObject::process_external_surfaces()
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,8 @@ class SurfaceCollection
|
|||||||
void append(const ExPolygons &src, const Surface &templ);
|
void append(const ExPolygons &src, const Surface &templ);
|
||||||
void append(const ExPolygons &src, SurfaceType surfaceType);
|
void append(const ExPolygons &src, SurfaceType surfaceType);
|
||||||
size_t polygons_count() const;
|
size_t polygons_count() const;
|
||||||
|
bool empty() const { return this->surfaces.empty(); };
|
||||||
|
void clear() { this->surfaces.clear(); };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ _constant()
|
|||||||
void set_step_started(PrintObjectStep step)
|
void set_step_started(PrintObjectStep step)
|
||||||
%code%{ THIS->state.set_started(step); %};
|
%code%{ THIS->state.set_started(step); %};
|
||||||
|
|
||||||
|
void detect_surfaces_type();
|
||||||
void process_external_surfaces();
|
void process_external_surfaces();
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
void _make_perimeters();
|
void _make_perimeters();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user