diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 129a9440c..579259a5f 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -28,6 +28,8 @@ struct SurfaceFillParams // coordf_t overlap = 0.; // Angle as provided by the region config, in radians. float angle = 0.f; + // Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set. + bool bridge; // Non-negative for a bridge. float bridge_angle = 0.f; @@ -73,18 +75,19 @@ struct SurfaceFillParams 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, 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->bridge == rhs.bridge && +// this->bridge_angle == rhs.bridge_angle && this->density == rhs.density && // this->dont_adjust == rhs.dont_adjust && this->anchor_length == rhs.anchor_length && @@ -128,6 +131,7 @@ std::vector group_fills(const Layer &layer) if (surface.is_solid()) { params.density = 100.f; + //FIXME for non-thick bridges, shall we allow a bottom surface pattern? params.pattern = (surface.is_external() && ! is_bridge) ? (surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) : region_config.top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear; @@ -144,9 +148,10 @@ std::vector group_fills(const Layer &layer) params.angle = float(Geometry::deg2rad(region_config.fill_angle.value)); // Calculate the actual flow we'll be using for this infill. - params.flow = is_bridge || Fill::use_bridge_flow(params.pattern) ? + params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern); + params.flow = params.bridge ? layerm.bridging_flow(extrusion_role) : - layerm.region()->flow(*layer.object(), extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness, layer.id() == 0); + layerm.flow(extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness); // Calculate flow spacing for infill pattern generation. if (surface.is_solid() || is_bridge) { @@ -159,7 +164,7 @@ std::vector group_fills(const Layer &layer) // 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(*layer.object(), frInfill, layer.object()->config().layer_height).spacing(); + params.spacing = layerm.flow(frInfill, layer.object()->config().layer_height).spacing(); // Anchor a sparse infill to inner perimeters with the following anchor length: params.anchor_length = float(region_config.infill_anchor); if (region_config.infill_anchor.percent) @@ -278,7 +283,7 @@ std::vector group_fills(const Layer &layer) 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(*layer.object(), frSolidInfill, layer.height, layer.id() == 0); + params.flow = layerm.flow(frSolidInfill); params.spacing = params.flow.spacing(); surface_fills.emplace_back(params); surface_fills.back().surface.surface_type = stInternalSolid; @@ -346,9 +351,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; // calculate flow spacing for infill pattern generation - bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge(); + bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; double link_max_length = 0.; - if (! surface_fill.params.flow.bridge()) { + if (! surface_fill.params.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); @@ -389,9 +394,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // so we can safely ignore the slight variation that might have // been applied to f->spacing } else { - Flow new_flow = surface_fill.params.flow.bridge() ? - Flow::bridging_flow_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter()) : - Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter(), surface_fill.params.flow.height()); + Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing)); flow_mm3_per_mm = new_flow.mm3_per_mm(); flow_width = new_flow.width(); } @@ -601,9 +604,9 @@ void Layer::make_ironing() fill.spacing = ironing_params.line_spacing; fill.angle = float(ironing_params.angle + 0.25 * M_PI); fill.link_max_length = (coord_t)scale_(3. * fill.spacing); - double height = ironing_params.height * fill.spacing / nozzle_dmr; - Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height)); - double flow_mm3_per_mm = flow.mm3_per_mm(); + double extrusion_height = ironing_params.height * fill.spacing / nozzle_dmr; + float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height)); + double flow_mm3_per_mm = nozzle_dmr * extrusion_height; Surface surface_fill(stTop, ExPolygon()); for (ExPolygon &expoly : ironing_areas) { surface_fill.expolygon = std::move(expoly); @@ -621,7 +624,7 @@ void Layer::make_ironing() extrusion_entities_append_paths( eec->entities, std::move(polylines), erIroning, - flow_mm3_per_mm, float(flow.width()), float(height)); + flow_mm3_per_mm, extrusion_width, float(extrusion_height)); } } } diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 103123cdb..6e54a517c 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -6,12 +6,6 @@ #include -// Overlap factor of perimeter lines. Currently no overlap. -// #define HAS_PERIMETER_LINE_OVERLAP -#ifdef HAS_PERIMETER_LINE_OVERLAP - #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 -#endif - // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) @@ -142,62 +136,82 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent w = float(width.get_abs_value(height)); } - return Flow(w, height, nozzle_diameter, false); + return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height), nozzle_diameter, false); } -// This constructor builds a Flow object from a given centerline spacing. -Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height) +// Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions. +Flow Flow::with_spacing(float new_spacing) const { - if (height <= 0) - throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()"); - // Calculate width from spacing. - // For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions. - float width = float( -#ifdef HAS_PERIMETER_LINE_OVERLAP - (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1. - 0.25 * PI)); -#else - (spacing + height * (1. - 0.25 * PI))); -#endif - return Flow(width, height, nozzle_diameter); + Flow out = *this; + if (m_bridge) { + // Diameter of the rounded extrusion. + assert(m_width == m_height); + float gap = m_spacing - m_width; + auto new_diameter = new_spacing - gap; + out.m_width = out.m_height = new_diameter; + } else { + out.m_width += new_spacing - m_spacing; + if (out.m_width < out.m_height) + throw Slic3r::InvalidArgument("Invalid spacing supplied to Flow::with_spacing()"); + } + out.m_spacing = new_spacing; + return out; } -// This method returns the centerline spacing between two adjacent extrusions -// having the same extrusion width (and other properties). -float Flow::spacing() const +// Adjust the width / height of a rounded extrusion model to reach the prescribed cross section area while maintaining extrusion spacing. +Flow Flow::with_cross_section(float area_new) const { -#ifdef HAS_PERIMETER_LINE_OVERLAP - if (m_bridge) - return m_width + BRIDGE_EXTRA_SPACING; - // rectangle with semicircles at the ends - float min_flow_spacing = m_width - m_height * (1. - 0.25 * PI); - float res = m_width - PERIMETER_LINE_OVERLAP_FACTOR * (m_width - min_flow_spacing); -#else - float res = float(m_bridge ? (m_width + BRIDGE_EXTRA_SPACING) : (m_width - m_height * (1. - 0.25 * PI))); -#endif -// assert(res > 0.f); - if (res <= 0.f) - throw FlowErrorNegativeSpacing(); - return res; + assert(! m_bridge); + assert(flow.width() >= flow.height()); + + // Adjust for bridge_flow_ratio, maintain the extrusion spacing. + float area = this->mm3_per_mm(); + if (area_new > area + EPSILON) { + // Increasing the flow rate. + float new_full_spacing = area_new / m_height; + if (new_full_spacing > m_spacing) { + // Filling up the spacing without an air gap. Grow the extrusion in height. + float height = area_new / m_spacing; + return Flow(rounded_rectangle_extrusion_width_from_spacing(m_spacing, height), height, m_spacing, m_nozzle_diameter, false); + } else { + return this->with_width(rounded_rectangle_extrusion_width_from_spacing(area / m_height, m_height)); + } + } else if (area_new < area - EPSILON) { + // Decreasing the flow rate. + float width_new = m_width - (area - area_new) / m_height; + assert(width_dif > 0); + if (width_new > m_height) { + // Shrink the extrusion width. + return this->with_width(width_new); + } else { + // Create a rounded extrusion. + auto dmr = float(sqrt(area_new / M_PI)); + return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false); + } + } else + return *this; } -// This method returns the centerline spacing between an extrusion using this -// flow and another one using another flow. -// this->spacing(other) shall return the same value as other.spacing(*this) -float Flow::spacing(const Flow &other) const +float Flow::rounded_rectangle_extrusion_spacing(float width, float height) { - assert(m_height == other.m_height); - assert(m_bridge == other.m_bridge); - float res = float(m_bridge ? - 0.5 * m_width + 0.5 * other.m_width + BRIDGE_EXTRA_SPACING : - 0.5 * this->spacing() + 0.5 * other.spacing()); -// assert(res > 0.f); - if (res <= 0.f) - throw FlowErrorNegativeSpacing(); - return res; + auto out = width - height * float(1. - 0.25 * PI); + if (out <= 0.f) + throw FlowErrorNegativeSpacing(); + return out; +} + +float Flow::rounded_rectangle_extrusion_width_from_spacing(float spacing, float height) +{ + return float(spacing + height * (1. - 0.25 * PI)); +} + +float Flow::bridge_extrusion_spacing(float dmr) +{ + return dmr + BRIDGE_EXTRA_SPACING; } // This method returns extrusion volume per head move unit. -double Flow::mm3_per_mm() const +double Flow::mm3_per_mm() const { float res = m_bridge ? // Area of a circle with dmr of this->width. diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 101dc6880..937fb176a 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -51,26 +51,26 @@ public: class Flow { public: + Flow() = default; + Flow(float width, float height, float nozzle_diameter) : + Flow(width, height, rounded_rectangle_extrusion_spacing(width, height), nozzle_diameter, false) {} + // Non bridging flow: Maximum width of an extrusion with semicircles at the ends. // Bridging flow: Bridge thread diameter. - float width() const { return m_width; } + float width() const { return m_width; } + coord_t scaled_width() const { return coord_t(scale_(m_width)); } // Non bridging flow: Layer height. // Bridging flow: Bridge thread diameter = layer height. - float height() const { return m_height; } + float height() const { return m_height; } + // Spacing between the extrusion centerlines. + float spacing() const { return m_spacing; } + coord_t scaled_spacing() const { return coord_t(scale_(m_spacing)); } // Nozzle diameter. - float nozzle_diameter() const { return m_nozzle_diameter; } + float nozzle_diameter() const { return m_nozzle_diameter; } // Is it a bridge? - bool bridge() const { return m_bridge; } - - Flow() = default; - Flow(float w, float h, float nozzle_diameter) : Flow(w, h, nozzle_diameter, false) {} - - float spacing() const; - float spacing(const Flow &other) const; - double mm3_per_mm() const; - coord_t scaled_width() const { return coord_t(scale_(m_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))); } + bool bridge() const { return m_bridge; } + // Cross section area of the extrusion. + double mm3_per_mm() const; // 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. @@ -80,18 +80,30 @@ public: bool operator==(const Flow &rhs) const { return m_width == rhs.m_width && m_height == rhs.m_height && m_nozzle_diameter == rhs.m_nozzle_diameter && m_bridge == rhs.m_bridge; } - Flow with_width (float width) const { assert(! m_bridge); return Flow(width, m_height, m_nozzle_diameter, m_bridge); } - Flow with_height(float height) const { assert(! m_bridge); return Flow(m_width, height, m_nozzle_diameter, m_bridge); } + Flow with_width (float width) const { + assert(! m_bridge); + return Flow(width, m_height, rounded_rectangle_extrusion_spacing(width, m_height), m_nozzle_diameter, m_bridge); + } + Flow with_height(float height) const { + assert(! m_bridge); + return Flow(m_width, height, rounded_rectangle_extrusion_spacing(m_width, height), m_nozzle_diameter, m_bridge); + } + // Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions. + Flow with_spacing(float spacing) const; + // Adjust the width / height of a rounded extrusion model to reach the prescribed cross section area while maintaining extrusion spacing. + Flow with_cross_section(float area) const; + Flow with_flow_ratio(double ratio) const { return this->with_cross_section(this->mm3_per_mm() * ratio); } + + static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, bridge_extrusion_spacing(dmr), nozzle_diameter, true }; } - static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, nozzle_diameter, true }; } - static Flow bridging_flow_from_spacing(float spacing, float nozzle_diameter) - { auto dmr = spacing - float(BRIDGE_EXTRA_SPACING); return Flow { dmr, dmr, nozzle_diameter, true }; } - static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height); - // Create a flow from the spacing of extrusion lines. - // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale - // to fit a region with integer number of lines. - static Flow new_from_spacing(float spacing, float nozzle_diameter, float height); + + // Spacing of extrusions with rounded extrusion model. + static float rounded_rectangle_extrusion_spacing(float width, float height); + // Width of extrusions with rounded extrusion model. + static float rounded_rectangle_extrusion_width_from_spacing(float spacing, float height); + // Spacing of round thread extrusions. + static float bridge_extrusion_spacing(float dmr); // Sane extrusion width defautl based on nozzle diameter. // The defaults were derived from manual Prusa MK3 profiles. @@ -104,10 +116,12 @@ public: static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); private: - Flow(float w, float h, float nozzle_diameter, bool bridge) : m_width(w), m_height(h), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) {} + Flow(float width, float height, float spacing, float nozzle_diameter, bool bridge) : + m_width(width), m_height(height), m_spacing(spacing), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) {} float m_width { 0 }; float m_height { 0 }; + float m_spacing { 0 }; float m_nozzle_diameter { 0 }; bool m_bridge { false }; }; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index ef3124dcb..8e2348530 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -60,6 +60,7 @@ public: ExtrusionEntityCollection fills; Flow flow(FlowRole role) const; + Flow flow(FlowRole role, double layer_height) const; Flow bridging_flow(FlowRole role) const; void slices_to_fill_surfaces_clipped(); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 4b8fcaef9..1bca95ca3 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -17,12 +17,29 @@ namespace Slic3r { Flow LayerRegion::flow(FlowRole role) const { - return m_region->flow(*m_layer->object(), role, m_layer->height, m_layer->id() == 0); + 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) const -{ - return this->layer()->object()->config().thick_bridges ? m_region->bridging_flow(role) : this->flow(role); +{ + const PrintRegion ®ion = *this->region(); + const PrintRegionConfig ®ion_config = region.config(); + if (this->layer()->object()->config().thick_bridges) { + // 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. + auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); + // Applies default bridge spacing. + return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter); + } else { + // The same way as other slicers: Use normal extrusions. Apply bridge_flow_ratio while maintaining the original spacing. + return this->flow(role).with_flow_ratio(region_config.bridge_flow_ratio); + } } // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index d33e1cfd4..6ec4dbf6b 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -286,7 +286,7 @@ void PerimeterGenerator::process() m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm(); coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width(); coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing(); - coord_t ext_perimeter_spacing2 = this->ext_perimeter_flow.scaled_spacing(this->perimeter_flow); + coord_t ext_perimeter_spacing2 = scaled(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing())); // overhang perimeters m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 04cf58fa9..91f86d010 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -66,7 +66,6 @@ public: // 1-based extruder identifier for this region and role. unsigned int extruder(FlowRole role) const; Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const; - Flow bridging_flow(FlowRole role) const; // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; // Average diameter of nozzles participating on extruding this region. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 593955576..3fe5f2421 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1457,19 +1457,18 @@ void PrintObject::bridge_over_infill() const PrintRegion ®ion = *m_print->regions()[region_id]; // skip bridging in case there are no voids - if (region.config().fill_density.value == 100) continue; - - // get bridge flow - Flow bridge_flow = region.bridging_flow(frSolidInfill); - + if (region.config().fill_density.value == 100) + continue; + for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { // skip first layer if (layer_it == m_layers.begin()) continue; - Layer* layer = *layer_it; - LayerRegion* layerm = layer->m_regions[region_id]; - + Layer *layer = *layer_it; + LayerRegion *layerm = layer->m_regions[region_id]; + Flow bridge_flow = layerm->bridging_flow(frSolidInfill); + // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index acb429821..837200984 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -48,17 +48,6 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)); } -Flow PrintRegion::bridging_flow(FlowRole role) const -{ - // 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. - auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); - double bfr = m_config.bridge_flow_ratio; - if (bfr != 1.) - bfr = sqrt(bfr); - return Flow::bridging_flow(float(bfr) * nozzle_diameter, nozzle_diameter); -} - coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const { return (print_config.nozzle_diameter.get_at(m_config.perimeter_extruder.value - 1) + diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 7c018c974..b68d1f2c3 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -334,7 +334,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object for (auto lh : m_print_config->min_layer_height.values) m_support_layer_height_min = std::min(m_support_layer_height_min, std::max(0.01, lh)); - if (m_object_config->support_material_interface_layers.value == 0) { + if (m_slicing_params.soluble_interface) { // No interface layers allowed, print everything with the base support pattern. m_support_material_interface_flow = m_support_material_flow; } @@ -342,11 +342,21 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // Evaluate the XY gap between the object outer perimeters and the support structures. // Evaluate the XY gap between the object outer perimeters and the support structures. coordf_t external_perimeter_width = 0.; + size_t num_nonempty_regions = 0; + coordf_t bridge_flow_ratio = 0; for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) - if (! object->region_volumes[region_id].empty()) - external_perimeter_width = std::max(external_perimeter_width, - (coordf_t)object->print()->get_region(region_id)->flow(*object, frExternalPerimeter, slicing_params.layer_height).width()); + if (! object->region_volumes[region_id].empty()) { + ++ num_nonempty_regions; + const PrintRegion ®ion = *object->print()->get_region(region_id); + external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); + bridge_flow_ratio += region.config().bridge_flow_ratio; + } m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); + bridge_flow_ratio /= num_nonempty_regions; + + m_support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? + m_support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : + Flow::bridging_flow(bridge_flow_ratio * m_support_material_interface_flow.nozzle_diameter(), m_support_material_interface_flow.nozzle_diameter()); m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; if (! m_can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { @@ -1231,8 +1241,8 @@ namespace SupportMaterialInternal { // since we're dealing with bridges, we can't assume width is larger than spacing, // so we take the largest value and also apply safety offset to be ensure no gaps // are left in between - Flow bridge_flow = layerm->bridging_flow(frPerimeter); - float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); + Flow perimeter_bridge_flow = layerm->bridging_flow(frPerimeter); + float w = float(std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { // This is a bridge @@ -1879,13 +1889,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta layer_new.height = m_slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. object.layers()[layer_id + 1]->height : - // Place a bridge flow interface layer over the top surface. - //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?) - // According to Jindrich the bottom surfaces work well. - //FIXME test the bridging flow instead? - m_object_config->thick_bridges.value ? m_support_material_interface_flow.nozzle_diameter() : - // Take the default layer height. - m_object_config->layer_height; + // Place a bridge flow interface layer or the normal flow interface layer over the top surface. + m_support_material_bottom_interface_flow.height(); layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; layer_new.bottom_z = layer.print_z; @@ -3497,7 +3502,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing); coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); - if (m_object_config->support_material_interface_layers.value == 0) { + if (m_slicing_params.soluble_interface) { // No interface layers allowed, print everything with the base support pattern. interface_spacing = support_spacing; interface_density = support_density; @@ -3738,13 +3743,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) continue; - bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; + bool interface_as_base = (&layer_ex == &interface_layer) && m_slicing_params.soluble_interface; //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) auto interface_flow = layer_ex.layer->bridging ? - Flow::bridging_flow(layer_ex.layer->height, m_support_material_interface_flow.nozzle_diameter()) : - Flow(interface_as_base ? m_support_material_flow.width() : m_support_material_interface_flow.width(), - float(layer_ex.layer->height), m_support_material_interface_flow.nozzle_diameter()); + Flow::bridging_flow(layer_ex.layer->height, m_support_material_bottom_interface_flow.nozzle_diameter()) : + (interface_as_base ? &m_support_material_flow : &m_support_material_interface_flow)->with_height(float(layer_ex.layer->height)); filler_interface->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 7123290a6..da13768f6 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -244,6 +244,7 @@ private: Flow m_first_layer_flow; Flow m_support_material_flow; Flow m_support_material_interface_flow; + Flow m_support_material_bottom_interface_flow; // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? bool m_can_merge_support_regions; diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index 6962085d5..019af16f2 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -16,8 +16,6 @@ float nozzle_diameter(); bool bridge(); float spacing(); - float spacing_to(Flow* other) - %code{% RETVAL = THIS->spacing(*other); %}; int scaled_width(); int scaled_spacing(); double mm3_per_mm();