Browse Source

WIP: Reworked the infill generator to merge areas with the same
properties.

Note for Vojtech:
Review src/libslic3r/Fill/Fill.cpp once again,
add test for G-code generator properties (extrusion speed, cooling?)

Fixes Modifier slice/move efficiency #1005

bubnikv 5 years ago
parent
commit
d146a0237e

+ 328 - 216
src/libslic3r/Fill/Fill.cpp

@@ -13,103 +13,200 @@
 
 namespace Slic3r {
 
-struct SurfaceGroupAttrib
+struct SurfaceFillParams
 {
-    SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {}
-    bool operator==(const SurfaceGroupAttrib &other) const
-        { return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; }
-    bool    is_solid;
-    float   flow_width;
-    // pattern is of type InfillPattern, -1 for an unset pattern.
-    int     pattern;
+	SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); }
+	// Zero based extruder ID.
+	unsigned int 	extruder;
+	// Infill pattern, adjusted for the density etc.
+	InfillPattern  	pattern;
+
+    // FillBase
+    // in unscaled coordinates
+    coordf_t    	spacing;
+    // infill / perimeter overlap, in unscaled coordinates
+    coordf_t    	overlap;
+    // Angle as provided by the region config, in radians.
+    float       	angle;
+    // Non-negative for a bridge.
+    float 			bridge_angle;
+
+    // FillParams
+    float       	density;
+    // Don't connect the fill lines around the inner perimeter.
+    bool        	dont_connect;
+    // Don't adjust spacing to fill the space evenly.
+    bool        	dont_adjust;
+
+    // width, height of extrusion, nozzle diameter, is bridge
+    // For the output, for fill generator.
+	Flow 			flow;
+
+	// For the output
+	ExtrusionRole	extrusion_role;
+
+	// Various print settings?
+
+	// Index of this entry in a linear vector.
+	size_t 			idx;
+
+
+	bool operator<(const SurfaceFillParams &rhs) const {
+#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
+#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
+
+		// Sort first by decreasing bridging angle, so that the bridges are processed with priority when trimming one layer by the other.
+		if (this->bridge_angle > rhs.bridge_angle) return true; 
+		if (this->bridge_angle < rhs.bridge_angle) return false;
+
+		RETURN_COMPARE_NON_EQUAL(extruder);
+		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, pattern);
+		RETURN_COMPARE_NON_EQUAL(spacing);
+		RETURN_COMPARE_NON_EQUAL(overlap);
+		RETURN_COMPARE_NON_EQUAL(angle);
+		RETURN_COMPARE_NON_EQUAL(density);
+		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect);
+		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
+		RETURN_COMPARE_NON_EQUAL(flow.width);
+		RETURN_COMPARE_NON_EQUAL(flow.height);
+		RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
+		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge);
+		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role);
+		return false;
+	}
+
+	bool operator==(const SurfaceFillParams &rhs) const {
+		return  this->extruder 			== rhs.extruder 		&&
+				this->pattern 			== rhs.pattern 			&&
+				this->pattern 			== rhs.pattern 			&&
+				this->spacing 			== rhs.spacing 			&&
+				this->overlap 			== rhs.overlap 			&&
+				this->angle   			== rhs.angle   			&&
+				this->density   		== rhs.density   		&&
+				this->dont_connect  	== rhs.dont_connect 	&&
+				this->dont_adjust   	== rhs.dont_adjust 		&&
+				this->flow 				== rhs.flow 			&&
+				this->extrusion_role	== rhs.extrusion_role;
+	}
 };
 
-// Generate infills for Slic3r::Layer::Region.
-// The Slic3r::Layer::Region at this point of time may contain
-// surfaces of various types (internal/bridge/top/bottom/solid).
-// The infills are generated on the groups of surfaces with a compatible type. 
-// Returns an array of Slic3r::ExtrusionPath::Collection objects containing the infills generaed now
-// and the thin fills generated by generate_perimeters().
-void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
-{    
-//    Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
-    
-    double  fill_density           = layerm.region()->config().fill_density;
-    Flow    infill_flow            = layerm.flow(frInfill);
-    Flow    solid_infill_flow      = layerm.flow(frSolidInfill);
-    Flow    top_solid_infill_flow  = layerm.flow(frTopSolidInfill);
-
-    Surfaces surfaces;
-    
-    // merge adjacent surfaces
-    // in case of bridge surfaces, the ones with defined angle will be attached to the ones
-    // without any angle (shouldn't this logic be moved to process_external_surfaces()?)
-    {
-        Polygons polygons_bridged;
-        polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size());
-        for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
-            if (it->bridge_angle >= 0)
-                polygons_append(polygons_bridged, *it);
-        
-        // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
-        // group is of type Slic3r::SurfaceCollection
-        //FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
-        std::vector<SurfacesPtr> groups;
-        layerm.fill_surfaces.group(&groups);
-        
-        // merge compatible groups (we can generate continuous infill for them)
-        {
-            // cache flow widths and patterns used for all solid groups
-            // (we'll use them for comparing compatible groups)
-            std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
-            for (size_t i = 0; i < groups.size(); ++ i) {
-                // we can only merge solid non-bridge surfaces, so discard
-                // non-solid surfaces
-                const Surface &surface = *groups[i].front();
-                if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
-                    group_attrib[i].is_solid = true;
-                    group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
-                    group_attrib[i].pattern = surface.is_external() ? 
+struct SurfaceFill {
+	SurfaceFill(const SurfaceFillParams& params) : region_id(size_t(-1)), surface(stCount, ExPolygon()), params(params) {}
+
+	size_t 				region_id;
+	Surface 			surface;
+	ExPolygons       	expolygons;
+	SurfaceFillParams	params;
+};
+
+std::vector<SurfaceFill> group_fills(const Layer &layer)
+{
+	std::vector<SurfaceFill> surface_fills;
+
+	// Fill in a map of a region & surface to SurfaceFillParams.
+	std::set<SurfaceFillParams> 						set_surface_params;
+	std::vector<std::vector<const SurfaceFillParams*>> 	region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>());
+    SurfaceFillParams									params;
+    bool 												has_internal_voids = false;
+	for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
+		const LayerRegion  &layerm = *layer.regions()[region_id];
+		region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
+	    for (const Surface &surface : layerm.fill_surfaces.surfaces)
+	        if (surface.surface_type == stInternalVoid)
+	        	has_internal_voids = true;
+	        else {
+		        FlowRole extrusion_role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
+		        bool     is_bridge 	    = layerm.layer()->id() > 0 && surface.is_bridge();
+		        params.extruder 	 = layerm.region()->extruder(extrusion_role);
+		        params.pattern 		 = layerm.region()->config().fill_pattern.value;
+		        params.density       = float(layerm.region()->config().fill_density);
+
+		        if (surface.is_solid()) {
+		            params.density = 100.f;
+		            params.pattern = (surface.is_external() && ! is_bridge) ? 
 						(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
-                        ipRectilinear;
-                }
-            }
-            // Loop through solid groups, find compatible groups and append them to this one.
-            for (size_t i = 0; i < groups.size(); ++ i) {
-                if (! group_attrib[i].is_solid)
-                    continue;
-                for (size_t j = i + 1; j < groups.size();) {
-                    if (group_attrib[i] == group_attrib[j]) {
-                        // groups are compatible, merge them
-                        groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
-                        groups.erase(groups.begin() + j);
-                        group_attrib.erase(group_attrib.begin() + j);
-                    } else
-                         ++ j;
-                }
-            }
-        }
-        
-        // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
-        for (size_t round = 0; round < 2; ++ round) {
-            for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
-                const SurfacesPtr &group = *it_group;
-                bool is_bridge = group.front()->bridge_angle >= 0;
-                if (is_bridge != (round == 0))
-                    continue;
-                // Make a union of polygons defining the infiill regions of a group, use a safety offset.
-                Polygons union_p = union_(to_polygons(*it_group), true);
-                // Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
-                if (! polygons_bridged.empty() && ! is_bridge)
-                    union_p = diff(union_p, polygons_bridged, true);
-                // subtract any other surface already processed
-                //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
-                // Using group.front() as a template.
-                surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
-            }
-        }
-    }
-    
+		                ipRectilinear;
+		        } else if (params.density <= 0)
+		            continue;
+
+		        params.extrusion_role =
+		            is_bridge ?
+		                erBridgeInfill :
+		                (surface.is_solid() ?
+		                    ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
+		                    erInternalInfill);
+		        params.bridge_angle = float(surface.bridge_angle);
+		        params.angle 		= float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
+		        
+		        // calculate the actual flow we'll be using for this infill
+		        params.flow = layerm.region()->flow(
+		            extrusion_role,
+		            (surface.thickness == -1) ? layerm.layer()->height : surface.thickness, // extrusion height
+		            is_bridge || Fill::use_bridge_flow(params.pattern), 					// bridge flow?
+		            layerm.layer()->id() == 0,          									// first layer?
+		            -1,                                 									// auto width
+		            *layerm.layer()->object()
+		        );
+		        
+		        // Calculate flow spacing for infill pattern generation.
+		        if (! surface.is_solid() && ! is_bridge) {
+		            // it's internal infill, so we can calculate a generic flow spacing 
+		            // for all layers, for avoiding the ugly effect of
+		            // misaligned infill on first layer because of different extrusion width and
+		            // layer height
+		            params.spacing = layerm.region()->flow(
+			                frInfill,
+			                layerm.layer()->object()->config().layer_height.value,  // TODO: handle infill_every_layers?
+			                false,  // no bridge
+			                false,  // no first layer
+			                -1,     // auto width
+			                *layer.object()
+			            ).spacing();
+		        } else
+		            params.spacing = params.flow.spacing();
+
+		        auto it_params = set_surface_params.find(params);
+		        if (it_params == set_surface_params.end())
+		        	it_params = set_surface_params.insert(it_params, params);
+		        region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params);
+		    }
+	}
+
+	surface_fills.reserve(set_surface_params.size());
+	for (const SurfaceFillParams &params : set_surface_params) {
+		const_cast<SurfaceFillParams&>(params).idx = surface_fills.size();
+		surface_fills.emplace_back(params);
+	}
+
+	for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
+		const LayerRegion &layerm = *layer.regions()[region_id];
+	    for (const Surface &surface : layerm.fill_surfaces.surfaces)
+	        if (surface.surface_type != stInternalVoid) {
+	        	const SurfaceFillParams *params = region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()];
+				if (params != nullptr) {
+	        		SurfaceFill &fill = surface_fills[params->idx];
+	        		if (fill.region_id = size_t(-1)) {
+	        			fill.region_id = region_id;
+	        			fill.surface = surface;
+	        			fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
+	        		} else
+	        			fill.expolygons.emplace_back(surface.expolygon);
+				}
+	        }
+	}
+
+	{
+		Polygons all_polygons;
+		for (SurfaceFill &fill : surface_fills)
+			if (! fill.expolygons.empty() && (fill.expolygons.size() > 1 || ! all_polygons.empty())) {
+				Polygons polys = to_polygons(std::move(fill.expolygons));
+	            // Make a union of polygons, use a safety offset, subtract the preceding polygons.
+			    // Bridges are processed first (see SurfaceFill::operator<())
+	            fill.expolygons = all_polygons.empty() ? union_ex(polys, true) : diff_ex(polys, all_polygons, true);
+				append(all_polygons, std::move(polys));
+	        }
+	}
+
     // we need to detect any narrow surfaces that might collapse
     // when adding spacing below
     // such narrow surfaces are often generated in sloping walls
@@ -119,155 +216,170 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
     // we are going to grow such regions by overlapping them with the void (if any)
     // TODO: detect and investigate whether there could be narrow regions without
     // any void neighbors
-    {
-        coord_t distance_between_surfaces = std::max(
-            std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()),
-            top_solid_infill_flow.scaled_spacing());
-        Polygons surfaces_polygons = to_polygons(surfaces);
-        Polygons collapsed = diff(
-            surfaces_polygons,
-            offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
-            true);
-        Polygons to_subtract;
-        to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
-        for (Surfaces::const_iterator it_surface = surfaces.begin(); it_surface != surfaces.end(); ++ it_surface)
-            if (it_surface->surface_type == stInternalVoid)
-                polygons_append(to_subtract, *it_surface);
-        polygons_append(to_subtract, collapsed);
-        surfaces_append(
-            surfaces,
-            intersection_ex(
-                offset(collapsed, (float)distance_between_surfaces),
-                to_subtract,
-                true),
-            stInternalSolid);
+    if (has_internal_voids) {
+    	// Internal voids are generated only if "infill_only_where_needed" or "infill_every_layers" are active.
+        coord_t  distance_between_surfaces = 0;
+        Polygons surfaces_polygons;
+        Polygons voids;
+		int      region_internal_infill = -1;
+		int		 region_solid_infill = -1;
+		int		 region_some_infill = -1;
+    	for (SurfaceFill &surface_fill : surface_fills)
+			if (! surface_fill.expolygons.empty()) {
+    			distance_between_surfaces = std::max(distance_between_surfaces, surface_fill.params.flow.scaled_spacing());
+				append((surface_fill.surface.surface_type == stInternalVoid) ? voids : surfaces_polygons, to_polygons(surface_fill.expolygons));
+				if (surface_fill.surface.surface_type == stInternalSolid)
+					region_internal_infill = (int)surface_fill.region_id;
+				if (surface_fill.surface.is_solid())
+					region_solid_infill = (int)surface_fill.region_id;
+				if (surface_fill.surface.surface_type != stInternalVoid)
+					region_some_infill = (int)surface_fill.region_id;
+			}
+    	if (! voids.empty() && ! surfaces_polygons.empty()) {
+    		// First clip voids by the printing polygons, as the voids were ignored by the loop above during mutual clipping.
+    		voids = diff(voids, surfaces_polygons);
+	        // Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2
+	        Polygons collapsed = diff(
+	            surfaces_polygons,
+	            offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
+	            true);
+	        //FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being
+	        // added if two offsetted void regions merge.
+	        // polygons_append(voids, collapsed);
+	        ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, true);
+	        // Now find an internal infill SurfaceFill to add these extrusions to.
+	        SurfaceFill *internal_solid_fill = nullptr;
+			unsigned int region_id = 0;
+			if (region_internal_infill != -1)
+				region_id = region_internal_infill;
+			else if (region_solid_infill != -1)
+				region_id = region_solid_infill;
+			else if (region_some_infill != -1)
+				region_id = region_some_infill;
+			const LayerRegion& layerm = *layer.regions()[region_id];
+	        for (SurfaceFill &surface_fill : surface_fills)
+	        	if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layerm.layer()->height - surface_fill.params.flow.height) < EPSILON) {
+	        		internal_solid_fill = &surface_fill;
+	        		break;
+	        	}
+	        if (internal_solid_fill == nullptr) {
+	        	// Produce another solid fill.
+		        params.extruder 	 = layerm.region()->extruder(frSolidInfill);
+	            params.pattern 		 = ipRectilinear;
+	            params.density 		 = 100.f;
+		        params.extrusion_role = erInternalInfill;
+		        params.angle 		= float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
+		        // calculate the actual flow we'll be using for this infill
+		        params.flow = layerm.region()->flow(
+		            frSolidInfill,
+		            layerm.layer()->height, 		// extrusion height
+		            false, 							// bridge flow?
+		            layerm.layer()->id() == 0,      // first layer?
+		            -1,                             // auto width
+		            *layer.object()
+		        );
+		        params.spacing = params.flow.spacing();	        
+				surface_fills.emplace_back(params);
+				surface_fills.back().surface.surface_type = stInternalSolid;
+				surface_fills.back().surface.thickness = layer.height;
+				surface_fills.back().expolygons = std::move(extensions);
+	        } else {
+	        	append(extensions, std::move(internal_solid_fill->expolygons));
+	        	internal_solid_fill->expolygons = union_ex(extensions);
+	        }
+		}
     }
 
-    if (0) {
-//        require "Slic3r/SVG.pm";
-//        Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
-//            expolygons      => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
-//            red_expolygons  => [ map $_->expolygon, grep  $_->is_solid, @surfaces ],
-//        );
-    }
+	return surface_fills;
+}
 
-    for (const Surface &surface : surfaces) {
-        if (surface.surface_type == stInternalVoid)
-            continue;
-        InfillPattern  fill_pattern = layerm.region()->config().fill_pattern.value;
-        double         density      = fill_density;
-        FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill :
-            (surface.is_solid() ? frSolidInfill : frInfill);
-        bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
-        
-        if (surface.is_solid()) {
-            density = 100.;
-            fill_pattern = (surface.is_external() && ! is_bridge) ? 
-				(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
-                ipRectilinear;
-        } else if (density <= 0)
-            continue;
-        
-        // get filler object
-        std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
-        f->set_bounding_box(layerm.layer()->object()->bounding_box());
-        
-        // calculate the actual flow we'll be using for this infill
-        coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness;
-        Flow flow = layerm.region()->flow(
-            role,
-            h,
-            is_bridge || f->use_bridge_flow(),  // bridge flow?
-            layerm.layer()->id() == 0,          // first layer?
-            -1,                                 // auto width
-            *layerm.layer()->object()
-        );
-        
-        // calculate flow spacing for infill pattern generation
-        bool using_internal_flow = false;
-        if (! surface.is_solid() && ! is_bridge) {
-            // it's internal infill, so we can calculate a generic flow spacing 
-            // for all layers, for avoiding the ugly effect of
-            // misaligned infill on first layer because of different extrusion width and
-            // layer height
-            Flow internal_flow = layerm.region()->flow(
-                frInfill,
-                layerm.layer()->object()->config().layer_height.value,  // TODO: handle infill_every_layers?
-                false,  // no bridge
-                false,  // no first layer
-                -1,     // auto width
-                *layerm.layer()->object()
-            );
-            f->spacing = internal_flow.spacing();
-            using_internal_flow = true;
-        } else {
-            f->spacing = flow.spacing();
-        }
+// friend to Layer
+void Layer::make_fills()
+{
+	for (LayerRegion *layerm : m_regions)
+		layerm->fills.clear();
+
+	std::vector<SurfaceFill>  surface_fills = group_fills(*this);
+	const Slic3r::BoundingBox bbox = this->object()->bounding_box();
 
+    for (SurfaceFill &surface_fill : surface_fills) {
+        // Create the filler object.
+        std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern));
+        f->set_bounding_box(bbox);
+        f->layer_id = this->id();
+        f->z 		= this->print_z;
+        f->angle 	= surface_fill.params.angle;
+        f->spacing  = surface_fill.params.spacing;
+
+        // calculate flow spacing for infill pattern generation
+        bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;
         double link_max_length = 0.;
-        if (! is_bridge) {
+        if (! surface_fill.params.flow.bridge) {
 #if 0
             link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
 //            printf("flow spacing: %f,  is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
 #else
-            if (density > 80.) // 80%
+            if (surface_fill.params.density > 80.) // 80%
                 link_max_length = 3. * f->spacing;
 #endif
         }
 
-        f->layer_id = layerm.layer()->id();
-        f->z = layerm.layer()->print_z;
-        f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
         // Maximum length of the perimeter segment linking two infill lines.
         f->link_max_length = (coord_t)scale_(link_max_length);
         // Used by the concentric infill pattern to clip the loops to create extrusion paths.
-        f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
-//        f->layer_height = h;
+        f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
 
         // apply half spacing using this flow's own spacing and generate infill
         FillParams params;
-        params.density = float(0.01 * density);
-//        params.dont_adjust = true;
-        params.dont_adjust = false;
-        Polylines polylines = f->fill_surface(&surface, params);
-        if (polylines.empty())
-            continue;
-
-        // calculate actual flow from spacing (which might have been adjusted by the infill
-        // pattern generator)
-        if (using_internal_flow) {
-            // if we used the internal flow we're not doing a solid infill
-            // so we can safely ignore the slight variation that might have
-            // been applied to $f->flow_spacing
-        } else {
-            flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow());
-        }
+        params.density 		= float(0.01 * surface_fill.params.density);
+        params.dont_adjust 	= surface_fill.params.dont_adjust; // false
 
-        // Save into layer.
-        auto *eec = new ExtrusionEntityCollection();
-        out.entities.push_back(eec);
-        // Only concentric fills are not sorted.
-        eec->no_sort = f->no_sort();
-        extrusion_entities_append_paths(
-            eec->entities, std::move(polylines),
-            is_bridge ?
-                erBridgeInfill :
-                (surface.is_solid() ?
-                    ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
-                    erInternalInfill),
-            flow.mm3_per_mm(), flow.width, flow.height);
+        for (ExPolygon &expoly : surface_fill.expolygons) {
+        	surface_fill.surface.expolygon = std::move(expoly);
+	        Polylines polylines = f->fill_surface(&surface_fill.surface, params);
+	        if (! polylines.empty()) {
+		        // calculate actual flow from spacing (which might have been adjusted by the infill
+		        // pattern generator)
+		        double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
+		        double flow_width      = surface_fill.params.flow.width;
+		        if (using_internal_flow) {
+		            // if we used the internal flow we're not doing a solid infill
+		            // so we can safely ignore the slight variation that might have
+		            // been applied to f->spacing
+		        } else {
+		            Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge);
+		        	flow_mm3_per_mm = new_flow.mm3_per_mm();
+		        	flow_width      = new_flow.width;
+		        }
+		        // Save into layer.
+		        auto *eec = new ExtrusionEntityCollection();
+		        m_regions[surface_fill.region_id]->fills.entities.push_back(eec);
+		        // Only concentric fills are not sorted.
+		        eec->no_sort = f->no_sort();
+		        extrusion_entities_append_paths(
+		            eec->entities, std::move(polylines),
+		            surface_fill.params.extrusion_role,
+		            flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height);
+		    }
+		}
     }
 
     // add thin fill regions
-    // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
     // Unpacks the collection, creates multiple collections per path.
     // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
     // Why the paths are unpacked?
-    for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) {
-        ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
-        out.entities.push_back(&collection);
-        collection.entities.push_back(thin_fill->clone());
-    }
+	for (LayerRegion *layerm : m_regions)
+	    for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) {
+	        ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
+	        layerm->fills.entities.push_back(&collection);
+	        collection.entities.push_back(thin_fill->clone());
+	    }
+
+#ifndef NDEBUG
+	for (LayerRegion *layerm : m_regions)
+	    for (size_t i = 0; i < layerm->fills.entities.size(); ++ i)
+    	    assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != nullptr);
+#endif
 }
 
 } // namespace Slic3r

+ 19 - 1
src/libslic3r/Fill/FillBase.cpp

@@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
     case ipArchimedeanChords:   return new FillArchimedeanChords();
     case ipHilbertCurve:        return new FillHilbertCurve();
     case ipOctagramSpiral:      return new FillOctagramSpiral();
-    default: throw std::invalid_argument("unknown type");;
+    default: throw std::invalid_argument("unknown type");
     }
 }
 
@@ -45,6 +45,24 @@ Fill* Fill::new_from_type(const std::string &type)
     return (it == enum_keys_map.end()) ? nullptr : new_from_type(InfillPattern(it->second));
 }
 
+// Force initialization of the Fill::use_bridge_flow() internal static map in a thread safe fashion even on compilers
+// not supporting thread safe non-static data member initializers.
+static bool use_bridge_flow_initializer = Fill::use_bridge_flow(ipGrid);
+
+bool Fill::use_bridge_flow(const InfillPattern type)
+{
+	static std::vector<unsigned char> cached;
+	if (cached.empty()) {
+		cached.assign(size_t(ipCount), 0);
+		for (size_t i = 0; i < cached.size(); ++ i) {
+			auto *fill = Fill::new_from_type((InfillPattern)i);
+			cached[i] = fill->use_bridge_flow();
+			delete fill;
+		}
+	}
+	return cached[type] != 0;
+}
+
 Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
 {
     // Perform offset.

+ 1 - 0
src/libslic3r/Fill/FillBase.hpp

@@ -70,6 +70,7 @@ public:
 
     static Fill* new_from_type(const InfillPattern type);
     static Fill* new_from_type(const std::string &type);
+    static bool  use_bridge_flow(const InfillPattern type);
 
     void         set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
 

+ 2 - 0
src/libslic3r/Flow.hpp

@@ -56,6 +56,8 @@ public:
     // 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())); }
+
+    bool operator==(const Flow &rhs) const { return this->width == rhs.width && this->height == rhs.height && this->nozzle_diameter == rhs.nozzle_diameter && this->bridge == rhs.bridge; }
     
     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.

+ 0 - 15
src/libslic3r/Layer.cpp

@@ -171,21 +171,6 @@ void Layer::make_perimeters()
     BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done";
 }
 
-void Layer::make_fills()
-{
-    #ifdef SLIC3R_DEBUG
-    printf("Making fills for layer " PRINTF_ZU "\n", this->id());
-    #endif
-    for (LayerRegion *layerm : m_regions) {
-        layerm->fills.clear();
-        make_fill(*layerm, layerm->fills);
-#ifndef NDEBUG
-        for (size_t i = 0; i < layerm->fills.entities.size(); ++ i)
-            assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL);
-#endif
-    }
-}
-
 void Layer::export_region_slices_to_svg(const char *path) const
 {
     BoundingBox bbox;

+ 1 - 1
src/libslic3r/PerimeterGenerator.hpp

@@ -74,7 +74,7 @@ public:
             config(config), object_config(object_config), print_config(print_config),
             loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
             _ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1)
-        {};
+        {}
     void process();
 
 private:

+ 2 - 0
src/libslic3r/Print.hpp

@@ -39,6 +39,8 @@ class PrintRegion
 public:
     const Print*                print() const { return m_print; }
     const PrintRegionConfig&    config() const { return m_config; }
+	// 1-based extruder identifier for this region and role.
+	unsigned int 				extruder(FlowRole role) const;
     Flow                        flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
     // Average diameter of nozzles participating on extruding this region.
     coordf_t                    nozzle_dmr_avg(const PrintConfig &print_config) const;

+ 1 - 1
src/libslic3r/PrintConfig.hpp

@@ -35,7 +35,7 @@ enum PrintHostType {
 
 enum InfillPattern {
     ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
-    ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
+    ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
 };
 
 enum SupportMaterialPattern {

+ 20 - 16
src/libslic3r/PrintRegion.cpp

@@ -2,6 +2,21 @@
 
 namespace Slic3r {
 
+// 1-based extruder identifier for this region and role.
+unsigned int PrintRegion::extruder(FlowRole role) const
+{
+    size_t extruder = 0;
+    if (role == frPerimeter || role == frExternalPerimeter)
+        extruder = m_config.perimeter_extruder;
+    else if (role == frInfill)
+        extruder = m_config.infill_extruder;
+    else if (role == frSolidInfill || role == frTopSolidInfill)
+        extruder = m_config.solid_infill_extruder;
+    else
+        throw std::invalid_argument("Unknown role");
+    return extruder;
+}
+
 Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
 {
     ConfigOptionFloatOrPercent config_width;
@@ -28,24 +43,13 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
             throw std::invalid_argument("Unknown role");
         }
     }
-    if (config_width.value == 0) {
+
+    if (config_width.value == 0)
         config_width = object.config().extrusion_width;
-    }
-    
-    // get the configured nozzle_diameter for the extruder associated
-    // to the flow role requested
-    size_t extruder = 0;    // 1-based
-    if (role == frPerimeter || role == frExternalPerimeter) {
-        extruder = m_config.perimeter_extruder;
-    } else if (role == frInfill) {
-        extruder = m_config.infill_extruder;
-    } else if (role == frSolidInfill || role == frTopSolidInfill) {
-        extruder = m_config.solid_infill_extruder;
-    } else {
-        throw std::invalid_argument("Unknown role");
-    }
-    double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1);
     
+    // 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.
+    double nozzle_diameter = m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1);
     return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f);
 }
 

+ 2 - 3
src/libslic3r/Surface.hpp

@@ -24,9 +24,8 @@ enum SurfaceType {
     stInternalVoid,
     // Inner/outer perimeters.
     stPerimeter,
-    // Last surface type, if the SurfaceType is used as an index into a vector.
-    stLast,
-    stCount = stLast + 1
+    // Number of SurfaceType enums.
+    stCount,
 };
 
 class Surface

Some files were not shown because too many files changed in this diff