Change char to int

char might be signed or unsigned but int is definitely signed.  This fixes prusa3d/Slic3r#93 .
This commit is contained in:
Eyal Soha 2017-01-12 10:59:33 +02:00
commit b851e04c17
90 changed files with 4977 additions and 2964 deletions

View file

@ -117,17 +117,21 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
# In order to generate the -l switches we need to know how Boost libraries are named
my $have_boost = 0;
my @boost_libraries = qw(system thread); # we need these
my @boost_libraries = qw(system thread log); # we need these
# check without explicit lib path (works on Linux)
if (! $mswin) {
$have_boost = 1
if check_lib(
lib => [ map "boost_${_}", @boost_libraries ],
);
if (!$ENV{SLIC3R_STATIC}) {
# Dynamic linking of boost libraries.
push @cflags, qw(-DBOOST_LOG_DYN_LINK);
if (! $mswin) {
# Check without explicit lib path (works on Linux and OSX).
$have_boost = 1
if check_lib(
lib => [ map "boost_${_}", @boost_libraries ],
);
}
}
if (!$ENV{SLIC3R_STATIC} && $have_boost) {
if ($have_boost) {
# The boost library was detected by check_lib on Linux.
push @LIBS, map "-lboost_${_}", @boost_libraries;
} else {
@ -138,8 +142,7 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) {
# Try to find the boost system library.
my @files = glob "$path/${lib_prefix}system*$lib_ext";
next if !@files;
if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) {
if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) {
# Suffix contains the version number, the build type etc.
my $suffix = $1;
# Verify existence of all required boost libraries at $path.
@ -212,6 +215,10 @@ if ($cpp_guess->is_gcc) {
}
}
print "\n";
print 'With @INC: ', join(', ', map "\"$_\"", @INC), "\n";
print 'With @LIBS: ', join(', ', map "\"$_\"", @LIBS), "\n";
my $build = Module::Build::WithXSpp->new(
module_name => 'Slic3r::XS',
dist_abstract => 'XS code for Slic3r',

View file

@ -32,6 +32,8 @@ src/libslic3r/ExtrusionEntityCollection.cpp
src/libslic3r/ExtrusionEntityCollection.hpp
src/libslic3r/ExtrusionSimulator.cpp
src/libslic3r/ExtrusionSimulator.hpp
src/libslic3r/Fill/Fill.cpp
src/libslic3r/Fill/Fill.hpp
src/libslic3r/Fill/FillBase.cpp
src/libslic3r/Fill/FillBase.hpp
src/libslic3r/Fill/FillConcentric.cpp
@ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp
src/libslic3r/GCodeSender.hpp
src/libslic3r/GCodeWriter.cpp
src/libslic3r/GCodeWriter.hpp
src/libslic3r/GCode/Analyzer.cpp
src/libslic3r/GCode/Analyzer.hpp
src/libslic3r/GCode/PressureEqualizer.cpp
src/libslic3r/GCode/PressureEqualizer.hpp
src/libslic3r/Geometry.cpp
@ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp
src/libslic3r/PrintConfig.hpp
src/libslic3r/PrintObject.cpp
src/libslic3r/PrintRegion.cpp
src/libslic3r/Slicing.cpp
src/libslic3r/Slicing.hpp
src/libslic3r/SlicingAdaptive.cpp
src/libslic3r/SlicingAdaptive.hpp
src/libslic3r/SupportMaterial.cpp
src/libslic3r/SupportMaterial.hpp
src/libslic3r/Surface.cpp
@ -99,6 +107,7 @@ src/libslic3r/SVG.hpp
src/libslic3r/TriangleMesh.cpp
src/libslic3r/TriangleMesh.hpp
src/libslic3r/utils.cpp
src/libslic3r/Utils.hpp
src/perlglue.cpp
src/poly2tri/common/shapes.cc
src/poly2tri/common/shapes.h
@ -200,7 +209,6 @@ xsp/Polygon.xsp
xsp/Polyline.xsp
xsp/PolylineCollection.xsp
xsp/Print.xsp
xsp/SupportMaterial.xsp
xsp/Surface.xsp
xsp/SurfaceCollection.xsp
xsp/TriangleMesh.xsp

View file

@ -26,7 +26,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <boost/predef/detail/endian_compat.h>
#include <boost/detail/endian.hpp>
#ifndef BOOST_LITTLE_ENDIAN
#error "admesh works correctly on little endian machines only!"

View file

@ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) {
void
stl_initialize(stl_file *stl) {
stl->error = 0;
stl->stats.degenerate_facets = 0;
stl->stats.edges_fixed = 0;
stl->stats.facets_added = 0;
stl->stats.facets_removed = 0;
stl->stats.facets_reversed = 0;
stl->stats.normals_fixed = 0;
stl->stats.number_of_parts = 0;
stl->stats.original_num_facets = 0;
stl->stats.number_of_facets = 0;
stl->stats.facets_malloced = 0;
memset(stl, 0, sizeof(stl_file));
stl->stats.volume = -1.0;
stl->neighbors_start = NULL;
stl->facet_start = NULL;
stl->v_indices = NULL;
stl->v_shared = NULL;
}
void
@ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) {
rewind(stl->fp);
}
char normal_buf[3][32];
for(i = first_facet; i < stl->stats.number_of_facets; i++) {
if(stl->stats.type == binary)
/* Read a single facet from a binary .STL file */
@ -287,17 +273,25 @@ stl_read(stl_file *stl, int first_facet, int first) {
fscanf(stl->fp, "endsolid\n");
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
if((fscanf(stl->fp, " facet normal %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \
fscanf(stl->fp, " outer loop\n") + \
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) + \
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) + \
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) + \
fscanf(stl->fp, " endloop\n") + \
if((fscanf(stl->fp, " facet normal %31s %31s %31s\n", normal_buf[0], normal_buf[1], normal_buf[2]) +
fscanf(stl->fp, " outer loop\n") +
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) +
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) +
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) +
fscanf(stl->fp, " endloop\n") +
fscanf(stl->fp, " endfacet\n")) != 12) {
perror("Something is syntactically very wrong with this ASCII STL!");
stl->error = 1;
return;
}
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 ||
sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 ||
sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it.
memset(&facet.normal, 0, sizeof(facet.normal));
}
}
#if 0

View file

@ -153,17 +153,35 @@ private:
PolyNode* GetNextSiblingUp() const;
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
friend class ClipperOffset;
friend class ClipperOffset;
friend class PolyTree; //to implement the PolyTree::move operator
};
class PolyTree: public PolyNode
{
public:
~PolyTree(){Clear();};
PolyTree() {}
PolyTree(PolyTree &&src) { *this = std::move(src); }
virtual ~PolyTree(){Clear();};
PolyTree& operator=(PolyTree &&src) {
AllNodes = std::move(src.AllNodes);
Contour = std::move(src.Contour);
Childs = std::move(src.Childs);
Parent = nullptr;
Index = src.Index;
m_IsOpen = src.m_IsOpen;
m_jointype = src.m_jointype;
m_endtype = src.m_endtype;
for (size_t i = 0; i < Childs.size(); ++ i)
Childs[i]->Parent = this;
return *this;
}
PolyNode* GetFirst() const;
void Clear();
int Total() const;
private:
PolyTree(const PolyTree &src) = delete;
PolyTree& operator=(const PolyTree &src) = delete;
PolyNodes AllNodes;
friend class Clipper; //to access AllNodes
};

View file

@ -277,4 +277,26 @@ BoundingBoxBase<PointClass>::overlap(const BoundingBoxBase<PointClass> &other) c
template bool BoundingBoxBase<Point>::overlap(const BoundingBoxBase<Point> &point) const;
template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &point) const;
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
// Current C++ standard defines the result of integer division to be rounded to zero,
// for both positive and negative numbers. Here we want to round down for negative
// numbers as well.
coord_t aligned = (coord < 0) ?
((coord - spacing + 1) / spacing) * spacing :
(coord / spacing) * spacing;
assert(aligned <= coord);
return aligned;
}
void BoundingBox::align_to_grid(const coord_t cell_size)
{
if (this->defined) {
min.x = _align_to_grid(min.x, cell_size);
min.y = _align_to_grid(min.y, cell_size);
}
}
}

View file

@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point>
BoundingBox rotated(double angle, const Point &center) const;
void rotate(double angle) { (*this) = this->rotated(angle); }
void rotate(double angle, const Point &center) { (*this) = this->rotated(angle, center); }
// Align the min corner to a grid of cell_size x cell_size cells,
// to encompass the original bounding box.
void align_to_grid(const coord_t cell_size);
BoundingBox() : BoundingBoxBase<Point>() {};
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};

View file

@ -40,13 +40,13 @@ void BridgeDetector::initialize()
this->angle = -1.;
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
Polygons grown = offset(this->expolygons, float(this->spacing));
Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
// Detect possible anchoring edges of this bridging region.
// Detect what edges lie on lower slices by turning bridge contour and holes
// into polylines and then clipping them with each lower slice's contour.
// Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges);
this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours());
#ifdef SLIC3R_DEBUG
printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size());
@ -117,7 +117,7 @@ BridgeDetector::detect_angle()
double total_length = 0;
double max_length = 0;
{
Lines clipped_lines = intersection(lines, clip_area);
Lines clipped_lines = intersection_ln(lines, clip_area);
for (size_t i = 0; i < clipped_lines.size(); ++i) {
const Line &line = clipped_lines[i];
if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
@ -203,76 +203,72 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const
return angles;
}
void
BridgeDetector::coverage(double angle, Polygons* coverage) const
Polygons BridgeDetector::coverage(double angle) const
{
if (angle == -1) angle = this->angle;
if (angle == -1) return;
if (angle == -1)
angle = this->angle;
// Get anchors, convert them to Polygons and rotate them.
Polygons anchors = to_polygons(this->_anchor_regions);
polygons_rotate(anchors, PI/2.0 - angle);
Polygons covered;
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
{
// Clone our expolygon and rotate it so that we work with vertical lines.
ExPolygon expolygon = *it_expoly;
expolygon.rotate(PI/2.0 - angle);
if (angle != -1) {
// Get anchors, convert them to Polygons and rotate them.
Polygons anchors = to_polygons(this->_anchor_regions);
polygons_rotate(anchors, PI/2.0 - angle);
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
we'll use this one to generate our trapezoids and be sure that their vertices
are inside the anchors and not on their contours leading to false negatives. */
ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
// Compute trapezoids according to a vertical orientation
Polygons trapezoids;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
it->get_trapezoids2(&trapezoids, PI/2.0);
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Lines supported = intersection(trapezoid->lines(), anchors);
size_t n_supported = 0;
// not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i)
if (supported[i].length() >= this->spacing)
++ n_supported;
if (n_supported >= 2)
covered.push_back(STDMOVE(*trapezoid));
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
{
// Clone our expolygon and rotate it so that we work with vertical lines.
ExPolygon expolygon = *it_expoly;
expolygon.rotate(PI/2.0 - angle);
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
we'll use this one to generate our trapezoids and be sure that their vertices
are inside the anchors and not on their contours leading to false negatives. */
ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
// Compute trapezoids according to a vertical orientation
Polygons trapezoids;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
it->get_trapezoids2(&trapezoids, PI/2.0);
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Lines supported = intersection_ln(trapezoid->lines(), anchors);
size_t n_supported = 0;
// not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i)
if (supported[i].length() >= this->spacing)
++ n_supported;
if (n_supported >= 2)
covered.push_back(STDMOVE(*trapezoid));
}
}
// Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
// instead of exact overlaps.
covered = union_(covered);
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
polygons_rotate(covered, -(PI/2.0 - angle));
covered = intersection(covered, to_polygons(this->expolygons));
/*
if (0) {
my @lines = map @{$_->lines}, @$trapezoids;
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"coverage_" . rad2deg($angle) . ".svg",
expolygons => [$self->expolygon],
green_expolygons => $self->_anchor_regions,
red_expolygons => $coverage,
lines => \@lines,
);
}
*/
}
// Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
// instead of exact overlaps.
covered = union_(covered);
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
polygons_rotate(covered, -(PI/2.0 - angle));
intersection(covered, to_polygons(this->expolygons), coverage);
/*
if (0) {
my @lines = map @{$_->lines}, @$trapezoids;
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"coverage_" . rad2deg($angle) . ".svg",
expolygons => [$self->expolygon],
green_expolygons => $self->_anchor_regions,
red_expolygons => $coverage,
lines => \@lines,
);
}
*/
}
Polygons
BridgeDetector::coverage(double angle) const
{
Polygons pp;
this->coverage(angle, &pp);
return pp;
return covered;
}
/* This method returns the bridge edges (as polylines) that are not supported
@ -288,9 +284,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
// get unsupported bridge edges (both contour and holes)
Polylines unuspported_polylines;
diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines);
Lines unsupported_lines = to_lines(unuspported_polylines);
Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower));
/* Split into individual segments and filter out edges parallel to the bridging angle
TODO: angle tolerance should probably be based on segment length and flow width,
so that we build supports whenever there's a chance that at least one or two bridge

View file

@ -32,7 +32,6 @@ public:
BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
bool detect_angle();
void coverage(double angle, Polygons* coverage) const;
Polygons coverage(double angle = -1) const;
void unsupported_edges(double angle, Polylines* unsupported) const;
Polylines unsupported_edges(double angle = -1) const;

File diff suppressed because it is too large Load diff

View file

@ -14,154 +14,202 @@ using ClipperLib::jtSquare;
namespace Slic3r {
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library.
//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling.
// How about 2^17=131072?
// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm,
// further scaling by 10e5 brings us to
#define CLIPPER_OFFSET_SCALE 100000.0
//-----------------------------------------------------------
// legacy code from Clipper documentation
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
//-----------------------------------------------------------
void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output);
template <class T>
void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output);
template <class T>
void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output);
template <class T>
void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output);
void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output);
void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale);
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input);
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input);
Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input);
Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input);
Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input);
Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input);
Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
// offset Polygons
void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
// This is a safe variant of the polygon offset, tailored for a single ExPolygon:
// a single polygon with multiple non-overlapping holes.
// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
double scale, ClipperLib::JoinType joinType, double miterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
double scale, ClipperLib::JoinType joinType, double miterLimit);
ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
// offset Polylines
void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
// offset expolygons and surfaces
ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit);
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
template <class T>
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, T* retval, bool safety_offset_);
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, Slic3r::Polylines* retval);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,
const Slic3r::Polygons &clip, Slic3r::Lines* retval);
Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType,
const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
template <class SubjectType, class ResultType>
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
// diff
inline Slic3r::Polygons
diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
template <class SubjectType, class ResultType>
void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false);
inline Slic3r::ExPolygons
diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
inline Slic3r::ExPolygons
diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
}
template <class SubjectType, class ClipType>
Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false);
inline Slic3r::Polygons
diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
}
template <class SubjectType, class ResultType>
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
inline Slic3r::Polylines
diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
template <class SubjectType>
SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
inline Slic3r::Polylines
diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
Slic3r::ExPolygons
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
inline Slic3r::Lines
diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
template <class SubjectType>
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
// intersection
inline Slic3r::Polygons
intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval,
bool safety_offset_ = false);
inline Slic3r::ExPolygons
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
template <class T>
void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false);
inline Slic3r::ExPolygons
intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
}
Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false);
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false);
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false);
inline Slic3r::Polygons
intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
}
void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false);
Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false);
inline Slic3r::Polylines
intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false);
void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false);
static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
inline Slic3r::Polylines
intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false);
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false);
inline Slic3r::Lines
intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
// union
inline Slic3r::Polygons
union_(const Slic3r::Polygons &subject, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::Polygons
union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_);
}
inline Slic3r::ExPolygons
union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::ExPolygons
union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::ExPolygons
union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
}
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false);
Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false);
void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
/* OTHER */
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
void safety_offset(ClipperLib::Paths* paths);
Polygons top_level_islands(const Slic3r::Polygons &polygons);
}
#endif
#endif

View file

@ -1,6 +1,7 @@
#ifndef slic3r_Config_hpp_
#define slic3r_Config_hpp_
#include <assert.h>
#include <map>
#include <climits>
#include <cstdio>
@ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase
};
T get_at(size_t i) const {
try {
return this->values.at(i);
} catch (const std::out_of_range& oor) {
return this->values.front();
}
assert(! this->values.empty());
return (i < this->values.size()) ? this->values[i] : this->values.front();
};
};

View file

@ -1,6 +1,7 @@
#include <algorithm>
#include <vector>
#include <float.h>
#include <unordered_map>
#ifdef SLIC3R_GUI
#include <wx/image.h>
@ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
{
// 1) Measure the bounding box.
m_bbox.defined = false;
for (size_t i = 0; i < m_contours.size(); ++ i) {
const Slic3r::Points &pts = *m_contours[i];
for (size_t j = 0; j < pts.size(); ++ j)
@ -839,6 +839,8 @@ void EdgeGrid::Grid::calculate_sdf()
}
#if 0
static int iRun = 0;
++ iRun;
//#ifdef SLIC3R_GUI
{
wxImage img(ncols, nrows);
@ -862,7 +864,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("unsigned_df.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("unsigned_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
{
wxImage img(ncols, nrows);
@ -895,7 +897,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("signed_df.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("signed_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
#endif /* SLIC3R_GUI */
@ -1020,7 +1022,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("signed_df-signs.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("signed_df-signs-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
#endif /* SLIC3R_GUI */
@ -1049,7 +1051,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("signed_df2.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("signed_df2-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
#endif /* SLIC3R_GUI */
}
@ -1118,7 +1120,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
return f;
}
bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const {
BoundingBox bbox;
bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y);
@ -1222,6 +1224,135 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true;
}
Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
{
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 0) Prepare a binary grid.
size_t cell_rows = m_rows + 2;
size_t cell_cols = m_cols + 2;
std::vector<char> cell_inside(cell_rows * cell_cols, false);
for (int r = 0; r < int(cell_rows); ++ r)
for (int c = 0; c < int(cell_cols); ++ c)
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
{
std::vector<char> cell_inside2(cell_inside);
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
int addr = r * cell_cols + c;
if ((cell_inside2[addr - 1] && cell_inside2[addr + 1]) ||
(cell_inside2[addr - cell_cols] && cell_inside2[addr + cell_cols]))
cell_inside[addr] = true;
}
}
}
// 1) Collect the lines.
std::vector<Line> lines;
EndPointMapType start_point_to_line_idx;
for (int r = 0; r <= int(m_rows); ++ r) {
for (int c = 0; c <= int(m_cols); ++ c) {
int addr = (r + 1) * cell_cols + c + 1;
bool left = cell_inside[addr - 1];
bool top = cell_inside[addr - cell_cols];
bool current = cell_inside[addr];
if (left != current) {
lines.push_back(
left ?
Line(Point(c, r+1), Point(c, r )) :
Line(Point(c, r ), Point(c, r+1)));
start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1));
}
if (top != current) {
lines.push_back(
top ?
Line(Point(c , r), Point(c+1, r)) :
Line(Point(c+1, r), Point(c , r)));
start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1));
}
}
}
// 2) Chain the lines.
std::vector<char> line_processed(lines.size(), false);
Polygons out;
for (int i_candidate = 0; i_candidate < int(lines.size()); ++ i_candidate) {
if (line_processed[i_candidate])
continue;
Polygon poly;
line_processed[i_candidate] = true;
poly.points.push_back(lines[i_candidate].b);
int i_line_current = i_candidate;
for (;;) {
std::pair<EndPointMapType::iterator,EndPointMapType::iterator> line_range =
start_point_to_line_idx.equal_range(lines[i_line_current].b);
// The interval has to be non empty, there shall be at least one line continuing the current one.
assert(line_range.first != line_range.second);
int i_next = -1;
for (EndPointMapType::iterator it = line_range.first; it != line_range.second; ++ it) {
if (it->second == i_candidate) {
// closing the loop.
goto end_of_poly;
}
if (line_processed[it->second])
continue;
if (i_next == -1) {
i_next = it->second;
} else {
// This is a corner, where two lines meet exactly. Pick the line, which encloses a smallest angle with
// the current edge.
const Line &line_current = lines[i_line_current];
const Line &line_next = lines[it->second];
const Vector v1 = line_current.vector();
const Vector v2 = line_next.vector();
int64_t cross = int64_t(v1.x) * int64_t(v2.y) - int64_t(v2.x) * int64_t(v1.y);
if (cross > 0) {
// This has to be a convex right angle. There is no better next line.
i_next = it->second;
break;
}
}
}
line_processed[i_next] = true;
i_line_current = i_next;
poly.points.push_back(lines[i_line_current].b);
}
end_of_poly:
out.push_back(std::move(poly));
}
// 3) Scale the polygons back into world, shrink slightly and remove collinear points.
for (size_t i = 0; i < out.size(); ++ i) {
Polygon &poly = out[i];
for (size_t j = 0; j < poly.points.size(); ++ j) {
Point &p = poly.points[j];
p.x *= m_resolution;
p.y *= m_resolution;
p.x += m_bbox.min.x;
p.y += m_bbox.min.y;
}
// Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result.
// Remove collineaer points.
Points pts;
pts.reserve(poly.points.size());
for (size_t j = 0; j < poly.points.size(); ++ j) {
size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1;
size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1;
Point v = poly.points[j2] - poly.points[j0];
if (v.x != 0 && v.y != 0) {
// This is a corner point. Copy it to the output contour.
Point p = poly.points[j];
p.y += (v.x < 0) ? - offset : offset;
p.x += (v.y > 0) ? - offset : offset;
pts.push_back(p);
}
}
poly.points = std::move(pts);
}
return out;
}
#ifdef SLIC3R_GUI
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
{
@ -1235,17 +1366,18 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
++iRun;
const coord_t search_radius = grid.resolution() * 2;
const coord_t display_blend_radius = grid.resolution() * 5;
const coord_t display_blend_radius = grid.resolution() * 2;
for (coord_t r = 0; r < h; ++r) {
for (coord_t c = 0; c < w; ++ c) {
unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3;
Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y);
coordf_t min_dist;
bool on_segment;
// if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) {
bool on_segment = true;
#if 0
if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) {
#else
if (grid.signed_distance(pt, search_radius, min_dist)) {
//FIXME
on_segment = true;
#endif
float s = 255 * std::abs(min_dist) / float(display_blend_radius);
int is = std::max(0, std::min(255, int(floor(s + 0.5f))));
if (min_dist < 0) {
@ -1254,9 +1386,9 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
pxl[1] = 255 - is;
pxl[2] = 255 - is;
} else {
pxl[0] = 128;
pxl[1] = 128;
pxl[2] = 255 - is;
pxl[0] = 255;
pxl[1] = 0;
pxl[2] = 255 - is;
}
}
else {

View file

@ -12,11 +12,14 @@
namespace Slic3r {
namespace EdgeGrid {
struct Grid
class Grid
{
public:
Grid();
~Grid();
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
void create(const Polygons &polygons, coord_t resolution);
void create(const ExPolygon &expoly, coord_t resolution);
void create(const ExPolygons &expolygons, coord_t resolution);
@ -54,6 +57,9 @@ struct Grid
const size_t rows() const { return m_rows; }
const size_t cols() const { return m_cols; }
// For supports: Contours enclosing the rasterized edges.
Polygons contours_simplified(coord_t offset) const;
protected:
struct Cell {
Cell() : begin(0), end(0) {}
@ -65,6 +71,18 @@ protected:
#if 0
bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell);
#endif
bool cell_inside_or_crossing(int r, int c) const
{
if (r < 0 || r >= m_rows ||
c < 0 || c >= m_cols)
// The cell is outside the domain. Hoping that the contours were correctly oriented, so
// there is a CCW outmost contour so the out of domain cells are outside.
return false;
const Cell &cell = m_cells[r * m_cols + c];
return
(cell.begin < cell.end) ||
(! m_signed_distance_field.empty() && m_signed_distance_field[r * (m_cols + 1) + c] <= 0.f);
}
// Bounding box around the contours.
BoundingBox m_bbox;

View file

@ -99,9 +99,7 @@ ExPolygon::contains(const Line &line) const
bool
ExPolygon::contains(const Polyline &polyline) const
{
Polylines pl_out;
diff((Polylines)polyline, *this, &pl_out);
return pl_out.empty();
return diff_pl((Polylines)polyline, *this).empty();
}
bool
@ -115,8 +113,7 @@ ExPolygon::contains(const Polylines &polylines) const
svg.draw_outline(*this);
svg.draw(polylines, "blue");
#endif
Polylines pl_out;
diff(polylines, *this, &pl_out);
Polylines pl_out = diff_pl(polylines, *this);
#if 0
svg.draw(pl_out, "red");
#endif
@ -162,8 +159,7 @@ ExPolygon::overlaps(const ExPolygon &other) const
svg.draw_outline(*this);
svg.draw_outline(other, "blue");
#endif
Polylines pl_out;
intersection((Polylines)other, *this, &pl_out);
Polylines pl_out = intersection_pl((Polylines)other, *this);
#if 0
svg.draw(pl_out, "red");
#endif
@ -396,11 +392,8 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
poly[3].y = bb.max.y;
// intersect with this expolygon
Polygons trapezoids;
intersection<Polygons,Polygons>(poly, *this, &trapezoids);
// append results to return value
polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());
polygons_append(*polygons, intersection(poly, to_polygons(*this)));
}
}
@ -434,16 +427,13 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
// convert polygons
std::list<TPPLPoly> input;
Polygons pp = *this;
simplify_polygons(pp, &pp, true);
ExPolygons expp;
union_(pp, &expp);
ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
// contour
{
TPPLPoly p;
p.Init(ex->contour.points.size());
p.Init(int(ex->contour.points.size()));
//printf(PRINTF_ZU "\n0\n", ex->contour.points.size());
for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) {
p[ point-ex->contour.points.begin() ].x = point->x;
@ -480,8 +470,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
Polygon p;
p.points.resize(num_points);
for (long i = 0; i < num_points; ++i) {
p.points[i].x = (*poly)[i].x;
p.points[i].y = (*poly)[i].y;
p.points[i].x = coord_t((*poly)[i].x);
p.points[i].y = coord_t((*poly)[i].y);
}
polygons->push_back(p);
}
@ -490,8 +480,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
void
ExPolygon::triangulate_p2t(Polygons* polygons) const
{
ExPolygons expp;
simplify_polygons(*this, &expp, true);
ExPolygons expp = simplify_polygons_ex(*this, true);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
// TODO: prevent duplicate points

View file

@ -13,9 +13,17 @@ typedef std::vector<ExPolygon> ExPolygons;
class ExPolygon
{
public:
public:
ExPolygon() {}
ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
Polygon contour;
Polygons holes;
operator Points() const;
operator Polygons() const;
operator Polylines() const;
@ -253,6 +261,18 @@ inline void polygons_append(Polygons &dst, ExPolygons &&src)
}
#endif
inline void expolygons_append(ExPolygons &dst, const ExPolygons &src)
{
dst.insert(dst.end(), src.begin(), src.end());
}
#if SLIC3R_CPPVER >= 11
inline void expolygons_append(ExPolygons &dst, ExPolygons &&src)
{
std::move(std::begin(src), std::end(src), std::back_inserter(dst));
}
#endif
inline void expolygons_rotate(ExPolygons &expolys, double angle)
{
for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p)
@ -281,37 +301,37 @@ extern bool remove_sticks(ExPolygon &poly);
#include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon {
template <>
struct polygon_traits<ExPolygon> {
struct polygon_traits<Slic3r::ExPolygon> {
typedef coord_t coordinate_type;
typedef Points::const_iterator iterator_type;
typedef Point point_type;
typedef Slic3r::Points::const_iterator iterator_type;
typedef Slic3r::Point point_type;
// Get the begin iterator
static inline iterator_type begin_points(const ExPolygon& t) {
static inline iterator_type begin_points(const Slic3r::ExPolygon& t) {
return t.contour.points.begin();
}
// Get the end iterator
static inline iterator_type end_points(const ExPolygon& t) {
static inline iterator_type end_points(const Slic3r::ExPolygon& t) {
return t.contour.points.end();
}
// Get the number of sides of the polygon
static inline std::size_t size(const ExPolygon& t) {
static inline std::size_t size(const Slic3r::ExPolygon& t) {
return t.contour.points.size();
}
// Get the winding direction of the polygon
static inline winding_direction winding(const ExPolygon& t) {
static inline winding_direction winding(const Slic3r::ExPolygon& t) {
return unknown_winding;
}
};
template <>
struct polygon_mutable_traits<ExPolygon> {
struct polygon_mutable_traits<Slic3r::ExPolygon> {
//expects stl style iterators
template <typename iT>
static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) {
static inline Slic3r::ExPolygon& set_points(Slic3r::ExPolygon& expolygon, iT input_begin, iT input_end) {
expolygon.contour.points.assign(input_begin, input_end);
// skip last point since Boost will set last point = first point
expolygon.contour.points.pop_back();
@ -321,27 +341,27 @@ namespace boost { namespace polygon {
template <>
struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; };
struct geometry_concept<Slic3r::ExPolygon> { typedef polygon_with_holes_concept type; };
template <>
struct polygon_with_holes_traits<ExPolygon> {
typedef Polygons::const_iterator iterator_holes_type;
typedef Polygon hole_type;
static inline iterator_holes_type begin_holes(const ExPolygon& t) {
struct polygon_with_holes_traits<Slic3r::ExPolygon> {
typedef Slic3r::Polygons::const_iterator iterator_holes_type;
typedef Slic3r::Polygon hole_type;
static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) {
return t.holes.begin();
}
static inline iterator_holes_type end_holes(const ExPolygon& t) {
static inline iterator_holes_type end_holes(const Slic3r::ExPolygon& t) {
return t.holes.end();
}
static inline unsigned int size_holes(const ExPolygon& t) {
static inline unsigned int size_holes(const Slic3r::ExPolygon& t) {
return (int)t.holes.size();
}
};
template <>
struct polygon_with_holes_mutable_traits<ExPolygon> {
struct polygon_with_holes_mutable_traits<Slic3r::ExPolygon> {
template <typename iT>
static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) {
static inline Slic3r::ExPolygon& set_holes(Slic3r::ExPolygon& t, iT inputBegin, iT inputEnd) {
t.holes.assign(inputBegin, inputEnd);
return t;
}
@ -349,32 +369,32 @@ namespace boost { namespace polygon {
//first we register CPolygonSet as a polygon set
template <>
struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; };
struct geometry_concept<Slic3r::ExPolygons> { typedef polygon_set_concept type; };
//next we map to the concept through traits
template <>
struct polygon_set_traits<ExPolygons> {
struct polygon_set_traits<Slic3r::ExPolygons> {
typedef coord_t coordinate_type;
typedef ExPolygons::const_iterator iterator_type;
typedef ExPolygons operator_arg_type;
typedef Slic3r::ExPolygons::const_iterator iterator_type;
typedef Slic3r::ExPolygons operator_arg_type;
static inline iterator_type begin(const ExPolygons& polygon_set) {
static inline iterator_type begin(const Slic3r::ExPolygons& polygon_set) {
return polygon_set.begin();
}
static inline iterator_type end(const ExPolygons& polygon_set) {
static inline iterator_type end(const Slic3r::ExPolygons& polygon_set) {
return polygon_set.end();
}
//don't worry about these, just return false from them
static inline bool clean(const ExPolygons& polygon_set) { return false; }
static inline bool sorted(const ExPolygons& polygon_set) { return false; }
static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; }
static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; }
};
template <>
struct polygon_set_mutable_traits<ExPolygons> {
struct polygon_set_mutable_traits<Slic3r::ExPolygons> {
template <typename input_iterator_type>
static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
static inline void set(Slic3r::ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
expolygons.assign(input_begin, input_end);
}
};

View file

@ -13,19 +13,13 @@ namespace Slic3r {
void
ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
{
// perform clipping
Polylines clipped;
intersection<Polylines,Polylines>(this->polyline, collection, &clipped);
return this->_inflate_collection(clipped, retval);
this->_inflate_collection(intersection_pl(this->polyline, collection), retval);
}
void
ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
{
// perform clipping
Polylines clipped;
diff<Polylines,Polylines>(this->polyline, collection, &clipped);
return this->_inflate_collection(clipped, retval);
this->_inflate_collection(diff_pl(this->polyline, collection), retval);
}
void
@ -58,9 +52,7 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
{
Polygons tmp;
offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon);
polygons_append(out, STDMOVE(tmp));
polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon));
}
void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
@ -68,9 +60,7 @@ 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, this->is_bridge());
Polygons tmp;
offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon);
polygons_append(out, STDMOVE(tmp));
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
}
bool

View file

@ -69,11 +69,11 @@ class ExtrusionPath : public ExtrusionEntity
public:
Polyline polyline;
ExtrusionRole role;
// Volumetric velocity. mm^3 of plastic per mm of linear head motion
// Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
double mm3_per_mm;
// Width of the extrusion.
// Width of the extrusion, used for visualization purposes.
float width;
// Height of the extrusion.
// Height of the extrusion, used for visualization purposed.
float height;
ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {};
@ -194,6 +194,48 @@ class ExtrusionLoop : public ExtrusionEntity
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
};
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
dst.back().polyline = *it_polyline;
}
}
#if SLIC3R_CPPVER >= 11
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
dst.back().polyline = std::move(*it_polyline);
}
}
#endif // SLIC3R_CPPVER >= 11
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
dst.push_back(extrusion_path);
extrusion_path->polyline = *it_polyline;
}
}
#if SLIC3R_CPPVER >= 11
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
dst.push_back(extrusion_path);
extrusion_path->polyline = std::move(*it_polyline);
}
}
#endif // SLIC3R_CPPVER >= 11
}
#endif

View file

@ -803,7 +803,7 @@ void gcode_spread_points(
const Cell &cell = cells[i];
acc[cell.idx.y()][cell.idx.x()] = (1.f - cell.fraction_covered) * cell.volume + cell.fraction_covered * cell.area * height_avg;
}
} else if (simulationType == ExtrusionSimulationSpreadExcess) {
} else if (simulationType == Slic3r::ExtrusionSimulationSpreadExcess) {
// The volume under the circle does not fit.
// 1) Fill the underfilled cells and remove them from the list.
float volume_borrowed_total = 0.;

View file

@ -13,7 +13,7 @@ enum ExtrusionSimulationType
ExtrusionSimulationDontSpread,
ExtrisopmSimulationSpreadNotOverfilled,
ExtrusionSimulationSpreadFull,
ExtrusionSimulationSpreadExcess,
ExtrusionSimulationSpreadExcess
};
// An opaque class, to keep the boost stuff away from the header.

View file

@ -246,22 +246,21 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
}
// save into layer
{
ExtrusionRole role = is_bridge ? erBridgeInfill :
(surface.is_solid() ? ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : erInternalInfill);
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
out.entities.push_back(&collection);
// Only concentric fills are not sorted.
collection.no_sort = f->no_sort();
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
ExtrusionPath *path = new ExtrusionPath(role, flow.mm3_per_mm(), flow.width, flow.height);
collection.entities.push_back(path);
path->polyline.points.swap(it->points);
}
}
// Save into layer.
auto *eec = new ExtrusionEntityCollection();
out.entities.push_back(eec);
// Only concentric fills are not sorted.
eec->no_sort = f->no_sort();
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines),
is_bridge ?
erBridgeInfill :
(surface.is_solid() ?
((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
erInternalInfill),
flow.mm3_per_mm(), flow.width, flow.height);
}
// add thin fill regions
// thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
// Unpacks the collection, creates multiple collections per path.

View file

@ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single(
it->translate(bb.min.x, bb.min.y);
// clip pattern to boundaries
intersection(polylines, (Polygons)expolygon, &polylines);
polylines = intersection_pl(polylines, (Polygons)expolygon);
// connect lines
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections

View file

@ -45,8 +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(surface->expolygon, &expp, -0.5*scale_(this->spacing));
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing)));
// Create the infills for each of the regions.
Polylines polylines_out;
for (size_t i = 0; i < expp.size(); ++ i)

View file

@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single(
// generate paths from the outermost to the innermost, to avoid
// adhesion problems of the first central tiny loops
union_pt_chained(loops, &loops, false);
loops = union_pt_chained(loops, false);
// split paths using a nearest neighbor search
size_t iPathFirst = polylines_out.size();

View file

@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
Polylines p;
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
p.push_back((Polyline)(*it));
intersection(p, (Polygons)expolygon, &paths);
paths = intersection_pl(p, to_polygons(expolygon));
}
// connect paths
@ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single(
}
// clip paths again to prevent connection segments from crossing the expolygon boundaries
intersection(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)), &paths);
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
// Move the polylines to the output, avoid a deep copy.
size_t j = polylines_out.size();
polylines_out.resize(j + paths.size(), Polyline());

View file

@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single(
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);
polylines = intersection_pl(polylines, to_polygons(expolygon));
/*
if (1) {

View file

@ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single(
pts.push_back(it->a);
pts.push_back(it->b);
}
Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false);
Polylines polylines = intersection_pl(polylines_src, offset(to_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;

View file

@ -372,11 +372,9 @@ public:
bool sticks_removed = remove_sticks(polygons_src);
// if (sticks_removed) printf("Sticks removed!\n");
polygons_outer = offset(polygons_src, aoffset1,
CLIPPER_OFFSET_SCALE,
ClipperLib::jtMiter,
mitterLimit);
polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
CLIPPER_OFFSET_SCALE,
ClipperLib::jtMiter,
mitterLimit);
// Filter out contours with zero area or small area, contours with 2 points only.
@ -884,7 +882,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
// _align_to_grid will not work correctly with positive pattern_shift.
coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
refpt.x -= (pattern_shift_scaled > 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
bounding_box.merge(_align_to_grid(
bounding_box.min,
Point(line_spacing, line_spacing),
@ -894,7 +892,9 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// Intersect a set of euqally spaced vertical lines wiht expolygon.
// 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 + (line_spacing + SCALED_EPSILON) / 2;
coord_t x0 = bounding_box.min.x;
if (full_infill)
x0 += (line_spacing + SCALED_EPSILON) / 2;
#ifdef SLIC3R_DEBUG
static int iRun = 0;

View file

@ -40,14 +40,17 @@ Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool
/* This method returns the centerline spacing between two adjacent extrusions
having the same extrusion width (and other properties). */
float
Flow::spacing() const {
if (this->bridge) {
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);
return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing);
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));
#endif
}
/* This method returns the centerline spacing between an extrusion using this
@ -57,23 +60,17 @@ float
Flow::spacing(const Flow &other) const {
assert(this->height == other.height);
assert(this->bridge == other.bridge);
if (this->bridge) {
return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING;
}
return this->spacing()/2 + other.spacing()/2;
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. */
double
Flow::mm3_per_mm() const {
if (this->bridge) {
return (this->width * this->width) * PI/4.0;
}
// rectangle with semicircles at the ends
return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0);
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. */
@ -85,8 +82,7 @@ float Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) {
}
/* This static method returns a sane extrusion width default. */
float
Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) {
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);
@ -106,14 +102,15 @@ Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) {
}
/* 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) {
if (bridge) {
return spacing - BRIDGE_EXTRA_SPACING;
}
// rectangle with semicircles at the ends
return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0);
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
}
}

View file

@ -7,8 +7,14 @@
namespace Slic3r {
// Extra spacing of bridge threads, in mm.
#define BRIDGE_EXTRA_SPACING 0.05
#define OVERLAP_FACTOR 1.0
// Overlap factor of perimeter lines. Currently no overlap.
// #define HAS_OVERLAP
#ifdef HAS_PERIMETER_LINE_OVERLAP
#define PERIMETER_LINE_OVERLAP_FACTOR 1.0
#endif
enum FlowRole {
frExternalPerimeter,
@ -22,9 +28,17 @@ enum FlowRole {
class Flow
{
public:
float width, height, nozzle_diameter;
bool bridge;
public:
// Non bridging flow: Maximum width of an extrusion with semicircles at the ends.
// Bridging flow: Bridge thread diameter.
float width;
// Non bridging flow: Layer height.
// Bridging flow: Bridge thread diameter = layer height.
float height;
// Nozzle diameter is
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) {};

View file

@ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer)
// avoid computing islands and overhangs if they're not needed
if (this->config.avoid_crossing_perimeters) {
ExPolygons islands;
union_(layer.slices, &islands, true);
ExPolygons islands = union_ex(layer.slices, true);
this->avoid_crossing_perimeters.init_layer_mp(islands);
}

View file

@ -105,7 +105,7 @@ Layer::make_slices()
FOREACH_LAYERREGION(this, layerm) {
polygons_append(slices_p, to_polygons((*layerm)->slices));
}
union_(slices_p, &slices);
slices = union_ex(slices_p);
}
this->slices.expolygons.clear();
@ -132,15 +132,11 @@ Layer::merge_slices()
if (this->regions.size() == 1) {
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
// but use the non-split islands of a layer. For a single region print, these shall be equal.
this->regions.front()->slices.surfaces.clear();
surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal);
this->regions.front()->slices.set(this->slices.expolygons, stInternal);
} else {
FOREACH_LAYERREGION(this, layerm) {
ExPolygons expp;
// without safety offset, artifacts are generated (GH #2494)
union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true);
(*layerm)->slices.surfaces.clear();
surfaces_append((*layerm)->slices.surfaces, expp, stInternal);
(*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
}
}
}
@ -223,7 +219,7 @@ Layer::make_perimeters()
}
// merge the surfaces assigned to each group
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front());
new_slices.append(union_ex(it->second, true), it->second.front());
}
// make perimeters
@ -236,8 +232,7 @@ Layer::make_perimeters()
// Separate the fill surfaces.
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.surfaces.clear();
surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front());
(*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
}
}
}
@ -318,9 +313,4 @@ SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height,
{
}
SupportLayer::~SupportLayer()
{
}
}

View file

@ -11,9 +11,6 @@
namespace Slic3r {
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
class Layer;
class PrintRegion;
class PrintObject;
@ -155,7 +152,7 @@ public:
protected:
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
coordf_t slice_z);
virtual ~SupportLayer();
virtual ~SupportLayer() {}
};

View file

@ -52,8 +52,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
this->fill_surfaces.surfaces.clear();
for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface)
surfaces_append(
this->fill_surfaces.surfaces,
this->fill_surfaces.append(
intersection_ex(to_polygons(surface->expolygon), fill_boundaries),
surface->surface_type);
}
@ -91,9 +90,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
g.process();
}
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
void
LayerRegion::process_external_surfaces(const Layer* lower_layer)
@ -194,7 +193,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
break;
}
// Grown by 3mm.
Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
if (idx_island == -1) {
printf("Bridge did not fall into the source region!\r\n");
} else {
@ -262,9 +261,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
BridgeDetector bd(
initial,
lower_layer->slices,
//FIXME parameters are not correct!
// flow(FlowRole role, bool bridge = false, double width = -1) const;
this->flow(frInfill, true, this->layer()->height).scaled_width()
this->flow(frInfill, true).scaled_width()
);
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
@ -305,8 +302,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
polygons_append(polys, STDMOVE(s1));
for (size_t j = i + 1; j < top.size(); ++ j) {
Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2))
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
s2.clear();
}
}
if (s1.surface_type == stTop)
// Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
@ -329,8 +328,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
polygons_append(polys, STDMOVE(s1));
for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2))
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
s2.clear();
}
}
ExPolygons new_expolys = diff_ex(polys, new_polygons);
polygons_append(new_polygons, to_polygons(new_expolys));

View file

@ -76,20 +76,20 @@ class Linef3
void scale(double factor);
};
}
} // namespace Slic3r
// start Boost
#include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon {
template <>
struct geometry_concept<Line> { typedef segment_concept type; };
struct geometry_concept<Slic3r::Line> { typedef segment_concept type; };
template <>
struct segment_traits<Line> {
struct segment_traits<Slic3r::Line> {
typedef coord_t coordinate_type;
typedef Point point_type;
typedef Slic3r::Point point_type;
static inline point_type get(const Line& line, direction_1d dir) {
static inline point_type get(const Slic3r::Line& line, direction_1d dir) {
return dir.to_int() ? line.b : line.a;
}
};

View file

@ -6,6 +6,7 @@
#include "Layer.hpp"
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "Slicing.hpp"
#include <map>
#include <string>
#include <utility>

View file

@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
{
// grow our environment slightly in order for simplify_by_visibility()
// to work best by considering moves on boundaries valid as well
ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON));
ExPolygonCollection grown_env(offset_ex(env.env.expolygons, +SCALED_EPSILON));
if (island_idx == -1) {
/* If 'from' or 'to' are not inside our env, they were connected using the
@ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
if (!grown_env.contains(from)) {
// delete second point while the line connecting first to third crosses the
// boundaries as many times as the current first to second
while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
while (polyline.points.size() > 2 && intersection_ln((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
polyline.points.erase(polyline.points.begin() + 1);
}
}
if (!grown_env.contains(to)) {
while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
while (polyline.points.size() > 2 && intersection_ln((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
polyline.points.erase(polyline.points.end() - 2);
}
}
@ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const
size_t result = from.nearest_waypoint_index(pp, to);
// as we assume 'from' is outside env, any node will require at least one crossing
if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) {
if (intersection_ln((Lines)Line(from, pp[result]), this->island).size() > 1) {
// discard result
pp.erase(pp.begin() + result);
} else {

View file

@ -54,8 +54,7 @@ PerimeterGenerator::process()
for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
surface != this->slices->surfaces.end(); ++surface) {
// detect how many perimeters must be generated for this island
signed short loop_number = this->config->perimeters + surface->extra_perimeters;
loop_number--; // 0-indexed loops
const int loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops
Polygons gaps;
@ -67,7 +66,7 @@ PerimeterGenerator::process()
ThickPolylines thin_walls;
// we loop one time more than needed in order to find gaps after the last perimeter was applied
for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0
for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0
Polygons offsets;
if (i == 0) {
// the minimum thickness of a single loop is:
@ -170,16 +169,16 @@ PerimeterGenerator::process()
}
// nest loops: holes first
for (signed short d = 0; d <= loop_number; ++d) {
for (int d = 0; d <= loop_number; ++d) {
PerimeterGeneratorLoops &holes_d = holes[d];
// loop through all holes having depth == d
for (signed short i = 0; i < holes_d.size(); ++i) {
for (int i = 0; i < (int)holes_d.size(); ++i) {
const PerimeterGeneratorLoop &loop = holes_d[i];
// find the hole loop that contains this one, if any
for (signed short t = d+1; t <= loop_number; ++t) {
for (signed short j = 0; j < holes[t].size(); ++j) {
for (int t = d+1; t <= loop_number; ++t) {
for (int j = 0; j < (int)holes[t].size(); ++j) {
PerimeterGeneratorLoop &candidate_parent = holes[t][j];
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
candidate_parent.children.push_back(loop);
@ -191,8 +190,8 @@ PerimeterGenerator::process()
}
// if no hole contains this hole, find the contour loop that contains it
for (signed short t = loop_number; t >= 0; --t) {
for (signed short j = 0; j < contours[t].size(); ++j) {
for (int t = loop_number; t >= 0; --t) {
for (int j = 0; j < (int)contours[t].size(); ++j) {
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
candidate_parent.children.push_back(loop);
@ -207,16 +206,16 @@ PerimeterGenerator::process()
}
// nest contour loops
for (signed short d = loop_number; d >= 1; --d) {
for (int d = loop_number; d >= 1; --d) {
PerimeterGeneratorLoops &contours_d = contours[d];
// loop through all contours having depth == d
for (signed short i = 0; i < contours_d.size(); ++i) {
for (int i = 0; i < (int)contours_d.size(); ++i) {
const PerimeterGeneratorLoop &loop = contours_d[i];
// find the contour loop that contains it
for (signed short t = d-1; t >= 0; --t) {
for (signed short j = 0; j < contours[t].size(); ++j) {
for (int t = d-1; t >= 0; --t) {
for (int j = 0; j < contours[t].size(); ++j) {
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
candidate_parent.children.push_back(loop);
@ -315,8 +314,7 @@ PerimeterGenerator::process()
coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
// append infill areas to fill_surfaces
surfaces_append(
this->fill_surfaces->surfaces,
this->fill_surfaces->append(
offset2_ex(
pp,
-inset -min_perimeter_infill_spacing/2,
@ -354,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
if (this->config->overhangs && this->layer_id > 0
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
// get non-overhang paths by intersecting this loop with the grown lower slices
{
Polylines polylines;
intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
ExtrusionPath path(role);
path.polyline = *polyline;
path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm;
path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
path.height = this->layer_height;
paths.push_back(path);
}
}
extrusion_paths_append(
paths,
intersection_pl(loop->polygon, this->_lower_slices_p),
role,
is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
this->layer_height);
// get overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter
{
Polylines polylines;
diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
ExtrusionPath path(erOverhangPerimeter);
path.polyline = *polyline;
path.mm3_per_mm = this->_mm3_per_mm_overhang;
path.width = this->overhang_flow.width;
path.height = this->overhang_flow.height;
paths.push_back(path);
}
}
extrusion_paths_append(
paths,
diff_pl(loop->polygon, this->_lower_slices_p),
erOverhangPerimeter,
this->_mm3_per_mm_overhang,
this->overhang_flow.width,
this->overhang_flow.height);
// reapply the nearest point search for starting point
// We allow polyline reversal because Clipper may have randomly
@ -459,7 +445,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo
ExtrusionPath path(role);
ThickLines lines = p->thicklines();
for (size_t i = 0; i < lines.size(); ++i) {
for (int i = 0; i < (int)lines.size(); ++i) {
const ThickLine& line = lines[i];
const coordf_t line_len = line.length();

View file

@ -12,12 +12,6 @@ Point::Point(double x, double y)
this->y = lrint(y);
}
bool
Point::operator==(const Point& rhs) const
{
return this->coincides_with(rhs);
}
std::string
Point::wkt() const
{

View file

@ -36,7 +36,7 @@ class Point
static Point new_scale(coordf_t x, coordf_t y) {
return Point(scale_(x), scale_(y));
};
bool operator==(const Point& rhs) const;
bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; }
std::string wkt() const;
std::string dump_perl() const;
void scale(double factor);
@ -70,6 +70,12 @@ inline Point operator+(const Point& point1, const Point& point2) { return Point(
inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); }
inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); }
struct PointHash {
size_t operator()(const Point &pt) const {
return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y);
}
};
class Point3 : public Point
{
public:
@ -105,6 +111,9 @@ class Pointf
inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); }
inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); }
inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); }
inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); }
inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; }
inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v1.y + v2.x * v2.y; }
class Pointf3 : public Pointf
{
@ -122,7 +131,7 @@ class Pointf3 : public Pointf
Vectorf3 vector_to(const Pointf3 &point) const;
};
}
} // namespace Slic3r
// start Boost
#include <boost/version.hpp>
@ -146,28 +155,28 @@ namespace boost { namespace polygon {
#endif
template <>
struct geometry_concept<Point> { typedef point_concept type; };
struct geometry_concept<Slic3r::Point> { typedef point_concept type; };
template <>
struct point_traits<Point> {
struct point_traits<Slic3r::Point> {
typedef coord_t coordinate_type;
static inline coordinate_type get(const Point& point, orientation_2d orient) {
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
return (orient == HORIZONTAL) ? point.x : point.y;
}
};
template <>
struct point_mutable_traits<Point> {
struct point_mutable_traits<Slic3r::Point> {
typedef coord_t coordinate_type;
static inline void set(Point& point, orientation_2d orient, coord_t value) {
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
if (orient == HORIZONTAL)
point.x = value;
else
point.y = value;
}
static inline Point construct(coord_t x_value, coord_t y_value) {
Point retval;
static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
Slic3r::Point retval;
retval.x = x_value;
retval.y = y_value;
return retval;

View file

@ -112,9 +112,7 @@ double Polygon::area() const
bool
Polygon::is_counter_clockwise() const
{
ClipperLib::Path p;
Slic3rMultiPoint_to_ClipperPath(*this, &p);
return ClipperLib::Orientation(p);
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
}
bool
@ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const
Polygons pp;
pp.push_back(p);
simplify_polygons(pp, &pp);
return pp;
return simplify_polygons(pp);
}
void

View file

@ -142,43 +142,43 @@ inline Polylines to_polylines(Polygons &&polys)
#include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon {
template <>
struct geometry_concept<Polygon>{ typedef polygon_concept type; };
struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
template <>
struct polygon_traits<Polygon> {
struct polygon_traits<Slic3r::Polygon> {
typedef coord_t coordinate_type;
typedef Points::const_iterator iterator_type;
typedef Point point_type;
typedef Slic3r::Points::const_iterator iterator_type;
typedef Slic3r::Point point_type;
// Get the begin iterator
static inline iterator_type begin_points(const Polygon& t) {
static inline iterator_type begin_points(const Slic3r::Polygon& t) {
return t.points.begin();
}
// Get the end iterator
static inline iterator_type end_points(const Polygon& t) {
static inline iterator_type end_points(const Slic3r::Polygon& t) {
return t.points.end();
}
// Get the number of sides of the polygon
static inline std::size_t size(const Polygon& t) {
static inline std::size_t size(const Slic3r::Polygon& t) {
return t.points.size();
}
// Get the winding direction of the polygon
static inline winding_direction winding(const Polygon& t) {
static inline winding_direction winding(const Slic3r::Polygon& t) {
return unknown_winding;
}
};
template <>
struct polygon_mutable_traits<Polygon> {
struct polygon_mutable_traits<Slic3r::Polygon> {
// expects stl style iterators
template <typename iT>
static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) {
static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) {
polygon.points.clear();
while (input_begin != input_end) {
polygon.points.push_back(Point());
polygon.points.push_back(Slic3r::Point());
boost::polygon::assign(polygon.points.back(), *input_begin);
++input_begin;
}
@ -189,32 +189,32 @@ namespace boost { namespace polygon {
};
template <>
struct geometry_concept<Polygons> { typedef polygon_set_concept type; };
struct geometry_concept<Slic3r::Polygons> { typedef polygon_set_concept type; };
//next we map to the concept through traits
template <>
struct polygon_set_traits<Polygons> {
struct polygon_set_traits<Slic3r::Polygons> {
typedef coord_t coordinate_type;
typedef Polygons::const_iterator iterator_type;
typedef Polygons operator_arg_type;
typedef Slic3r::Polygons::const_iterator iterator_type;
typedef Slic3r::Polygons operator_arg_type;
static inline iterator_type begin(const Polygons& polygon_set) {
static inline iterator_type begin(const Slic3r::Polygons& polygon_set) {
return polygon_set.begin();
}
static inline iterator_type end(const Polygons& polygon_set) {
static inline iterator_type end(const Slic3r::Polygons& polygon_set) {
return polygon_set.end();
}
//don't worry about these, just return false from them
static inline bool clean(const Polygons& polygon_set) { return false; }
static inline bool sorted(const Polygons& polygon_set) { return false; }
static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; }
static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; }
};
template <>
struct polygon_set_mutable_traits<Polygons> {
struct polygon_set_mutable_traits<Slic3r::Polygons> {
template <typename input_iterator_type>
static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
static inline void set(Slic3r::Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
polygons.assign(input_begin, input_end);
}
};

View file

@ -133,12 +133,6 @@ Print::clear_regions()
this->delete_region(i);
}
PrintRegion*
Print::get_region(size_t idx)
{
return regions.at(idx);
}
PrintRegion*
Print::add_region()
{
@ -608,20 +602,15 @@ Print::validate() const
object->model_object()->instances.front()->transform_polygon(&convex_hull);
// grow convex hull with the clearance margin
{
Polygons grown_hull;
offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1));
convex_hull = grown_hull.front();
}
convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front();
// now we check that no instance of convex_hull intersects any of the previously checked object instances
for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) {
Polygon p = convex_hull;
p.translate(*copy);
if (intersects(a, p))
if (! intersection(a, p).empty())
return "Some objects are too close; your extruder will collide with them.";
union_(a, p, &a);
polygons_append(a, p);
}
}
}

View file

@ -12,7 +12,7 @@
#include "Layer.hpp"
#include "Model.hpp"
#include "PlaceholderParser.hpp"
#include "Slicing.hpp"
namespace Slic3r {
@ -78,6 +78,10 @@ public:
std::map< size_t,std::vector<int> > region_volumes;
PrintObjectConfig config;
t_layer_height_ranges layer_height_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
std::vector<coordf_t> layer_height_profile;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
@ -136,13 +140,27 @@ public:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
bool invalidate_step(PrintObjectStep step);
bool invalidate_all_steps();
// Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
// The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
void update_layer_height_profile();
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
// by the interactive layer height editor and by the printing process itself.
// The slicing parameters are dependent on various configuration values
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
void _slice();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void _make_perimeters();
void _infill();
void _generate_support_material();
private:
Print* _print;
ModelObject* _model_object;
@ -152,6 +170,8 @@ private:
// parameter
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
~PrintObject() {}
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
};
typedef std::vector<PrintObject*> PrintObjectPtrs;
@ -186,7 +206,8 @@ class Print
bool reload_model_instances();
// methods for handling regions
PrintRegion* get_region(size_t idx);
PrintRegion* get_region(size_t idx) { return regions.at(idx); }
const PrintRegion* get_region(size_t idx) const { return regions.at(idx); }
PrintRegion* add_region();
// methods for handling state

View file

@ -1,4 +1,5 @@
#include "PrintConfig.hpp"
#include <boost/thread.hpp>
namespace Slic3r {
@ -1120,6 +1121,17 @@ PrintConfigDef::PrintConfigDef()
def->cli = "support-material!";
def->default_value = new ConfigOptionBool(false);
def = this->add("support_material_xy_spacing", coFloatOrPercent);
def->label = "XY separation between an object and its support";
def->category = "Support material";
def->tooltip = "XY separation between an object and its support. If expressed as percentage (for example 50%), it will be calculated over external perimeter width.";
def->sidetext = "mm or %";
def->cli = "support-material-xy-spacing=s";
def->ratio_over = "external_perimeter_extrusion_width";
def->min = 0;
// Default is half the external perimeter width.
def->default_value = new ConfigOptionFloatOrPercent(50, true);
def = this->add("support_material_angle", coInt);
def->label = "Pattern angle";
def->category = "Support material";
@ -1177,6 +1189,13 @@ PrintConfigDef::PrintConfigDef()
def->cli = "support-material-extrusion-width=s";
def->default_value = new ConfigOptionFloatOrPercent(0, false);
def = this->add("support_material_interface_contact_loops", coBool);
def->label = "Interface circles";
def->category = "Support material";
def->tooltip = "Cover the top most interface layer with contact loops";
def->cli = "support-material-interface-contact-loops!";
def->default_value = new ConfigOptionBool(true);
def = this->add("support_material_interface_extruder", coInt);
def->label = "Support material/raft interface extruder";
def->category = "Extruders";
@ -1247,6 +1266,13 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
def = this->add("support_material_synchronize_layers", coBool);
def->label = "Synchronize with object layers";
def->category = "Support material";
def->tooltip = "Synchronize support layers with the object print layers. This is useful with multi-material printers, where the extruder switch is expensive.";
def->cli = "support-material-synchronize-layers!";
def->default_value = new ConfigOptionBool(false);
def = this->add("support_material_threshold", coInt);
def->label = "Overhang threshold";
def->category = "Support material";
@ -1290,9 +1316,11 @@ PrintConfigDef::PrintConfigDef()
def->cli = "threads|j=i";
def->readonly = true;
def->min = 1;
def->max = 16;
def->default_value = new ConfigOptionInt(2);
{
unsigned int threads = boost::thread::hardware_concurrency();
def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2);
}
def = this->add("toolchange_gcode", coString);
def->label = "Tool change G-code";
def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder].";

View file

@ -153,6 +153,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
ConfigOptionInt support_material_enforce_layers;
ConfigOptionInt support_material_extruder;
ConfigOptionFloatOrPercent support_material_extrusion_width;
ConfigOptionBool support_material_interface_contact_loops;
ConfigOptionInt support_material_interface_extruder;
ConfigOptionInt support_material_interface_layers;
ConfigOptionFloat support_material_interface_spacing;
@ -160,8 +161,10 @@ class PrintObjectConfig : public virtual StaticPrintConfig
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionBool support_material_synchronize_layers;
ConfigOptionInt support_material_threshold;
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
ConfigOptionFloat xy_size_compensation;
PrintObjectConfig(bool initialize = true) : StaticPrintConfig() {
@ -185,6 +188,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
OPT_PTR(support_material_enforce_layers);
OPT_PTR(support_material_interface_contact_loops);
OPT_PTR(support_material_extruder);
OPT_PTR(support_material_extrusion_width);
OPT_PTR(support_material_interface_extruder);
@ -194,6 +198,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig
OPT_PTR(support_material_pattern);
OPT_PTR(support_material_spacing);
OPT_PTR(support_material_speed);
OPT_PTR(support_material_synchronize_layers);
OPT_PTR(support_material_xy_spacing);
OPT_PTR(support_material_threshold);
OPT_PTR(support_material_with_sheath);
OPT_PTR(xy_size_compensation);

View file

@ -2,10 +2,28 @@
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "SVG.hpp"
#include "SupportMaterial.hpp"
#include <boost/log/trivial.hpp>
#include <Shiny/Shiny.h>
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#define SLIC3R_DEBUG
#endif
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#undef NDEBUG
#define DEBUG
#define _DEBUG
#include "SVG.hpp"
#undef assert
#include <cassert>
#endif
namespace Slic3r {
PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox)
@ -115,8 +133,12 @@ PrintObject::layer_count() const
void
PrintObject::clear_layers()
{
for (int i = this->layers.size()-1; i >= 0; --i)
this->delete_layer(i);
for (size_t i = 0; i < this->layers.size(); ++ i) {
Layer *layer = this->layers[i];
layer->upper_layer = layer->lower_layer = nullptr;
delete layer;
}
this->layers.clear();
}
Layer*
@ -144,8 +166,12 @@ PrintObject::support_layer_count() const
void
PrintObject::clear_support_layers()
{
for (int i = this->support_layers.size()-1; i >= 0; --i)
this->delete_support_layer(i);
for (size_t i = 0; i < this->support_layers.size(); ++ i) {
Layer *layer = this->support_layers[i];
layer->upper_layer = layer->lower_layer = nullptr;
delete layer;
}
this->support_layers.clear();
}
SupportLayer*
@ -197,12 +223,15 @@ PrintObject::invalidate_state_by_config_options(const std::vector<t_config_optio
|| *opt_key == "support_material_extruder"
|| *opt_key == "support_material_extrusion_width"
|| *opt_key == "support_material_interface_layers"
|| *opt_key == "support_material_interface_contact_loops"
|| *opt_key == "support_material_interface_extruder"
|| *opt_key == "support_material_interface_spacing"
|| *opt_key == "support_material_interface_speed"
|| *opt_key == "support_material_buildplate_only"
|| *opt_key == "support_material_pattern"
|| *opt_key == "support_material_xy_spacing"
|| *opt_key == "support_material_spacing"
|| *opt_key == "support_material_synchronize_layers"
|| *opt_key == "support_material_threshold"
|| *opt_key == "support_material_with_sheath"
|| *opt_key == "dont_support_bridges"
@ -323,7 +352,8 @@ PrintObject::has_support_material() const
// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
void PrintObject::detect_surfaces_type()
{
// Slic3r::debugf "Detecting solid surfaces...\n";
BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces...";
for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) {
@ -435,7 +465,7 @@ void PrintObject::detect_surfaces_type()
{
Polygons topbottom = to_polygons(top);
polygons_append(topbottom, to_polygons(bottom));
surfaces_append(layerm->slices.surfaces,
layerm->slices.append(
#if 0
offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset),
#else
@ -444,8 +474,8 @@ void PrintObject::detect_surfaces_type()
stInternal);
}
surfaces_append(layerm->slices.surfaces, STDMOVE(top));
surfaces_append(layerm->slices.surfaces, STDMOVE(bottom));
layerm->slices.append(STDMOVE(top));
layerm->slices.append(STDMOVE(bottom));
// Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
// $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
@ -469,6 +499,8 @@ void PrintObject::detect_surfaces_type()
void
PrintObject::process_external_surfaces()
{
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
FOREACH_REGION(this->_print, region) {
size_t region_id = region - this->_print->regions.begin();
@ -497,6 +529,8 @@ PrintObject::discover_vertical_shells()
{
PROFILE_FUNC();
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
@ -518,8 +552,19 @@ PrintObject::discover_vertical_shells()
{
PROFILE_BLOCK(discover_vertical_shells_region_layer);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static size_t debug_idx = 0;
++ debug_idx;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Layer *layer = this->layers[idx_layer];
LayerRegion *layerm = layer->get_region(idx_region);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Flow solid_infill_flow = layerm->flow(frSolidInfill);
coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
@ -532,16 +577,16 @@ PrintObject::discover_vertical_shells()
if (1)
{
PROFILE_BLOCK(discover_vertical_shells_region_layer_collect);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#if 0
// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box());
Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box());
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) {
if (n < 0 || n >= (int)this->layers.size())
continue;
ExPolygons &expolys = this->layers[n]->perimeter_expolygons;
for (size_t i = 0; i < expolys.size(); ++ i) {
SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", idx, n, i), get_extents(expolys[i]));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i]));
svg.draw(expolys[i]);
svg.draw_outline(expolys[i].contour, "black", scale_(0.05));
svg.draw_outline(expolys[i].holes, "blue", scale_(0.05));
@ -552,7 +597,6 @@ PrintObject::discover_vertical_shells()
svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
}
}
++ idx;
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
@ -610,8 +654,7 @@ PrintObject::discover_vertical_shells()
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
svg.draw(shell);
svg.draw_outline(shell, "black", scale_(0.05));
svg.Close();
@ -634,8 +677,7 @@ PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell));
svg.draw(shell_ex);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
@ -644,8 +686,7 @@ PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
@ -653,8 +694,7 @@ PrintObject::discover_vertical_shells()
svg.Close();
}
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
@ -662,8 +702,7 @@ PrintObject::discover_vertical_shells()
svg.Close();
}
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
@ -674,7 +713,7 @@ PrintObject::discover_vertical_shells()
// Trim the shells region by the internal & internal void surfaces.
const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid };
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2));
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 3));
shell = intersection(shell, polygonsInternal, true);
polygons_append(shell, diff(polygonsInternal, holes));
if (shell.empty())
@ -692,8 +731,7 @@ PrintObject::discover_vertical_shells()
#if 1
// Intentionally inflate a bit more than how much the region has been shrunk,
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing,
CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare);
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
if (shell.empty())
continue;
#else
@ -705,7 +743,7 @@ PrintObject::discover_vertical_shells()
// get a triangle in $too_narrow; if we grow it below then the shell
// would have a different shape from the external surface and we'd still
// have the same angle, so the next shell would be grown even more and so on.
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true);
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true);
if (! too_narrow.empty()) {
// grow the collapsing parts and add the extra area to the neighbor layer
// as well as to our original surfaces so that we support this
@ -717,8 +755,7 @@ PrintObject::discover_vertical_shells()
ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell, false);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", idx ++), get_extents(shell_before));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before));
// Source shell.
svg.draw(union_ex(shell_before, true));
// Shell trimmed to the internal surfaces.
@ -743,26 +780,27 @@ PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05));
++ idx;
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", debug_idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", debug_idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", debug_idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05));
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Assign resulting internal surfaces to layer.
const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge };
layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType));
layerm->fill_surfaces.append(stInternal , new_internal);
layerm->fill_surfaces.append(stInternalVoid , new_internal_void);
layerm->fill_surfaces.append(stInternalSolid, new_internal_solid);
layerm->fill_surfaces.append(new_internal, stInternal);
layerm->fill_surfaces.append(new_internal_void, stInternalVoid);
layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
} // for each layer
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells");
for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) {
LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region);
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final");
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each layer
} // for each region
// Write the profiler measurements to file
@ -775,6 +813,8 @@ PrintObject::discover_vertical_shells()
void
PrintObject::bridge_over_infill()
{
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
FOREACH_REGION(this->_print, region) {
size_t region_id = region - this->_print->regions.begin();
@ -846,7 +886,7 @@ PrintObject::bridge_over_infill()
#endif
// compute the remaning internal solid surfaces as difference
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true);
to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true);
// build the new collection of fill_surfaces
@ -902,4 +942,332 @@ PrintObject::bridge_over_infill()
}
}
SlicingParameters PrintObject::slicing_parameters() const
{
return SlicingParameters::create_from_config(
this->print()->config, this->config,
unscale(this->size.z), this->print()->object_extruders());
}
void PrintObject::update_layer_height_profile()
{
if (this->layer_height_profile.empty()) {
if (0)
// if (this->layer_height_profile.empty())
this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges,
this->model_object()->volumes);
else
this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges);
}
}
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
// 3) Slices the object meshes
// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
// 5) Applies size compensation (offsets the slices in XY plane)
// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
// Resulting expolygons of layer regions are marked as Internal.
//
// this should be idempotent
void PrintObject::_slice()
{
SlicingParameters slicing_params = this->slicing_parameters();
// 1) Initialize layers and their slice heights.
std::vector<float> slice_zs;
{
this->clear_layers();
// Object layers (pairs of bottom/top Z coordinate), without the raft.
this->update_layer_height_profile();
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile);
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
int id = int(slicing_params.raft_layers());
slice_zs.reserve(object_layers.size());
Layer *prev = nullptr;
for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z);
slice_zs.push_back(float(slice_z));
if (prev != nullptr) {
prev->upper_layer = layer;
layer->lower_layer = prev;
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id)
layer->add_region(this->print()->regions[region_id]);
prev = layer;
}
}
if (this->print()->regions.size() == 1) {
// Optimized for a single region. Slice the single non-modifier mesh.
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(0, slice_zs, false);
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
this->layers[layer_id]->regions.front()->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
} else {
// Slice all non-modifier volumes.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
}
// Slice all modifier volumes.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
// loop through the other regions and 'steal' the slices belonging to this one
for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
Layer *layer = layers[layer_id];
LayerRegion *layerm = layer->regions[region_id];
LayerRegion *other_layerm = layer->regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
}
}
// remove last layer(s) if empty
while (! this->layers.empty()) {
const Layer *layer = this->layers.back();
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id)
if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty())
// Non empty layer.
goto end;
this->delete_layer(int(this->layers.size()) - 1);
}
end:
;
for (size_t layer_id = 0; layer_id < layers.size(); ++ layer_id) {
Layer *layer = this->layers[layer_id];
// apply size compensation
if (this->config.xy_size_compensation.value != 0.) {
float delta = float(scale_(this->config.xy_size_compensation.value));
if (layer->regions.size() == 1) {
// single region
LayerRegion *layerm = layer->regions.front();
layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal);
} else {
if (delta < 0) {
// multiple regions, shrinking
// we apply the offset to the combined shape, then intersect it
// with the original slices for each region
Polygons region_slices;
for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id)
polygons_append(region_slices, layer->regions[region_id]->slices.surfaces);
Polygons slices = offset(union_(region_slices), delta);
for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) {
LayerRegion *layerm = layer->regions[region_id];
layerm->slices.set(std::move(intersection_ex(slices, to_polygons(std::move(layerm->slices.surfaces)))), stInternal);
}
} else {
// multiple regions, growing
// this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
// so we give priority to the first one and so on
Polygons processed;
for (size_t region_id = 0;; ++ region_id) {
LayerRegion *layerm = layer->regions[region_id];
ExPolygons slices = offset_ex(to_expolygons(layerm->slices.surfaces), delta);
if (region_id > 0)
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (region_id + 1 == layer->regions.size()) {
layerm->slices.set(std::move(slices), stInternal);
break;
}
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
}
}
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
layer->make_slices();
}
}
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{
std::vector<ExPolygons> layers;
assert(region_id < this->region_volumes.size());
std::vector<int> &volumes = this->region_volumes[region_id];
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh;
for (std::vector<int>::const_iterator it_volume = volumes.begin(); it_volume != volumes.end(); ++ it_volume) {
ModelVolume *volume = this->model_object()->volumes[*it_volume];
if (volume->modifier == modifier)
mesh.merge(volume->mesh);
}
if (mesh.stl.stats.number_of_facets > 0) {
// transform mesh
// we ignore the per-instance transformations currently and only
// consider the first one
this->model_object()->instances.front()->transform_mesh(&mesh, true);
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
mesh.translate(- unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), -this->model_object()->bounding_box().min.z);
// perform actual slicing
TriangleMeshSlicer mslicer(&mesh);
mslicer.slice(z, &layers);
}
}
return layers;
}
void
PrintObject::_make_perimeters()
{
if (this->state.is_done(posPerimeters)) return;
this->state.set_started(posPerimeters);
// merge slices if they were split into types
if (this->typed_slices) {
FOREACH_LAYER(this, layer_it)
(*layer_it)->merge_slices();
this->typed_slices = false;
this->state.invalidate(posPrepareInfill);
}
// compare each layer to the one below, and mark those slices needing
// one additional inner perimeter, like the top of domed objects-
// this algorithm makes sure that at least one perimeter is overlapping
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
FOREACH_REGION(this->_print, region_it) {
size_t region_id = region_it - this->_print->regions.begin();
const PrintRegion &region = **region_it;
if (!region.config.extra_perimeters
|| region.config.perimeters == 0
|| region.config.fill_density == 0
|| this->layer_count() < 2) continue;
for (int i = 0; i < int(this->layer_count()) - 1; ++i) {
LayerRegion &layerm = *this->get_layer(i)->get_region(region_id);
const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id);
const Polygons upper_layerm_polygons = upper_layerm.slices;
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
double total_loop_length = 0;
for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it)
total_loop_length += it->length();
const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
for (Surfaces::iterator slice = layerm.slices.surfaces.begin();
slice != layerm.slices.surfaces.end(); ++slice) {
while (true) {
// compute the total thickness of perimeters
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
+ (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing;
// define a critical area where we don't want the upper slice to fall into
// (it should either lay over our perimeters or outside this area)
const coord_t critical_area_depth = perimeter_spacing * 1.5;
const Polygons critical_area = diff(
offset(slice->expolygon, -perimeters_thickness),
offset(slice->expolygon, -(perimeters_thickness + critical_area_depth))
);
// check whether a portion of the upper slices falls inside the critical area
const Polylines intersection = intersection_pl(
to_polylines(upper_layerm_polygons),
critical_area
);
// only add an additional loop if at least 30% of the slice loop would benefit from it
{
double total_intersection_length = 0;
for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it)
total_intersection_length += it->length();
if (total_intersection_length <= total_loop_length*0.3) break;
}
/*
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"extra.svg",
no_arrows => 1,
expolygons => union_ex($critical_area),
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
);
}
*/
slice->extra_perimeters++;
}
#ifdef DEBUG
if (slice->extra_perimeters > 0)
printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i);
#endif
}
}
}
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::make_perimeters, _1),
this->_print->config.threads.value
);
/*
simplify slices (both layer and region slices),
we only need the max resolution for perimeters
### This makes this method not-idempotent, so we keep it disabled for now.
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
*/
this->state.set_done(posPerimeters);
}
void
PrintObject::_infill()
{
if (this->state.is_done(posInfill)) return;
this->state.set_started(posInfill);
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::make_fills, _1),
this->_print->config.threads.value
);
/* we could free memory now, but this would make this step not idempotent
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
*/
this->state.set_done(posInfill);
}
void PrintObject::_generate_support_material()
{
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
support_material.generate(*this);
}
} // namespace Slic3r

View file

@ -12,7 +12,7 @@ namespace Slic3r {
class SVG
{
public:
public:
bool arrows;
std::string fill, stroke;
Point origin;
@ -89,6 +89,10 @@ public:
static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0);
static void export_expolygons(const std::string &path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)
{ export_expolygons(path.c_str(), bbox, expolygons, stroke_outer, stroke_holes, stroke_width); }
static void export_expolygons(const char *path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)
{ export_expolygons(path, get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); }
static void export_expolygons(const std::string &path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)
{ export_expolygons(path.c_str(), get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); }
};
}

View file

@ -0,0 +1,631 @@
#include "Slicing.hpp"
#include "SlicingAdaptive.hpp"
#include "PrintConfig.hpp"
#include "Model.hpp"
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#undef NDEBUG
#define DEBUG
#define _DEBUG
#include "SVG.hpp"
#undef assert
#include <cassert>
#endif
namespace Slic3r
{
SlicingParameters SlicingParameters::create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::set<size_t> &object_extruders)
{
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
object_config.layer_height.value :
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
coordf_t support_material_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1);
coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1);
bool soluble_interface = object_config.support_material_contact_distance.value == 0.;
SlicingParameters params;
params.layer_height = object_config.layer_height.value;
params.first_print_layer_height = first_layer_height;
params.first_object_layer_height = first_layer_height;
params.object_print_z_min = 0.;
params.object_print_z_max = object_height;
params.base_raft_layers = object_config.raft_layers.value;
params.soluble_interface = soluble_interface;
if (! soluble_interface) {
params.gap_raft_object = object_config.support_material_contact_distance.value;
params.gap_object_support = object_config.support_material_contact_distance.value;
params.gap_support_object = object_config.support_material_contact_distance.value;
}
if (params.base_raft_layers > 0) {
params.interface_raft_layers = (params.base_raft_layers + 1) / 2;
params.base_raft_layers -= params.interface_raft_layers;
// Use as large as possible layer height for the intermediate raft layers.
params.base_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_extruder_dmr);
params.interface_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
params.contact_raft_layer_height_bridging = false;
params.first_object_layer_bridging = false;
#if 1
params.contact_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
if (! soluble_interface) {
// Compute the average of all nozzles used for printing the object over a raft.
//FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
coordf_t average_object_extruder_dmr = 0.;
if (! object_extruders.empty()) {
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder);
average_object_extruder_dmr /= coordf_t(object_extruders.size());
}
params.first_object_layer_height = average_object_extruder_dmr;
params.first_object_layer_bridging = true;
}
#else
params.contact_raft_layer_height = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr;
params.contact_raft_layer_height_bridging = ! soluble_interface;
...
#endif
}
if (params.has_raft()) {
// Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic.
//FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
if (params.raft_layers() == 1) {
// There is only the contact layer.
params.contact_raft_layer_height = first_layer_height;
params.raft_contact_top_z = first_layer_height;
} else {
assert(params.base_raft_layers > 0);
assert(params.interface_raft_layers > 0);
// Number of the base raft layers is decreased by the first layer.
params.raft_base_top_z = first_layer_height + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height;
// Number of the interface raft layers is decreased by the contact layer.
params.raft_interface_top_z = params.raft_base_top_z + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height;
params.raft_contact_top_z = params.raft_interface_top_z + params.contact_raft_layer_height;
}
coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object;
params.object_print_z_min = print_z;
params.object_print_z_max += print_z;
}
params.min_layer_height = std::min(params.layer_height, first_layer_height);
params.max_layer_height = std::max(params.layer_height, first_layer_height);
//FIXME add it to the print configuration
params.min_layer_height = 0.05;
// Calculate the maximum layer height as 0.75 from the minimum nozzle diameter.
if (! object_extruders.empty()) {
coordf_t min_object_extruder_dmr = 1000000.;
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder));
// Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr
params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr);
}
return params;
}
// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges)
{
// 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
if (slicing_params.first_object_layer_height_fixed())
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
t_layer_height_range(0., slicing_params.first_object_layer_height),
slicing_params.first_object_layer_height));
// The height ranges are sorted lexicographically by low / high layer boundaries.
for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
coordf_t lo = it_range->first.first;
coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
coordf_t height = it_range->second;
if (! ranges_non_overlapping.empty())
// Trim current low with the last high.
lo = std::max(lo, ranges_non_overlapping.back().first.second);
if (lo + EPSILON < hi)
// Ignore too narrow ranges.
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(t_layer_height_range(lo, hi), height));
}
// 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max()
// with slicing_params.layer_height
std::vector<coordf_t> layer_height_profile;
for (std::vector<std::pair<t_layer_height_range,coordf_t>>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) {
coordf_t lo = it_range->first.first;
coordf_t hi = it_range->first.second;
coordf_t height = it_range->second;
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (lo > last_z + EPSILON) {
// Insert a step of normal layer height.
layer_height_profile.push_back(last_z);
layer_height_profile.push_back(slicing_params.layer_height);
layer_height_profile.push_back(lo);
layer_height_profile.push_back(slicing_params.layer_height);
}
// Insert a step of the overriden layer height.
layer_height_profile.push_back(lo);
layer_height_profile.push_back(height);
layer_height_profile.push_back(hi);
layer_height_profile.push_back(height);
}
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (last_z < slicing_params.object_print_z_height()) {
// Insert a step of normal layer height up to the object top.
layer_height_profile.push_back(last_z);
layer_height_profile.push_back(slicing_params.layer_height);
layer_height_profile.push_back(slicing_params.object_print_z_height());
layer_height_profile.push_back(slicing_params.layer_height);
}
return layer_height_profile;
}
// Based on the work of @platsch
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges,
const ModelVolumePtrs &volumes)
{
// 1) Initialize the SlicingAdaptive class with the object meshes.
SlicingAdaptive as;
as.set_slicing_parameters(slicing_params);
for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
if (! (*it)->modifier)
as.add_mesh(&(*it)->mesh);
as.prepare();
// 2) Generate layers using the algorithm of @platsch
// loop until we have at least one layer and the max slice_z reaches the object height
//FIXME make it configurable
// Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
std::vector<coordf_t> layer_height_profile;
layer_height_profile.push_back(0.);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
if (slicing_params.first_object_layer_height_fixed()) {
layer_height_profile.push_back(slicing_params.first_object_layer_height);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
}
coordf_t slice_z = slicing_params.first_object_layer_height;
coordf_t height = slicing_params.first_object_layer_height;
coordf_t cusp_height = 0.;
int current_facet = 0;
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
height = 999;
// Slic3r::debugf "\n Slice layer: %d\n", $id;
// determine next layer height
coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet);
// check for horizontal features and object size
/*
if($self->config->get_value('match_horizontal_surfaces')) {
my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height);
if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) {
Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist;
# can we shrink the current layer a bit?
if($cusp_height-($min_height-$horizontal_dist) > $min_height) {
# yes we can
$cusp_height = $cusp_height-($min_height-$horizontal_dist);
Slic3r::debugf "Shrink layer height to %f\n", $cusp_height;
}else{
# no, current layer would become too thin
$cusp_height = $cusp_height+$horizontal_dist;
Slic3r::debugf "Widen layer height to %f\n", $cusp_height;
}
}
}
*/
height = std::min(cusp_height, height);
// apply z-gradation
/*
my $gradation = $self->config->get_value('adaptive_slicing_z_gradation');
if($gradation > 0) {
$height = $height - unscale((scale($height)) % (scale($gradation)));
}
*/
// look for an applicable custom range
/*
if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
$height = $range->[2];
# if user set custom height to zero we should just skip the range and resume slicing over it
if ($height == 0) {
$slice_z += $range->[1] - $range->[0];
next;
}
}
*/
layer_height_profile.push_back(slice_z);
layer_height_profile.push_back(height);
slice_z += height;
layer_height_profile.push_back(slice_z);
layer_height_profile.push_back(height);
}
coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
layer_height_profile.push_back(last);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
layer_height_profile.push_back(slicing_params.object_print_z_height());
layer_height_profile.push_back(slicing_params.first_object_layer_height);
return layer_height_profile;
}
template <typename T>
static inline T clamp(const T low, const T high, const T value)
{
return std::max(low, std::min(high, value));
}
template <typename T>
static inline T lerp(const T a, const T b, const T t)
{
assert(t >= T(-EPSILON) && t <= T(1.+EPSILON));
return (1. - t) * a + t * b;
}
void adjust_layer_height_profile(
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
coordf_t layer_thickness_delta,
coordf_t band_width,
LayerHeightEditActionType action)
{
// Constrain the profile variability by the 1st layer height.
std::pair<coordf_t, coordf_t> z_span_variable =
std::pair<coordf_t, coordf_t>(
slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
slicing_params.object_print_z_height());
if (z < z_span_variable.first || z > z_span_variable.second)
return;
assert(layer_height_profile.size() >= 2);
// 1) Get the current layer thickness at z.
coordf_t current_layer_height = slicing_params.layer_height;
for (size_t i = 0; i < layer_height_profile.size(); i += 2) {
if (i + 2 == layer_height_profile.size()) {
current_layer_height = layer_height_profile[i + 1];
break;
} else if (layer_height_profile[i + 2] > z) {
coordf_t z1 = layer_height_profile[i];
coordf_t h1 = layer_height_profile[i + 1];
coordf_t z2 = layer_height_profile[i + 2];
coordf_t h2 = layer_height_profile[i + 3];
current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1));
break;
}
}
// 2) Is it possible to apply the delta?
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
layer_thickness_delta = - layer_thickness_delta;
// fallthrough
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
if (layer_thickness_delta > 0) {
if (current_layer_height >= slicing_params.max_layer_height - EPSILON)
return;
layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height);
} else {
if (current_layer_height <= slicing_params.min_layer_height + EPSILON)
return;
layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height);
}
break;
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
layer_thickness_delta = std::abs(layer_thickness_delta);
layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height));
if (layer_thickness_delta < EPSILON)
return;
break;
default:
assert(false);
break;
}
// 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band.
coordf_t lo = std::max(z_span_variable.first, z - 0.5 * band_width);
coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width);
coordf_t z_step = 0.1;
size_t i = 0;
while (i < layer_height_profile.size() && layer_height_profile[i] < lo)
i += 2;
i -= 2;
std::vector<double> profile_new;
profile_new.reserve(layer_height_profile.size());
assert(i >= 0 && i + 1 < layer_height_profile.size());
profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2);
coordf_t zz = lo;
size_t i_resampled_start = profile_new.size();
while (zz < hi) {
size_t next = i + 2;
coordf_t z1 = layer_height_profile[i];
coordf_t h1 = layer_height_profile[i + 1];
coordf_t height = h1;
if (next < layer_height_profile.size()) {
coordf_t z2 = layer_height_profile[next];
coordf_t h2 = layer_height_profile[next + 1];
height = lerp(h1, h2, (zz - z1) / (z2 - z1));
}
// Adjust height by layer_thickness_delta.
coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
coordf_t height_new = height;
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
height += weight * layer_thickness_delta;
break;
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
{
coordf_t delta = height - slicing_params.layer_height;
coordf_t step = weight * layer_thickness_delta;
step = (std::abs(delta) > step) ?
(delta > 0) ? -step : step :
-delta;
height += step;
break;
}
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
{
// Don't modify the profile during resampling process, do it at the next step.
break;
}
default:
assert(false);
break;
}
// Avoid entering a too short segment.
if (profile_new[profile_new.size() - 2] + EPSILON < zz) {
profile_new.push_back(zz);
profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height));
}
zz += z_step;
i = next;
while (i < layer_height_profile.size() && layer_height_profile[i] < zz)
i += 2;
i -= 2;
}
i += 2;
assert(i > 0);
size_t i_resampled_end = profile_new.size();
if (i < layer_height_profile.size()) {
assert(zz >= layer_height_profile[i - 2]);
assert(zz <= layer_height_profile[i]);
// profile_new.push_back(zz);
// profile_new.push_back(layer_height_profile[i + 1]);
profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end());
}
layer_height_profile = std::move(profile_new);
if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) {
size_t n_rounds = 6;
for (size_t i_round = 0; i_round < n_rounds; ++ i_round) {
profile_new = layer_height_profile;
for (size_t i = i_resampled_start; i < i_resampled_end; i += 2) {
coordf_t zz = profile_new[i];
coordf_t t = std::abs(zz - z) < 0.5 * band_width ? (0.25 + 0.25 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
assert(t >= 0. && t <= 0.5000001);
if (i == 0)
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i + 3];
else if (i + 1 == profile_new.size())
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i - 1];
else
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + 0.5 * t * (profile_new[i - 1] + profile_new[i + 3]);
}
}
}
assert(layer_height_profile.size() > 2);
assert(layer_height_profile.size() % 2 == 0);
assert(layer_height_profile[0] == 0.);
#ifdef _DEBUG
for (size_t i = 2; i < layer_height_profile.size(); i += 2)
assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
for (size_t i = 1; i < layer_height_profile.size(); i += 2) {
assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON);
assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON);
}
#endif /* _DEBUG */
}
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
std::vector<coordf_t> generate_object_layers(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layer_height_profile)
{
coordf_t print_z = 0;
coordf_t height = 0;
std::vector<coordf_t> out;
if (slicing_params.first_object_layer_height_fixed()) {
out.push_back(0);
print_z = slicing_params.first_object_layer_height;
out.push_back(print_z);
}
size_t idx_layer_height_profile = 0;
// loop until we have at least one layer and the max slice_z reaches the object height
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
while (slice_z < slicing_params.object_print_z_height()) {
height = slicing_params.min_layer_height;
if (idx_layer_height_profile < layer_height_profile.size()) {
size_t next = idx_layer_height_profile + 2;
for (;;) {
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
break;
idx_layer_height_profile = next;
next += 2;
}
coordf_t z1 = layer_height_profile[idx_layer_height_profile];
coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
height = h1;
if (next < layer_height_profile.size()) {
coordf_t z2 = layer_height_profile[next];
coordf_t h2 = layer_height_profile[next + 1];
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
}
}
slice_z = print_z + 0.5 * height;
if (slice_z >= slicing_params.object_print_z_height())
break;
assert(height > slicing_params.min_layer_height - EPSILON);
assert(height < slicing_params.max_layer_height + EPSILON);
out.push_back(print_z);
print_z += height;
slice_z = print_z + 0.5 * slicing_params.min_layer_height;
out.push_back(print_z);
}
//FIXME Adjust the last layer to align with the top object layer exactly?
return out;
}
int generate_layer_height_texture(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layers,
void *data, int rows, int cols, bool level_of_detail_2nd_level)
{
// https://github.com/aschn/gnuplot-colorbrewer
std::vector<Point3> palette_raw;
palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B));
palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D));
palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082));
palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7));
palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0));
palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE));
palette_raw.push_back(Point3(0x043, 0x093, 0x0C3));
palette_raw.push_back(Point3(0x021, 0x066, 0x0AC));
// Clear the main texture and the 2nd LOD level.
memset(data, 0, rows * cols * 5);
// 2nd LOD level data start
unsigned char *data1 = reinterpret_cast<unsigned char*>(data) + rows * cols * 4;
int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height))));
int ncells1 = ncells / 2;
int cols1 = cols / 2;
coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height();
coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1);
coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height();
coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1);
// for color scaling
coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height);
if (hscale == 0)
// All layers have the same height. Provide some height scale to avoid division by zero.
hscale = slicing_params.layer_height;
for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) {
coordf_t lo = layers[idx_layer];
coordf_t hi = layers[idx_layer + 1];
coordf_t mid = 0.5f * (lo + hi);
assert(mid <= slicing_params.object_print_z_height());
coordf_t h = hi - lo;
hi = std::min(hi, slicing_params.object_print_z_height());
int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell)));
int cell_last = clamp(0, ncells-1, int(floor(hi * z_to_cell)));
for (int cell = cell_first; cell <= cell_last; ++ cell) {
coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
coordf_t t = idxf - coordf_t(idx1);
const Point3 &color1 = palette_raw[idx1];
const Point3 &color2 = palette_raw[idx2];
coordf_t z = cell_to_z * coordf_t(cell);
assert(z >= lo && z <= hi);
// Intensity profile to visualize the layers.
coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
// Color mapping from layer height to RGB.
Pointf3 color(
intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t),
intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t),
intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t));
int row = cell / (cols - 1);
int col = cell - row * (cols - 1);
assert(row >= 0 && row < rows);
assert(col >= 0 && col < cols);
unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4;
ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
ptr[3] = 255;
if (col == 0 && row > 0) {
// Duplicate the first value in a row as a last value of the preceding row.
ptr[-4] = ptr[0];
ptr[-3] = ptr[1];
ptr[-2] = ptr[2];
ptr[-1] = ptr[3];
}
}
if (level_of_detail_2nd_level) {
cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1)));
cell_last = clamp(0, ncells1-1, int(floor(hi * z_to_cell1)));
for (int cell = cell_first; cell <= cell_last; ++ cell) {
coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
coordf_t t = idxf - coordf_t(idx1);
const Point3 &color1 = palette_raw[idx1];
const Point3 &color2 = palette_raw[idx2];
coordf_t z = cell_to_z1 * coordf_t(cell);
assert(z >= lo && z <= hi);
// Color mapping from layer height to RGB.
Pointf3 color(
lerp(coordf_t(color1.x), coordf_t(color2.x), t),
lerp(coordf_t(color1.y), coordf_t(color2.y), t),
lerp(coordf_t(color1.z), coordf_t(color2.z), t));
int row = cell / (cols1 - 1);
int col = cell - row * (cols1 - 1);
assert(row >= 0 && row < rows/2);
assert(col >= 0 && col < cols/2);
unsigned char *ptr = data1 + (row * cols1 + col) * 4;
ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
ptr[3] = 255;
if (col == 0 && row > 0) {
// Duplicate the first value in a row as a last value of the preceding row.
ptr[-4] = ptr[0];
ptr[-3] = ptr[1];
ptr[-2] = ptr[2];
ptr[-1] = ptr[3];
}
}
}
}
// Returns number of cells of the 0th LOD level.
return ncells;
}
}; // namespace Slic3r

View file

@ -0,0 +1,138 @@
// Based on implementation by @platsch
#ifndef slic3r_Slicing_hpp_
#define slic3r_Slicing_hpp_
#include <set>
#include <vector>
#include "libslic3r.h"
namespace Slic3r
{
class PrintConfig;
class PrintObjectConfig;
class ModelVolume;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
// Parameters to guide object slicing and support generation.
// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support).
struct SlicingParameters
{
SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); }
static SlicingParameters create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::set<size_t> &object_extruders);
// Has any raft layers?
bool has_raft() const { return raft_layers() > 0; }
size_t raft_layers() const { return base_raft_layers + interface_raft_layers; }
// Is the 1st object layer height fixed, or could it be varied?
bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; }
// Height of the object to be printed. This value does not contain the raft height.
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
// Number of raft layers.
size_t base_raft_layers;
// Number of interface layers including the contact layer.
size_t interface_raft_layers;
// Layer heights of the raft (base, interface and a contact layer).
coordf_t base_raft_layer_height;
coordf_t interface_raft_layer_height;
coordf_t contact_raft_layer_height;
bool contact_raft_layer_height_bridging;
// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
// or by the variable layer thickness table.
coordf_t layer_height;
// First layer height of the print, this may be used for the first layer of the raft
// or for the first layer of the print.
coordf_t first_print_layer_height;
// Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
// or a bridging flow thickness if printed over a non-soluble raft,
// or a normal layer height if printed over a soluble raft.
coordf_t first_object_layer_height;
// If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
bool first_object_layer_bridging;
// Soluble interface? (PLA soluble in water, HIPS soluble in lemonen)
// otherwise the interface must be broken off.
bool soluble_interface;
// Gap when placing object over raft.
coordf_t gap_raft_object;
// Gap when placing support over object.
coordf_t gap_object_support;
// Gap when placing object over support.
coordf_t gap_support_object;
// Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
// or by an interactive layer height editor.
coordf_t min_layer_height;
coordf_t max_layer_height;
// Bottom and top of the printed object.
// If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
// Otherwise object_print_z_min is equal to the raft height.
coordf_t raft_base_top_z;
coordf_t raft_interface_top_z;
coordf_t raft_contact_top_z;
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
coordf_t object_print_z_min;
coordf_t object_print_z_max;
};
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
extern std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges);
extern std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges,
const ModelVolumePtrs &volumes);
enum LayerHeightEditActionType {
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
LAYER_HEIGHT_EDIT_ACTION_SMOOTH = 3
};
extern void adjust_layer_height_profile(
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
coordf_t layer_thickness_delta,
coordf_t band_width,
LayerHeightEditActionType action);
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
// The object layers are based at z=0, ignoring the raft layers.
extern std::vector<coordf_t> generate_object_layers(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layer_height_profile);
// Produce a 1D texture packed into a 2D texture describing in the RGBA format
// the planned object layers.
// Returns number of cells used by the texture of the 0th LOD level.
extern int generate_layer_height_texture(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layers,
void *data, int rows, int cols, bool level_of_detail_2nd_level);
}; // namespace Slic3r
#endif /* slic3r_Slicing_hpp_ */

View file

@ -0,0 +1,140 @@
#include "libslic3r.h"
#include "TriangleMesh.hpp"
#include "SlicingAdaptive.hpp"
namespace Slic3r
{
void SlicingAdaptive::clear()
{
m_meshes.clear();
m_faces.clear();
m_face_normal_z.clear();
}
std::pair<float, float> face_z_span(const stl_facet *f)
{
return std::pair<float, float>(
std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z),
std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z));
}
void SlicingAdaptive::prepare()
{
// 1) Collect faces of all meshes.
int nfaces_total = 0;
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
m_faces.reserve(nfaces_total);
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i)
m_faces.push_back((*it_mesh)->stl.facet_start + i);
// 2) Sort faces lexicographically by their Z span.
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
std::pair<float, float> span1 = face_z_span(f1);
std::pair<float, float> span2 = face_z_span(f2);
return span1 < span2;
});
// 3) Generate Z components of the facet normals.
m_face_normal_z.assign(m_faces.size(), 0.f);
for (size_t iface = 0; iface < m_faces.size(); ++ iface)
m_face_normal_z[iface] = m_faces[iface]->normal.z;
}
float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet)
{
float height = m_slicing_params.max_layer_height;
bool first_hit = false;
// find all facets intersecting the slice-layer
int ordered_id = current_facet;
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
// facet's minimum is higher than slice_z -> end loop
if (zspan.first >= z)
break;
// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
if (zspan.second > z) {
// first event?
if (! first_hit) {
first_hit = true;
current_facet = ordered_id;
}
// skip touching facets which could otherwise cause small cusp values
if (zspan.second <= z + EPSILON)
continue;
// compute cusp-height for this facet and store minimum of all heights
float normal_z = m_face_normal_z[ordered_id];
height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z));
}
}
// lower height limit due to printer capabilities
height = std::max(height, float(m_slicing_params.min_layer_height));
// check for sloped facets inside the determined layer and correct height if necessary
if (height > m_slicing_params.min_layer_height) {
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
// facet's minimum is higher than slice_z + height -> end loop
if (zspan.first >= z + height)
break;
// skip touching facets which could otherwise cause small cusp values
if (zspan.second <= z + EPSILON)
continue;
// Compute cusp-height for this facet and check against height.
float normal_z = m_face_normal_z[ordered_id];
float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z);
float z_diff = zspan.first - z;
// handle horizontal facets
if (m_face_normal_z[ordered_id] > 0.999) {
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = z_diff;
// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
} else if (cusp > z_diff) {
if (cusp < height) {
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = cusp;
// Slic3r::debugf "to %f due to new cusp height\n", $height;
}
} else {
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = z_diff;
// Slic3r::debugf "to z-diff: %f\n", $height;
}
}
// lower height limit due to printer capabilities again
height = std::max(height, float(m_slicing_params.min_layer_height));
}
// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;
return height;
}
// Returns the distance to the next horizontal facet in Z-dir
// to consider horizontal object features in slice thickness
float SlicingAdaptive::horizontal_facet_distance(float z)
{
for (size_t i = 0; i < m_faces.size(); ++ i) {
std::pair<float, float> zspan = face_z_span(m_faces[i]);
// facet's minimum is higher than max forward distance -> end loop
if (zspan.first > z + m_slicing_params.max_layer_height)
break;
// min_z == max_z -> horizontal facet
if (zspan.first > z && zspan.first == zspan.second)
return zspan.first - z;
}
// objects maximum?
return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?
std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) :
m_slicing_params.max_layer_height;
}
}; // namespace Slic3r

View file

@ -0,0 +1,36 @@
// Based on implementation by @platsch
#ifndef slic3r_SlicingAdaptive_hpp_
#define slic3r_SlicingAdaptive_hpp_
#include "Slicing.hpp"
#include "admesh/stl.h"
namespace Slic3r
{
class TriangleMesh;
class SlicingAdaptive
{
public:
void clear();
void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); }
void prepare();
float cusp_height(float z, float cusp_value, int &current_facet);
float horizontal_facet_distance(float z);
protected:
SlicingParameters m_slicing_params;
std::vector<const TriangleMesh*> m_meshes;
// Collected faces of all meshes, sorted by raising Z of the bottom most face.
std::vector<const stl_facet*> m_faces;
// Z component of face normals, normalized.
std::vector<float> m_face_normal_z;
};
}; // namespace Slic3r
#endif /* slic3r_SlicingAdaptive_hpp_ */

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
@ -20,19 +21,33 @@ class PrintObjectConfig;
class PrintObjectSupportMaterial
{
public:
// Support layer type to be used by MyLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
sltUnknown = 0,
sltRaft,
stlFirstLayer,
// Ratft base layer, to be printed with the support material.
sltRaftBase,
// Raft interface layer, to be printed with the support interface material.
sltRaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
sltBottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an sltBottomContact layer.
sltBottomInterface,
// Sparse base support layer, to be printed with a support material.
sltBase,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with sltTopContact layer.
sltTopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
sltTopContact,
// Some undecided type yet. It will turn into stlBase first, then it may turn into stlBottomInterface or stlTopInterface.
stlIntermediate,
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
sltIntermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
class MyLayer
{
public:
@ -57,6 +72,7 @@ public:
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const MyLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
@ -72,12 +88,12 @@ public:
}
SupporLayerType layer_type;
// Z used for printing in unscaled coordinates
// Z used for printing, in unscaled coordinates.
coordf_t print_z;
// Bottom height of this layer. For soluble layers, bottom_z + height = print_z,
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z;
// layer height in unscaled coordinates
// Layer height in unscaled coordinates.
coordf_t height;
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
@ -91,63 +107,31 @@ public:
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only: Overhangs are stored here.
// MyLayer owns the aux_polygons, they are freed by the destructor.
Polygons *aux_polygons;
};
struct LayerExtreme
{
LayerExtreme(MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {}
MyLayer *layer;
// top or bottom extreme
bool is_top;
coordf_t z() const { return is_top ? layer->print_z : layer->print_z - layer->height; }
bool operator<(const LayerExtreme &other) const { return z() < other.z(); }
};
/*
struct LayerPrintZ_Hash {
size_t operator()(const MyLayer &layer) const {
return std::hash<double>()(layer.print_z)^std::hash<double>()(layer.height)^size_t(layer.bridging);
}
};
*/
typedef std::vector<MyLayer*> MyLayersPtr;
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
typedef std::deque<MyLayer> MyLayerStorage;
typedef std::vector<MyLayer*> MyLayersPtr;
public:
PrintObjectSupportMaterial(const PrintObject *object);
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Height of the 1st layer is user configured as it is important for the print
// to stick to he print bed.
coordf_t first_layer_height() const { return m_object_config->first_layer_height.value; }
// Is raft enabled?
bool has_raft() const { return m_has_raft; }
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->support_material.value; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; }
// How many raft layers are there below the 1st object layer?
// The 1st object layer_id will be offsetted by this number.
size_t num_raft_layers() const { return m_object_config->raft_layers.value; }
// num_raft_layers() == num_raft_base_layers() + num_raft_interface_layers() + num_raft_contact_layers().
size_t num_raft_base_layers() const { return m_num_base_raft_layers; }
size_t num_raft_interface_layers() const { return m_num_interface_raft_layers; }
size_t num_raft_contact_layers() const { return m_num_contact_raft_layers; }
coordf_t raft_height() const { return m_raft_height; }
coordf_t raft_base_height() const { return m_raft_base_height; }
coordf_t raft_interface_height() const { return m_raft_interface_height; }
coordf_t raft_contact_height() const { return m_raft_contact_height; }
bool raft_bridging() const { return m_raft_contact_layer_bridging; }
// 1st layer of the object will be printed depeding on the raft settings.
coordf_t first_object_layer_print_z() const { return m_object_1st_layer_print_z; }
coordf_t first_object_layer_height() const { return m_object_1st_layer_height; }
coordf_t first_object_layer_gap() const { return m_object_1st_layer_gap; }
bool first_object_layer_bridging() const { return m_object_1st_layer_bridging; }
bool synchronize_layers() const { return m_object_config->support_material_synchronize_layers.value; }
bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; }
// Generate support material for the object.
// New support layers will be added to the object,
@ -163,7 +147,9 @@ private:
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const;
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
@ -176,17 +162,23 @@ private:
MyLayerStorage &layer_storage,
const coordf_t max_object_layer_height) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers) const;
MyLayersPtr &intermediate_layers,
std::vector<Polygons> &layer_support_areas) const;
Polygons generate_raft_base(
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
MyLayersPtr &intermediate_layers) const;
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Turn some of the base layers into interface layers.
MyLayersPtr generate_interface_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
@ -194,6 +186,15 @@ private:
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
MyLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
@ -202,59 +203,28 @@ private:
// Produce the actual G-code.
void generate_toolpaths(
const PrintObject &object,
const Polygons &raft,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
const MyLayersPtr &intermediate_layers,
const MyLayersPtr &interface_layers) const;
// Following objects are not owned by SupportMaterial class.
const PrintObject *m_object;
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
Flow m_first_layer_flow;
Flow m_support_material_flow;
Flow m_support_material_interface_flow;
bool m_soluble_interface;
Flow m_support_material_raft_base_flow;
Flow m_support_material_raft_interface_flow;
Flow m_support_material_raft_contact_flow;
bool m_has_raft;
size_t m_num_base_raft_layers;
size_t m_num_interface_raft_layers;
size_t m_num_contact_raft_layers;
// If set, the raft contact layer is laid with round strings, which are easily detachable
// from both the below and above layes.
// Otherwise a normal flow is used and the strings are squashed against the layer below,
// creating a firm bond with the layer below and making the interface top surface flat.
coordf_t m_raft_height;
coordf_t m_raft_base_height;
coordf_t m_raft_interface_height;
coordf_t m_raft_contact_height;
bool m_raft_contact_layer_bridging;
coordf_t m_object_1st_layer_print_z;
coordf_t m_object_1st_layer_height;
coordf_t m_object_1st_layer_gap;
bool m_object_1st_layer_bridging;
coordf_t m_object_layer_height_max;
coordf_t m_support_layer_height_min;
coordf_t m_support_layer_height_max;
coordf_t m_support_interface_layer_height_max;
coordf_t m_gap_extra_above;
coordf_t m_gap_extra_below;
coordf_t m_gap_xy;
// If enabled, the support layers will be synchronized with object layers.
// This does not prevent the support layers to be combined.
bool m_synchronize_support_layers_with_object;
// If disabled and m_synchronize_support_layers_with_object,
// the support layers will be synchronized with the object layers exactly, no layer will be combined.
bool m_combine_support_layers;
coordf_t m_gap_xy;
};
} // namespace Slic3r

View file

@ -57,6 +57,7 @@ public:
operator Polygons() const;
double area() const;
bool empty() const { return expolygon.empty(); }
void clear() { expolygon.clear(); }
bool is_solid() const;
bool is_external() const;
bool is_internal() const;

View file

@ -8,22 +8,12 @@ namespace Slic3r {
SurfaceCollection::operator Polygons() const
{
Polygons polygons;
for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
Polygons surface_p = surface->expolygon;
polygons.insert(polygons.end(), surface_p.begin(), surface_p.end());
}
return polygons;
return to_polygons(surfaces);
}
SurfaceCollection::operator ExPolygons() const
{
ExPolygons expp;
expp.reserve(this->surfaces.size());
for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
expp.push_back(surface->expolygon);
}
return expp;
return to_expolygons(surfaces);
}
void
@ -196,19 +186,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
surfaces.erase(surfaces.begin() + j, surfaces.end());
}
void
SurfaceCollection::append(const SurfaceCollection &coll)
{
this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end());
}
void
SurfaceCollection::append(const SurfaceType surfaceType, const Slic3r::ExPolygons &expoly)
{
for (Slic3r::ExPolygons::const_iterator it = expoly.begin(); it != expoly.end(); ++ it)
this->surfaces.push_back(Slic3r::Surface(surfaceType, *it));
}
void SurfaceCollection::export_to_svg(const char *path, bool show_labels)
{
BoundingBox bbox;

View file

@ -28,8 +28,27 @@ class SurfaceCollection
void remove_type(const SurfaceType type);
void remove_types(const SurfaceType *types, int ntypes);
void filter_by_type(SurfaceType type, Polygons* polygons);
void append(const SurfaceCollection &coll);
void append(const SurfaceType surfaceType, const ExPolygons &expoly);
void clear() { surfaces.clear(); }
bool empty() const { return surfaces.empty(); }
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); }
void set(const ExPolygons &src, const Surface &surfaceTempl) { clear(); this->append(src, surfaceTempl); }
void set(const Surfaces &src) { clear(); this->append(src); }
void set(ExPolygons &&src, SurfaceType surfaceType) { clear(); this->append(std::move(src), surfaceType); }
void set(ExPolygons &&src, const Surface &surfaceTempl) { clear(); this->append(std::move(src), surfaceTempl); }
void set(Surfaces &&src) { clear(); this->append(std::move(src)); }
void append(const SurfaceCollection &coll) { this->append(coll.surfaces); }
void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); }
void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); }
void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); }
void append(const Surfaces &src) { surfaces_append(this->surfaces, src); }
void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); }
void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); }
void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); }
// For debugging purposes:
void export_to_svg(const char *path, bool show_labels);

View file

@ -2,8 +2,8 @@
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include <cmath>
#include <queue>
#include <deque>
#include <queue>
#include <set>
#include <vector>
#include <map>
@ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor)
void TriangleMesh::translate(float x, float y, float z)
{
if (x == 0.f && y == 0.f && z == 0.f)
return;
stl_translate_relative(&(this->stl), x, y, z);
stl_invalidate_shared_vertices(&this->stl);
}
void TriangleMesh::rotate(float angle, const Axis &axis)
{
if (angle == 0.f)
return;
// admesh uses degrees
angle = Slic3r::Geometry::rad2deg(angle);
@ -265,6 +270,8 @@ void TriangleMesh::align_to_origin()
void TriangleMesh::rotate(double angle, Point* center)
{
if (angle == 0.)
return;
this->translate(-center->x, -center->y, 0);
stl_rotate_z(&(this->stl), (float)angle);
this->translate(+center->x, +center->y, 0);
@ -363,10 +370,7 @@ TriangleMesh::horizontal_projection() const
}
// the offset factor was tuned using groovemount.stl
offset(pp, &pp, 0.01 / SCALING_FACTOR);
ExPolygons retval;
union_(pp, &retval, true);
return retval;
return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true);
}
Polygon
@ -403,7 +407,7 @@ TriangleMesh::require_shared_vertices()
}
void
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers)
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
{
/*
This method gets called with a list of unscaled Z coordinates and outputs
@ -427,58 +431,66 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
At the end, we free the tables generated by analyze() as we don't
need them anymore.
FUTURE: parallelize slice_facet() and make_loops()
NOTE: this method accepts a vector of floats because the mesh coordinate
type is float.
*/
std::vector<IntersectionLines> lines(z.size());
for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) {
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// find facet extents
float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z));
float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z));
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z,
facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z,
facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z);
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif
// find layer extents
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
#endif
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin();
this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]);
}
{
boost::mutex lines_mutex;
parallelize<int>(
0,
this->mesh->stl.stats.number_of_facets-1,
boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z)
);
}
// v_scaled_shared could be freed here
// build loops
layers->resize(z.size());
for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) {
size_t layer_idx = it - lines.begin();
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer " PRINTF_ZU ":\n", layer_idx);
#endif
this->make_loops(*it, &(*layers)[layer_idx]);
parallelize<size_t>(
0,
lines.size()-1,
boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers)
);
}
void
TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
const std::vector<float> &z) const
{
const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
// find facet extents
const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
#ifdef SLIC3R_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif
// find layer extents
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
#ifdef SLIC3R_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
#endif
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin();
this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex);
}
}
void
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers)
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p);
@ -495,7 +507,9 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
}
void
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
boost::mutex* lines_mutex) const
{
std::vector<IntersectionPoint> points;
std::vector< std::vector<IntersectionPoint>::size_type > points_on_layer;
@ -547,7 +561,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
line.b.y = b->y;
line.a_id = a_id;
line.b_id = b_id;
lines->push_back(line);
if (lines_mutex != NULL) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
lines->push_back(line);
} else {
lines->push_back(line);
}
found_horizontal_edge = true;
@ -600,13 +619,24 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
line.b_id = points[0].point_id;
line.edge_a_id = points[1].edge_id;
line.edge_b_id = points[0].edge_id;
lines->push_back(line);
if (lines_mutex != NULL) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
lines->push_back(line);
} else {
lines->push_back(line);
}
return;
}
}
void
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
TriangleMeshSlicer::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const
{
this->make_loops((*lines)[i], &(*layers)[i]);
}
void
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
{
/*
SVG svg("lines.svg");
@ -707,6 +737,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) {
p.points.push_back((*lineptr)->a);
}
loops->push_back(p);
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
@ -746,7 +777,7 @@ class _area_comp {
};
void
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices)
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
{
Polygons loops;
this->make_loops(lines, &loops);
@ -780,7 +811,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines,
}
void
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
{
/*
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
@ -818,17 +849,15 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
of the loops, since the Orientation() function provided by Clipper
would do the same, thus repeating the calculation */
Polygons::const_iterator loop = loops.begin() + *loop_idx;
if (area[*loop_idx] > +EPSILON) {
if (area[*loop_idx] > +EPSILON)
p_slices.push_back(*loop);
} else if (area[*loop_idx] < -EPSILON) {
diff(p_slices, *loop, &p_slices);
}
else if (area[*loop_idx] < -EPSILON)
p_slices = diff(p_slices, *loop);
}
// perform a safety offset to merge very close facets (TODO: find test case for this)
double safety_offset = scale_(0.0499);
ExPolygons ex_slices;
offset2(p_slices, &ex_slices, +safety_offset, -safety_offset);
ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
size_t holes_count = 0;
@ -840,11 +869,11 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
#endif
// append to the supplied collection
slices->insert(slices->end(), ex_slices.begin(), ex_slices.end());
expolygons_append(*slices, ex_slices);
}
void
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices)
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
{
Polygons pp;
this->make_loops(lines, &pp);
@ -852,7 +881,7 @@ TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPoly
}
void
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const
{
IntersectionLines upper_lines, lower_lines;
@ -1004,7 +1033,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
stl_get_size(&(upper->stl));
stl_get_size(&(lower->stl));
}
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL)

View file

@ -4,6 +4,7 @@
#include "libslic3r.h"
#include <admesh/stl.h>
#include <vector>
#include <boost/thread.hpp>
#include "BoundingBox.hpp"
#include "Line.hpp"
#include "Point.hpp"
@ -88,19 +89,23 @@ class TriangleMeshSlicer
TriangleMesh* mesh;
TriangleMeshSlicer(TriangleMesh* _mesh);
~TriangleMeshSlicer();
void slice(const std::vector<float> &z, std::vector<Polygons>* layers);
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers);
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
boost::mutex* lines_mutex = NULL) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
private:
typedef std::vector< std::vector<int> > t_facets_edges;
t_facets_edges facets_edges;
stl_vertex* v_scaled_shared;
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops);
void make_expolygons(const Polygons &loops, ExPolygons* slices);
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices);
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices);
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
void _make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const;
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
};
}

View file

@ -0,0 +1,10 @@
#ifndef slic3r_Utils_hpp_
#define slic3r_Utils_hpp_
namespace Slic3r {
extern void set_logging_level(unsigned int level);
} // namespace Slic3r
#endif // slic3r_Utils_hpp_

View file

@ -4,10 +4,14 @@
// this needs to be included early for MSVC (listing it in Build.PL is not enough)
#include <ostream>
#include <iostream>
#include <math.h>
#include <queue>
#include <sstream>
#include <cstdio>
#include <stdint.h>
#include <stdarg.h>
#include <vector>
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.31.6"
@ -40,13 +44,6 @@
typedef long coord_t;
typedef double coordf_t;
namespace Slic3r {
enum Axis { X=0, Y, Z };
}
using namespace Slic3r;
/* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
@ -91,4 +88,55 @@ inline std::string debug_out_path(const char *name, ...)
// Write slices as SVG images into out directory during the 2D processing of the slices.
// #define SLIC3R_DEBUG_SLICE_PROCESSING
namespace Slic3r {
enum Axis { X=0, Y, Z };
template <class T>
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
{
dst.insert(dst.end(), src.begin(), src.end());
}
template <class T> void
_parallelize_do(std::queue<T>* queue, boost::mutex* queue_mutex, boost::function<void(T)> func)
{
//std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
while (true) {
T i;
{
boost::lock_guard<boost::mutex> l(*queue_mutex);
if (queue->empty()) return;
i = queue->front();
queue->pop();
}
//std::cout << " Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl;
func(i);
boost::this_thread::interruption_point();
}
}
template <class T> void
parallelize(std::queue<T> queue, boost::function<void(T)> func,
int threads_count = boost::thread::hardware_concurrency())
{
if (threads_count == 0) threads_count = 2;
boost::mutex queue_mutex;
boost::thread_group workers;
for (int i = 0; i < std::min(threads_count, int(queue.size())); ++ i)
workers.add_thread(new boost::thread(&_parallelize_do<T>, &queue, &queue_mutex, func));
workers.join_all();
}
template <class T> void
parallelize(T start, T end, boost::function<void(T)> func,
int threads_count = boost::thread::hardware_concurrency())
{
std::queue<T> queue;
for (T i = start; i <= end; ++i) queue.push(i);
parallelize(queue, func, threads_count);
}
} // namespace Slic3r
#endif

View file

@ -1,3 +1,30 @@
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal;
void set_logging_level(unsigned int level)
{
switch (level) {
case 0: logSeverity = boost::log::trivial::fatal; break;
case 1: logSeverity = boost::log::trivial::error; break;
case 2: logSeverity = boost::log::trivial::warning; break;
case 3: logSeverity = boost::log::trivial::info; break;
case 4: logSeverity = boost::log::trivial::debug; break;
default: logSeverity = boost::log::trivial::trace; break;
}
boost::log::core::get()->set_filter
(
boost::log::trivial::severity >= logSeverity
);
}
} // namespace Slic3r
#ifdef SLIC3R_HAS_BROKEN_CROAK
// Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism

View file

@ -532,8 +532,7 @@ SV*
polynode2perl(const ClipperLib::PolyNode& node)
{
HV* hv = newHV();
Slic3r::Polygon p;
ClipperPath_to_Slic3rMultiPoint(node.Contour, &p);
Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour);
if (node.IsHole()) {
(void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) );
} else {

View file

@ -31,6 +31,7 @@
#include <ostream>
#include <iostream>
#include <sstream>
#include <libslic3r.h>
#ifdef SLIC3RXS
extern "C" {
@ -42,15 +43,18 @@ extern "C" {
#undef do_close
#undef bind
#undef seed
#undef push
#undef pop
#ifdef _MSC_VER
// Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
#undef send
#undef connect
#undef seek
#undef send
#undef write
#endif /* _MSC_VER */
}
#endif
#include <libslic3r.h>
#include <ClipperUtils.hpp>
#include <Config.hpp>
#include <ExPolygon.hpp>
@ -163,4 +167,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node);
#endif
#endif
using namespace Slic3r;
#endif

View file

@ -5,7 +5,7 @@ use warnings;
use List::Util qw(sum);
use Slic3r::XS;
use Test::More tests => 23;
use Test::More tests => 16;
my $square = Slic3r::Polygon->new( # ccw
[200, 100],
@ -121,41 +121,6 @@ if (0) { # Clipper does not preserve polyline orientation
is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved';
}
if (0) { # Clipper does not preserve polyline orientation
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]);
is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp,
'intersection_ppl - clipping cw polygon as polyline preserves winding order';
}
{
my $square2 = $square->clone;
$square2->translate(50,50);
{
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
# Clipper does not preserve polyline orientation so we only check the middle point
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
}
}
{
my $square2 = $square->clone;
$square2->reverse;
$square2->translate(50,50);
{
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
# Clipper does not preserve polyline orientation so we only check the middle point
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
}
}
{
# Clipper bug #96 (our issue #2028)
my $subject = Slic3r::Polyline->new(
@ -168,17 +133,6 @@ if (0) { # Clipper does not preserve polyline orientation
is scalar(@$result), 1, 'intersection_pl - result is not empty';
}
{
my $subject = Slic3r::Polygon->new(
[44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000]
);
my $clip = [
Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]),
];
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip);
is scalar(@$result), 1, 'intersection_ppl - result is not empty';
}
{
# Clipper bug #122
my $subject = [

View file

@ -30,7 +30,8 @@
long y_min() %code{% RETVAL = THIS->min.y; %};
long y_max() %code{% RETVAL = THIS->max.y; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
%{
BoundingBox*
@ -69,7 +70,8 @@ new_from_points(CLASS, points)
void set_y_min(double val) %code{% THIS->min.y = val; %};
void set_y_max(double val) %code{% THIS->max.y = val; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
%{
BoundingBoxf*
@ -106,4 +108,5 @@ new_from_points(CLASS, points)
double z_min() %code{% RETVAL = THIS->min.z; %};
double z_max() %code{% RETVAL = THIS->max.z; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
};

View file

@ -16,58 +16,53 @@ _constant()
JT_MITER = jtMiter
JT_ROUND = jtRound
JT_SQUARE = jtSquare
CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE
CODE:
RETVAL = ix;
OUTPUT: RETVAL
Polygons
offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons
const float delta
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
RETVAL = offset(polygons, delta, joinType, miterLimit);
OUTPUT:
RETVAL
ExPolygons
offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons
const float delta
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
RETVAL = offset_ex(polygons, delta, joinType, miterLimit);
OUTPUT:
RETVAL
Polygons
offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons
const float delta1
const float delta2
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit);
OUTPUT:
RETVAL
ExPolygons
offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons
const float delta1
const float delta2
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit);
OUTPUT:
RETVAL
@ -77,7 +72,7 @@ diff(subject, clip, safety_offset = false)
Polygons clip
bool safety_offset
CODE:
diff(subject, clip, &RETVAL, safety_offset);
RETVAL = diff(subject, clip, safety_offset);
OUTPUT:
RETVAL
@ -87,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false)
Polygons clip
bool safety_offset
CODE:
diff(subject, clip, &RETVAL, safety_offset);
RETVAL = diff_ex(subject, clip, safety_offset);
OUTPUT:
RETVAL
@ -96,16 +91,7 @@ diff_pl(subject, clip)
Polylines subject
Polygons clip
CODE:
diff(subject, clip, &RETVAL);
OUTPUT:
RETVAL
Polylines
diff_ppl(subject, clip)
Polygons subject
Polygons clip
CODE:
diff(subject, clip, &RETVAL);
RETVAL = diff_pl(subject, clip);
OUTPUT:
RETVAL
@ -115,7 +101,7 @@ intersection(subject, clip, safety_offset = false)
Polygons clip
bool safety_offset
CODE:
intersection(subject, clip, &RETVAL, safety_offset);
RETVAL = intersection(subject, clip, safety_offset);
OUTPUT:
RETVAL
@ -125,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false)
Polygons clip
bool safety_offset
CODE:
intersection(subject, clip, &RETVAL, safety_offset);
RETVAL = intersection_ex(subject, clip, safety_offset);
OUTPUT:
RETVAL
@ -134,26 +120,7 @@ intersection_pl(subject, clip)
Polylines subject
Polygons clip
CODE:
intersection(subject, clip, &RETVAL);
OUTPUT:
RETVAL
Polylines
intersection_ppl(subject, clip)
Polygons subject
Polygons clip
CODE:
intersection(subject, clip, &RETVAL);
OUTPUT:
RETVAL
ExPolygons
xor_ex(subject, clip, safety_offset = false)
Polygons subject
Polygons clip
bool safety_offset
CODE:
xor_(subject, clip, &RETVAL, safety_offset);
RETVAL = intersection_pl(subject, clip);
OUTPUT:
RETVAL
@ -162,7 +129,7 @@ union(subject, safety_offset = false)
Polygons subject
bool safety_offset
CODE:
union_(subject, &RETVAL, safety_offset);
RETVAL = union_(subject, safety_offset);
OUTPUT:
RETVAL
@ -171,20 +138,7 @@ union_ex(subject, safety_offset = false)
Polygons subject
bool safety_offset
CODE:
union_(subject, &RETVAL, safety_offset);
OUTPUT:
RETVAL
SV*
union_pt(subject, safety_offset = false)
Polygons subject
bool safety_offset
CODE:
// perform operation
ClipperLib::PolyTree polytree;
union_pt(subject, &polytree, safety_offset);
RETVAL = polynode_children_2_perl(polytree);
RETVAL = union_ex(subject, safety_offset);
OUTPUT:
RETVAL
@ -193,7 +147,7 @@ union_pt_chained(subject, safety_offset = false)
Polygons subject
bool safety_offset
CODE:
union_pt_chained(subject, &RETVAL, safety_offset);
RETVAL = union_pt_chained(subject, safety_offset);
OUTPUT:
RETVAL
@ -201,7 +155,7 @@ Polygons
simplify_polygons(subject)
Polygons subject
CODE:
simplify_polygons(subject, &RETVAL);
RETVAL = simplify_polygons(subject);
OUTPUT:
RETVAL

View file

@ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv)
THIS->rotate(angle, center);
Polygons
Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3)
Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3)
const float delta
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
RETVAL = offset(*THIS, delta, joinType, miterLimit);
OUTPUT:
RETVAL

View file

@ -3,6 +3,7 @@
%{
#include <xsinit.h>
#include "libslic3r/Print.hpp"
#include "libslic3r/Slicing.hpp"
#include "libslic3r/PlaceholderParser.hpp"
%}
@ -58,6 +59,8 @@ _constant()
Points copies();
t_layer_height_ranges layer_height_ranges()
%code%{ RETVAL = THIS->layer_height_ranges; %};
std::vector<double> layer_height_profile()
%code%{ RETVAL = THIS->layer_height_profile; %};
Ref<Point3> size()
%code%{ RETVAL = &THIS->size; %};
Clone<BoundingBox> bounding_box();
@ -82,6 +85,8 @@ _constant()
bool reload_model_instances();
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
void set_layer_height_profile(std::vector<double> profile)
%code%{ THIS->layer_height_profile = profile; %};
size_t total_layer_count();
size_t layer_count();
@ -106,11 +111,32 @@ _constant()
%code%{ THIS->state.set_done(step); %};
void set_step_started(PrintObjectStep step)
%code%{ THIS->state.set_started(step); %};
void _slice();
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void _make_perimeters();
void _infill();
void _generate_support_material();
void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
%code%{
THIS->update_layer_height_profile();
adjust_layer_height_profile(
THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
%};
int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true)
%code%{
THIS->update_layer_height_profile();
SlicingParameters slicing_params = THIS->slicing_parameters();
RETVAL = generate_layer_height_texture(
slicing_params,
generate_object_layers(slicing_params, THIS->layer_height_profile),
data, rows, cols, level_of_detail_2nd_level);
%};
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};

View file

@ -1,26 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/SupportMaterial.hpp"
%}
%name{Slic3r::Print::SupportMaterial2} class PrintObjectSupportMaterial {
PrintObjectSupportMaterial(PrintObject *print_object);
~PrintObjectSupportMaterial();
void generate(PrintObject *object)
%code{% THIS->generate(*object); %};
};
%package{Slic3r::Print::SupportMaterial};
%{
SV*
MARGIN()
PROTOTYPE:
CODE:
RETVAL = newSVnv(SUPPORT_MATERIAL_MARGIN);
OUTPUT: RETVAL
%}

View file

@ -83,13 +83,12 @@ Surface::polygons()
RETVAL
Surfaces
Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
const float delta
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS);
OUTPUT:
RETVAL

View file

@ -2,6 +2,7 @@
%package{Slic3r::XS};
#include <xsinit.h>
#include "Utils.hpp"
%{
@ -28,6 +29,12 @@ FORK_NAME()
RETVAL = newSVpv(SLIC3R_FORK_NAME, 0);
OUTPUT: RETVAL
void
set_logging_level(level)
unsigned int level;
CODE:
Slic3r::set_logging_level(level);
void
xspp_test_croak_hangs_on_strawberry()
CODE: