Fills were reimplemented in C++.
While reimplementing the FillPlanePath code, the octagon infill was fixed to extrude the right amount of material.
This commit is contained in:
parent
7da68c91a5
commit
be3e4caf1d
12 changed files with 1162 additions and 0 deletions
223
xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
Normal file
223
xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
Normal file
|
@ -0,0 +1,223 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "Fill3DHoneycomb.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/*
|
||||
Creates a contiguous sequence of points at a specified height that make
|
||||
up a horizontal slice of the edges of a space filling truncated
|
||||
octahedron tesselation. The octahedrons are oriented so that the
|
||||
square faces are in the horizontal plane with edges parallel to the X
|
||||
and Y axes.
|
||||
|
||||
Credits: David Eccles (gringer).
|
||||
*/
|
||||
|
||||
// Generate an array of points that are in the same direction as the
|
||||
// basic printing line (i.e. Y points for columns, X points for rows)
|
||||
// Note: a negative offset only causes a change in the perpendicular
|
||||
// direction
|
||||
static std::vector<coordf_t> colinearPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength)
|
||||
{
|
||||
const coordf_t offset2 = std::abs(offset / coordf_t(2.));
|
||||
std::vector<coordf_t> points;
|
||||
points.push_back(baseLocation - offset2);
|
||||
for (size_t i = 0; i < gridLength; ++i) {
|
||||
points.push_back(baseLocation + i + offset2);
|
||||
points.push_back(baseLocation + i + 1 - offset2);
|
||||
}
|
||||
points.push_back(baseLocation + gridLength + offset2);
|
||||
return points;
|
||||
}
|
||||
|
||||
// Generate an array of points for the dimension that is perpendicular to
|
||||
// the basic printing line (i.e. X points for columns, Y points for rows)
|
||||
static std::vector<coordf_t> perpendPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength)
|
||||
{
|
||||
coordf_t offset2 = offset / coordf_t(2.);
|
||||
coord_t side = 2 * (baseLocation & 1) - 1;
|
||||
std::vector<coordf_t> points;
|
||||
points.push_back(baseLocation - offset2 * side);
|
||||
for (size_t i = 0; i < gridLength; ++i) {
|
||||
side = 2*((i+baseLocation) & 1) - 1;
|
||||
points.push_back(baseLocation + offset2 * side);
|
||||
points.push_back(baseLocation + offset2 * side);
|
||||
}
|
||||
points.push_back(baseLocation - offset2 * side);
|
||||
return points;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T clamp(T low, T high, T x)
|
||||
{
|
||||
return std::max<T>(low, std::min<T>(high, x));
|
||||
}
|
||||
|
||||
// Trims an array of points to specified rectangular limits. Point
|
||||
// components that are outside these limits are set to the limits.
|
||||
static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY)
|
||||
{
|
||||
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) {
|
||||
it->x = clamp(minX, maxX, it->x);
|
||||
it->y = clamp(minY, maxY, it->y);
|
||||
}
|
||||
}
|
||||
|
||||
static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coordf_t> &y)
|
||||
{
|
||||
assert(x.size() == y.size());
|
||||
Pointfs out;
|
||||
out.reserve(x.size());
|
||||
for (size_t i = 0; i < x.size(); ++ i)
|
||||
out.push_back(Pointf(x[i], y[i]));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Generate a set of curves (array of array of 2d points) that describe a
|
||||
// horizontal slice of a truncated regular octahedron with edge length 1.
|
||||
// curveType specifies which lines to print, 1 for vertical lines
|
||||
// (columns), 2 for horizontal lines (rows), and 3 for both.
|
||||
static std::vector<Pointfs> makeNormalisedGrid(coordf_t z, size_t gridWidth, size_t gridHeight, size_t curveType)
|
||||
{
|
||||
// offset required to create a regular octagram
|
||||
coordf_t octagramGap = coordf_t(0.5);
|
||||
|
||||
// sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap]
|
||||
coordf_t a = std::sqrt(coordf_t(2.)); // period
|
||||
coordf_t wave = abs(fmod(z, a) - a/2.)/a*4. - 1.;
|
||||
coordf_t offset = wave * octagramGap;
|
||||
|
||||
std::vector<Pointfs> points;
|
||||
if ((curveType & 1) != 0) {
|
||||
for (size_t x = 0; x <= gridWidth; ++x) {
|
||||
points.push_back(Pointfs());
|
||||
Pointfs &newPoints = points.back();
|
||||
newPoints = zip(
|
||||
perpendPoints(offset, x, gridHeight),
|
||||
colinearPoints(offset, 0, gridHeight));
|
||||
// trim points to grid edges
|
||||
trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight));
|
||||
if (x & 1)
|
||||
std::reverse(newPoints.begin(), newPoints.end());
|
||||
}
|
||||
}
|
||||
if ((curveType & 2) != 0) {
|
||||
for (size_t y = 0; y <= gridHeight; ++y) {
|
||||
points.push_back(Pointfs());
|
||||
Pointfs &newPoints = points.back();
|
||||
newPoints = zip(
|
||||
colinearPoints(offset, 0, gridWidth),
|
||||
perpendPoints(offset, y, gridWidth));
|
||||
// trim points to grid edges
|
||||
trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight));
|
||||
if (y & 1)
|
||||
std::reverse(newPoints.begin(), newPoints.end());
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
// Generate a set of curves (array of array of 2d points) that describe a
|
||||
// horizontal slice of a truncated regular octahedron with a specified
|
||||
// grid square size.
|
||||
static Polylines makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_t curveType)
|
||||
{
|
||||
coord_t scaleFactor = gridSize;
|
||||
coordf_t normalisedZ = coordf_t(z) / coordf_t(scaleFactor);
|
||||
std::vector<Pointfs> polylines = makeNormalisedGrid(normalisedZ, gridWidth, gridHeight, curveType);
|
||||
Polylines result;
|
||||
result.reserve(polylines.size());
|
||||
for (std::vector<Pointfs>::const_iterator it_polylines = polylines.begin(); it_polylines != polylines.end(); ++ it_polylines) {
|
||||
result.push_back(Polyline());
|
||||
Polyline &polyline = result.back();
|
||||
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
|
||||
polyline.points.push_back(Point(coord_t(it->x * scaleFactor), coord_t(it->y * scaleFactor)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Polylines Fill3DHoneycomb::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
ExPolygon expolygon = surface->expolygon;
|
||||
BoundingBox bb = expolygon.contour.bounding_box();
|
||||
Point size = bb.size();
|
||||
coord_t distance = coord_t(scale_(this->spacing) / params.density);
|
||||
|
||||
// align bounding box to a multiple of our honeycomb grid module
|
||||
// (a module is 2*$distance since one $distance half-module is
|
||||
// growing while the other $distance half-module is shrinking)
|
||||
bb.merge(Point(
|
||||
bb.min.x - (bb.min.x % (2*distance)),
|
||||
bb.min.y - (bb.min.y % (2*distance))));
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines = makeGrid(
|
||||
scale_(this->z),
|
||||
distance,
|
||||
ceil(size.x / distance) + 1,
|
||||
ceil(size.y / distance) + 1,
|
||||
((this->layer_id / surface->thickness_layers) % 2) + 1);
|
||||
|
||||
// move pattern in place
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it)
|
||||
it->translate(bb.min.x, bb.min.y);
|
||||
|
||||
// clip pattern to boundaries
|
||||
intersection(polylines, (Polygons)expolygon, &polylines);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
std::move(polylines),
|
||||
#else
|
||||
polylines,
|
||||
#endif
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
assert(polylines.empty());
|
||||
#else
|
||||
polylines.clear();
|
||||
#endif
|
||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
||||
if (! polylines.empty()) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines.back().points;
|
||||
const Point &first_point = it_polyline->points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
if (first_point.distance_to(last_point) <= 1.5 * distance &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
polylines.push_back(std::move(*it_polyline));
|
||||
#else
|
||||
polylines.push_back(Polyline());
|
||||
std::swap(polylines.back(), *it_polyline);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: return ExtrusionLoop objects to get better chained paths
|
||||
return polylines;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
24
xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp
Normal file
24
xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef slic3r_Fill3DHoneycomb_hpp_
|
||||
#define slic3r_Fill3DHoneycomb_hpp_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Fill3DHoneycomb : public FillWithDirection
|
||||
{
|
||||
public:
|
||||
virtual ~Fill3DHoneycomb() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return true; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_Fill3DHoneycomb_hpp_
|
83
xs/src/libslic3r/Fill/FillBase.cpp
Normal file
83
xs/src/libslic3r/Fill/FillBase.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
#include "FillConcentric.hpp"
|
||||
#include "FillHoneycomb.hpp"
|
||||
#include "Fill3DHoneycomb.hpp"
|
||||
#include "FillPlanePath.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Fill* Fill::new_from_type(const std::string &type)
|
||||
{
|
||||
if (type == "concentric")
|
||||
return new FillConcentric();
|
||||
if (type == "honeycomb")
|
||||
return new FillHoneycomb();
|
||||
if (type == "3dhoneycomb")
|
||||
return new Fill3DHoneycomb();
|
||||
if (type == "rectilinear")
|
||||
return new FillRectilinear();
|
||||
if (type == "line")
|
||||
return new FillLine();
|
||||
if (type == "grid")
|
||||
return new FillGrid();
|
||||
if (type == "archimedeanchords")
|
||||
return new FillArchimedeanChords();
|
||||
if (type == "hilbertcurve")
|
||||
return new FillHilbertCurve();
|
||||
if (type == "octagramspiral")
|
||||
return new FillOctagramSpiral();
|
||||
CONFESS("unknown type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
coord_t Fill::adjust_solid_spacing(const coord_t width, const coord_t distance)
|
||||
{
|
||||
coord_t number_of_lines = coord_t(coordf_t(width) / coordf_t(distance)) + 1;
|
||||
coord_t extra_space = width % distance;
|
||||
return (number_of_lines <= 1) ?
|
||||
distance :
|
||||
distance + extra_space / (number_of_lines - 1);
|
||||
}
|
||||
|
||||
std::pair<float, Point> FillWithDirection::infill_direction(const Surface *surface) const
|
||||
{
|
||||
// set infill angle
|
||||
float out_angle = this->angle;
|
||||
|
||||
if (out_angle == FLT_MAX) {
|
||||
//FIXME Vojtech: Add a warning?
|
||||
// warn "Using undefined infill angle";
|
||||
out_angle = 0.f;
|
||||
}
|
||||
|
||||
Point out_shift = empty(this->bounding_box) ?
|
||||
surface->expolygon.contour.bounding_box().center() :
|
||||
this->bounding_box.center();
|
||||
|
||||
if (surface->bridge_angle >= 0) {
|
||||
// use bridge angle
|
||||
//FIXME Vojtech: Add a debugf?
|
||||
// Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle);
|
||||
#if 1
|
||||
//#ifdef _DEBUG
|
||||
printf("Filling bridge with angle %f\n", surface->bridge_angle);
|
||||
#endif /* _DEBUG */
|
||||
out_angle = surface->bridge_angle;
|
||||
} else if (this->layer_id != size_t(-1)) {
|
||||
// alternate fill direction
|
||||
printf("Filling layer %d, thickness %d, id: %d\n",
|
||||
this->layer_id, surface->thickness_layers, int(this->layer_id / surface->thickness_layers));
|
||||
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
|
||||
} else {
|
||||
printf("Layer_ID undefined!\n");
|
||||
}
|
||||
|
||||
out_angle += float(M_PI/2.);
|
||||
printf("out_angle: %f", out_angle);
|
||||
return std::pair<float, Point>(out_angle, out_shift);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
102
xs/src/libslic3r/Fill/FillBase.hpp
Normal file
102
xs/src/libslic3r/Fill/FillBase.hpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
#ifndef slic3r_FillBase_hpp_
|
||||
#define slic3r_FillBase_hpp_
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../BoundingBox.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
struct FillParams
|
||||
{
|
||||
coordf_t width;
|
||||
// Fraction in <0, 1>
|
||||
float density;
|
||||
coordf_t distance;
|
||||
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect;
|
||||
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust;
|
||||
|
||||
// For Honeycomb.
|
||||
// we were requested to complete each loop;
|
||||
// in this case we don't try to make more continuous paths
|
||||
bool complete;
|
||||
};
|
||||
|
||||
class Fill
|
||||
{
|
||||
public:
|
||||
// Index of the layer.
|
||||
size_t layer_id;
|
||||
// Height of the layer, in unscaled coordinates
|
||||
coordf_t z;
|
||||
// in unscaled coordinates
|
||||
coordf_t spacing;
|
||||
// in radians, ccw, 0 = East
|
||||
float angle;
|
||||
// in scaled coordinates
|
||||
coord_t loop_clipping;
|
||||
// in scaled coordinates
|
||||
BoundingBox bounding_box;
|
||||
|
||||
public:
|
||||
virtual ~Fill() {}
|
||||
|
||||
static Fill* new_from_type(const std::string &type);
|
||||
|
||||
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
|
||||
|
||||
// Use bridge flow for the fill?
|
||||
virtual bool use_bridge_flow() const { return false; }
|
||||
|
||||
// Do not sort the fill lines to optimize the print head path?
|
||||
virtual bool no_sort() const { return false; }
|
||||
|
||||
// Perform the fill.
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) = 0;
|
||||
|
||||
protected:
|
||||
Fill() :
|
||||
layer_id(size_t(-1)),
|
||||
z(0.f),
|
||||
spacing(0.f),
|
||||
// Initial angle is undefined.
|
||||
angle(FLT_MAX),
|
||||
loop_clipping(0),
|
||||
// The initial bounding box is empty, therefore undefined.
|
||||
bounding_box(Point(0, 0), Point(-1, -1))
|
||||
{}
|
||||
|
||||
static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||
};
|
||||
|
||||
// An interface class to Perl, aggregating an instance of a Fill and a FillData.
|
||||
class Filler
|
||||
{
|
||||
public:
|
||||
Filler() : fill(NULL) {}
|
||||
~Filler() { delete fill; fill = NULL; }
|
||||
Fill *fill;
|
||||
FillParams params;
|
||||
};
|
||||
|
||||
class FillWithDirection : public Fill
|
||||
{
|
||||
public:
|
||||
virtual float _layer_angle(size_t idx) const {
|
||||
bool odd = idx & 1;
|
||||
printf("_layer_angle: %s\n", odd ? "odd" : "even");
|
||||
return (idx & 1) ? float(M_PI/2.) : 0;
|
||||
}
|
||||
virtual std::pair<float, Point> infill_direction(const Surface *surface) const ;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillBase_hpp_
|
60
xs/src/libslic3r/Fill/FillConcentric.cpp
Normal file
60
xs/src/libslic3r/Fill/FillConcentric.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillConcentric.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Polylines FillConcentric::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
ExPolygon expolygon = surface->expolygon;
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
coord_t distance = coord_t(min_spacing / params.density);
|
||||
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
distance = this->adjust_solid_spacing(bounding_box.size().x, distance);
|
||||
this->spacing = unscale(distance);
|
||||
}
|
||||
|
||||
Polygons loops = (Polygons)expolygon;
|
||||
Polygons last = loops;
|
||||
while (! last.empty()) {
|
||||
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
|
||||
loops.insert(loops.end(), last.begin(), last.end());
|
||||
}
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
union_pt_chained(loops, &loops, false);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
Polylines paths;
|
||||
Point last_pos(0, 0);
|
||||
for (Polygons::const_iterator it_loop = loops.begin(); it_loop != loops.end(); ++ it_loop) {
|
||||
paths.push_back(it_loop->split_at_index(last_pos.nearest_point_index(*it_loop)));
|
||||
last_pos = paths.back().last_point();
|
||||
}
|
||||
|
||||
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
|
||||
// Keep valid paths only.
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < paths.size(); ++ i) {
|
||||
paths[i].clip_end(this->loop_clipping);
|
||||
if (paths[i].is_valid()) {
|
||||
if (j < i)
|
||||
std::swap(paths[j], paths[i]);
|
||||
++ j;
|
||||
}
|
||||
}
|
||||
if (j < paths.size())
|
||||
paths.erase(paths.begin() + j, paths.end());
|
||||
|
||||
// TODO: return ExtrusionLoop objects to get better chained paths
|
||||
return paths;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
20
xs/src/libslic3r/Fill/FillConcentric.hpp
Normal file
20
xs/src/libslic3r/Fill/FillConcentric.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef slic3r_FillConcentric_hpp_
|
||||
#define slic3r_FillConcentric_hpp_
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class FillConcentric : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillConcentric() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
virtual bool no_sort() const { return true; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillConcentric_hpp_
|
133
xs/src/libslic3r/Fill/FillHoneycomb.cpp
Normal file
133
xs/src/libslic3r/Fill/FillHoneycomb.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillHoneycomb.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Polylines FillHoneycomb::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
|
||||
|
||||
// cache hexagons math
|
||||
CacheID cache_id(params.density, this->spacing);
|
||||
Cache::iterator it_m = this->cache.find(cache_id);
|
||||
if (it_m == this->cache.end()) {
|
||||
#if SLIC3R_CPPVER > 11
|
||||
it_m = this->cache.emplace_hint(it_m);
|
||||
#else
|
||||
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
|
||||
#endif
|
||||
CacheData &m = it_m->second;
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
m.distance = min_spacing / params.density;
|
||||
m.hex_side = m.distance / (sqrt(3)/2);
|
||||
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
coord_t hex_height = m.hex_side * 2;
|
||||
m.pattern_height = hex_height + m.hex_side;
|
||||
m.y_short = m.distance * sqrt(3)/3;
|
||||
m.x_offset = min_spacing / 2;
|
||||
m.y_offset = m.x_offset * sqrt(3)/3;
|
||||
m.hex_center = Point(m.hex_width/2, m.hex_side);
|
||||
}
|
||||
CacheData &m = it_m->second;
|
||||
|
||||
Polygons polygons;
|
||||
{
|
||||
// adjust actual bounding box to the nearest multiple of our hex pattern
|
||||
// and align it so that it matches across layers
|
||||
|
||||
BoundingBox bounding_box = surface->expolygon.contour.bounding_box();
|
||||
{
|
||||
// rotate bounding box according to infill direction
|
||||
Polygon bb_polygon = bounding_box.polygon();
|
||||
bb_polygon.rotate(rotate_vector.first, m.hex_center);
|
||||
bounding_box = bb_polygon.bounding_box();
|
||||
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
|
||||
bounding_box.merge(Point(
|
||||
bounding_box.min.x - (bounding_box.min.x % m.hex_width),
|
||||
bounding_box.min.y - (bounding_box.min.y % m.pattern_height)));
|
||||
}
|
||||
|
||||
coord_t x = bounding_box.min.x;
|
||||
while (x <= bounding_box.max.x) {
|
||||
Polygon p;
|
||||
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
|
||||
for (size_t i = 0; i < 2; ++ i) {
|
||||
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
|
||||
for (coord_t y = bounding_box.min.y; y <= bounding_box.max.y; y += m.y_short + m.hex_side + m.y_short + m.hex_side) {
|
||||
p.points.push_back(Point(ax[1], y + m.y_offset));
|
||||
p.points.push_back(Point(ax[0], y + m.y_short - m.y_offset));
|
||||
p.points.push_back(Point(ax[0], y + m.y_short + m.hex_side + m.y_offset));
|
||||
p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short - m.y_offset));
|
||||
p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short + m.hex_side + m.y_offset));
|
||||
}
|
||||
ax[0] = ax[0] + m.distance;
|
||||
ax[1] = ax[1] + m.distance;
|
||||
std::swap(ax[0], ax[1]); // draw symmetrical pattern
|
||||
x += m.distance;
|
||||
}
|
||||
p.rotate(-rotate_vector.first, m.hex_center);
|
||||
polygons.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
Polylines paths;
|
||||
if (params.complete || true) {
|
||||
// we were requested to complete each loop;
|
||||
// in this case we don't try to make more continuous paths
|
||||
Polygons polygons_trimmed = intersection((Polygons)*surface, polygons);
|
||||
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it)
|
||||
paths.push_back(it->split_at_first_point());
|
||||
} else {
|
||||
// consider polygons as polylines without re-appending the initial point:
|
||||
// this cuts the last segment on purpose, so that the jump to the next
|
||||
// path is more straight
|
||||
{
|
||||
Polylines p;
|
||||
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
p.push_back((Polyline)(*it));
|
||||
paths = intersection(p, (Polygons)*surface);
|
||||
}
|
||||
|
||||
// connect paths
|
||||
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
std::move(paths),
|
||||
#else
|
||||
paths,
|
||||
#endif
|
||||
PolylineCollection::leftmost_point(paths), false);
|
||||
assert(paths.empty());
|
||||
paths.clear();
|
||||
for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) {
|
||||
if (! paths.empty()) {
|
||||
// distance between first point of this path and last point of last path
|
||||
double distance = paths.back().last_point().distance_to(it_path->first_point());
|
||||
if (distance <= m.hex_width) {
|
||||
paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Don't connect the paths.
|
||||
paths.push_back(*it_path);
|
||||
}
|
||||
}
|
||||
|
||||
// clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||
Polylines paths_trimmed = intersection(paths, to_polygons(offset_ex(surface->expolygon, SCALED_EPSILON)));
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
paths = std::move(paths_trimmed);
|
||||
#else
|
||||
std::swap(paths, paths_trimmed);
|
||||
#endif
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
50
xs/src/libslic3r/Fill/FillHoneycomb.hpp
Normal file
50
xs/src/libslic3r/Fill/FillHoneycomb.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef slic3r_FillHoneycomb_hpp_
|
||||
#define slic3r_FillHoneycomb_hpp_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class FillHoneycomb : public FillWithDirection
|
||||
{
|
||||
public:
|
||||
virtual ~FillHoneycomb() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// Caching the
|
||||
struct CacheID
|
||||
{
|
||||
CacheID(float adensity, coordf_t aspacing) :
|
||||
density(adensity), spacing(aspacing) {}
|
||||
float density;
|
||||
coordf_t spacing;
|
||||
bool operator<(const CacheID &other) const
|
||||
{ return (density < other.density) || (density == other.density && spacing < other.spacing); }
|
||||
bool operator==(const CacheID &other) const
|
||||
{ return density == other.density && spacing == other.spacing; }
|
||||
};
|
||||
struct CacheData
|
||||
{
|
||||
coord_t distance;
|
||||
coord_t hex_side;
|
||||
coord_t hex_width;
|
||||
coord_t pattern_height;
|
||||
coord_t y_short;
|
||||
coord_t x_offset;
|
||||
coord_t y_offset;
|
||||
Point hex_center;
|
||||
};
|
||||
typedef std::map<CacheID, CacheData> Cache;
|
||||
Cache cache;
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return 0.5f * float(M_PI) * (idx % 3); }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillHoneycomb_hpp_
|
197
xs/src/libslic3r/Fill/FillPlanePath.cpp
Normal file
197
xs/src/libslic3r/Fill/FillPlanePath.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillPlanePath.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Polylines FillPlanePath::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
ExPolygon expolygon = surface->expolygon;
|
||||
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
|
||||
expolygon.rotate(- rotate_vector.first);
|
||||
|
||||
coord_t distance_between_lines = scale_(this->spacing) / params.density;
|
||||
|
||||
// align infill across layers using the object's bounding box
|
||||
Polygon bb_polygon = this->bounding_box.polygon();
|
||||
bb_polygon.rotate(- rotate_vector.first);
|
||||
BoundingBox bounding_box = bb_polygon.bounding_box();
|
||||
|
||||
Point shift = this->_centered() ?
|
||||
bounding_box.center() :
|
||||
bounding_box.min;
|
||||
expolygon.translate(-shift.x, -shift.y);
|
||||
bounding_box.translate(-shift.x, -shift.y);
|
||||
|
||||
Pointfs pts = _generate(
|
||||
coord_t(ceil(coordf_t(bounding_box.min.x) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.min.y) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max.x) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max.y) / distance_between_lines)));
|
||||
|
||||
Polylines polylines;
|
||||
if (pts.size() >= 2) {
|
||||
// Convert points to a polyline, upscale.
|
||||
polylines.push_back(Polyline());
|
||||
Polyline &polyline = polylines.back();
|
||||
polyline.points.reserve(pts.size());
|
||||
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it)
|
||||
polyline.points.push_back(Point(
|
||||
coord_t(floor(it->x * distance_between_lines + 0.5)),
|
||||
coord_t(floor(it->y * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
intersection(polylines, (Polygons)expolygon, &polylines);
|
||||
|
||||
/*
|
||||
if (1) {
|
||||
require "Slic3r/SVG.pm";
|
||||
print "Writing fill.svg\n";
|
||||
Slic3r::SVG::output("fill.svg",
|
||||
no_arrows => 1,
|
||||
polygons => \@$expolygon,
|
||||
green_polygons => [ $bounding_box->polygon ],
|
||||
polylines => [ $polyline ],
|
||||
red_polylines => \@paths,
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
// paths must be repositioned and rotated back
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
|
||||
it->translate(shift.x, shift.y);
|
||||
it->rotate(rotate_vector.first);
|
||||
}
|
||||
}
|
||||
|
||||
return polylines;
|
||||
}
|
||||
|
||||
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
|
||||
Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
|
||||
{
|
||||
// Radius to achieve.
|
||||
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
|
||||
// Now unwind the spiral.
|
||||
coordf_t a = 1.;
|
||||
coordf_t b = 1./(2.*M_PI);
|
||||
coordf_t theta = 0.;
|
||||
coordf_t r = 1;
|
||||
Pointfs out;
|
||||
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
|
||||
out.push_back(Pointf(0, 0));
|
||||
out.push_back(Pointf(1, 0));
|
||||
while (r < rmax) {
|
||||
theta += 1. / r;
|
||||
r = a + b * theta;
|
||||
out.push_back(Pointf(r * cos(theta), r * sin(theta)));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Adapted from
|
||||
// http://cpansearch.perl.org/src/KRYDE/Math-PlanePath-122/lib/Math/PlanePath/HilbertCurve.pm
|
||||
//
|
||||
// state=0 3--2 plain
|
||||
// |
|
||||
// 0--1
|
||||
//
|
||||
// state=4 1--2 transpose
|
||||
// | |
|
||||
// 0 3
|
||||
//
|
||||
// state=8
|
||||
//
|
||||
// state=12 3 0 rot180 + transpose
|
||||
// | |
|
||||
// 2--1
|
||||
//
|
||||
static inline Point hilbert_n_to_xy(const size_t n)
|
||||
{
|
||||
static const int next_state[16] = { 4,0,0,12, 0,4,4,8, 12,8,8,4, 8,12,12,0 };
|
||||
static const int digit_to_x[16] = { 0,1,1,0, 0,0,1,1, 1,0,0,1, 1,1,0,0 };
|
||||
static const int digit_to_y[16] = { 0,0,1,1, 0,1,1,0, 1,1,0,0, 1,0,0,1 };
|
||||
|
||||
// Number of 2 bit digits.
|
||||
size_t ndigits = 0;
|
||||
{
|
||||
size_t nc = n;
|
||||
while(nc > 0) {
|
||||
nc >>= 2;
|
||||
++ ndigits;
|
||||
}
|
||||
}
|
||||
int state = (ndigits & 1) ? 4 : 0;
|
||||
int dirstate = (ndigits & 1) ? 0 : 4;
|
||||
coord_t x = 0;
|
||||
coord_t y = 0;
|
||||
for (int i = (int)ndigits - 1; i >= 0; -- i) {
|
||||
int digit = (n >> (i * 2)) & 3;
|
||||
state += digit;
|
||||
if (digit != 3)
|
||||
dirstate = state; // lowest non-3 digit
|
||||
x |= digit_to_x[state] << i;
|
||||
y |= digit_to_y[state] << i;
|
||||
state = next_state[state];
|
||||
}
|
||||
return Point(x, y);
|
||||
}
|
||||
|
||||
Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
|
||||
{
|
||||
// Minimum power of two square to fit the domain.
|
||||
size_t sz = 2;
|
||||
size_t pw = 1;
|
||||
{
|
||||
size_t sz0 = std::max(max_x + 1 - min_x, max_y + 1 - min_y);
|
||||
while (sz < sz0) {
|
||||
sz = sz << 1;
|
||||
++ pw;
|
||||
}
|
||||
}
|
||||
|
||||
size_t sz2 = sz * sz;
|
||||
Pointfs line;
|
||||
line.reserve(sz2);
|
||||
for (size_t i = 0; i < sz2; ++ i) {
|
||||
Point p = hilbert_n_to_xy(i);
|
||||
line.push_back(Pointf(p.x + min_x, p.y + min_y));
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
|
||||
{
|
||||
// Radius to achieve.
|
||||
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
|
||||
// Now unwind the spiral.
|
||||
coordf_t r = 0;
|
||||
coordf_t r_inc = sqrt(2.);
|
||||
Pointfs out;
|
||||
out.push_back(Pointf(0, 0));
|
||||
while (r < rmax) {
|
||||
r += r_inc;
|
||||
coordf_t rx = r / sqrt(2.);
|
||||
coordf_t r2 = r + rx;
|
||||
out.push_back(Pointf( r, 0.));
|
||||
out.push_back(Pointf( r2, rx));
|
||||
out.push_back(Pointf( rx, rx));
|
||||
out.push_back(Pointf( rx, r2));
|
||||
out.push_back(Pointf(0., r));
|
||||
out.push_back(Pointf(-rx, r2));
|
||||
out.push_back(Pointf(-rx, rx));
|
||||
out.push_back(Pointf(-r2, rx));
|
||||
out.push_back(Pointf(-r, 0.));
|
||||
out.push_back(Pointf(-r2, -rx));
|
||||
out.push_back(Pointf(-rx, -rx));
|
||||
out.push_back(Pointf(-rx, -r2));
|
||||
out.push_back(Pointf(0., -r));
|
||||
out.push_back(Pointf( rx, -r2));
|
||||
out.push_back(Pointf( rx, -rx));
|
||||
out.push_back(Pointf( r2+r_inc, -rx));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
60
xs/src/libslic3r/Fill/FillPlanePath.hpp
Normal file
60
xs/src/libslic3r/Fill/FillPlanePath.hpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef slic3r_FillPlanePath_hpp_
|
||||
#define slic3r_FillPlanePath_hpp_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// The original Perl code used path generators from Math::PlanePath library:
|
||||
// http://user42.tuxfamily.org/math-planepath/
|
||||
// http://user42.tuxfamily.org/math-planepath/gallery.html
|
||||
|
||||
class FillPlanePath : public FillWithDirection
|
||||
{
|
||||
public:
|
||||
virtual ~FillPlanePath() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
virtual bool _centered() const = 0;
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0;
|
||||
};
|
||||
|
||||
class FillArchimedeanChords : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual ~FillArchimedeanChords() {}
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
};
|
||||
|
||||
class FillHilbertCurve : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual ~FillHilbertCurve() {}
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return false; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
};
|
||||
|
||||
class FillOctagramSpiral : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual ~FillOctagramSpiral() {}
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillPlanePath_hpp_
|
138
xs/src/libslic3r/Fill/FillRectilinear.cpp
Normal file
138
xs/src/libslic3r/Fill/FillRectilinear.cpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillRectilinear.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Polylines FillRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// rotate polygons so that we can work with vertical lines here
|
||||
ExPolygon expolygon = surface->expolygon;
|
||||
std::pair<float, Point> rotate_vector = this->infill_direction(surface);
|
||||
expolygon.rotate(- rotate_vector.first);
|
||||
// No need to translate the polygon anyhow for the infill.
|
||||
// The infill will be performed inside a bounding box of the expolygon and its absolute position does not matter.
|
||||
// expolygon.translate(rotate_vector.second.x, rotate_vector.second.y);
|
||||
|
||||
this->_min_spacing = scale_(this->spacing);
|
||||
assert(params.density > 0.0001f && params.density <= 1.f);
|
||||
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density);
|
||||
this->_diagonal_distance = this->_line_spacing * 2;
|
||||
this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
// define flow spacing according to requested density
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
this->_line_spacing = this->adjust_solid_spacing(bounding_box.size().x, this->_line_spacing);
|
||||
this->spacing = unscale(this->_line_spacing);
|
||||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
bounding_box.merge(Point(
|
||||
bounding_box.min.x - (bounding_box.min.x % this->_line_spacing),
|
||||
bounding_box.min.y - (bounding_box.min.y % this->_line_spacing)));
|
||||
}
|
||||
|
||||
// generate the basic pattern
|
||||
coord_t x_max = bounding_box.max.x + SCALED_EPSILON;
|
||||
Lines lines;
|
||||
for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing)
|
||||
lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y));
|
||||
if (this->_horizontal_lines()) {
|
||||
coord_t y_max = bounding_box.max.y + SCALED_EPSILON;
|
||||
for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing)
|
||||
lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y)));
|
||||
}
|
||||
|
||||
// clip paths against a slightly larger expolygon, so that the first and last paths
|
||||
// are kept even if the expolygon has vertical sides
|
||||
// the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON;
|
||||
// however we use a larger offset to support expolygons with slightly skewed sides and
|
||||
// not perfectly straight
|
||||
//FIXME Vojtech: Update the intersecton function to work directly with lines.
|
||||
Polylines polylines_src;
|
||||
polylines_src.reserve(lines.size());
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) {
|
||||
polylines_src.push_back(Polyline());
|
||||
Points &pts = polylines_src.back().points;
|
||||
pts.reserve(2);
|
||||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false);
|
||||
|
||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
||||
coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f));
|
||||
for (Polylines::iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
|
||||
Point *first_point = &it_polyline->points.front();
|
||||
Point *last_point = &it_polyline->points.back();
|
||||
if (first_point->y > last_point->y)
|
||||
std::swap(first_point, last_point);
|
||||
first_point->y -= extra;
|
||||
last_point->y += extra;
|
||||
}
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
// offset the expolygon by max(min_spacing/2, extra)
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
std::move(polylines),
|
||||
#else
|
||||
polylines,
|
||||
#endif
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
assert(polylines.empty());
|
||||
#else
|
||||
polylines.clear();
|
||||
#endif
|
||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
||||
if (! polylines.empty()) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines.back().points;
|
||||
const Point &first_point = it_polyline->points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// Distance in X, Y.
|
||||
const Vector distance = first_point.vector_to(last_point);
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
if (this->_can_connect(std::abs(distance.x), std::abs(distance.y)) &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
polylines.push_back(std::move(*it_polyline));
|
||||
#else
|
||||
polylines.push_back(Polyline());
|
||||
std::swap(polylines.back(), *it_polyline);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// paths must be rotated back
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
|
||||
// No need to translate, the absolute position is irrelevant.
|
||||
// it->translate(- rotate_vector.second.x, - rotate_vector.second.y);
|
||||
it->rotate(rotate_vector.first);
|
||||
}
|
||||
return polylines;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
72
xs/src/libslic3r/Fill/FillRectilinear.hpp
Normal file
72
xs/src/libslic3r/Fill/FillRectilinear.hpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#ifndef slic3r_FillRectilinear_hpp_
|
||||
#define slic3r_FillRectilinear_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillRectilinear : public FillWithDirection
|
||||
{
|
||||
public:
|
||||
virtual ~FillRectilinear() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
coord_t _min_spacing;
|
||||
coord_t _line_spacing;
|
||||
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
|
||||
coord_t _diagonal_distance;
|
||||
// only for line infill
|
||||
coord_t _line_oscillation;
|
||||
|
||||
// Enabled for the grid infill, disabled for the rectilinear and line infill.
|
||||
virtual bool _horizontal_lines() const { return false; }
|
||||
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const
|
||||
{ return Line(Point(x, y_min), Point(x, y_max)); }
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) {
|
||||
return dist_X <= this->_diagonal_distance
|
||||
&& dist_Y <= this->_diagonal_distance;
|
||||
}
|
||||
};
|
||||
|
||||
class FillLine : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
virtual ~FillLine() {}
|
||||
|
||||
protected:
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
|
||||
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
|
||||
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
|
||||
}
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
||||
{
|
||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
||||
&& (dist_Y <= this->_diagonal_distance);
|
||||
}
|
||||
};
|
||||
|
||||
class FillGrid : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
virtual ~FillGrid() {}
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill::Base.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
// Flag for Slic3r::Fill::Rectilinear to fill both directions.
|
||||
virtual bool _horizontal_lines() const { return true; }
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear_hpp_
|
Loading…
Reference in a new issue