Skip to content

Commit

Permalink
Refactored Fill / Flow for readability.
Browse files Browse the repository at this point in the history
Added an "overlap" member variable to fill classes in the preparation
for futher move of the "infill / perimeter" overlap to the Fill class.
Moved the orientation predicates from Fill to Geometry.
  • Loading branch information
bubnikv committed Jul 19, 2017
1 parent 9c1b182 commit 2f2c0dd
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 150 deletions.
8 changes: 4 additions & 4 deletions xs/src/libslic3r/Fill/Fill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ namespace Slic3r {

struct SurfaceGroupAttrib
{
SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {}
SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {}
bool operator==(const SurfaceGroupAttrib &other) const
{ return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; }
{ return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; }
bool is_solid;
float fw;
float flow_width;
// pattern is of type InfillPattern, -1 for an unset pattern.
int pattern;
};
Expand Down Expand Up @@ -68,7 +68,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
const Surface &surface = *groups[i].front();
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
group_attrib[i].is_solid = true;
group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear;
}
}
Expand Down
2 changes: 1 addition & 1 deletion xs/src/libslic3r/Fill/FillBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Fill* Fill::new_from_type(const std::string &type)
Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
{
// Perform offset.
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing)));
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
// Create the infills for each of the regions.
Polylines polylines_out;
for (size_t i = 0; i < expp.size(); ++ i)
Expand Down
10 changes: 8 additions & 2 deletions xs/src/libslic3r/Fill/FillBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ struct FillParams
dont_adjust = true;
}

bool full_infill() const { return density > 0.9999f; }

// Fill density, fraction in <0, 1>
float density;

Expand All @@ -46,6 +48,8 @@ class Fill
coordf_t z;
// in unscaled coordinates
coordf_t spacing;
// infill / perimeter overlap, in unscaled coordinates
coordf_t overlap;
// in radians, ccw, 0 = East
float angle;
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
Expand Down Expand Up @@ -77,8 +81,10 @@ class Fill
protected:
Fill() :
layer_id(size_t(-1)),
z(0.f),
spacing(0.f),
z(0.),
spacing(0.),
// Infill / perimeter overlap.
overlap(0.),
// Initial angle is undefined.
angle(FLT_MAX),
link_max_length(0),
Expand Down
63 changes: 7 additions & 56 deletions xs/src/libslic3r/Fill/FillRectilinear2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Geometry.hpp"
#include "../Surface.hpp"

#include "FillRectilinear2.hpp"
Expand Down Expand Up @@ -62,55 +63,6 @@ static inline coordf_t mag(const Point &p)
}
#endif /* mag */

enum Orientation
{
ORIENTATION_CCW = 1,
ORIENTATION_CW = -1,
ORIENTATION_COLINEAR = 0
};

// Return orientation of the three points (clockwise, counter-clockwise, colinear)
// The predicate is exact for the coord_t type, using 64bit signed integers for the temporaries.
//FIXME Make sure the temporaries do not overflow,
// which means, the coord_t types must not have some of the topmost bits utilized.
static inline Orientation orient(const Point &a, const Point &b, const Point &c)
{
// BOOST_STATIC_ASSERT(sizeof(coord_t) * 2 == sizeof(int64_t));
int64_t u = int64_t(b.x) * int64_t(c.y) - int64_t(b.y) * int64_t(c.x);
int64_t v = int64_t(a.x) * int64_t(c.y) - int64_t(a.y) * int64_t(c.x);
int64_t w = int64_t(a.x) * int64_t(b.y) - int64_t(a.y) * int64_t(b.x);
int64_t d = u - v + w;
return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW);
}

// Return orientation of the polygon.
// The input polygon must not contain duplicate points.
static inline bool is_ccw(const Polygon &poly)
{
// The polygon shall be at least a triangle.
myassert(poly.points.size() >= 3);
if (poly.points.size() < 3)
return true;

// 1) Find the lowest lexicographical point.
int imin = 0;
for (size_t i = 1; i < poly.points.size(); ++ i) {
const Point &pmin = poly.points[imin];
const Point &p = poly.points[i];
if (p.x < pmin.x || (p.x == pmin.x && p.y < pmin.y))
imin = i;
}

// 2) Detect the orientation of the corner imin.
size_t iPrev = ((imin == 0) ? poly.points.size() : imin) - 1;
size_t iNext = ((imin + 1 == poly.points.size()) ? 0 : imin + 1);
Orientation o = orient(poly.points[iPrev], poly.points[imin], poly.points[iNext]);
// The lowest bottom point must not be collinear if the polygon does not contain duplicate points
// or overlapping segments.
myassert(o != ORIENTATION_COLINEAR);
return o == ORIENTATION_CCW;
}

// Having a segment of a closed polygon, calculate its Euclidian length.
// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop,
// therefore the point p1 lies on poly.points[seg1-1], poly.points[seg1] etc.
Expand Down Expand Up @@ -390,7 +342,7 @@ struct ExPolygonWithOffset
for (size_t i = 0; i < n_contours; ++ i) {
contour(i).remove_duplicate_points();
myassert(! contour(i).has_duplicate_points());
polygons_ccw[i] = is_ccw(contour(i));
polygons_ccw[i] = Slic3r::Geometry::is_ccw(contour(i));
}
}

Expand Down Expand Up @@ -861,8 +813,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
ExPolygonWithOffset poly_with_offset(
surface->expolygon,
- rotate_vector.first,
scale_(- (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing),
scale_(- 0.5 * this->spacing));
scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing),
scale_(this->overlap - 0.5 * this->spacing));
if (poly_with_offset.n_contours_inner == 0) {
// Not a single infill line fits.
//FIXME maybe one shall trigger the gap fill here?
Expand All @@ -872,8 +824,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
BoundingBox bounding_box = poly_with_offset.bounding_box_src();

// define flow spacing according to requested density
bool full_infill = params.density > 0.9999f;
if (full_infill && !params.dont_adjust) {
if (params.full_infill() && !params.dont_adjust) {
line_spacing = this->_adjust_solid_spacing(bounding_box.size().x, line_spacing);
this->spacing = unscale(line_spacing);
} else {
Expand All @@ -893,7 +844,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// n_vlines = ceil(bbox_width / line_spacing)
size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing;
coord_t x0 = bounding_box.min.x;
if (full_infill)
if (params.full_infill())
x0 += (line_spacing + SCALED_EPSILON) / 2;

#ifdef SLIC3R_DEBUG
Expand Down Expand Up @@ -1108,7 +1059,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW &&
seg.intersections[i_intersection+1].type == SegmentIntersection::OUTER_HIGH) {
bool consumed = false;
// if (full_infill) {
// if (params.full_infill()) {
// measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection);
// } else
consumed = true;
Expand Down
174 changes: 100 additions & 74 deletions xs/src/libslic3r/Flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,113 +5,139 @@

namespace Slic3r {

/* 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) {
// This static method returns a sane extrusion width default.
static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, float height)
{
#if 1
// Here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate.
// shape: rectangle with semicircles at the ends
// This "sane" extrusion width gives the following results for a 0.4mm dmr nozzle:
// Layer Calculated Calculated width
// heigh extrusion over nozzle
// width diameter
// 0.40 0.40 1.00
// 0.35 0.43 1.09
// 0.30 0.48 1.21
// 0.25 0.56 1.39
// 0.20 0.67 1.68
// 0.15 0.87 2.17
// 0.10 1.28 3.20
// 0.05 2.52 6.31
//
float width = 0.25 * (nozzle_diameter * nozzle_diameter) * PI / height + height * (1.0 - 0.25 * PI);

switch (role) {
case frExternalPerimeter:
case frSupportMaterial:
case frSupportMaterialInterface:
return nozzle_diameter;
case frPerimeter:
case frSolidInfill:
case frTopSolidInfill:
// do not limit width for sparse infill so that we use full native flow for it
return std::min(std::max(width, nozzle_diameter * 1.05f), nozzle_diameter * 1.7f);
case frInfill:
default:
return std::max(width, nozzle_diameter * 1.05f);
}
#else
// 1.125f * nozzle_diameter;
switch (role) {
case frSupportMaterial:
case frSupportMaterialInterface:
case frTopSolidInfill:
return nozzle_diameter;
default:
case frExternalPerimeter:
1.125f * nozzle_diameter;
case frPerimeter:
case frSolidInfill:
// do not limit width for sparse infill so that we use full native flow for it
return std::min(std::max(width, nozzle_diameter * 1.05), nozzle_diameter * 1.7);
case frInfill:
return std::max(width, nozzle_diameter * 1.05);
}
#endif
}

// 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)
{
// we need layer height unless it's a bridge
if (height <= 0 && bridge_flow_ratio == 0) CONFESS("Invalid flow height supplied to new_from_config_width()");

if (height <= 0 && bridge_flow_ratio == 0)
CONFESS("Invalid flow height supplied to new_from_config_width()");

float w;
if (bridge_flow_ratio > 0) {
// if bridge flow was requested, calculate bridge width
height = w = Flow::_bridge_width(nozzle_diameter, bridge_flow_ratio);
} else if (!width.percent && width.value == 0) {
// if user left option to 0, calculate a sane default width
w = Flow::_auto_width(role, nozzle_diameter, height);
// 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 user left option to 0, calculate a sane default width.
w = auto_extrusion_width(role, nozzle_diameter, height);
} else {
// if user set a manual value, use it
// If user set a manual value, use it.
w = width.get_abs_value(height);
}

return Flow(w, height, nozzle_diameter, bridge_flow_ratio > 0);
}

/* 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) {
// 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)
{
// we need layer height unless it's a bridge
if (height <= 0 && !bridge) CONFESS("Invalid flow height supplied to new_from_spacing()");

float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge);
if (bridge) height = w;
return Flow(w, height, nozzle_diameter, bridge);
if (height <= 0 && !bridge)
CONFESS("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 = bridge ?
(spacing - BRIDGE_EXTRA_SPACING) :
#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);
}

/* This method returns the centerline spacing between two adjacent extrusions
having the same extrusion width (and other properties). */
float
Flow::spacing() const
// 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 - PI/4.0);
float min_flow_spacing = this->width - this->height * (1. - 0.25 * PI);
return this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing);
#else
return this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1 - PI/4.0));
return this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1. - 0.25 * PI));
#endif
}

/* 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 {
// 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);
return this->bridge ?
0.5f * this->width + 0.5f * other.width + BRIDGE_EXTRA_SPACING :
0.5f * this->spacing() + 0.5f * other.spacing();
}

/* This method returns extrusion volume per head move unit. */
// This method returns extrusion volume per head move unit.
double Flow::mm3_per_mm() const
{
return this->bridge ?
(this->width * this->width) * PI/4.0 :
this->width * this->height + (this->height * this->height) / 4.0 * (PI-4.0);
}

/* This static method returns bridge width for a given nozzle diameter. */
float Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) {
return (bridge_flow_ratio == 1.) ?
// optimization to avoid sqrt()
nozzle_diameter :
sqrt(bridge_flow_ratio) * nozzle_diameter;
}

/* This static method returns a sane extrusion width default. */
float Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) {
// here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
// shape: rectangle with semicircles at the ends
float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height);

float min = nozzle_diameter * 1.05;
float max = -1;
if (role == frExternalPerimeter || role == frSupportMaterial || role == frSupportMaterialInterface) {
min = max = nozzle_diameter;
} else if (role != frInfill) {
// do not limit width for sparse infill so that we use full native flow for it
max = nozzle_diameter * 1.7;
}
if (max != -1 && width > max) width = max;
if (width < min) width = min;

return width;
}

/* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */
float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge)
{
return bridge ?
(spacing - BRIDGE_EXTRA_SPACING) :
#ifdef HAS_PERIMETER_LINE_OVERLAP
(spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1 - PI/4.0));
#else
(spacing + height * (1 - PI/4.0));
#endif
(this->width * this->width) * 0.25 * PI :
this->width * this->height + 0.25 * (this->height * this->height) / (PI - 4.0);
}

Flow support_material_flow(const PrintObject *object, float layer_height)
Expand Down
Loading

0 comments on commit 2f2c0dd

Please sign in to comment.