mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			618 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			618 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "Layer.hpp"
 | |
| #include "BridgeDetector.hpp"
 | |
| #include "ClipperUtils.hpp"
 | |
| #include "Geometry.hpp"
 | |
| #include "PerimeterGenerator.hpp"
 | |
| #include "Print.hpp"
 | |
| #include "Surface.hpp"
 | |
| #include "BoundingBox.hpp"
 | |
| #include "SVG.hpp"
 | |
| 
 | |
| #include <string>
 | |
| #include <map>
 | |
| 
 | |
| #include <boost/log/trivial.hpp>
 | |
| #include <boost/algorithm/clamp.hpp>
 | |
| 
 | |
| namespace Slic3r {
 | |
| 
 | |
| Flow LayerRegion::flow(FlowRole role) const
 | |
| {
 | |
|     return this->flow(role, m_layer->height);
 | |
| }
 | |
| 
 | |
| Flow LayerRegion::flow(FlowRole role, double layer_height) const
 | |
| {
 | |
|     return m_region->flow(*m_layer->object(), role, layer_height, m_layer->id() == 0);
 | |
| }
 | |
| 
 | |
| Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const
 | |
| {
 | |
|     const PrintRegion       ®ion         = this->region();
 | |
|     const PrintRegionConfig ®ion_config  = region.config();
 | |
|     const PrintObject       &print_object   = *this->layer()->object();
 | |
|     Flow bridge_flow;
 | |
|     auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
 | |
|     if (thick_bridge) {
 | |
|         // The old Slic3r way (different from all other slicers): Use rounded extrusions.
 | |
|         // Get the configured nozzle_diameter for the extruder associated to the flow role requested.
 | |
|         // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
 | |
|         // Applies default bridge spacing.
 | |
|         bridge_flow = Flow::bridging_flow(float(sqrt(region_config.bridge_flow)) * nozzle_diameter, nozzle_diameter);
 | |
|     } else {
 | |
|         // The same way as other slicers: Use normal extrusions. Apply bridge_flow while maintaining the original spacing.
 | |
|         bridge_flow = this->flow(role).with_flow_ratio(region_config.bridge_flow);
 | |
|     }
 | |
|     return bridge_flow;
 | |
| 
 | |
| }
 | |
| 
 | |
| // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
 | |
| void LayerRegion::slices_to_fill_surfaces_clipped()
 | |
| {
 | |
|     // 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.
 | |
|     // Collect polygons per surface type.
 | |
|     std::array<SurfacesPtr, size_t(stCount)> by_surface;
 | |
|     for (Surface &surface : this->slices.surfaces)
 | |
|         by_surface[size_t(surface.surface_type)].emplace_back(&surface);
 | |
|     // Trim surfaces by the fill_boundaries.
 | |
|     this->fill_surfaces.surfaces.clear();
 | |
|     for (size_t surface_type = 0; surface_type < size_t(stCount); ++ surface_type) {
 | |
|         const SurfacesPtr &this_surfaces = by_surface[surface_type];
 | |
|         if (! this_surfaces.empty())
 | |
|             this->fill_surfaces.append(intersection_ex(this_surfaces, this->fill_expolygons), SurfaceType(surface_type));
 | |
|     }
 | |
| }
 | |
| 
 | |
| void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap)
 | |
| {
 | |
|     this->perimeters.clear();
 | |
|     this->thin_fills.clear();
 | |
| 
 | |
|     const PrintConfig       &print_config  = this->layer()->object()->print()->config();
 | |
|     const PrintRegionConfig ®ion_config = this->region().config();
 | |
|     const PrintObjectConfig& object_config = this->layer()->object()->config();
 | |
|     // This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
 | |
|     bool spiral_mode = print_config.spiral_mode &&
 | |
|         //FIXME account for raft layers.
 | |
|         (this->layer()->id() >= size_t(region_config.bottom_shell_layers.value) &&
 | |
|          this->layer()->print_z >= region_config.bottom_shell_thickness - EPSILON);
 | |
| 
 | |
|     PerimeterGenerator g(
 | |
|         // input:
 | |
|         &slices,
 | |
|         this->layer()->height,
 | |
|         this->flow(frPerimeter),
 | |
|         ®ion_config,
 | |
|         &this->layer()->object()->config(),
 | |
|         &print_config,
 | |
|         spiral_mode,
 | |
|         
 | |
|         // output:
 | |
|         &this->perimeters,
 | |
|         &this->thin_fills,
 | |
|         fill_surfaces,
 | |
|         //BBS
 | |
|         fill_no_overlap
 | |
|     );
 | |
|     
 | |
|     if (this->layer()->lower_layer != nullptr)
 | |
|         // Cummulative sum of polygons over all the regions.
 | |
|         g.lower_slices = &this->layer()->lower_layer->lslices;
 | |
|     if (this->layer()->upper_layer != NULL)
 | |
|         g.upper_slices = &this->layer()->upper_layer->lslices;
 | |
|     
 | |
|     g.layer_id              = (int)this->layer()->id();
 | |
|     g.ext_perimeter_flow    = this->flow(frExternalPerimeter);
 | |
|     g.overhang_flow         = this->bridging_flow(frPerimeter, object_config.thick_bridges);
 | |
|     g.solid_infill_flow     = this->flow(frSolidInfill);
 | |
| 
 | |
|     if (this->layer()->object()->config().wall_generator.value == PerimeterGeneratorType::Arachne && !spiral_mode)
 | |
|         g.process_arachne();
 | |
|     else
 | |
|         g.process_classic();
 | |
| }
 | |
| 
 | |
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
 | |
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
 | |
| #define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
 | |
| 
 | |
| void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
 | |
| {
 | |
|     const bool      has_infill = this->region().config().sparse_infill_density.value > 0.;
 | |
|     //BBS
 | |
|     auto nozzle_diameter = this->region().nozzle_dmr_avg(this->layer()->object()->print()->config());
 | |
|     const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
 | |
|     const float bridge_margin = std::min(float(scale_(BRIDGE_INFILL_MARGIN)), float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4)));
 | |
| 
 | |
|     // BBS
 | |
|     const PrintObjectConfig& object_config = this->layer()->object()->config();
 | |
| 
 | |
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | |
|     export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
 | |
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | |
| 
 | |
|     // 1) Collect bottom and bridge surfaces, each of them grown by a fixed 3mm offset
 | |
|     // for better anchoring.
 | |
|     // Bottom surfaces, grown.
 | |
|     Surfaces                    bottom;
 | |
|     // Bridge surfaces, initialy not grown.
 | |
|     Surfaces                    bridges;
 | |
|     // Top surfaces, grown.
 | |
|     Surfaces                    top;
 | |
|     // Internal surfaces, not grown.
 | |
|     Surfaces                    internal;
 | |
|     // Areas, where an infill of various types (top, bottom, bottom bride, sparse, void) could be placed.
 | |
|     Polygons                    fill_boundaries = to_polygons(this->fill_expolygons);
 | |
|     Polygons  					lower_layer_covered_tmp;
 | |
| 
 | |
|     // Collect top surfaces and internal surfaces.
 | |
|     // Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
 | |
|     // This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
 | |
| 
 | |
|     {
 | |
|         // Voids are sparse infills if infill rate is zero.
 | |
|         Polygons voids;
 | |
| 
 | |
|         double max_grid_area = -1;
 | |
|         if (this->layer()->lower_layer != nullptr)
 | |
|             max_grid_area = this->layer()->lower_layer->get_sparse_infill_max_void_area();
 | |
|         for (const Surface &surface : this->fill_surfaces.surfaces) {
 | |
|             if (surface.is_top()) {
 | |
|                 // Collect the top surfaces, inflate them and trim them by the bottom surfaces.
 | |
|                 // This gives the priority to bottom surfaces.
 | |
|                 if (max_grid_area < 0 || surface.expolygon.area() < max_grid_area)
 | |
|                     surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
 | |
|                 else
 | |
|                     //BBS: Don't need to expand too much in this situation. Expand 3mm to eliminate hole and 1mm for contour
 | |
|                     surfaces_append(top, intersection_ex(offset(surface.expolygon.contour, margin / 3.0, EXTERNAL_SURFACES_OFFSET_PARAMETERS),
 | |
|                                                          offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS)), surface);
 | |
|             } else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == nullptr)) {
 | |
|                 // Grown by 3mm.
 | |
|                 surfaces_append(bottom, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
 | |
|             } else if (surface.surface_type == stBottomBridge) {
 | |
|                 if (! surface.empty())
 | |
|                     bridges.emplace_back(surface);
 | |
|             }
 | |
|             if (surface.is_internal()) {
 | |
|             	assert(surface.surface_type == stInternal || surface.surface_type == stInternalSolid);
 | |
|             	if (! has_infill && lower_layer != nullptr)
 | |
|             		polygons_append(voids, surface.expolygon);
 | |
|             	internal.emplace_back(std::move(surface));
 | |
|             }
 | |
|         }
 | |
|         if (! has_infill && lower_layer != nullptr && ! voids.empty()) {
 | |
|         	// Remove voids from fill_boundaries, that are not supported by the layer below.
 | |
|             if (lower_layer_covered == nullptr) {
 | |
|             	lower_layer_covered = &lower_layer_covered_tmp;
 | |
|             	lower_layer_covered_tmp = to_polygons(lower_layer->lslices);
 | |
|             }
 | |
|             if (! lower_layer_covered->empty())
 | |
|             	voids = diff(voids, *lower_layer_covered);
 | |
|             fill_boundaries = diff(fill_boundaries, voids);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| #if 0
 | |
|     {
 | |
|         static int iRun = 0;
 | |
|         bridges.export_to_svg(debug_out_path("bridges-before-grouping-%d.svg", iRun ++), true);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     if (bridges.empty())
 | |
|     {
 | |
|         fill_boundaries = union_safety_offset(fill_boundaries);
 | |
|     } else
 | |
|     {
 | |
|         // 1) Calculate the inflated bridge regions, each constrained to its island.
 | |
|         ExPolygons               fill_boundaries_ex = union_safety_offset_ex(fill_boundaries);
 | |
|         std::vector<Polygons>    bridges_grown;
 | |
|         std::vector<BoundingBox> bridge_bboxes;
 | |
| 
 | |
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | |
|         {
 | |
|             static int iRun = 0;
 | |
|             SVG svg(debug_out_path("3_process_external_surfaces-fill_regions-%d.svg", iRun ++).c_str(), get_extents(fill_boundaries_ex));
 | |
|             svg.draw(fill_boundaries_ex);
 | |
|             svg.draw_outline(fill_boundaries_ex, "black", "blue", scale_(0.05)); 
 | |
|             svg.Close();
 | |
|         }
 | |
| 
 | |
| //        export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
 | |
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | |
|  
 | |
|         {
 | |
|             // Bridge expolygons, grown, to be tested for intersection with other bridge regions.
 | |
|             std::vector<BoundingBox> fill_boundaries_ex_bboxes = get_extents_vector(fill_boundaries_ex);
 | |
|             bridges_grown.reserve(bridges.size());
 | |
|             bridge_bboxes.reserve(bridges.size());
 | |
|             for (size_t i = 0; i < bridges.size(); ++ i) {
 | |
|                 // Find the island of this bridge.
 | |
|                 const Point pt = bridges[i].expolygon.contour.points.front();
 | |
|                 int idx_island = -1;
 | |
|                 for (int j = 0; j < int(fill_boundaries_ex.size()); ++ j)
 | |
|                     if (fill_boundaries_ex_bboxes[j].contains(pt) && 
 | |
|                         fill_boundaries_ex[j].contains(pt)) {
 | |
|                         idx_island = j;
 | |
|                         break;
 | |
|                     }
 | |
|                 // Grown by 3mm.
 | |
|                 //BBS: eliminate too narrow area to avoid generating bridge on top layer when wall loop is 1
 | |
|                 //Polygons polys = offset(bridges[i].expolygon, bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
 | |
|                 Polygons polys = offset2({ bridges[i].expolygon }, -scale_(nozzle_diameter * 0.1), bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
 | |
|                 if (idx_island == -1) {
 | |
| 				    BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
 | |
|                 } else {
 | |
|                     // Found an island, to which this bridge region belongs. Trim it,
 | |
|                     polys = intersection(polys, fill_boundaries_ex[idx_island]);
 | |
|                 }
 | |
|                 bridge_bboxes.push_back(get_extents(polys));
 | |
|                 bridges_grown.push_back(std::move(polys));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // 2) Group the bridge surfaces by overlaps.
 | |
|         std::vector<size_t> bridge_group(bridges.size(), (size_t)-1);
 | |
|         size_t n_groups = 0; 
 | |
|         for (size_t i = 0; i < bridges.size(); ++ i) {
 | |
|             // A grup id for this bridge.
 | |
|             size_t group_id = (bridge_group[i] == size_t(-1)) ? (n_groups ++) : bridge_group[i];
 | |
|             bridge_group[i] = group_id;
 | |
|             // For all possibly overlaping bridges:
 | |
|             for (size_t j = i + 1; j < bridges.size(); ++ j) {
 | |
|                 if (! bridge_bboxes[i].overlap(bridge_bboxes[j]))
 | |
|                     continue;
 | |
|                 if (intersection(bridges_grown[i], bridges_grown[j]).empty())
 | |
|                     continue;
 | |
|                 // The two bridge regions intersect. Give them the same group id.
 | |
|                 if (bridge_group[j] != size_t(-1)) {
 | |
|                     // The j'th bridge has been merged with some other bridge before.
 | |
|                     size_t group_id_new = bridge_group[j];
 | |
|                     for (size_t k = 0; k < j; ++ k)
 | |
|                         if (bridge_group[k] == group_id)
 | |
|                             bridge_group[k] = group_id_new;
 | |
|                     group_id = group_id_new;
 | |
|                 }
 | |
|                 bridge_group[j] = group_id;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // 3) Merge the groups with the same group id, detect bridges.
 | |
|         {
 | |
| 			BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges. layer" << this->layer()->print_z << ", bridge groups: " << n_groups;
 | |
|             for (size_t group_id = 0; group_id < n_groups; ++ group_id) {
 | |
|                 size_t n_bridges_merged = 0;
 | |
|                 size_t idx_last = (size_t)-1;
 | |
|                 for (size_t i = 0; i < bridges.size(); ++ i) {
 | |
|                     if (bridge_group[i] == group_id) {
 | |
|                         ++ n_bridges_merged;
 | |
|                         idx_last = i;
 | |
|                     }
 | |
|                 }
 | |
|                 if (n_bridges_merged == 0)
 | |
|                     // This group has no regions assigned as these were moved into another group.
 | |
|                     continue;
 | |
|                 // Collect the initial ungrown regions and the grown polygons.
 | |
|                 ExPolygons  initial;
 | |
|                 Polygons    grown;
 | |
|                 for (size_t i = 0; i < bridges.size(); ++ i) {
 | |
|                     if (bridge_group[i] != group_id)
 | |
|                         continue;
 | |
|                     initial.push_back(std::move(bridges[i].expolygon));
 | |
|                     polygons_append(grown, bridges_grown[i]);
 | |
|                 }
 | |
|                 // detect bridge direction before merging grown surfaces otherwise adjacent bridges
 | |
|                 // would get merged into a single one while they need different directions
 | |
|                 // also, supply the original expolygon instead of the grown one, because in case
 | |
|                 // of very thin (but still working) anchors, the grown expolygon would go beyond them
 | |
|                 double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
 | |
|                 if (custom_angle > 0.0) {
 | |
|                     bridges[idx_last].bridge_angle = custom_angle;
 | |
|                 } else {
 | |
|                     auto [bridging_dir, unsupported_dist] = detect_bridging_direction(to_polygons(initial), to_polygons(lower_layer->lslices));
 | |
|                     bridges[idx_last].bridge_angle = PI + std::atan2(bridging_dir.y(), bridging_dir.x());
 | |
|                 }
 | |
| 
 | |
|                 /*
 | |
|                 BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill, object_config.thick_bridges).scaled_width());
 | |
|                 #ifdef SLIC3R_DEBUG
 | |
|                 printf("Processing bridge at layer %zu:\n", this->layer()->id());
 | |
|                 #endif
 | |
|                 //BBS: use 0 as custom angle to enable auto detection all the time
 | |
|                 double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
 | |
|                 if(custom_angle > 0)
 | |
|                         bridges[idx_last].bridge_angle = custom_angle;
 | |
| 				else if (bd.detect_angle(custom_angle)) {
 | |
|                     bridges[idx_last].bridge_angle = bd.angle;
 | |
|                     if (this->layer()->object()->has_support()) {
 | |
| //                        polygons_append(this->bridged, bd.coverage());
 | |
|                         append(this->unsupported_bridge_edges, bd.unsupported_edges());
 | |
|                     }
 | |
| 				} else if (custom_angle > 0) {
 | |
| 					// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
 | |
| 					// using a bridging flow, therefore it makes sense to respect the custom bridging direction.
 | |
| 					bridges[idx_last].bridge_angle = custom_angle;
 | |
| 				}
 | |
|                 */
 | |
|                 // without safety offset, artifacts are generated (GH #2494)
 | |
|                 surfaces_append(bottom, union_safety_offset_ex(grown), bridges[idx_last]);
 | |
|             }
 | |
| 
 | |
|             fill_boundaries = to_polygons(fill_boundaries_ex);
 | |
| 			BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
 | |
| 		}
 | |
| 
 | |
|     #if 0
 | |
|         {
 | |
|             static int iRun = 0;
 | |
|             bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true);
 | |
|         }
 | |
|     #endif
 | |
|     }
 | |
| 
 | |
|     Surfaces new_surfaces;
 | |
|     {
 | |
|         // Merge top and bottom in a single collection.
 | |
|         surfaces_append(top, std::move(bottom));
 | |
|         // Intersect the grown surfaces with the actual fill boundaries.
 | |
|         Polygons bottom_polygons = to_polygons(bottom);
 | |
|         for (size_t i = 0; i < top.size(); ++ i) {
 | |
|             Surface &s1 = top[i];
 | |
|             if (s1.empty())
 | |
|                 continue;
 | |
|             Polygons polys;
 | |
|             polygons_append(polys, to_polygons(std::move(s1)));
 | |
|             for (size_t j = i + 1; j < top.size(); ++ j) {
 | |
|                 Surface &s2 = top[j];
 | |
|                 if (! s2.empty() && surfaces_could_merge(s1, s2)) {
 | |
|                     polygons_append(polys, to_polygons(std::move(s2)));
 | |
|                     s2.clear();
 | |
|                 }
 | |
|             }
 | |
|             if (s1.is_top())
 | |
|                 // Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
 | |
|                 polys = diff(polys, bottom_polygons);
 | |
|             surfaces_append(
 | |
|                 new_surfaces,
 | |
|                 // Don't use a safety offset as fill_boundaries were already united using the safety offset.
 | |
|                 intersection_ex(polys, fill_boundaries),
 | |
|                 s1);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     // Subtract the new top surfaces from the other non-top surfaces and re-add them.
 | |
|     Polygons new_polygons = to_polygons(new_surfaces);
 | |
|     for (size_t i = 0; i < internal.size(); ++ i) {
 | |
|         Surface &s1 = internal[i];
 | |
|         if (s1.empty())
 | |
|             continue;
 | |
|         Polygons polys;
 | |
|         polygons_append(polys, to_polygons(std::move(s1)));
 | |
|         for (size_t j = i + 1; j < internal.size(); ++ j) {
 | |
|             Surface &s2 = internal[j];
 | |
|             if (! s2.empty() && surfaces_could_merge(s1, s2)) {
 | |
|                 polygons_append(polys, to_polygons(std::move(s2)));
 | |
|                 s2.clear();
 | |
|             }
 | |
|         }
 | |
|         ExPolygons new_expolys = diff_ex(polys, new_polygons);
 | |
|         polygons_append(new_polygons, to_polygons(new_expolys));
 | |
|         surfaces_append(new_surfaces, std::move(new_expolys), s1);
 | |
|     }
 | |
|     
 | |
|     this->fill_surfaces.surfaces = std::move(new_surfaces);
 | |
| 
 | |
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | |
|     export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");
 | |
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | |
| }
 | |
| 
 | |
| void LayerRegion::prepare_fill_surfaces()
 | |
| {
 | |
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | |
|     export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial");
 | |
|     export_region_fill_surfaces_to_svg_debug("2_prepare_fill_surfaces-initial");
 | |
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ 
 | |
| 
 | |
|     /*  Note: in order to make the psPrepareInfill step idempotent, we should never
 | |
|         alter fill_surfaces boundaries on which our idempotency relies since that's
 | |
|         the only meaningful information returned by psPerimeters. */
 | |
|     
 | |
|     bool spiral_mode = this->layer()->object()->print()->config().spiral_mode;
 | |
| 
 | |
|     // if no solid layers are requested, turn top/bottom surfaces to internal
 | |
|     if (! spiral_mode && this->region().config().top_shell_layers == 0) {
 | |
|         for (Surface &surface : this->fill_surfaces.surfaces)
 | |
|             if (surface.is_top())
 | |
|                 //BBS
 | |
|                 //surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
 | |
|                 surface.surface_type = PrintObject::infill_only_where_needed ? stInternalVoid : stInternal;
 | |
|     }
 | |
|     if (this->region().config().bottom_shell_layers == 0) {
 | |
|         for (Surface &surface : this->fill_surfaces.surfaces)
 | |
|             if (surface.is_bottom()) // (surface.surface_type == stBottom)
 | |
|                 surface.surface_type = stInternal;
 | |
|     }
 | |
| 
 | |
|     // turn too small internal regions into solid regions according to the user setting
 | |
|     if (! spiral_mode && this->region().config().sparse_infill_density.value > 0) {
 | |
|         // scaling an area requires two calls!
 | |
|         double min_area = scale_(scale_(this->region().config().minimum_sparse_infill_area.value));
 | |
|         for (Surface &surface : this->fill_surfaces.surfaces)
 | |
|             if (surface.surface_type == stInternal && surface.area() <= min_area)
 | |
|                 surface.surface_type = stInternalSolid;
 | |
|     }
 | |
| 
 | |
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | |
|     export_region_slices_to_svg_debug("2_prepare_fill_surfaces-final");
 | |
|     export_region_fill_surfaces_to_svg_debug("2_prepare_fill_surfaces-final");
 | |
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | |
| }
 | |
| 
 | |
| double LayerRegion::infill_area_threshold() const
 | |
| {
 | |
|     double ss = this->flow(frSolidInfill).scaled_spacing();
 | |
|     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(this->slices.surfaces, trimming_polygons), 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 */
 | |
|     Polygons tmp = intersection(this->slices.surfaces, trimming_polygons);
 | |
|     append(tmp, diff(this->slices.surfaces, opening(this->slices.surfaces, elephant_foot_compensation_perimeter_step)));
 | |
|     this->slices.set(union_ex(tmp), stInternal);
 | |
| }
 | |
| 
 | |
| void LayerRegion::export_region_slices_to_svg(const char *path) const
 | |
| {
 | |
|     BoundingBox bbox;
 | |
|     for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++surface)
 | |
|         bbox.merge(get_extents(surface->expolygon));
 | |
|     Point legend_size = export_surface_type_legend_to_svg_box_size();
 | |
|     Point legend_pos(bbox.min(0), bbox.max(1));
 | |
|     bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
 | |
| 
 | |
|     SVG svg(path, bbox);
 | |
|     const float transparency = 0.5f;
 | |
|     for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++surface)
 | |
|         svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency);
 | |
|     for (Surfaces::const_iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface)
 | |
|         svg.draw(surface->expolygon.lines(), surface_type_to_color_name(surface->surface_type));
 | |
|     export_surface_type_legend_to_svg(svg, legend_pos);
 | |
|     svg.Close();
 | |
| }
 | |
| 
 | |
| // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
 | |
| void LayerRegion::export_region_slices_to_svg_debug(const char *name) const
 | |
| {
 | |
|     static std::map<std::string, size_t> idx_map;
 | |
|     size_t &idx = idx_map[name];
 | |
|     this->export_region_slices_to_svg(debug_out_path("LayerRegion-slices-%s-%d.svg", name, idx ++).c_str());
 | |
| }
 | |
| 
 | |
| void LayerRegion::export_region_fill_surfaces_to_svg(const char *path) const
 | |
| {
 | |
|     BoundingBox bbox;
 | |
|     for (Surfaces::const_iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface)
 | |
|         bbox.merge(get_extents(surface->expolygon));
 | |
|     Point legend_size = export_surface_type_legend_to_svg_box_size();
 | |
|     Point legend_pos(bbox.min(0), bbox.max(1));
 | |
|     bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
 | |
| 
 | |
|     SVG svg(path, bbox);
 | |
|     const float transparency = 0.5f;
 | |
|     for (const Surface &surface : this->fill_surfaces.surfaces) {
 | |
|         svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
 | |
|         svg.draw_outline(surface.expolygon, "black", "blue", scale_(0.05)); 
 | |
|     }
 | |
|     export_surface_type_legend_to_svg(svg, legend_pos);
 | |
|     svg.Close();
 | |
| }
 | |
| 
 | |
| // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
 | |
| void LayerRegion::export_region_fill_surfaces_to_svg_debug(const char *name) const
 | |
| {
 | |
|     static std::map<std::string, size_t> idx_map;
 | |
|     size_t &idx = idx_map[name];
 | |
|     this->export_region_fill_surfaces_to_svg(debug_out_path("LayerRegion-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
 | |
| }
 | |
| 
 | |
| //BBS
 | |
| void LayerRegion::simplify_extrusion_entity()
 | |
| {
 | |
|     simplify_entity_collection(&perimeters);
 | |
|     simplify_entity_collection(&fills);
 | |
| }
 | |
| 
 | |
| void LayerRegion::simplify_entity_collection(ExtrusionEntityCollection* entity_collection)
 | |
| {
 | |
|     for (size_t i = 0; i < entity_collection->entities.size(); i++) {
 | |
|         if (ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(entity_collection->entities[i]))
 | |
|             this->simplify_entity_collection(collection);
 | |
|         else if (ExtrusionPath* path = dynamic_cast<ExtrusionPath*>(entity_collection->entities[i]))
 | |
|             this->simplify_path(path);
 | |
|         else if (ExtrusionMultiPath* multipath = dynamic_cast<ExtrusionMultiPath*>(entity_collection->entities[i]))
 | |
|             this->simplify_multi_path(multipath);
 | |
|         else if (ExtrusionLoop* loop = dynamic_cast<ExtrusionLoop*>(entity_collection->entities[i]))
 | |
|             this->simplify_loop(loop);
 | |
|         else
 | |
|             throw Slic3r::InvalidArgument("Invalid extrusion entity supplied to simplify_entity_collection()");
 | |
|     }
 | |
| }
 | |
| 
 | |
| void LayerRegion::simplify_path(ExtrusionPath* path)
 | |
| {
 | |
|     const auto print_config = this->layer()->object()->print()->config();
 | |
|     const bool spiral_mode = print_config.spiral_mode;
 | |
|     const bool enable_arc_fitting = print_config.enable_arc_fitting;
 | |
|     const auto scaled_resolution = scaled<double>(print_config.resolution.value);
 | |
| 
 | |
|     if (enable_arc_fitting &&
 | |
|         !spiral_mode) {
 | |
|         if (path->role() == erInternalInfill)
 | |
|             path->simplify_by_fitting_arc(SCALED_SPARSE_INFILL_RESOLUTION);
 | |
|         else
 | |
|             path->simplify_by_fitting_arc(scaled_resolution);
 | |
|     } else {
 | |
|         path->simplify(scaled_resolution);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
 | |
| {
 | |
|     const auto print_config = this->layer()->object()->print()->config();
 | |
|     const bool spiral_mode = print_config.spiral_mode;
 | |
|     const bool enable_arc_fitting = print_config.enable_arc_fitting;
 | |
|     const auto scaled_resolution = scaled<double>(print_config.resolution.value);
 | |
| 
 | |
|     for (size_t i = 0; i < multipath->paths.size(); ++i) {
 | |
|         if (enable_arc_fitting &&
 | |
|             !spiral_mode) {
 | |
|             if (multipath->paths[i].role() == erInternalInfill)
 | |
|                 multipath->paths[i].simplify_by_fitting_arc(SCALED_SPARSE_INFILL_RESOLUTION);
 | |
|             else
 | |
|                 multipath->paths[i].simplify_by_fitting_arc(scaled_resolution);
 | |
|         } else {
 | |
|             multipath->paths[i].simplify(scaled_resolution);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void LayerRegion::simplify_loop(ExtrusionLoop* loop)
 | |
| {
 | |
|     const auto print_config = this->layer()->object()->print()->config();
 | |
|     const bool spiral_mode = print_config.spiral_mode;
 | |
|     const bool enable_arc_fitting = print_config.enable_arc_fitting;
 | |
|     const auto scaled_resolution = scaled<double>(print_config.resolution.value);
 | |
| 
 | |
|     for (size_t i = 0; i < loop->paths.size(); ++i) {
 | |
|         if (enable_arc_fitting &&
 | |
|             !spiral_mode) {
 | |
|             if (loop->paths[i].role() == erInternalInfill)
 | |
|                 loop->paths[i].simplify_by_fitting_arc(SCALED_SPARSE_INFILL_RESOLUTION);
 | |
|             else
 | |
|                 loop->paths[i].simplify_by_fitting_arc(scaled_resolution);
 | |
|         } else {
 | |
|             loop->paths[i].simplify(scaled_resolution);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| }
 | |
|  
 | 
