123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779 |
- ///|/ Copyright (c) Prusa Research 2016 - 2023 Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros
- ///|/ Copyright (c) Slic3r 2014 - 2015 Alessandro Ranellucci @alranel
- ///|/ Copyright (c) 2014 Petr Ledvina @ledvinap
- ///|/
- ///|/ ported from lib/Slic3r/Flow.pm:
- ///|/ Copyright (c) Prusa Research 2022 Vojtěch Bubník @bubnikv
- ///|/ Copyright (c) Slic3r 2012 - 2014 Alessandro Ranellucci @alranel
- ///|/
- ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
- ///|/
- #include "Flow.hpp"
- #include "I18N.hpp"
- #include "Print.hpp"
- #include "Layer.hpp"
- #include "Layer.hpp"
- #include <cmath>
- #include <assert.h>
- #include <boost/algorithm/string/predicate.hpp>
- #include <boost/algorithm/string/replace.hpp>
- namespace Slic3r {
- FlowErrorNegativeSpacing::FlowErrorNegativeSpacing() :
- FlowError("Flow::spacing() produced negative spacing. Did you set some extrusion width too small?") {}
- FlowErrorNegativeFlow::FlowErrorNegativeFlow() :
- FlowError("Flow::mm3_per_mm() produced negative flow. Did you set some extrusion width too small?") {}
- // This static method returns a sane extrusion width default.
- float Flow::auto_extrusion_width(FlowRole role, float nozzle_diameter)
- {
- switch (role) {
- case frSupportMaterial:
- case frSupportMaterialInterface:
- case frTopSolidInfill:
- case frExternalPerimeter:
- return 1.05f * nozzle_diameter;
- default:
- case frPerimeter:
- case frSolidInfill:
- case frInfill:
- return 1.125f * nozzle_diameter;
- }
- }
- // Used by the Flow::extrusion_width() funtion to provide hints to the user on default extrusion width values,
- // and to provide reasonable values to the PlaceholderParser.
- static inline FlowRole opt_key_to_flow_role(const std::string &opt_key)
- {
- if (opt_key == "perimeter_extrusion_width" ||
- // or almost all the defaults:
- opt_key == "extrusion_width" || opt_key == "first_layer_extrusion_width")
- return frPerimeter;
- else if (opt_key == "external_perimeter_extrusion_width")
- return frExternalPerimeter;
- else if (opt_key == "infill_extrusion_width")
- return frInfill;
- else if (opt_key == "solid_infill_extrusion_width"
- // or the first layer infill:
- || opt_key == "first_layer_infill_extrusion_width")
- return frSolidInfill;
- else if (opt_key == "top_infill_extrusion_width")
- return frTopSolidInfill;
- else if (opt_key == "support_material_extrusion_width")
- return frSupportMaterial;
- else
- throw Slic3r::RuntimeError("opt_key_to_flow_role: invalid argument");
- };
- static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key)
- {
- throw FlowErrorMissingVariable((boost::format(_u8L("Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible.")) % opt_key % dependent_opt_key).str());
- }
- // Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser.
- double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloatOrPercent* opt, const ConfigOptionResolver& config, const unsigned int first_printing_extruder)
- {
- assert(opt != nullptr);
- bool first_layer = boost::starts_with(opt_key, "first_layer_") || boost::starts_with(opt_key, "brim_");
- if (!first_layer && boost::starts_with(opt_key, "skirt_")) {
- const ConfigOptionInt* optInt = config.option<ConfigOptionInt>("skirt_height");
- const ConfigOptionBool* optBool = config.option<ConfigOptionBool>("draft_shield");
- first_layer = (optBool && optInt && optInt->value == 1 && !optBool->value);
- }
- if (opt->value == 0.) {
- // The role specific extrusion width value was set to zero, get a not-0 one (if possible)
- opt = extrusion_width_option(opt_key, config);
- }
- if (opt->percent) {
- // first_layer_height depends on first_printing_extruder
- auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
- if (opt_nozzle_diameters == nullptr)
- throw_on_missing_variable(opt_key, "nozzle_diameter");
- return opt->get_abs_value(float(opt_nozzle_diameters->get_at(first_printing_extruder)));
- }
- if (opt->value == 0.) {
- // If user left option to 0, calculate a sane default width.
- auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
- if (opt_nozzle_diameters == nullptr)
- throw_on_missing_variable(opt_key, "nozzle_diameter");
- return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder)));
- }
- return opt->value;
- }
- //used to get brim & skirt extrusion config
- const ConfigOptionFloatOrPercent* Flow::extrusion_width_option(std::string opt_key, const ConfigOptionResolver& config)
- {
- if (!boost::ends_with(opt_key, "_extrusion_width")) {
- opt_key += "_extrusion_width";
- }
- const ConfigOptionFloatOrPercent* opt = config.option<ConfigOptionFloatOrPercent>(opt_key);
- //brim is first_layer_extrusion_width then perimeter_extrusion_width
- if (!opt && boost::starts_with(opt_key, "brim")) {
- const ConfigOptionFloatOrPercent* optTest = config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
- opt = optTest;
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "first_layer_extrusion_width");
- if (opt->value == 0) {
- opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_width");
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "perimeter_extrusion_width");
- }
- }
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, opt_key.c_str());
- // This is the logic used for skit / brim, but not for the rest of the 1st layer.
- if (opt->value == 0. && boost::starts_with(opt_key, "skirt")) {
- // The "skirt_extrusion_width" was set to zero, try a substitute.
- const ConfigOptionFloatOrPercent* opt_first_layer_extrusion_width = config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
- const ConfigOptionInt* opt_skirt_height = config.option<ConfigOptionInt>("skirt_height");
- const ConfigOptionEnum<DraftShield>* opt_draft_shield = config.option<ConfigOptionEnum<DraftShield>>("draft_shield");
- if (opt_first_layer_extrusion_width == nullptr)
- throw_on_missing_variable(opt_key, "first_layer_extrusion_width");
- if (opt_draft_shield == nullptr)
- throw_on_missing_variable(opt_key, "draft_shield");
- if (opt_skirt_height == nullptr)
- throw_on_missing_variable(opt_key, "skirt_height");
- // The "first_layer_extrusion_width" was set to zero, try a substitute.
- if (opt_first_layer_extrusion_width && opt_draft_shield && opt_skirt_height && opt_first_layer_extrusion_width->value > 0 && opt_skirt_height->value == 1 && opt_draft_shield->value != DraftShield::dsDisabled)
- opt = opt_first_layer_extrusion_width;
- if (opt->value == 0) {
- opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_width");
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "perimeter_extrusion_width");
- }
- }
- // external_perimeter_extrusion_width default is perimeter_extrusion_width
- //if (opt->value == 0. && boost::starts_with(opt_key, "external_perimeter_extrusion_width")) {
- // // The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
- // opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_width");
- // if (opt == nullptr)
- // throw_on_missing_variable(opt_key, "perimeter_extrusion_width");
- //}
- // top_infill_extrusion_width default is solid_infill_extrusion_width
- //if (opt->value == 0. && boost::starts_with(opt_key, "top_infill_extrusion_width")) {
- // // The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
- // opt = config.option<ConfigOptionFloatOrPercent>("solid_infill_extrusion_width");
- // if (opt == nullptr)
- // throw_on_missing_variable(opt_key, "solid_infill_extrusion_width");
- //}
- if (opt->value == 0.) {
- // The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
- opt = config.option<ConfigOptionFloatOrPercent>("extrusion_width");
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "extrusion_width");
- }
- return opt;
- }
- //used to get brim & skirt extrusion config
- const ConfigOptionFloatOrPercent* Flow::extrusion_spacing_option(std::string opt_key, const ConfigOptionResolver& config)
- {
- std::string opt_key_width;
- if (boost::starts_with(opt_key, "skirt")) {
- //skirt have only width setting
- if (!boost::ends_with(opt_key, "_extrusion_width")) {
- opt_key += "_extrusion_width";
- }
- opt_key_width = opt_key;
- } else {//brim
- if (boost::ends_with(opt_key, "_extrusion_width")) {
- boost::replace_first(opt_key, "_width", "_spacing");
- }
- if (!boost::ends_with(opt_key, "_extrusion_spacing")) {
- opt_key_width = opt_key + "_extrusion_width";
- opt_key += "_extrusion_spacing";
- } else {
- opt_key_width = opt_key;
- boost::replace_first(opt_key_width, "_spacing", "_width");
- }
- }
- const ConfigOptionFloatOrPercent* opt = config.option<ConfigOptionFloatOrPercent>(opt_key);
- //brim is first_layer_extrusion_spacing then perimeter_extrusion_spacing
- if (!opt && boost::starts_with(opt_key, "brim_")) {
- const ConfigOptionFloatOrPercent* optTest = config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_spacing");
- opt = optTest;
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "first_layer_extrusion_spacing");
- if (opt->value == 0) {
- opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_spacing");
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "perimeter_extrusion_spacing");
- }
- }
- if (opt == nullptr) {
- opt_key = opt_key_width;
- opt = config.option<ConfigOptionFloatOrPercent>(opt_key_width);
- }
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, opt_key.c_str());
- // This is the logic used for skit / brim, but not for the rest of the 1st layer.
- if (opt->value == 0. && boost::starts_with(opt_key, "skirt")) {
- // The "skirt_extrusion_spacing" was set to zero, try a substitute.
- const ConfigOptionFloatOrPercent* opt_first_layer_extrusion_spacing = config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_spacing");
- const ConfigOptionInt* opt_skirt_height = config.option<ConfigOptionInt>("skirt_height");
- const ConfigOptionEnum<DraftShield>* opt_draft_shield = config.option<ConfigOptionEnum<DraftShield>>("draft_shield");
- if (opt_first_layer_extrusion_spacing == nullptr)
- throw_on_missing_variable(opt_key, "first_layer_extrusion_spacing");
- if (opt_draft_shield == nullptr)
- throw_on_missing_variable(opt_key, "draft_shield");
- if (opt_skirt_height == nullptr)
- throw_on_missing_variable(opt_key, "skirt_height");
- // The "first_layer_extrusion_spacing" was set to zero, try a substitute.
- if (opt_first_layer_extrusion_spacing && opt_draft_shield && opt_skirt_height && opt_first_layer_extrusion_spacing->value > 0 && opt_skirt_height->value == 1 && opt_draft_shield->value != DraftShield::dsDisabled)
- opt = opt_first_layer_extrusion_spacing;
- if (opt->value == 0) {
- opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_spacing");
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "perimeter_extrusion_spacing");
- }
- }
- // external_perimeter_extrusion_spacing default is perimeter_extrusion_spacing
- //if (opt->value == 0. && boost::starts_with(opt_key, "external_perimeter_extrusion_spacing")) {
- // // The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
- // opt = config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_spacing");
- // if (opt == nullptr)
- // throw_on_missing_variable(opt_key, "perimeter_extrusion_spacing");
- //}
- // top_infill_extrusion_spacing default is solid_infill_extrusion_spacing
- //if (opt->value == 0. && boost::starts_with(opt_key, "top_infill_extrusion_spacing")) {
- // // The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
- // opt = config.option<ConfigOptionFloatOrPercent>("solid_infill_extrusion_spacing");
- // if (opt == nullptr)
- // throw_on_missing_variable(opt_key, "solid_infill_extrusion_spacing");
- //}
- if (opt->value == 0.) {
- // The role specific extrusion width value was set to zero, try the role non-specific extrusion width.
- opt = config.option<ConfigOptionFloatOrPercent>("extrusion_spacing");
- if (opt == nullptr)
- throw_on_missing_variable(opt_key, "extrusion_spacing");
- }
- return opt;
- }
- // Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser.
- double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder)
- {
- return extrusion_width(opt_key, config.option<ConfigOptionFloatOrPercent>(opt_key), config, first_printing_extruder);
- }
- Flow Flow::new_from_config(FlowRole role, const DynamicConfig& print_config, float nozzle_diameter, float layer_height, float filament_max_overlap, bool first_layer) {
- ConfigOptionFloatOrPercent config_width;
- ConfigOptionFloatOrPercent config_spacing;
- // Get extrusion width from configuration.
- float overlap = 1.f;
- // (might be an absolute value, or a percent value, or zero for auto)
- if (role == frExternalPerimeter) {
- config_width = print_config.opt<ConfigOptionFloatOrPercent>("external_perimeter_extrusion_width");
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("external_perimeter_extrusion_spacing");
- // external peri spacing is only half spacing -> transform it into a full spacing
- if (!config_spacing.is_phony() && !config_spacing.value == 0) {
- double raw_spacing = config_spacing.get_abs_value(nozzle_diameter);
- config_spacing.percent = false;
- config_spacing.value = rounded_rectangle_extrusion_spacing(
- rounded_rectangle_extrusion_width_from_spacing(raw_spacing, layer_height, 0.5f),
- layer_height, 1.f);
- }
- overlap = (float)print_config.get_abs_value("external_perimeter_overlap", 1.0);
- } else if (role == frPerimeter) {
- config_width = print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_width");
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_spacing");
- overlap = (float)print_config.get_abs_value("perimeter_overlap", 1.);
- } else if (role == frInfill) {
- config_width = print_config.opt<ConfigOptionFloatOrPercent>("infill_extrusion_width");
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("infill_extrusion_spacing");
- } else if (role == frSolidInfill) {
- config_width = print_config.opt<ConfigOptionFloatOrPercent>("solid_infill_extrusion_width");
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("solid_infill_extrusion_spacing");
- overlap = (float)print_config.get_abs_value("solid_infill_overlap", 1.);
- } else if (role == frTopSolidInfill) {
- config_width = print_config.opt<ConfigOptionFloatOrPercent>("top_infill_extrusion_width");
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("top_infill_extrusion_spacing");
- overlap = (float)print_config.get_abs_value("top_solid_infill_overlap", 1.);
- } else {
- throw Slic3r::InvalidArgument("Unknown role");
- }
- if (first_layer) {
- auto opt_fl_width = print_config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
- auto opt_fli_width = print_config.opt<ConfigOptionFloatOrPercent>("first_layer_infill_extrusion_width");
- if ((role == frInfill || role == frSolidInfill || role == frTopSolidInfill) && opt_fli_width->is_enabled()) {
- config_width = opt_fli_width;
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("first_layer_infill_extrusion_spacing");
- } else if (opt_fl_width->is_enabled()) {
- config_width = opt_fl_width;
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_spacing");
- }
- }
- if (config_width.value == 0) {
- config_width = print_config.opt<ConfigOptionFloatOrPercent>("extrusion_width");
- config_spacing = print_config.opt<ConfigOptionFloatOrPercent>("extrusion_spacing");
- }
- // 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.
- return Flow::new_from_config_width(role, config_width, config_spacing, nozzle_diameter, layer_height,
- std::min(role == frTopSolidInfill ? 1.f : overlap, filament_max_overlap));
- //bridge ? (float)m_config.bridge_flow_ratio.get_abs_value(1) : 0.0f);
- }
- // 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, const ConfigOptionFloatOrPercent& spacing, float nozzle_diameter, float height, float spacing_ratio, float bridge_flow_ratio)
- {
- if (height <= 0)
- throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
- float w = 0.f;
- 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.is_phony()) {
- 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 {
- // If user set a manual value, use it.
- w = float(width.get_abs_value(nozzle_diameter));
- }
- } else {
- if (!spacing.percent && spacing.value == 0.) {
- // If user left option to 0, calculate a sane default width.
- w = auto_extrusion_width(role, nozzle_diameter);
- } else {
- // If user set a manual value, use it.
- return new_from_spacing(float(spacing.get_abs_value(nozzle_diameter)), nozzle_diameter, height, spacing_ratio, false);
- }
- }
- }
-
- return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height, spacing_ratio), nozzle_diameter, spacing_ratio, bridge_flow_ratio > 0);
- }
- // 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, const ConfigOptionFloatOrPercent& spacing, float nozzle_diameter, float height, float spacing_ratio)
- {
- if (height <= 0)
- throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
- float w;
- if (!width.is_phony()) {
- 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 {
- // If user set a manual value, use it.
- w = float(width.get_abs_value(nozzle_diameter));
- }
- } else {
- if (!spacing.percent && spacing.value == 0.) {
- // If user left option to 0, calculate a sane default width.
- w = auto_extrusion_width(role, nozzle_diameter);
- } else {
- // If user set a manual value, use it.
- return new_from_spacing(float(spacing.get_abs_value(nozzle_diameter)), nozzle_diameter, height, spacing_ratio, false);
- }
- }
- return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height, spacing_ratio), nozzle_diameter, spacing_ratio, false);
- }
- // This constructor builds a Flow object from a given centerline spacing.
- Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, float spacing_ratio, bool bridge)
- {
- // we need layer height unless it's a bridge
- if (height <= 0 && !bridge)
- 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_MULT (0.125) * nozzle_diameter*/) :
- rounded_rectangle_extrusion_width_from_spacing(spacing, height, spacing_ratio));
- return Flow(width, bridge ? width : height, spacing, nozzle_diameter, bridge ? 0 : spacing_ratio, bridge);
- }
- // This constructor builds a Flow object from a given centerline spacing.
- Flow Flow::new_from_width(float width, float nozzle_diameter, float height, float spacing_ratio, bool bridge)
- {
- // we need layer height unless it's a bridge
- if (height <= 0 && !bridge)
- throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_width()");
- float spacing = float(bridge ?
- (width /*- BRIDGE_EXTRA_SPACING_MULT (0.125) * nozzle_diameter*/) :
- rounded_rectangle_extrusion_spacing(width, height, spacing_ratio));
- return Flow(width, bridge ? width : height, spacing, nozzle_diameter, bridge ? 0 : spacing_ratio, bridge);
- }
- // Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions.
- Flow Flow::with_spacing(float new_spacing) const
- {
- 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 {
- assert(m_width >= m_height);
- 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;
- }
- Flow Flow::with_spacing_ratio_from_width(float new_spacing_ratio) const
- {
- return Flow::new_from_width(m_width, m_nozzle_diameter, m_height, new_spacing_ratio, m_bridge);
- }
- // This method returns the centerline spacing between two adjacent extrusions
- // having the same extrusion width (and other properties).
- float Flow::spacing() const
- {
- #ifdef HAS_PERIMETER_LINE_OVERLAP
- if (this->bridge)
- return this->width + BRIDGE_EXTRA_SPACING;
- // rectangle with semicircles at the ends
- float min_flow_spacing = this->width - this->height * (1. - 0.25 * PI) * spacing_ratio;
- float res = this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing);
- #else
- float res = float(this->bridge() ? (this->width() /*+ BRIDGE_EXTRA_SPACING_MULT * nozzle_diameter*/) : (this->width() - this->height() * (1. - 0.25 * PI) * m_spacing_ratio));
- #endif
- // assert(res > 0.f);
- if (res <= 0.f)
- throw FlowErrorNegativeSpacing();
- return res;
- }
- // 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
- {
- assert(! m_bridge);
- assert(m_width >= m_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, m_spacing_ratio), height, m_spacing, m_nozzle_diameter, m_spacing_ratio, false);
- } else {
- return this->with_width(rounded_rectangle_extrusion_width_from_spacing(area / m_height, m_height, m_spacing_ratio));
- }
- } else if (area_new < area - EPSILON) {
- // Decreasing the flow rate.
- float width_new = m_width - (area - area_new) / m_height;
- assert(width_new > 0);
- if (width_new > m_height) {
- // Shrink the extrusion width.
- return this->with_width(width_new);
- } else {
- // Create a rounded extrusion.
- auto dmr = 2.0 * float(sqrt(area_new / M_PI));
- return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, m_spacing_ratio, 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
- {
- assert(this->height() == other.height());
- assert(this->bridge() == other.bridge());
- float res = float(this->bridge() || other.bridge() ?
- 0.5 * this->width() + 0.5 * other.width() :
- 0.5 * this->spacing() + 0.5 * other.spacing());
- // assert(res > 0.f);
- if (res <= 0.f)
- throw FlowErrorNegativeSpacing();
- return res;
- }
- float Flow::rounded_rectangle_extrusion_spacing(float width, float height, float m_spacing_ratio)
- {
- #ifdef HAS_PERIMETER_LINE_OVERLAP
- return (spacing - PERIMETER_LINE_OVERLAP_FACTOR * height * (1. - 0.25 * PI) * spacing_ratio);
- #else
- if (width == height && width == 0)
- return 0.f;
- float out = width - height * float(1. - 0.25 * PI) * m_spacing_ratio;
- if (out <= 0.f)
- throw FlowErrorNegativeSpacing();
- return out;
- #endif
- }
- float Flow::rounded_rectangle_extrusion_width_from_spacing(float spacing, float height, float spacing_ratio)
- {
- #ifdef HAS_PERIMETER_LINE_OVERLAP
- return (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1. - 0.25 * PI) * spacing_ratio);
- #else
- return float(spacing + height * (1. - 0.25 * PI) * spacing_ratio);
- #endif
- }
- 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
- {
- float res = m_bridge ?
- // Area of a circle with dmr of this->width.
- float((m_width * m_width) * 0.25 * PI) :
- // Rectangle with semicircles at the ends. ~ h (w - 0.215 h)
- float(m_height * (m_width - m_height * (1. - 0.25 * PI)));
- //assert(res > 0.);
- if (res <= 0.)
- throw FlowErrorNegativeFlow();
- return res;
- }
- Flow support_material_flow(const PrintObject* object, float layer_height)
- {
- int extruder_id = object->config().support_material_extruder.value - 1;
- if (extruder_id < 0) {
- if (!object->layers().empty()) {
- extruder_id = object->layers().front()->get_region(0)->region().config().infill_extruder - 1;
- } else {
- extruder_id = object->default_region_config(object->print()->default_region_config()).infill_extruder - 1;
- }
- }
- double nzd = object->print()->config().nozzle_diameter.get_at(extruder_id);
- const ConfigOptionFloatOrPercent& width = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width;
- const ConfigOptionFloatOrPercent& spacing = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_spacing;
- float max_height = 0.f;
- if (!width.percent && width.value <= 0.) {
- // If user left option to 0, calculate a sane default width.
- max_height = Flow::auto_extrusion_width(frSupportMaterialInterface, nzd);
- } else {
- // If user set a manual value, use it.
- max_height = float(width.get_abs_value(nzd));
- }
- if (layer_height <= 0) { // get default layer height for material interface
- layer_height = object->config().support_material_layer_height.get_abs_value(nzd);
- if (layer_height == 0) {
- layer_height = object->print()->config().max_layer_height.get_abs_value(extruder_id, nzd);
- if (layer_height == 0 || !object->print()->config().max_layer_height.is_enabled()) {
- layer_height = nzd * 0.75;
- }
- }
- }
- layer_height = std::min(layer_height, max_height);
- return Flow::new_from_config_width(
- frSupportMaterial,
- // 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,
- spacing,
- // 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(nzd),
- layer_height,
- extruder_id < 0 ? 1 : object->config().get_computed_value("filament_max_overlap", extruder_id), //if can get an extruder, then use its param, or use full overlap if we don't know the extruder id.
- // bridge_flow_ratio
- 0.f);
- }
- Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
- {
- const PrintConfig &print_config = object->print()->config();
- const auto& width = object->config().first_layer_extrusion_width.is_enabled() ? object->config().first_layer_extrusion_width : object->config().support_material_extrusion_width;
- const auto& spacing = object->config().first_layer_extrusion_width.is_enabled() ? object->config().first_layer_extrusion_spacing : object->config().support_material_extrusion_width;
- float slice_height = layer_height;
- if (layer_height <= 0.f && !object->print()->config().nozzle_diameter.empty()){
- slice_height = (float)object->get_first_layer_height();
- }
- int extruder_id = object->config().support_material_extruder.value -1;
- if (extruder_id < 0) {
- if (!object->layers().empty()) {
- extruder_id = object->layers().front()->get_region(0)->region().config().infill_extruder - 1;
- } else {
- extruder_id = object->default_region_config(object->print()->default_region_config()).infill_extruder - 1;
- }
- }
- return Flow::new_from_config_width(
- frSupportMaterial,
- // 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,
- (spacing.value > 0) ? spacing : object->config().extrusion_spacing, // can be used if first_layer_extrusion_width is phony
- float(print_config.nozzle_diameter.get_at(extruder_id)),
- slice_height,
- extruder_id < 0 ? 1 : object->config().get_computed_value("filament_max_overlap", extruder_id), //if can get an extruder, then use its param, or use full overlap if we don't know the extruder id.
- // bridge_flow_ratio
- 0.f);
- }
- Flow support_material_interface_flow(const PrintObject* object, float layer_height)
- {
- int extruder_id = object->config().support_material_interface_extruder.value - 1;
- if (extruder_id < 0) {
- assert(!object->layers().empty() || object->num_printing_regions() > 0);
- if (!object->layers().empty()) {
- extruder_id = object->layers().front()->get_region(0)->region().config().infill_extruder - 1;
- } else {
- extruder_id = object->default_region_config(object->print()->default_region_config()).infill_extruder - 1;
- }
- }
- double nzd = object->print()->config().nozzle_diameter.get_at(extruder_id);
- const ConfigOptionFloatOrPercent& width = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width;
- const ConfigOptionFloatOrPercent& spacing = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_spacing;
- float max_height = 0.f;
- if (!width.percent && width.value <= 0.) {
- // If user left option to 0, calculate a sane default width.
- max_height = Flow::auto_extrusion_width(frSupportMaterialInterface, nzd);
- } else {
- // If user set a manual value, use it.
- max_height = float(width.get_abs_value(nzd));
- }
- if (layer_height <= 0) { // get default layer height for material interface
- layer_height = object->config().support_material_interface_layer_height.get_abs_value(nzd);
- if (layer_height == 0) {
- layer_height = object->print()->config().max_layer_height.get_abs_value(extruder_id, nzd);
- if (layer_height == 0 || !object->print()->config().max_layer_height.is_enabled()) {
- layer_height = nzd * 0.75;
- }
- }
- }
- layer_height = std::min(layer_height, max_height);
- return Flow::new_from_config_width(
- frSupportMaterialInterface,
- // 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,
- spacing,
- // 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(nzd),
- layer_height,
- extruder_id < 0 ? 1 : object->config().get_computed_value("filament_max_overlap", extruder_id), //if can get an extruder, then use its param, or use full overlap if we don't know the extruder id.
- // bridge_flow_ratio
- 0.f);
- }
- Flow raft_flow(const PrintObject* object, float layer_height)
- {
- int extruder_id = object->config().support_material_interface_extruder.value - 1;
- if (extruder_id < 0) {
- extruder_id = object->layers().front()->get_region(0)->region().config().perimeter_extruder - 1;
- }
- double nzd = object->print()->config().nozzle_diameter.get_at(extruder_id);
- const ConfigOptionFloatOrPercent& width = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width;
- const ConfigOptionFloatOrPercent& spacing = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_spacing;
- float max_height = 0.f;
- if (!width.percent && width.value <= 0.) {
- // If user left option to 0, calculate a sane default width.
- max_height = Flow::auto_extrusion_width(frSupportMaterial, nzd);
- } else {
- // If user set a manual value, use it.
- max_height = float(width.get_abs_value(nzd));
- }
- if (layer_height <= 0) { // get default layer height for material interface
- layer_height = object->config().raft_interface_layer_height.get_abs_value(nzd);
- if (layer_height == 0) {
- layer_height = object->print()->config().max_layer_height.get_abs_value(extruder_id, nzd);
- if (layer_height == 0 || !object->print()->config().max_layer_height.is_enabled()) {
- layer_height = nzd * 0.75;
- }
- }
- }
- layer_height = std::min(layer_height, max_height);
- return Flow::new_from_config_width(
- frSupportMaterial,
- // 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,
- spacing,
- // 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(nzd),
- layer_height,
- extruder_id < 0 ? 1 : object->config().get_computed_value("filament_max_overlap", extruder_id), //if can get an extruder, then use its param, or use full overlap if we don't know the extruder id.
- // bridge_flow_ratio
- 0.f);
- }
- Flow raft_interface_flow(const PrintObject* object, float layer_height)
- {
- int extruder_id = object->config().support_material_interface_extruder.value - 1;
- if (extruder_id < 0) {
- extruder_id = object->layers().front()->get_region(0)->region().config().infill_extruder - 1;
- }
- double nzd = object->print()->config().nozzle_diameter.get_at(extruder_id);
- const ConfigOptionFloatOrPercent& width = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width;
- const ConfigOptionFloatOrPercent& spacing = (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_spacing;
- float max_height = 0.f;
- if (!width.percent && width.value <= 0.) {
- // If user left option to 0, calculate a sane default width.
- max_height = Flow::auto_extrusion_width(frSupportMaterialInterface, nzd);
- } else {
- // If user set a manual value, use it.
- max_height = float(width.get_abs_value(nzd));
- }
- if (layer_height <= 0) { // get default layer height for material interface
- layer_height = object->config().raft_interface_layer_height.get_abs_value(nzd);
- if (layer_height == 0) {
- layer_height = object->print()->config().max_layer_height.get_abs_value(extruder_id, nzd);
- if (layer_height == 0 || !object->print()->config().max_layer_height.is_enabled()) {
- layer_height = nzd * 0.75;
- }
- }
- }
- layer_height = std::min(layer_height, max_height);
- return Flow::new_from_config_width(
- frSupportMaterialInterface,
- // 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,
- spacing,
- // 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(nzd),
- layer_height,
- extruder_id < 0 ? 1 : object->config().get_computed_value("filament_max_overlap", extruder_id), //if can get an extruder, then use its param, or use full overlap if we don't know the extruder id.
- // bridge_flow_ratio
- 0.f);
- }
- }
|