Elephant foot compensation improvements.
Fix of the negative XY compensation on multi-material (or multi-region) prints, where the negative XY compensation created gaps between parts.
This commit is contained in:
parent
c56004f946
commit
5e582efc5c
5 changed files with 139 additions and 27 deletions
|
@ -42,14 +42,20 @@ public:
|
|||
bool bridge;
|
||||
|
||||
Flow(float _w, float _h, float _nd, bool _bridge = false) :
|
||||
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {};
|
||||
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}
|
||||
|
||||
float spacing() const;
|
||||
float spacing(const Flow &other) const;
|
||||
double mm3_per_mm() const;
|
||||
coord_t scaled_width() const { return coord_t(scale_(this->width)); };
|
||||
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); };
|
||||
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); };
|
||||
coord_t scaled_width() const { return coord_t(scale_(this->width)); }
|
||||
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); }
|
||||
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }
|
||||
|
||||
// Elephant foot compensation spacing to be used to detect narrow parts, where the elephant foot compensation cannot be applied.
|
||||
// To be used on frExternalPerimeter only.
|
||||
// Enable some perimeter squish (see INSET_OVERLAP_TOLERANCE).
|
||||
// Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation.
|
||||
coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); }
|
||||
|
||||
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
|
||||
// Create a flow from the spacing of extrusion lines.
|
||||
|
|
|
@ -65,6 +65,7 @@ void Layer::make_slices()
|
|||
this->slices.expolygons.push_back(std::move(slices[i]));
|
||||
}
|
||||
|
||||
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
|
||||
void Layer::merge_slices()
|
||||
{
|
||||
if (m_regions.size() == 1) {
|
||||
|
@ -78,6 +79,24 @@ void Layer::merge_slices()
|
|||
}
|
||||
}
|
||||
|
||||
ExPolygons Layer::merged(float offset_scaled) const
|
||||
{
|
||||
assert(offset_scaled >= 0.f);
|
||||
// If no offset is set, apply EPSILON offset before union, and revert it afterwards.
|
||||
float offset_scaled2 = 0;
|
||||
if (offset_scaled == 0.f) {
|
||||
offset_scaled = float( EPSILON);
|
||||
offset_scaled2 = float(- EPSILON);
|
||||
}
|
||||
Polygons polygons;
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
|
||||
ExPolygons out = union_ex(polygons);
|
||||
if (offset_scaled2 != 0.f)
|
||||
out = offset_ex(out, offset_scaled2);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters.
|
||||
// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region.
|
||||
// The resulting fill surface is split back among the originating regions.
|
||||
|
|
|
@ -63,7 +63,12 @@ public:
|
|||
void prepare_fill_surfaces();
|
||||
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
|
||||
void process_external_surfaces(const Layer* lower_layer);
|
||||
double infill_area_threshold() const;
|
||||
double infill_area_threshold() const;
|
||||
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
|
||||
void trim_surfaces(const Polygons &trimming_polygons);
|
||||
// Single elephant foot compensation step, used by the elephant foor compensation at the 1st layer.
|
||||
// Trim surfaces by trimming polygons (shrunk by an elephant foot compensation step), but don't shrink narrow parts so much that no perimeter would fit.
|
||||
void elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons);
|
||||
|
||||
void export_region_slices_to_svg(const char *path) const;
|
||||
void export_region_fill_surfaces_to_svg(const char *path) const;
|
||||
|
@ -117,7 +122,10 @@ public:
|
|||
// Test whether whether there are any slices assigned to this layer.
|
||||
bool empty() const;
|
||||
void make_slices();
|
||||
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
|
||||
void merge_slices();
|
||||
// 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 {
|
||||
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true;
|
||||
return false;
|
||||
|
|
|
@ -74,7 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||
// Cummulative sum of polygons over all the regions.
|
||||
g.lower_slices = &this->layer()->lower_layer->slices;
|
||||
|
||||
g.layer_id = this->layer()->id();
|
||||
g.layer_id = (int)this->layer()->id();
|
||||
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object());
|
||||
g.solid_infill_flow = this->flow(frSolidInfill);
|
||||
|
@ -385,6 +385,28 @@ double LayerRegion::infill_area_threshold() const
|
|||
return ss*ss;
|
||||
}
|
||||
|
||||
void LayerRegion::trim_surfaces(const Polygons &trimming_polygons)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
for (const Surface &surface : this->slices.surfaces)
|
||||
assert(surface.surface_type == stInternal);
|
||||
#endif /* NDEBUG */
|
||||
this->slices.set(intersection_ex(to_polygons(std::move(this->slices.surfaces)), trimming_polygons, false), stInternal);
|
||||
}
|
||||
|
||||
void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
for (const Surface &surface : this->slices.surfaces)
|
||||
assert(surface.surface_type == stInternal);
|
||||
#endif /* NDEBUG */
|
||||
ExPolygons slices_expolygons = to_expolygons(std::move(this->slices.surfaces));
|
||||
Polygons slices_polygons = to_polygons(slices_expolygons);
|
||||
Polygons tmp = intersection(slices_polygons, trimming_polygons, false);
|
||||
append(tmp, diff(slices_polygons, offset(offset_ex(slices_expolygons, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
|
||||
this->slices.set(std::move(union_ex(tmp)), stInternal);
|
||||
}
|
||||
|
||||
void LayerRegion::export_region_slices_to_svg(const char *path) const
|
||||
{
|
||||
BoundingBox bbox;
|
||||
|
|
|
@ -1551,32 +1551,89 @@ end:
|
|||
Layer *layer = m_layers[layer_id];
|
||||
// Apply size compensation and perform clipping of multi-part objects.
|
||||
float delta = float(scale_(m_config.xy_size_compensation.value));
|
||||
float elephant_foot_compensation = 0.f;
|
||||
if (layer_id == 0)
|
||||
delta -= float(scale_(m_config.elefant_foot_compensation.value));
|
||||
bool scale = delta != 0.f;
|
||||
bool clip = m_config.clip_multipart_objects.value || delta > 0.f;
|
||||
elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value));
|
||||
if (layer->m_regions.size() == 1) {
|
||||
if (scale) {
|
||||
// Optimized version for a single region layer.
|
||||
if (layer_id == 0) {
|
||||
if (delta > elephant_foot_compensation) {
|
||||
delta -= elephant_foot_compensation;
|
||||
elephant_foot_compensation = 0.f;
|
||||
} else if (delta > 0)
|
||||
elephant_foot_compensation -= delta;
|
||||
}
|
||||
if (delta != 0.f || elephant_foot_compensation > 0.f) {
|
||||
// Single region, growing or shrinking.
|
||||
LayerRegion *layerm = layer->m_regions.front();
|
||||
layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal);
|
||||
// Apply the XY compensation.
|
||||
ExPolygons expolygons = (delta == 0.f) ?
|
||||
to_expolygons(std::move(layerm->slices.surfaces)) :
|
||||
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
|
||||
// Apply the elephant foot compensation.
|
||||
if (elephant_foot_compensation > 0) {
|
||||
float elephant_foot_spacing = layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing();
|
||||
float external_perimeter_nozzle = scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1));
|
||||
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
|
||||
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
|
||||
size_t nsteps = size_t(steps);
|
||||
float step = elephant_foot_compensation / steps;
|
||||
for (size_t i = 0; i < nsteps; ++ i) {
|
||||
Polygons tmp = offset(expolygons, - step);
|
||||
append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing - step), elephant_foot_spacing + step)));
|
||||
expolygons = union_ex(tmp);
|
||||
}
|
||||
}
|
||||
layerm->slices.set(std::move(expolygons), stInternal);
|
||||
}
|
||||
} else if (scale || clip) {
|
||||
// Multiple regions, growing, shrinking or just clipping one region by the other.
|
||||
// When clipping the regions, priority is given to the first regions.
|
||||
Polygons processed;
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
|
||||
LayerRegion *layerm = layer->m_regions[region_id];
|
||||
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
|
||||
if (scale)
|
||||
slices = offset_ex(slices, delta);
|
||||
if (region_id > 0 && clip)
|
||||
// Trim by the slices of already processed regions.
|
||||
slices = diff_ex(to_polygons(std::move(slices)), processed);
|
||||
if (clip && region_id + 1 < layer->m_regions.size())
|
||||
// Collect the already processed regions to trim the to be processed regions.
|
||||
polygons_append(processed, slices);
|
||||
layerm->slices.set(std::move(slices), stInternal);
|
||||
} else {
|
||||
bool upscale = delta > 0.f;
|
||||
bool downscale = delta < 0.f || elephant_foot_compensation > 0.f;
|
||||
bool clip = m_config.clip_multipart_objects.value;
|
||||
if (upscale || clip) {
|
||||
// Multiple regions, growing or just clipping one region by the other.
|
||||
// When clipping the regions, priority is given to the first regions.
|
||||
Polygons processed;
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
|
||||
LayerRegion *layerm = layer->m_regions[region_id];
|
||||
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
|
||||
if (upscale)
|
||||
slices = offset_ex(std::move(slices), delta);
|
||||
if (region_id > 0 && clip)
|
||||
// Trim by the slices of already processed regions.
|
||||
slices = diff_ex(to_polygons(std::move(slices)), processed);
|
||||
if (clip && (region_id + 1 < layer->m_regions.size()))
|
||||
// Collect the already processed regions to trim the to be processed regions.
|
||||
polygons_append(processed, slices);
|
||||
layerm->slices.set(std::move(slices), stInternal);
|
||||
}
|
||||
}
|
||||
if (delta < 0.f) {
|
||||
// Apply the negative XY compensation.
|
||||
Polygons trimming = offset(layer->merged(EPSILON), delta - EPSILON);
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
|
||||
layer->m_regions[region_id]->trim_surfaces(trimming);
|
||||
}
|
||||
if (elephant_foot_compensation > 0.f) {
|
||||
// Apply the elephant foot compensation.
|
||||
std::vector<float> elephant_foot_spacing;
|
||||
elephant_foot_spacing.reserve(layer->m_regions.size());
|
||||
float external_perimeter_nozzle = 0.f;
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
|
||||
LayerRegion *layerm = layer->m_regions[region_id];
|
||||
elephant_foot_spacing.emplace_back(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing());
|
||||
external_perimeter_nozzle += scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1));
|
||||
}
|
||||
external_perimeter_nozzle /= (float)layer->m_regions.size();
|
||||
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
|
||||
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
|
||||
size_t nsteps = size_t(steps);
|
||||
float step = elephant_foot_compensation / steps;
|
||||
for (size_t i = 0; i < nsteps; ++ i) {
|
||||
Polygons trimming_polygons = offset(layer->merged(EPSILON), - step - EPSILON);
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
|
||||
layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] + step, trimming_polygons);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Merge all regions' slices to get islands, chain them by a shortest path.
|
||||
|
|
Loading…
Reference in a new issue