mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	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; |     bool  bridge; | ||||||
|      |      | ||||||
|     Flow(float _w, float _h, float _nd, bool _bridge = false) : |     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; | ||||||
|     float   spacing(const Flow &other) const; |     float   spacing(const Flow &other) const; | ||||||
|     double  mm3_per_mm() const; |     double  mm3_per_mm() const; | ||||||
|     coord_t scaled_width() const { return coord_t(scale_(this->width)); }; |     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 { return coord_t(scale_(this->spacing())); } | ||||||
|     coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }; |     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); |     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.
 |     // 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])); |         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() | void Layer::merge_slices() | ||||||
| { | { | ||||||
|     if (m_regions.size() == 1) { |     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.
 | // 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 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.
 | // The resulting fill surface is split back among the originating regions.
 | ||||||
|  |  | ||||||
|  | @ -64,6 +64,11 @@ public: | ||||||
|     void    make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); |     void    make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); | ||||||
|     void    process_external_surfaces(const Layer* lower_layer); |     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_slices_to_svg(const char *path) const; | ||||||
|     void    export_region_fill_surfaces_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.
 |     // Test whether whether there are any slices assigned to this layer.
 | ||||||
|     bool                    empty() const;     |     bool                    empty() const;     | ||||||
|     void                    make_slices(); |     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(); |     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 { |     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; |         for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true; | ||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
|  | @ -74,7 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec | ||||||
|         // Cummulative sum of polygons over all the regions.
 |         // Cummulative sum of polygons over all the regions.
 | ||||||
|         g.lower_slices = &this->layer()->lower_layer->slices; |         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.ext_perimeter_flow    = this->flow(frExternalPerimeter); | ||||||
|     g.overhang_flow         = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object()); |     g.overhang_flow         = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object()); | ||||||
|     g.solid_infill_flow     = this->flow(frSolidInfill); |     g.solid_infill_flow     = this->flow(frSolidInfill); | ||||||
|  | @ -385,6 +385,28 @@ double LayerRegion::infill_area_threshold() const | ||||||
|     return ss*ss; |     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 | void LayerRegion::export_region_slices_to_svg(const char *path) const | ||||||
| { | { | ||||||
|     BoundingBox bbox; |     BoundingBox bbox; | ||||||
|  |  | ||||||
|  | @ -1551,34 +1551,91 @@ end: | ||||||
|                 Layer *layer = m_layers[layer_id]; |                 Layer *layer = m_layers[layer_id]; | ||||||
|                 // Apply size compensation and perform clipping of multi-part objects.
 |                 // Apply size compensation and perform clipping of multi-part objects.
 | ||||||
|                 float delta = float(scale_(m_config.xy_size_compensation.value)); |                 float delta = float(scale_(m_config.xy_size_compensation.value)); | ||||||
|  |                 float elephant_foot_compensation = 0.f; | ||||||
|                 if (layer_id == 0) |                 if (layer_id == 0) | ||||||
|                     delta -= float(scale_(m_config.elefant_foot_compensation.value)); |                     elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value)); | ||||||
|                 bool  scale = delta != 0.f; |  | ||||||
|                 bool  clip  = m_config.clip_multipart_objects.value || delta > 0.f; |  | ||||||
|                 if (layer->m_regions.size() == 1) { |                 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.
 |                         // Single region, growing or shrinking.
 | ||||||
|                         LayerRegion *layerm = layer->m_regions.front(); |                         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); | ||||||
|                             } |                             } | ||||||
|                 } else if (scale || clip) { |                         } | ||||||
|                     // Multiple regions, growing, shrinking or just clipping one region by the other.
 |                         layerm->slices.set(std::move(expolygons), 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.
 |                         // When clipping the regions, priority is given to the first regions.
 | ||||||
|                         Polygons processed; |                         Polygons processed; | ||||||
|             			for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { |             			for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { | ||||||
|                             LayerRegion *layerm = layer->m_regions[region_id]; |                             LayerRegion *layerm = layer->m_regions[region_id]; | ||||||
|             				ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces)); |             				ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces)); | ||||||
|         				if (scale) |             				if (upscale) | ||||||
|         					slices = offset_ex(slices, delta); |             					slices = offset_ex(std::move(slices), delta); | ||||||
|                             if (region_id > 0 && clip) |                             if (region_id > 0 && clip) | ||||||
|                                 // Trim by the slices of already processed regions.
 |                                 // Trim by the slices of already processed regions.
 | ||||||
|                                 slices = diff_ex(to_polygons(std::move(slices)), processed); |                                 slices = diff_ex(to_polygons(std::move(slices)), processed); | ||||||
|                         if (clip && region_id + 1 < layer->m_regions.size()) |                             if (clip && (region_id + 1 < layer->m_regions.size())) | ||||||
|                                 // Collect the already processed regions to trim the to be processed regions.
 |                                 // Collect the already processed regions to trim the to be processed regions.
 | ||||||
|                                 polygons_append(processed, slices); |                                 polygons_append(processed, slices); | ||||||
|                             layerm->slices.set(std::move(slices), stInternal); |                             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.
 |                 // Merge all regions' slices to get islands, chain them by a shortest path.
 | ||||||
|                 layer->make_slices(); |                 layer->make_slices(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv