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.
This commit is contained in:
Vojtech Bubnik 2021-03-08 13:44:00 +01:00
parent 1569dad5de
commit ceea9de8b8
21 changed files with 228 additions and 283 deletions

View File

@ -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<ExtrusionPath*>(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);

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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,15 +143,10 @@ std::vector<SurfaceFill> 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 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) {
@ -164,14 +159,7 @@ std::vector<SurfaceFill> 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<SurfaceFill> 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<SurfaceFill> 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));
}
}
}

View File

@ -6,6 +6,12 @@
#include <boost/algorithm/string/predicate.hpp>
// 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));
}
}

View File

@ -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; }
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
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);
// 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);

View File

@ -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<size_t, size_t> 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<const ExtrusionLoop*>(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.

View File

@ -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);

View File

@ -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

View File

@ -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<float>(w) + flow.height * float(1. - 0.25 * PI);
Flow new_flow = flow.with_width(unscale<float>(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<ExtrusionEntity*> &out)
static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const Flow &flow, std::vector<ExtrusionEntity*> &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
@ -206,7 +206,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
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_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.
@ -227,7 +227,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
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.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),

View File

@ -430,7 +430,7 @@ const std::vector<std::string>& 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",

View File

@ -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();

View File

@ -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.

View File

@ -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");

View File

@ -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);
}

View File

@ -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];

View File

@ -18,15 +18,10 @@ 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;
} else {
// otherwise, get extrusion width from configuration
// 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;
@ -43,15 +38,25 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
} else {
throw Slic3r::InvalidArgument("Unknown role");
}
}
if (config_width.value == 0)
config_width = object.config().extrusion_width;
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// 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

View File

@ -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<float>(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::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
// Compress contact_out, remove the nullptr items.
remove_nulls(interface_layers);
remove_nulls(base_interface_layers);
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
}
return base_and_interface_layers;
@ -2835,7 +2841,7 @@ static inline void fill_expolygon_generate_paths(
dst,
std::move(polylines),
role,
flow.mm3_per_mm(), flow.width, flow.height);
flow.mm3_per_mm(), flow.width(), flow.height());
}
static inline void fill_expolygons_generate_paths(
@ -2903,7 +2909,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
pl.clip_end(clip_length);
polylines.emplace_back(std::move(pl));
}
extrusion_entities_append_paths(eec->entities, 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);

View File

@ -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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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 += ", ";

View File

@ -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"));

View File

@ -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") {