diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index fbdb3d0c2..ffae7fa5b 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -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; }; @@ -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; } } diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp index 7258e11fe..d2f874436 100644 --- a/xs/src/libslic3r/Fill/FillBase.cpp +++ b/xs/src/libslic3r/Fill/FillBase.cpp @@ -45,7 +45,7 @@ Fill* Fill::new_from_type(const std::string &type) Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) { // 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) diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/xs/src/libslic3r/Fill/FillBase.hpp index 5f83e6b9c..594c498dd 100644 --- a/xs/src/libslic3r/Fill/FillBase.hpp +++ b/xs/src/libslic3r/Fill/FillBase.hpp @@ -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; @@ -46,6 +48,8 @@ public: 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. @@ -77,8 +81,10 @@ public: 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), diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index c334cd366..7af92a6ac 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -9,6 +9,7 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" +#include "../Geometry.hpp" #include "../Surface.hpp" #include "FillRectilinear2.hpp" @@ -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. @@ -390,7 +342,7 @@ public: 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)); } } @@ -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? @@ -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 { @@ -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 @@ -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; diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index d3414da57..2581403bf 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -5,60 +5,126 @@ 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 ? @@ -66,52 +132,12 @@ Flow::spacing(const Flow &other) const { 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) diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp index a01070c45..a058cc8b7 100644 --- a/xs/src/libslic3r/Flow.hpp +++ b/xs/src/libslic3r/Flow.hpp @@ -13,7 +13,6 @@ class PrintObject; #define BRIDGE_EXTRA_SPACING 0.05 // Overlap factor of perimeter lines. Currently no overlap. -// #define HAS_OVERLAP #ifdef HAS_PERIMETER_LINE_OVERLAP #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 #endif @@ -37,28 +36,23 @@ public: // Non bridging flow: Layer height. // Bridging flow: Bridge thread diameter = layer height. float height; - // Nozzle diameter is + // Nozzle diameter. float nozzle_diameter; // Is it a bridge? bool bridge; - Flow(float _w, float _h, float _nd, bool _bridge = false) - : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; - float spacing() const; - float spacing(const Flow &other) const; - double mm3_per_mm() const; + Flow(float _w, float _h, float _nd, bool _bridge = false) : + width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; + + 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_spacing() const { return coord_t(scale_(this->spacing())); }; coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }; static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); - - private: - static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio); - static float _auto_width(FlowRole role, float nozzle_diameter, float height); - static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); - static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio); }; extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index 08ebe93ee..8e7fb5c8c 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -13,6 +13,59 @@ using boost::polygon::voronoi_diagram; namespace Slic3r { namespace Geometry { +// Generic result of an orientation predicate. +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. +// which means, the coord_t types must not have some of the topmost bits utilized. +// As the points are limited to 30 bits + signum, +// the temporaries u, v, w are limited to 61 bits + signum, +// and d is limited to 63 bits + signum and we are good. +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 by checking orientation of the left bottom corner of the polygon +// using exact arithmetics. The input polygon must not contain duplicate points +// (or at least the left bottom corner point must not have duplicates). +static inline bool is_ccw(const Polygon &poly) +{ + // The polygon shall be at least a triangle. + assert(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. + assert(o != ORIENTATION_COLINEAR); + return o == ORIENTATION_CCW; +} + inline bool ray_ray_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res) { double denom = v1.x * v2.y - v2.x * v1.y;