From ceea9de8b8b1d4b35bdd1180051033680c405ba1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 Mar 2021 13:44:00 +0100 Subject: [PATCH] WIP: Refactored bridging flow from normal flow, new config value 'thick_bridges' to switch between the Slic3r vs. S3D/Cura/Ideamaker way of printing 1st object layer over supports. Simplified the PresetHints. --- src/libslic3r/Brim.cpp | 8 +- src/libslic3r/ElephantFootCompensation.cpp | 2 +- src/libslic3r/ExtrusionEntity.cpp | 4 +- src/libslic3r/Fill/Fill.cpp | 67 ++++++---------- src/libslic3r/Flow.cpp | 68 +++++++--------- src/libslic3r/Flow.hpp | 40 ++++++---- src/libslic3r/GCode.cpp | 19 +++-- src/libslic3r/Layer.hpp | 4 +- src/libslic3r/LayerRegion.cpp | 24 +++--- src/libslic3r/PerimeterGenerator.cpp | 28 +++---- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 10 +-- src/libslic3r/Print.hpp | 3 +- src/libslic3r/PrintConfig.cpp | 7 ++ src/libslic3r/PrintConfig.hpp | 2 + src/libslic3r/PrintObject.cpp | 12 +-- src/libslic3r/PrintRegion.cpp | 53 ++++++------ src/libslic3r/SupportMaterial.cpp | 62 +++++++-------- src/slic3r/GUI/PresetHints.cpp | 93 ++++++---------------- src/slic3r/GUI/Tab.cpp | 1 + tests/fff_print/test_flow.cpp | 2 +- 21 files changed, 228 insertions(+), 283 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index d5ec0d928..08bedc5c0 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -320,7 +320,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_ loops = union_pt_chained_outside_in(loops, false); std::reverse(loops.begin(), loops.end()); extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), - float(flow.width), float(print.skirt_first_layer_height())); + float(flow.width()), float(print.skirt_first_layer_height())); } // Produce brim lines around those objects, that have the brim enabled. @@ -495,7 +495,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) { auto *loop = new ExtrusionLoop(); brim.entities.emplace_back(loop); - loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())); + loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); Points &points = loop->paths.front().polyline.points; points.reserve(first_path.size()); for (const ClipperLib_Z::IntPoint &pt : first_path) @@ -506,7 +506,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance ExtrusionEntityCollection this_loop_trimmed; this_loop_trimmed.entities.reserve(j - i); for (; i < j; ++ i) { - this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()))); + this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()))); const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; Points &points = static_cast(this_loop_trimmed.entities.back())->polyline.points; points.reserve(path.size()); @@ -522,7 +522,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance } } } else { - extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); } make_inner_brim(print, top_level_objects_with_brim, brim); diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index f28d88f7e..0895e16d6 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -621,7 +621,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c ExPolygon elephant_foot_compensation(const ExPolygon &input, const Flow &external_perimeter_flow, const double compensation) { // The contour shall be wide enough to apply the external perimeter plus compensation on both sides. - double min_contour_width = double(external_perimeter_flow.width + external_perimeter_flow.spacing()); + double min_contour_width = double(external_perimeter_flow.width() + external_perimeter_flow.spacing()); return elephant_foot_compensation(input, min_contour_width, compensation); } diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 390d107f2..3284bc39e 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -52,7 +52,9 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale { // Instantiating the Flow class to get the line spacing. // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. - Flow flow(this->width, this->height, 0.f, is_bridge(this->role())); + bool bridge = is_bridge(this->role()); + assert(! bridge || this->width == this->height); + auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f); polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index ee493ca9c..129a9440c 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -42,7 +42,7 @@ struct SurfaceFillParams // width, height of extrusion, nozzle diameter, is bridge // For the output, for fill generator. - Flow flow = Flow(0.f, 0.f, 0.f, false); + Flow flow; // For the output ExtrusionRole extrusion_role = ExtrusionRole(0); @@ -70,10 +70,10 @@ struct SurfaceFillParams // RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); RETURN_COMPARE_NON_EQUAL(anchor_length); RETURN_COMPARE_NON_EQUAL(anchor_length_max); - 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(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; } @@ -143,17 +143,12 @@ std::vector group_fills(const Layer &layer) params.bridge_angle = float(surface.bridge_angle); params.angle = float(Geometry::deg2rad(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) ? layer.height : surface.thickness, // extrusion height - is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow? - layer.id() == 0, // first layer? - -1, // auto width - *layer.object() - ); - - // Calculate flow spacing for infill pattern generation. + // Calculate the actual flow we'll be using for this infill. + params.flow = is_bridge || Fill::use_bridge_flow(params.pattern) ? + layerm.bridging_flow(extrusion_role) : + layerm.region()->flow(*layer.object(), extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness, layer.id() == 0); + + // Calculate flow spacing for infill pattern generation. if (surface.is_solid() || is_bridge) { params.spacing = params.flow.spacing(); // Don't limit anchor length for solid or bridging infill. @@ -164,14 +159,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( - frInfill, - layer.object()->config().layer_height.value, // TODO: handle infill_every_layers? - false, // no bridge - false, // no first layer - -1, // auto width - *layer.object() - ).spacing(); + params.spacing = layerm.region()->flow(*layer.object(), 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 +266,7 @@ std::vector group_fills(const Layer &layer) 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(layer.height - surface_fill.params.flow.height) < EPSILON) { + if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height()) < EPSILON) { internal_solid_fill = &surface_fill; break; } @@ -290,14 +278,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( - frSolidInfill, - layer.height, // extrusion height - false, // bridge flow? - layer.id() == 0, // first layer? - -1, // auto width - *layer.object() - ); + params.flow = layerm.region()->flow(*layer.object(), frSolidInfill, layer.height, layer.id() == 0); params.spacing = params.flow.spacing(); surface_fills.emplace_back(params); surface_fills.back().surface.surface_type = stInternalSolid; @@ -365,9 +346,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.flow.bridge(); double link_max_length = 0.; - if (! surface_fill.params.flow.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); @@ -380,7 +361,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // 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_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); + 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; @@ -402,15 +383,17 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // 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; + 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 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_mm3_per_mm = new_flow.mm3_per_mm(); - flow_width = new_flow.width; + flow_width = new_flow.width(); } // Save into layer. ExtrusionEntityCollection* eec = nullptr; @@ -420,7 +403,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: 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); + flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height()); } } } @@ -619,7 +602,7 @@ void Layer::make_ironing() 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), false); + Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height)); double flow_mm3_per_mm = flow.mm3_per_mm(); Surface surface_fill(stTop, ExPolygon()); for (ExPolygon &expoly : ironing_areas) { @@ -638,7 +621,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, float(flow.width()), float(height)); } } } diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index e5dcf0731..103123cdb 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -6,6 +6,12 @@ #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) @@ -122,20 +128,13 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResol // This constructor builds a Flow object from an extrusion width config setting // and other context properties. -Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) +Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height) { - // we need layer height unless it's a bridge - if (height <= 0 && bridge_flow_ratio == 0) + if (height <= 0) throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()"); float w; - if (bridge_flow_ratio > 0) { - // If bridge flow was requested, calculate the bridge width. - height = w = (bridge_flow_ratio == 1.) ? - // optimization to avoid sqrt() - nozzle_diameter : - sqrt(bridge_flow_ratio) * nozzle_diameter; - } else if (! width.percent && width.value == 0.) { + if (! width.percent && width.value == 0.) { // If user left option to 0, calculate a sane default width. w = auto_extrusion_width(role, nozzle_diameter); } else { @@ -143,26 +142,23 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent w = float(width.get_abs_value(height)); } - return Flow(w, height, nozzle_diameter, bridge_flow_ratio > 0); + return Flow(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, bool bridge) +Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height) { - // we need layer height unless it's a bridge - if (height <= 0 && !bridge) + 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. - // For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads. - float width = float(bridge ? - (spacing - BRIDGE_EXTRA_SPACING) : + 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, bridge ? width : height, nozzle_diameter, bridge); + return Flow(width, height, nozzle_diameter); } // This method returns the centerline spacing between two adjacent extrusions @@ -170,13 +166,13 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, float Flow::spacing() const { #ifdef HAS_PERIMETER_LINE_OVERLAP - if (this->bridge) - return this->width + BRIDGE_EXTRA_SPACING; + if (m_bridge) + return m_width + BRIDGE_EXTRA_SPACING; // rectangle with semicircles at the ends - float min_flow_spacing = this->width - this->height * (1. - 0.25 * PI); - float res = this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing); + 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(this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1. - 0.25 * PI))); + 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) @@ -189,10 +185,10 @@ float Flow::spacing() const // this->spacing(other) shall return the same value as other.spacing(*this) float Flow::spacing(const Flow &other) const { - assert(this->height == other.height); - assert(this->bridge == other.bridge); - float res = float(this->bridge ? - 0.5 * this->width + 0.5 * other.width + BRIDGE_EXTRA_SPACING : + 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) @@ -203,11 +199,11 @@ float Flow::spacing(const Flow &other) const // This method returns extrusion volume per head move unit. double Flow::mm3_per_mm() const { - float res = this->bridge ? + float res = m_bridge ? // Area of a circle with dmr of this->width. - float((this->width * this->width) * 0.25 * PI) : + float((m_width * m_width) * 0.25 * PI) : // Rectangle with semicircles at the ends. ~ h (w - 0.215 h) - float(this->height * (this->width - this->height * (1. - 0.25 * PI))); + float(m_height * (m_width - m_height * (1. - 0.25 * PI))); //assert(res > 0.); if (res <= 0.) throw FlowErrorNegativeFlow(); @@ -222,9 +218,7 @@ Flow support_material_flow(const PrintObject *object, float layer_height) (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), - // bridge_flow_ratio - 0.f); + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) @@ -235,9 +229,7 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (width.value > 0) ? width : object->config().extrusion_width, float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)), - // bridge_flow_ratio - 0.f); + (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value))); } Flow support_material_interface_flow(const PrintObject *object, float layer_height) @@ -248,9 +240,7 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig (object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), - // bridge_flow_ratio - 0.f); + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } } diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 9e57ce907..101dc6880 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -13,11 +13,6 @@ class PrintObject; // Extra spacing of bridge threads, in mm. #define BRIDGE_EXTRA_SPACING 0.05 -// Overlap factor of perimeter lines. Currently no overlap. -#ifdef HAS_PERIMETER_LINE_OVERLAP - #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 -#endif - enum FlowRole { frExternalPerimeter, frPerimeter, @@ -58,22 +53,22 @@ class Flow public: // Non bridging flow: Maximum width of an extrusion with semicircles at the ends. // Bridging flow: Bridge thread diameter. - float width; + float width() const { return m_width; } // Non bridging flow: Layer height. // Bridging flow: Bridge thread diameter = layer height. - float height; + float height() const { return m_height; } // Nozzle diameter. - float nozzle_diameter; + float nozzle_diameter() const { return m_nozzle_diameter; } // Is it a bridge? - bool bridge; + bool bridge() const { return m_bridge; } - Flow(float _w, float _h, float _nd, bool _bridge = false) : - width(_w), height(_h), nozzle_diameter(_nd), bridge(_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_(this->width)); } + 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))); } @@ -83,13 +78,20 @@ public: // 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; } + 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); } + + 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, float bridge_flow_ratio); + 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, bool bridge); + static Flow new_from_spacing(float spacing, float nozzle_diameter, float height); // Sane extrusion width defautl based on nozzle diameter. // The defaults were derived from manual Prusa MK3 profiles. @@ -100,6 +102,14 @@ public: // on active extruder etc. Therefore the value calculated by this function shall be used as a hint only. static double extrusion_width(const std::string &opt_key, const ConfigOptionFloatOrPercent *opt, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); 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) {} + + float m_width { 0 }; + float m_height { 0 }; + float m_nozzle_diameter { 0 }; + bool m_bridge { false }; }; extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 512035798..fd6c82b84 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1105,15 +1105,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu const double layer_height = first_object->config().layer_height.value; const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height); for (const PrintRegion* region : print.regions()) { - _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); + _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width()); + _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width()); + _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width()); + _write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width()); + _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width()); if (print.has_support_material()) - _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); + _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); if (print.config().first_layer_extrusion_width.value > 0) - _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); + _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width()); _write_format(file, "\n"); } print.throw_if_canceled(); @@ -2170,14 +2170,13 @@ void GCode::process_layer( const std::pair loops = loops_it->second; this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); - Flow layer_skirt_flow(print.skirt_flow()); - layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])); + Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2]))); double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); for (ExtrusionPath &path : loop.paths) { - path.height = layer_skirt_flow.height; + path.height = layer_skirt_flow.height(); path.mm3_per_mm = mm3_per_mm; } //FIXME using the support_material_speed of the 1st object printed. diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 9a3fe368d..ef3124dcb 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -59,7 +59,9 @@ public: // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection fills; - Flow flow(FlowRole role, bool bridge = false, double width = -1) const; + Flow flow(FlowRole role) const; + Flow bridging_flow(FlowRole role) const; + void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 1a0bd341c..4b8fcaef9 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -15,16 +15,14 @@ namespace Slic3r { -Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const +Flow LayerRegion::flow(FlowRole role) const { - return m_region->flow( - role, - m_layer->height, - bridge, - m_layer->id() == 0, - width, - *m_layer->object() - ); + return m_region->flow(*m_layer->object(), role, m_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); } // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. @@ -84,7 +82,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec g.layer_id = (int)this->layer()->id(); g.ext_perimeter_flow = this->flow(frExternalPerimeter); - g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object()); + g.overhang_flow = this->bridging_flow(frPerimeter); g.solid_infill_flow = this->flow(frSolidInfill); g.process(); @@ -266,11 +264,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly // 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 - BridgeDetector bd( - initial, - lower_layer->lslices, - this->flow(frInfill, true).scaled_width() - ); + BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill).scaled_width()); #ifdef SLIC3R_DEBUG printf("Processing bridge at layer %zu:\n", this->layer()->id()); #endif diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7cfdc5847..d33e1cfd4 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -8,7 +8,7 @@ namespace Slic3r { -static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance) +static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance) { ExtrusionPaths paths; ExtrusionPath path(role); @@ -62,15 +62,15 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi path.polyline.append(line.b); // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. - flow.width = unscale(w) + flow.height * float(1. - 0.25 * PI); + Flow new_flow = flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); #ifdef SLIC3R_DEBUG printf(" filling %f gap\n", flow.width); #endif - path.mm3_per_mm = flow.mm3_per_mm(); - path.width = flow.width; - path.height = flow.height; + path.mm3_per_mm = new_flow.mm3_per_mm(); + path.width = new_flow.width(); + path.height = new_flow.height(); } else { - thickness_delta = fabs(scale_(flow.width) - w); + thickness_delta = fabs(scale_(flow.width()) - w); if (thickness_delta <= tolerance) { // the width difference between this line and the current flow width is // within the accepted tolerance @@ -88,7 +88,7 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi return paths; } -static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow, std::vector &out) +static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const Flow &flow, std::vector &out) { // This value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount @@ -205,8 +205,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime paths, intersection_pl({ polygon }, perimeter_generator.lower_slices_polygons()), role, - is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(), - is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width, + is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(), + is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width(), (float)perimeter_generator.layer_height); // get overhang paths by checking what parts of this loop fall @@ -217,8 +217,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime diff_pl({ polygon }, perimeter_generator.lower_slices_polygons()), erOverhangPerimeter, perimeter_generator.mm3_per_mm_overhang(), - perimeter_generator.overhang_flow.width, - perimeter_generator.overhang_flow.height); + perimeter_generator.overhang_flow.width(), + perimeter_generator.overhang_flow.height()); // Reapply the nearest point search for starting point. // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. @@ -226,8 +226,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } else { ExtrusionPath path(role); path.polyline = polygon.split_at_first_point(); - path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(); - path.width = is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width; + path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(); + path.width = is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width(); path.height = (float)perimeter_generator.layer_height; paths.push_back(path); } @@ -346,7 +346,7 @@ void PerimeterGenerator::process() if (this->config->thin_walls) { // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // (actually, something larger than that still may exist due to mitering or other causes) - coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter / 3)); + coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3)); ExPolygons expp = offset2_ex( // medial axis requires non-overlapping geometry diff_ex(to_polygons(last), diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 67b1ebd4f..81a14eaec 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -430,7 +430,7 @@ const std::vector& Preset::print_options() "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c5babb248..79c4e72a7 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1577,9 +1577,7 @@ Flow Print::brim_flow() const frPerimeter, width, (float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), - (float)this->skirt_first_layer_height(), - 0 - ); + (float)this->skirt_first_layer_height()); } Flow Print::skirt_flow() const @@ -1599,9 +1597,7 @@ Flow Print::skirt_flow() const frPerimeter, width, (float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1), - (float)this->skirt_first_layer_height(), - 0 - ); + (float)this->skirt_first_layer_height()); } bool Print::has_support_material() const @@ -1818,7 +1814,7 @@ void Print::_make_skirt() ExtrusionPath( erSkirt, (float)mm3_per_mm, // this will be overridden at G-code export time - flow.width, + flow.width(), (float)first_layer_height // this will be overridden at G-code export time ))); eloop.paths.back().polyline = loop.split_at_first_point(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7311bdc79..04cf58fa9 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -65,7 +65,8 @@ public: 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; + 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/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e534f140a..b87042b89 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2415,6 +2415,13 @@ void PrintConfigDef::init_fff_params() def->max = max_temp; def->set_default_value(new ConfigOptionInts { 200 }); + def = this->add("thick_bridges", coBool); + def->label = L("Thick bridges"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Print bridges with round extrusions."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("thin_walls", coBool); def->label = L("Detect thin walls"); def->category = L("Layers and Perimeters"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 76085c941..c92abf51f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -522,6 +522,7 @@ public: ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; ConfigOptionFloatOrPercent support_material_xy_spacing; + ConfigOptionBool thick_bridges; ConfigOptionFloat xy_size_compensation; ConfigOptionBool wipe_into_objects; @@ -569,6 +570,7 @@ protected: OPT_PTR(support_material_xy_spacing); OPT_PTR(support_material_threshold); OPT_PTR(support_material_with_sheath); + OPT_PTR(thick_bridges); OPT_PTR(xy_size_compensation); OPT_PTR(wipe_into_objects); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 52bdc87e7..593955576 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -554,6 +554,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "perimeter_extrusion_width" || opt_key == "infill_overlap" || opt_key == "thin_walls" + || opt_key == "thick_bridges" || opt_key == "external_perimeters_first") { steps.emplace_back(posPerimeters); } else if ( @@ -1459,14 +1460,7 @@ void PrintObject::bridge_over_infill() if (region.config().fill_density.value == 100) continue; // get bridge flow - Flow bridge_flow = region.flow( - frSolidInfill, - -1, // layer height, not relevant for bridge flow - true, // bridge - false, // first layer - -1, // custom width, not relevant for bridge flow - *this - ); + Flow bridge_flow = region.bridging_flow(frSolidInfill); for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { // skip first layer @@ -1488,7 +1482,7 @@ void PrintObject::bridge_over_infill() Polygons to_bridge_pp = internal_solid; // iterate through lower layers spanned by bridge_flow - double bottom_z = layer->print_z - bridge_flow.height; + double bottom_z = layer->print_z - bridge_flow.height(); for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = m_layers[i]; diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 79eb647f6..acb429821 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -18,31 +18,25 @@ unsigned int PrintRegion::extruder(FlowRole role) const return extruder; } -Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const +Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const { ConfigOptionFloatOrPercent config_width; - if (width != -1) { - // use the supplied custom width, if any - config_width.value = width; - config_width.percent = false; + // Get extrusion width from configuration. + // (might be an absolute value, or a percent value, or zero for auto) + if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { + config_width = m_print->config().first_layer_extrusion_width; + } else if (role == frExternalPerimeter) { + config_width = m_config.external_perimeter_extrusion_width; + } else if (role == frPerimeter) { + config_width = m_config.perimeter_extrusion_width; + } else if (role == frInfill) { + config_width = m_config.infill_extrusion_width; + } else if (role == frSolidInfill) { + config_width = m_config.solid_infill_extrusion_width; + } else if (role == frTopSolidInfill) { + config_width = m_config.top_infill_extrusion_width; } else { - // otherwise, get extrusion width from configuration - // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { - config_width = m_print->config().first_layer_extrusion_width; - } else if (role == frExternalPerimeter) { - config_width = m_config.external_perimeter_extrusion_width; - } else if (role == frPerimeter) { - config_width = m_config.perimeter_extrusion_width; - } else if (role == frInfill) { - config_width = m_config.infill_extrusion_width; - } else if (role == frSolidInfill) { - config_width = m_config.solid_infill_extrusion_width; - } else if (role == frTopSolidInfill) { - config_width = m_config.top_infill_extrusion_width; - } else { - throw Slic3r::InvalidArgument("Unknown role"); - } + throw Slic3r::InvalidArgument("Unknown role"); } if (config_width.value == 0) @@ -50,8 +44,19 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir // 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); + auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); + 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 diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 459a15603..7c018c974 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -345,7 +345,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object 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(frExternalPerimeter, slicing_params.layer_height, false, false, -1, *object).width); + (coordf_t)object->print()->get_region(region_id)->flow(*object, frExternalPerimeter, slicing_params.layer_height).width()); m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; @@ -1231,7 +1231,7 @@ 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->flow(frPerimeter, true); + Flow bridge_flow = layerm->bridging_flow(frPerimeter); float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { @@ -1542,16 +1542,20 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } // Offset the contact polygons outside. +#if 0 for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) { diff_polygons = diff( offset( diff_polygons, - SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS, + scaled(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS), ClipperLib::jtRound, // round mitter limit scale_(0.05)), slices_margin_cached); } +#else + diff_polygons = diff(diff_polygons, slices_margin_cached); +#endif } polygons_append(contact_polygons, diff_polygons); } // for each layer.region @@ -1597,7 +1601,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Contact layer will be printed with a normal flow, but // it will support layers printed with a bridging flow. - if (SupportMaterialInternal::has_bridging_extrusions(layer)) { + if (m_object_config->thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) { coordf_t bridging_height = 0.; for (const LayerRegion *region : layer.regions()) bridging_height += region->region()->bridging_height_avg(*m_print_config); @@ -1879,12 +1883,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta //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_support_material_interface_flow.nozzle_diameter; + m_object_config->thick_bridges.value ? m_support_material_interface_flow.nozzle_diameter() : + // Take the default layer height. + m_object_config->layer_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; layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = ! m_slicing_params.soluble_interface; + layer_new.bridging = ! m_slicing_params.soluble_interface && m_object_config->thick_bridges; //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow. //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks. layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); @@ -2512,7 +2518,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( break; polygons_append(polygons_trimming, offset(object_layer.lslices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } - if (! m_slicing_params.soluble_interface) { + if (! m_slicing_params.soluble_interface && m_object_config->thick_bridges) { // Collect all bottom surfaces, which will be extruded with a bridging flow. for (; i < object.layers().size(); ++ i) { const Layer &object_layer = *object.layers()[i]; @@ -2810,7 +2816,7 @@ std::pairentities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); + extrusion_entities_append_paths(eec->entities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); // Fill in the rest. fill_expolygons_generate_paths(eec->entities, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); dst.emplace_back(eec.release()); @@ -3011,8 +3017,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const if (n_contact_loops == 0 || top_contact_layer.empty()) return; - Flow flow = interface_flow_src; - flow.height = float(top_contact_layer.layer->height); + Flow flow = interface_flow_src.with_height(top_contact_layer.layer->height); Polygons overhang_polygons; if (top_contact_layer.layer->overhang_polygons != nullptr) @@ -3209,7 +3214,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const extrusion_entities_append_paths( top_contact_layer.extrusions, std::move(loop_lines), - erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); + erSupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); } #ifdef SLIC3R_DEBUG @@ -3342,7 +3347,7 @@ void modulate_extrusion_by_overlapping_layers( // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). assert(this_layer.print_z > overlapping_layer.print_z); frag.height = float(this_layer.print_z - overlapping_layer.print_z); - frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f, false).mm3_per_mm(); + frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); #ifdef SLIC3R_DEBUG svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); #endif /* SLIC3R_DEBUG */ @@ -3565,7 +3570,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); if (! to_infill_polygons.empty()) { - Flow flow(float(m_support_material_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging); + assert(! raft_layer.bridging); + Flow flow(float(m_support_material_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter()); Fill * filler = filler_support.get(); filler->angle = raft_angle_base; filler->spacing = m_support_material_flow.spacing(); @@ -3596,7 +3602,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. filler->spacing = m_support_material_flow.spacing(); - flow = Flow(float(m_support_material_interface_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging); + assert(! raft_layer.bridging); + flow = Flow(float(m_support_material_interface_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter()); density = float(interface_density); } else continue; @@ -3734,11 +3741,10 @@ void PrintObjectSupportMaterial::generate_toolpaths( bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; //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) - Flow interface_flow( - float(layer_ex.layer->bridging ? layer_ex.layer->height : (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, - layer_ex.layer->bridging); + 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()); 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()] : @@ -3762,11 +3768,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( Fill *filler = filler_base_interface.get(); //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) - Flow interface_flow( - float(base_interface_layer.layer->bridging ? base_interface_layer.layer->height : m_support_material_flow.width), // m_support_material_interface_flow.width)), - float(base_interface_layer.layer->height), - m_support_material_flow.nozzle_diameter, - base_interface_layer.layer->bridging); + assert(! base_interface_layer.layer->bridging); + Flow interface_flow = m_support_material_flow.with_height(float(base_interface_layer.layer->height)); filler->angle = interface_angle; filler->spacing = m_support_material_interface_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / interface_density)); @@ -3788,11 +3791,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler->angle = angles[support_layer_id % angles.size()]; // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. - Flow flow( - float(base_layer.layer->bridging ? base_layer.layer->height : m_support_material_flow.width), - float(base_layer.layer->height), - m_support_material_flow.nozzle_diameter, - base_layer.layer->bridging); + assert(! base_layer.layer->bridging); + auto flow = m_support_material_flow.with_height(float(base_layer.layer->height)); filler->spacing = m_support_material_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); float density = float(support_density); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 8621586f8..0e5e10d45 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -148,74 +148,33 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle speed_normal = first_layer_speed.get_abs_value(speed_normal); return (speed_normal > 0.) ? speed_normal : speed_max; }; + auto test_flow = + [first_layer_extrusion_width_ptr, extrusion_width, nozzle_diameter, lh, bridging, bridge_speed, bridge_flow_ratio, limit_by_first_layer_speed, max_print_speed, &max_flow, &max_flow_extrusion_type] + (FlowRole flow_role, const ConfigOptionFloatOrPercent &this_extrusion_width, double speed, const char *err_msg) { + Flow flow = bridging ? + Flow::new_from_config_width(flow_role, first_positive(first_layer_extrusion_width_ptr, this_extrusion_width, extrusion_width), nozzle_diameter, lh) : + Flow::bridging_flow(nozzle_diameter * bridge_flow_ratio, nozzle_diameter); + double volumetric_flow = flow.mm3_per_mm() * (bridging ? bridge_speed : limit_by_first_layer_speed(speed, max_print_speed)); + if (max_flow < volumetric_flow) { + max_flow = volumetric_flow; + max_flow_extrusion_type = _utf8(err_msg); + } + }; if (perimeter_extruder_active) { - double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter, - first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : - limit_by_first_layer_speed(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed)); - if (max_flow < external_perimeter_rate) { - max_flow = external_perimeter_rate; - max_flow_extrusion_type = _utf8(L("external perimeters")); - } - double perimeter_rate = Flow::new_from_config_width(frPerimeter, - first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : - limit_by_first_layer_speed(std::max(perimeter_speed, small_perimeter_speed), max_print_speed)); - if (max_flow < perimeter_rate) { - max_flow = perimeter_rate; - max_flow_extrusion_type = _utf8(L("perimeters")); - } - } - if (! bridging && infill_extruder_active) { - double infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed); - if (max_flow < infill_rate) { - max_flow = infill_rate; - max_flow_extrusion_type = _utf8(L("infill")); - } + test_flow(frExternalPerimeter, external_perimeter_extrusion_width, std::max(external_perimeter_speed, small_perimeter_speed), L("external perimeters")); + test_flow(frPerimeter, perimeter_extrusion_width, std::max(perimeter_speed, small_perimeter_speed), L("perimeters")); } + if (! bridging && infill_extruder_active) + test_flow(frInfill, infill_extrusion_width, infill_speed, L("infill")); if (solid_infill_extruder_active) { - double solid_infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, 0).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed)); - if (max_flow < solid_infill_rate) { - max_flow = solid_infill_rate; - max_flow_extrusion_type = _utf8(L("solid infill")); - } - if (! bridging) { - double top_solid_infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed); - if (max_flow < top_solid_infill_rate) { - max_flow = top_solid_infill_rate; - max_flow_extrusion_type = _utf8(L("top solid infill")); - } - } - } - if (support_material_extruder_active) { - double support_material_rate = Flow::new_from_config_width(frSupportMaterial, - first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(support_material_speed, max_print_speed)); - if (max_flow < support_material_rate) { - max_flow = support_material_rate; - max_flow_extrusion_type = _utf8(L("support")); - } - } - if (support_material_interface_extruder_active) { - double support_material_interface_rate = Flow::new_from_config_width(frSupportMaterialInterface, - first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(support_material_interface_speed, max_print_speed)); - if (max_flow < support_material_interface_rate) { - max_flow = support_material_interface_rate; - max_flow_extrusion_type = _utf8(L("support interface")); - } + test_flow(frInfill, solid_infill_extrusion_width, solid_infill_speed, L("solid infill")); + if (! bridging) + test_flow(frInfill, top_infill_extrusion_width, top_solid_infill_speed, L("top solid infill")); } + if (! bridging && support_material_extruder_active) + test_flow(frSupportMaterial, support_material_extrusion_width, support_material_speed, L("support")); + if (support_material_interface_extruder_active) + test_flow(frSupportMaterialInterface, support_material_extrusion_width, support_material_interface_speed, L("support interface")); //FIXME handle gap_fill_speed if (! out.empty()) out += "\n"; @@ -254,11 +213,11 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre Flow external_perimeter_flow = Flow::new_from_config_width( frExternalPerimeter, *print_config.opt("external_perimeter_extrusion_width"), - nozzle_diameter, layer_height, false); + nozzle_diameter, layer_height); Flow perimeter_flow = Flow::new_from_config_width( frPerimeter, *print_config.opt("perimeter_extrusion_width"), - nozzle_diameter, layer_height, false); + nozzle_diameter, layer_height); if (num_perimeters > 0) { @@ -266,7 +225,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre out += (boost::format(_utf8(L("Recommended object thin wall thickness for layer height %.2f and"))) % layer_height).str() + " "; // Start with the width of two closely spaced try { - double width = external_perimeter_flow.width + external_perimeter_flow.spacing(); + double width = external_perimeter_flow.width() + external_perimeter_flow.spacing(); for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) { if (i > 2) out += ", "; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d737bae10..645d31f4b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1433,6 +1433,7 @@ void TabPrint::build() optgroup->append_single_option_line("avoid_crossing_perimeters", category_path + "avoid-crossing-perimeters"); optgroup->append_single_option_line("avoid_crossing_perimeters_max_detour", category_path + "avoid_crossing_perimeters_max_detour"); optgroup->append_single_option_line("thin_walls", category_path + "detect-thin-walls"); + optgroup->append_single_option_line("thick_bridges", category_path + "thick_bridges"); optgroup->append_single_option_line("overhangs", category_path + "detect-bridging-perimeters"); optgroup = page->new_optgroup(L("Advanced")); diff --git a/tests/fff_print/test_flow.cpp b/tests/fff_print/test_flow.cpp index 969ae3c82..63b62a4d7 100644 --- a/tests/fff_print/test_flow.cpp +++ b/tests/fff_print/test_flow.cpp @@ -143,7 +143,7 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { GIVEN("Input spacing of 0.414159 and a total width of 2") { double in_spacing = 0.414159; double total_width = 2.0; - auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3, false); + auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3); WHEN("solid_spacing() is called") { double result = flow.solid_spacing(total_width, in_spacing); THEN("Yielded spacing is greater than 0") {