2022-07-15 15:37:19 +00:00
# include "Flow.hpp"
# include "I18N.hpp"
# include "Print.hpp"
# include <cmath>
# include <assert.h>
# include <boost/algorithm/string/predicate.hpp>
2024-08-27 07:20:53 +00:00
# include <boost/log/trivial.hpp>
2022-07-15 15:37:19 +00:00
// Mark string for localization and translate.
# define L(s) Slic3r::I18N::translate(s)
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 frSupportTransition :
case frTopSolidInfill :
return nozzle_diameter ;
default :
case frExternalPerimeter :
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 = = " inner_wall_line_width " | |
// or all the defaults:
opt_key = = " line_width " | | opt_key = = " initial_layer_line_width " )
return frPerimeter ;
else if ( opt_key = = " outer_wall_line_width " )
return frExternalPerimeter ;
else if ( opt_key = = " sparse_infill_line_width " )
return frInfill ;
else if ( opt_key = = " internal_solid_infill_line_width " )
return frSolidInfill ;
else if ( opt_key = = " top_surface_line_width " )
return frTopSolidInfill ;
else if ( opt_key = = " support_line_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 ( L ( " Failed to calculate line width of %1%. Can not get value of \" %2% \" " ) ) % 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 , " initial_layer_ " ) ;
#if 0
// This is the logic used for skit / brim, but not for the rest of the 1st layer.
if ( opt - > value = = 0. & & first_layer ) {
// The "initial_layer_line_width" was set to zero, try a substitute.
opt = config . option < ConfigOptionFloatOrPercent > ( " inner_wall_line_width " ) ;
if ( opt = = nullptr )
throw_on_missing_variable ( opt_key , " inner_wall_line_width " ) ;
}
# endif
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 > ( " line_width " ) ;
if ( opt = = nullptr )
throw_on_missing_variable ( opt_key , " line_width " ) ;
// Use the "layer_height" instead of "initial_layer_print_height".
first_layer = false ;
}
if ( opt - > percent ) {
auto opt_key_layer_height = first_layer ? " initial_layer_print_height " : " layer_height " ;
auto opt_layer_height = config . option ( opt_key_layer_height ) ;
if ( opt_layer_height = = nullptr )
throw_on_missing_variable ( opt_key , opt_key_layer_height ) ;
assert ( ! first_layer | | ! static_cast < const ConfigOptionFloatOrPercent * > ( opt_layer_height ) - > percent ) ;
return opt - > get_abs_value ( opt_layer_height - > getFloat ( ) ) ;
}
if ( opt - > value = = 0. ) {
// If user left option to 0, calculate a sane default width.
2024-07-19 08:35:23 +00:00
auto opt_nozzle_diameters = config . option < ConfigOptionFloatsNullable > ( " nozzle_diameter " ) ;
2022-07-15 15:37:19 +00:00
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 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 ) ;
}
// 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 ConfigOptionFloat & width , float nozzle_diameter , float height )
{
if ( height < = 0 )
throw Slic3r : : InvalidArgument ( " Invalid flow height supplied to new_from_config_width() " ) ;
float w ;
if ( 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 . value ) ;
}
return Flow ( w , height , rounded_rectangle_extrusion_spacing ( w , height ) , nozzle_diameter , false ) ;
}
// 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 ;
}
// 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, maintain the extrusion spacing.
float area = this - > mm3_per_mm ( ) ;
if ( area_new > area + EPSILON ) {
// Increasing the flow rate.
float new_full_spacing = area_new / m_height ;
if ( new_full_spacing > m_spacing ) {
// Filling up the spacing without an air gap. Grow the extrusion in height.
float height = area_new / m_spacing ;
return Flow ( rounded_rectangle_extrusion_width_from_spacing ( m_spacing , height ) , height , m_spacing , m_nozzle_diameter , false ) ;
} else {
return this - > with_width ( rounded_rectangle_extrusion_width_from_spacing ( area / m_height , m_height ) ) ;
}
} else if ( area_new < area - EPSILON ) {
// Decreasing the flow rate.
float width_new = m_width - ( area - area_new ) / m_height ;
assert ( width_new > 0 ) ;
if ( width_new > m_height ) {
// Shrink the extrusion width.
return this - > with_width ( width_new ) ;
} else {
// Create a rounded extrusion.
auto dmr = float ( sqrt ( area_new / M_PI ) ) ;
return Flow ( dmr , dmr , m_spacing , m_nozzle_diameter , false ) ;
}
} else
return * this ;
}
float Flow : : rounded_rectangle_extrusion_spacing ( float width , float height )
{
auto out = width - height * float ( 1. - 0.25 * PI ) ;
2024-08-21 04:28:13 +00:00
if ( out < = 0.f ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " negative extrusion : width %1% height %2% " ) % width % height ;
2022-07-15 15:37:19 +00:00
throw FlowErrorNegativeSpacing ( ) ;
2024-08-21 04:28:13 +00:00
}
2022-07-15 15:37:19 +00:00
return out ;
}
float Flow : : rounded_rectangle_extrusion_width_from_spacing ( float spacing , float height )
{
return float ( spacing + height * ( 1. - 0.25 * PI ) ) ;
}
float Flow : : bridge_extrusion_spacing ( float dmr )
{
return dmr + BRIDGE_EXTRA_SPACING ;
}
// This method returns extrusion volume per head move unit.
double Flow : : mm3_per_mm ( ) const
{
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 )
{
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.
( object - > config ( ) . support_line_width . value > 0 ) ? object - > config ( ) . support_line_width : object - > config ( ) . line_width ,
// if object->config().support_filament == 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_filament - 1 ) ) ,
( layer_height > 0.f ) ? layer_height : float ( object - > config ( ) . layer_height . value ) ) ;
}
//BBS
Flow support_transition_flow ( const PrintObject * object )
{
//BBS: support transition of tree support is bridge flow
float dmr = float ( object - > print ( ) - > config ( ) . nozzle_diameter . get_at ( object - > config ( ) . support_filament - 1 ) ) ;
return Flow : : bridging_flow ( dmr , dmr ) ;
}
Flow support_material_1st_layer_flow ( const PrintObject * object , float layer_height )
{
const PrintConfig & print_config = object - > print ( ) - > config ( ) ;
const auto & width = ( print_config . initial_layer_line_width . value > 0 ) ? print_config . initial_layer_line_width : object - > config ( ) . support_line_width ;
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 ( ) . line_width ,
float ( print_config . nozzle_diameter . get_at ( object - > config ( ) . support_filament - 1 ) ) ,
( layer_height > 0.f ) ? layer_height : float ( print_config . initial_layer_print_height . value ) ) ;
}
Flow support_material_interface_flow ( const PrintObject * object , float layer_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.
( object - > config ( ) . support_line_width > 0 ) ? object - > config ( ) . support_line_width : object - > config ( ) . line_width ,
// if object->config().support_interface_filament == 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_interface_filament - 1 ) ) ,
( layer_height > 0.f ) ? layer_height : float ( object - > config ( ) . layer_height . value ) ) ;
}
}