Parallelized detect_surfaces_type

This commit is contained in:
Alessandro Ranellucci 2017-03-09 20:31:00 +01:00
parent 306264c214
commit db8396a290
3 changed files with 181 additions and 161 deletions

View File

@ -247,4 +247,176 @@ Layer::make_fills()
}
}
// 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
Layer::detect_surfaces_type()
{
PrintObject &object = *this->object();
for (size_t region_id = 0; region_id < this->regions.size(); ++region_id) {
LayerRegion &layerm = *this->regions[region_id];
// comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested
// We call layer->slices or layerm->slices on these neighbor layers
// and we convert them into Polygons so we only care about their total
// coverage. We only write to layerm->slices so we can read layer->slices safely.
Layer* const &upper_layer = this->upper_layer;
Layer* const &lower_layer = this->lower_layer;
// collapse very narrow parts (using the safety offset in the diff is not enough)
const float offs = 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) {
Polygons upper_slices;
if (object.config.interface_shells.value) {
const LayerRegion* upper_layerm = upper_layer->get_region(region_id);
boost::lock_guard<boost::mutex> l(upper_layerm->_slices_mutex);
upper_slices = upper_layerm->slices;
} else {
upper_slices = upper_layer->slices;
}
top.append(
offset2_ex(
diff(layerm_slices_surfaces, upper_slices, true),
-offs, offs
),
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 (Surface &s : top.surfaces) s.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 =
(object.config.support_material.value && object.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),
-offs, offs
),
surface_type_bottom
);
// if user requested internal shells, we need to identify surfaces
// lying on other slices not belonging to this region
if (object.config.interface_shells) {
// non-bridging bottom surfaces: any part of this layer lying
// on something else, excluding those lying on our own region
const LayerRegion* lower_layerm = lower_layer->get_region(region_id);
boost::lock_guard<boost::mutex> l(lower_layerm->_slices_mutex);
bottom.append(
offset2_ex(
diff(
intersection(layerm_slices_surfaces, lower_layer->slices), // supported
lower_layerm->slices,
true
),
-offs, offs
),
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 =
(object.config.raft_layers.value > 0 && object.config.support_material_contact_distance.value > 0)
? stBottomBridge
: stBottom;
for (Surface &s : bottom.surfaces) s.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(
// TODO: maybe we don't need offset2?
offset2_ex(diff(top_polygons, bottom, true), -offs, offs),
stTop
);
}
// save surfaces to layer
{
boost::lock_guard<boost::mutex> l(layerm._slices_mutex);
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(
// TODO: maybe we don't need offset2?
offset2_ex(
diff(layerm_slices_surfaces, topbottom, true),
-offs, offs
),
stInternal
);
}
}
#ifdef SLIC3R_DEBUG
printf(" layer %zu has %zu bottom, %zu top and %zu internal surfaces\n",
this->id(), bottom.size(), top.size(),
layerm.slices.size()-bottom.size()-top.size());
#endif
{
/* 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. */
const Polygons fill_boundaries = layerm.fill_surfaces;
layerm.fill_surfaces.clear();
// No other instance of this function is writing to this layer, so we can read safely.
for (const Surface &surface : layerm.slices.surfaces) {
// No other instance of this function modifies fill_surfaces.
layerm.fill_surfaces.append(
intersection_ex(surface, fill_boundaries),
surface.surface_type
);
}
}
}
}
}

View File

@ -7,6 +7,7 @@
#include "ExtrusionEntityCollection.hpp"
#include "ExPolygonCollection.hpp"
#include "PolylineCollection.hpp"
#include <boost/thread.hpp>
namespace Slic3r {
@ -66,6 +67,7 @@ class LayerRegion
private:
Layer *_layer;
PrintRegion *_region;
mutable boost::mutex _slices_mutex;
LayerRegion(Layer *layer, PrintRegion *region)
: _layer(layer), _region(region) {};
@ -108,12 +110,12 @@ class Layer {
template <class T> bool any_bottom_region_slice_contains(const T &item) const;
void make_perimeters();
void make_fills();
void detect_surfaces_type();
protected:
size_t _id; // sequential number of layer, 0-based
PrintObject* _object;
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
coordf_t slice_z);
virtual ~Layer();

View File

@ -3,6 +3,7 @@
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include <algorithm>
#include <vector>
namespace Slic3r {
@ -357,169 +358,14 @@ PrintObject::has_support_material() const
|| 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 offs = 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) {
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),
-offs, offs
),
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),
-offs, offs
),
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
),
-offs, offs
),
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), -offs, offs),
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),
-offs, offs
),
stInternal
);
}
#ifdef SLIC3R_DEBUG
printf(" layer %zu has %zu bottom, %zu top and %zu internal surfaces\n",
layerm.layer()->id(), bottom.size(), top.size(),
layerm.slices.size()-bottom.size()-top.size());
#endif
} // 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
);
}
}
}
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::detect_surfaces_type, _1),
this->_print->config.threads.value
);
}
void